Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ink! e2e-tests #196

Merged
merged 12 commits into from
Feb 26, 2024
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "swanky-env",
"image": "ghcr.io/swankyhub/swanky-cli/swanky-base:swanky3.1.0-beta.0_v2.1.0",
"image": "ghcr.io/inkdevhub/swanky-cli/swanky-base:swanky3.1.0-beta.0_v2.1.1",

// Mount the workspace volume
"mounts": ["source=${localWorkspaceFolder},target=/workspaces,type=bind,consistency=cached"],
Expand Down
8 changes: 1 addition & 7 deletions src/commands/contract/compile.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Args, Flags } from "@oclif/core";
import path from "node:path";
import { storeArtifacts, Spinner, generateTypes } from "../../lib/index.js";
import { storeArtifacts, Spinner } from "../../lib/index.js";
import { spawn } from "node:child_process";
import { pathExists } from "fs-extra/esm";
import { SwankyCommand } from "../../lib/swankyCommand.js";
Expand Down Expand Up @@ -108,12 +108,6 @@ export class CompileContract extends SwankyCommand<typeof CompileContract> {
await spinner.runCommand(async () => {
return storeArtifacts(artifactsPath, contractInfo.name, contractInfo.moduleName);
}, "Moving artifacts");

await spinner.runCommand(
async () => await generateTypes(contractInfo.name),
`Generating ${contractName} contract ts types`,
`${contractName} contract's TS types Generated successfully`
);
pmikolajczyk41 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
16 changes: 14 additions & 2 deletions src/commands/contract/new.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Args, Flags } from "@oclif/core";
import path from "node:path";
import { ensureDir, pathExists, writeJSON } from "fs-extra/esm";
import { ensureDir, pathExists, pathExistsSync, writeJSON } from "fs-extra/esm";
import {
checkCliDependencies,
copyContractTemplateFiles,
processTemplates,
getTemplates,
prepareTestFiles,
} from "../../lib/index.js";
import { email, name, pickTemplate } from "../../lib/prompts.js";
import { paramCase, pascalCase, snakeCase } from "change-case";
Expand Down Expand Up @@ -78,6 +79,18 @@ export class NewContract extends SwankyCommand<typeof NewContract> {
"Copying contract template files"
);

if (contractTemplate === "psp22") {
const e2eTestHelpersPath = path.resolve(projectPath, "tests", "test_helpers");
if (!pathExistsSync(e2eTestHelpersPath)) {
await this.spinner.runCommand(
() => prepareTestFiles("e2e", path.resolve(templates.templatesPath), projectPath),
"Copying e2e test helpers"
);
} else {
console.log("e2e test helpers already exist. No files were copied.");
}
}

