Skip to content

Commit

Permalink
feat: Add ink! e2e-tests (#127)
Browse files Browse the repository at this point in the history
Co-authored-by: Igor Papandinas <igor.papandinas@posteo.net>
  • Loading branch information
prxgr4mm3r and ipapandinas committed Mar 25, 2024
1 parent b3840b1 commit 17f83f0
Show file tree
Hide file tree
Showing 16 changed files with 579 additions and 130 deletions.
8 changes: 1 addition & 7 deletions src/commands/contract/compile.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Args, Flags } from "@oclif/core";
import path from "node:path";
import { ensureCargoContractVersionCompatibility, extractCargoContractVersion, generateTypes, Spinner, storeArtifacts } from "../../lib/index.js";
import { spawn } from "node:child_process";
import { pathExists } from "fs-extra/esm";
import { SwankyCommand } from "../../lib/swankyCommand.js";
import { ensureCargoContractVersionCompatibility, extractCargoContractVersion, Spinner, storeArtifacts } from "../../lib/index.js";
import { ConfigError, InputError, ProcessError } from "../../lib/errors.js";

export class CompileContract extends SwankyCommand<typeof CompileContract> {
Expand Down Expand Up @@ -126,12 +126,6 @@ export class CompileContract extends SwankyCommand<typeof CompileContract> {
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`,
);

this.swankyConfig.contracts[contractName].build = {
timestamp: Date.now(),
artifactsPath,
Expand Down
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
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

0 comments on commit 17f83f0

Please sign in to comment.