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
}
}
}
115 changes: 0 additions & 115 deletions src/commands/contract/test.ts
ipapandinas marked this conversation as resolved.
Show resolved Hide resolved

This file was deleted.

ipapandinas marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Args } from "@oclif/core";
import { generateTypes } from "../../lib/index.js";
import { Contract } from "../../lib/contract.js";
import { SwankyCommand } from "../../lib/swankyCommand.js";
import { ConfigError, FileError } from "../../lib/errors.js";
import { generateTypes } from "../../../lib/index.js";
import { Contract } from "../../../lib/contract.js";
import { SwankyCommand } from "../../../lib/swankyCommand.js";
import { ConfigError, FileError } from "../../../lib/errors.js";

export class TypegenCommand extends SwankyCommand<typeof TypegenCommand> {
export class ContractTestTypegen extends SwankyCommand<typeof ContractTestTypegen> {
static description = "Generate types from compiled contract metadata";

static args = {
Expand All @@ -16,7 +16,7 @@ export class TypegenCommand extends SwankyCommand<typeof TypegenCommand> {
};

async run(): Promise<void> {
const { args } = await this.parse(TypegenCommand);
const { args } = await this.parse(ContractTestTypegen);

const contractRecord = this.swankyConfig.contracts[args.contractName];
if (!contractRecord) {
Expand Down
182 changes: 182 additions & 0 deletions src/commands/contract/test/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import "ts-mocha";
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 shell from "shelljs";
import { Contract } from "../../../lib/contract.js";
import { SwankyCommand } from "../../../lib/swankyCommand.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
}

export class TestContract extends SwankyCommand<typeof TestContract> {
static description = "Run tests for a given contact";

static flags = {
all: Flags.boolean({
default: false,
char: "a",
description: "Set all to true to compile all contracts",
}),
mocha: Flags.boolean({
ipapandinas marked this conversation as resolved.
Show resolved Hide resolved
default: false,
description: "Run tests with mocha",
}),
};

static args = {
contractName: Args.string({
name: "contractName",
default: "",
description: "Name of the contract to test",
}),
};

async run(): Promise<void> {
const { args, flags } = await this.parse(TestContract);

if (args.contractName === undefined && !flags.all) {
throw new InputError("No contracts were selected to compile");
}

const contractNames = flags.all
? Object.keys(this.swankyConfig.contracts)
: [args.contractName];

const testDir = path.resolve("tests");
ipapandinas marked this conversation as resolved.
Show resolved Hide resolved

const spinner = new Spinner();

for (const contractName of contractNames) {
const contractRecord = this.swankyConfig.contracts[contractName];
if (!contractRecord) {
throw new ConfigError(
`Cannot find a contract named ${args.contractName} in swanky.config.json`
);
}

const contract = new Contract(contractRecord);

if (!(await contract.pathExists())) {
throw new FileError(
`Path to contract ${args.contractName} does not exist: ${contract.contractPath}`
);
}

console.log(`Testing contract: ${contractName}`);

if (!flags.mocha) {
await spinner.runCommand(
async () => {
return new Promise<string>((resolve, reject) => {
const compileArgs = [
"test",
ipapandinas marked this conversation as resolved.
Show resolved Hide resolved
"--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 {

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,
},
});

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 });
}
}
}
}
}
Loading
Loading