await this.spinner.runCommand(
() =>
processTemplates(projectPath, {
Expand All @@ -93,7 +106,6 @@ export class NewContract extends SwankyCommand<typeof NewContract> {
);

await ensureDir(path.resolve(projectPath, "artifacts", args.contractName));
await ensureDir(path.resolve(projectPath, "tests", args.contractName));

await this.spinner.runCommand(async () => {
this.swankyConfig.contracts[args.contractName] = {
Expand Down
165 changes: 118 additions & 47 deletions src/commands/contract/test.ts
ipapandinas marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { Flags, Args } from "@oclif/core";
import path from "node:path";
import { globby } from "globby";
import Mocha from "mocha";
import { emptyDir } from "fs-extra/esm";
import { emptyDir, pathExistsSync } from "fs-extra/esm";
import shell from "shelljs";
import { Contract } from "../../lib/contract.js";
import { SwankyCommand } from "../../lib/swankyCommand.js";
import { ConfigError, FileError, InputError, TestError } from "../../lib/errors.js";
import { ConfigError, FileError, InputError, ProcessError, TestError } from "../../lib/errors.js";
import { spawn } from "node:child_process";
import { Spinner } from "../../lib/index.js";

declare global {
var contractTypesPath: string; // eslint-disable-line no-var
Expand All @@ -20,7 +22,11 @@ export class TestContract extends SwankyCommand<typeof TestContract> {
all: Flags.boolean({
default: false,
char: "a",
description: "Set all to true to compile all contracts",
description: "Run tests for all contracts",
}),
mocha: Flags.boolean({
default: false,
description: "Run tests with mocha",
}),
};

Expand All @@ -43,7 +49,7 @@ export class TestContract extends SwankyCommand<typeof TestContract> {
? Object.keys(this.swankyConfig.contracts)
: [args.contractName];

const testDir = path.resolve("tests");
const spinner = new Spinner();

for (const contractName of contractNames) {
const contractRecord = this.swankyConfig.contracts[contractName];
Expand All @@ -61,54 +67,119 @@ export class TestContract extends SwankyCommand<typeof TestContract> {
);
}

const artifactsCheck = await contract.artifactsExist();
console.log(`Testing contract: ${contractName}`);

if (!artifactsCheck.result) {
throw new FileError(
`No artifact file found at path: ${artifactsCheck.missingPaths.toString()}`
if (!flags.mocha) {
await spinner.runCommand(
async () => {
return new Promise<string>((resolve, reject) => {
const compileArgs = [
"test",
"--features",
"e2e-tests",
"--manifest-path",
`contracts/${contractName}/Cargo.toml`,
"--release"
];

const compile = spawn("cargo", compileArgs);
this.logger.info(`Running e2e-tests command: [${JSON.stringify(compile.spawnargs)}]`);
let outputBuffer = "";
let errorBuffer = "";

compile.stdout.on("data", (data) => {
outputBuffer += data.toString();
spinner.ora.clear();
});
compile.stdout.pipe(process.stdout);

compile.stderr.on("data", (data) => {
errorBuffer += data;
});

compile.on("exit", (code) => {
if (code === 0) {
const regex = /test result: (.*)/;
const match = outputBuffer.match(regex);
if (match) {
this.logger.info(`Contract ${contractName} e2e-testing done.`);
resolve(match[1]);
}
} else {
reject(new ProcessError(errorBuffer));
}
});
});
},
`Testing ${contractName} contract`,
`${contractName} testing finished successfully`
);
}
} else {

console.log(`Testing contract: ${contractName}`);
const testDir = path.resolve("tests");

const reportDir = path.resolve(testDir, contract.name, "testReports");

await emptyDir(reportDir);

const mocha = new Mocha({
timeout: 200000,
reporter: "mochawesome",
reporterOptions: {
reportDir,
charts: true,
reportTitle: `${contractName} test report`,
quiet: true,
json: false,
},
});

const tests = await globby(`${path.resolve(testDir, contractName)}/*.test.ts`);

tests.forEach((test) => {
mocha.addFile(test);
});

global.contractTypesPath = path.resolve(testDir, contractName, "typedContract");

shell.cd(`${testDir}/${contractName}`);
try {
await new Promise<void>((resolve, reject) => {
mocha.run((failures) => {
if (failures) {
reject(`At least one of the tests failed. Check report for details: ${reportDir}`);
} else {
this.log(`All tests passing. Check the report for details: ${reportDir}`);
resolve();
}
});
if (!pathExistsSync(testDir)) {
throw new FileError(`Tests folder does not exist: ${testDir}`);
}

const artifactsCheck = await contract.artifactsExist();

if (!artifactsCheck.result) {
throw new FileError(
`No artifact file found at path: ${artifactsCheck.missingPaths.toString()}`
);
}

const artifactPath = path.resolve("typedContracts", `${contractName}`);
const typedContractCheck = await contract.typedContractExists(contractName);

this.log(`artifactPath: ${artifactPath}`);

if (!typedContractCheck.result) {
throw new FileError(
`No typed contract found at path: ${typedContractCheck.missingPaths.toString()}`
);
}

const reportDir = path.resolve(testDir, contract.name, "testReports");

await emptyDir(reportDir);

const mocha = new Mocha({
timeout: 200000,
reporter: "mochawesome",
reporterOptions: {
reportDir,
charts: true,
reportTitle: `${contractName} test report`,
quiet: true,
json: false,
},
});
} catch (cause) {
throw new TestError("Error in test", { cause });

const tests = await globby(`${path.resolve(testDir, contractName)}/*.test.ts`);

tests.forEach((test) => {
mocha.addFile(test);
});

global.contractTypesPath = path.resolve(testDir, contractName, "typedContract");

shell.cd(`${testDir}/${contractName}`);
try {
await new Promise<void>((resolve, reject) => {
mocha.run((failures) => {
if (failures) {
reject(`At least one of the tests failed. Check report for details: ${reportDir}`);
} else {
this.log(`All tests passing. Check the report for details: ${reportDir}`);
resolve();
}
});
});
} catch (cause) {
throw new TestError("Error in test", { cause });
}
}
}
}
Expand Down
Loading
Loading