diff --git a/src/asm/coverage/test/asm-coverage.spec.ts b/src/asm/coverage/test/asm-coverage.spec.ts index d53a80d5df..f8f232b046 100644 --- a/src/asm/coverage/test/asm-coverage.spec.ts +++ b/src/asm/coverage/test/asm-coverage.spec.ts @@ -5,6 +5,7 @@ import { collectAsmCoverage } from "@/asm/coverage/index"; import { generateTextReport } from "@/asm/coverage/text"; import { generateHtml } from "@/asm/coverage/html"; import { mkdirSync, writeFileSync, existsSync } from "node:fs"; +import { step } from "@/test/allure/allure"; describe("asm coverage", () => { const test = @@ -19,16 +20,23 @@ describe("asm coverage", () => { const [_, logs] = await executeInstructions(res.instructions, id); const { lines, summary } = collectAsmCoverage(cell, logs); - const report = generateTextReport(lines, summary); - expect(report).toMatchSnapshot(); - + const report = await step("Generating text report", () => { + return generateTextReport(lines, summary); + }); + await step("Report should match snapshot", () => { + expect(report).toMatchSnapshot(); + }); const outDirname = `${__dirname}/output`; if (!existsSync(outDirname)) { mkdirSync(outDirname); } - const htmlReport = generateHtml(lines); - writeFileSync(`${__dirname}/output/${name}.html`, htmlReport); + const htmlReport = await step("Generating HTML report", () => { + return generateHtml(lines); + }); + await step("Write html report", () => { + writeFileSync(`${__dirname}/output/${name}.html`, htmlReport); + }); }; it( @@ -55,9 +63,9 @@ describe("asm coverage", () => { ` DROP PUSHINT -1 // cond - + IFRET - + PUSHINT 1 PUSHINT 2 ADD @@ -89,14 +97,14 @@ describe("asm coverage", () => { "while loop with break", ` PUSHINT 10 // a = 10 - + PUSHCONT { DUP GTINT 0 } // a > 0 PUSHCONT { // if (a < 5) { break } DUP LESSINT 5 IFRETALT - + // a -= 1; DEC } diff --git a/src/asm/helpers/measure-gas.spec.ts b/src/asm/helpers/measure-gas.spec.ts index 8a08a5fb07..e0a3ffcc7d 100644 --- a/src/asm/helpers/measure-gas.spec.ts +++ b/src/asm/helpers/measure-gas.spec.ts @@ -7,6 +7,7 @@ import { CALLDICT_LONG, } from "@/asm/runtime"; import { measureGas } from "@/asm/helpers/measure-gas"; +import { parameter, step } from "@/test/allure/allure"; interface TestCase { readonly name: string; @@ -48,11 +49,11 @@ const TESTS: TestCase[] = [ ]; describe("tests", () => { - // TODO: rewrite with just `it()` - for (const { name, instructions, expectedGas } of TESTS) { - it(`Test ${name}`, async () => { - const gasUsed = await measureGas(instructions); + it.each(TESTS)("Test $name", async ({ instructions, expectedGas }) => { + const gasUsed = await measureGas(instructions); + await parameter("Gas used", gasUsed.toString()); + await step("Used gas should be same", () => { expect(gasUsed).toEqual(expectedGas); }); - } + }); }); diff --git a/src/asm/logs/test/parse.spec.ts b/src/asm/logs/test/parse.spec.ts index 8a01652264..57e59dd397 100644 --- a/src/asm/logs/test/parse.spec.ts +++ b/src/asm/logs/test/parse.spec.ts @@ -1,21 +1,24 @@ import { parse } from "@/asm/logs/parse"; +import { step } from "@/test/allure/allure"; describe("logs-parse", () => { - it("should parse NaN", () => { + it("should parse NaN", async () => { const res = parse(` - stack: [ 0 ] + stack: [ 0 ] code cell hash: C4252597808DE321E4DBEDFCF683B8D9A53BB1E5A77FDD44091B1163114468FA offset: 0 execute PUSHINT 200 gas remaining: 9999977 - stack: [ 0 200 ] + stack: [ 0 200 ] code cell hash: C4252597808DE321E4DBEDFCF683B8D9A53BB1E5A77FDD44091B1163114468FA offset: 32 execute FITS 1 gas remaining: 9999943 - stack: [ 0 NaN ] + stack: [ 0 NaN ] execute implicit RET gas remaining: 9999938 `); - expect(res).toMatchSnapshot(); + await step("Parse result should match snapshot", () => { + expect(res).toMatchSnapshot(); + }); }); }); diff --git a/src/asm/runtime/test/signatures.spec.ts b/src/asm/runtime/test/signatures.spec.ts index e9760bbe46..764d6c3a65 100644 --- a/src/asm/runtime/test/signatures.spec.ts +++ b/src/asm/runtime/test/signatures.spec.ts @@ -1,10 +1,13 @@ import { signatureOf, signatureString } from "@/asm/runtime/stack-signatures"; +import { step } from "@/test/allure/allure"; describe("instructions signatures", () => { it("should find correct signatures", async () => { { const signature = await signatureOf("ADD"); - expect(signature).toBeDefined(); + await step("Signature should be defined", () => { + expect(signature).toBeDefined(); + }); if (signature) { expect(signatureString(signature)).toEqual( "x:Int y:Int -> result:Int", @@ -13,54 +16,78 @@ describe("instructions signatures", () => { } { const signature = await signatureOf("PUSHINT_LONG"); - expect(signature).toBeDefined(); + await step("Signature should be defined", () => { + expect(signature).toBeDefined(); + }); if (signature) { - expect(signatureString(signature)).toEqual("∅ -> x:Int"); + await step("Signature should match expected", () => { + expect(signatureString(signature)).toEqual("∅ -> x:Int"); + }); } } { const signature = await signatureOf("STDICT"); - expect(signature).toBeDefined(); + await step("Signature should be defined", () => { + expect(signature).toBeDefined(); + }); if (signature) { - expect(signatureString(signature)).toEqual( - "D:Cell|Null b:Builder -> b2:Builder", - ); + await step("Signature should match expected", () => { + expect(signatureString(signature)).toEqual( + "D:Cell|Null b:Builder -> b2:Builder", + ); + }); } } { const signature = await signatureOf("TUPLE"); - expect(signature).toBeDefined(); + await step("Signature should be defined", () => { + expect(signature).toBeDefined(); + }); if (signature) { - expect(signatureString(signature)).toEqual( - "x_1...x_n -> t:Tuple", - ); + await step("Signature should match expected", () => { + expect(signatureString(signature)).toEqual( + "x_1...x_n -> t:Tuple", + ); + }); } } { const signature = await signatureOf("UNTUPLE"); - expect(signature).toBeDefined(); + await step("Signature should be defined", () => { + expect(signature).toBeDefined(); + }); if (signature) { - expect(signatureString(signature)).toEqual( - "t:Tuple -> x_1...x_n", - ); + await step("Signature should match expected", () => { + expect(signatureString(signature)).toEqual( + "t:Tuple -> x_1...x_n", + ); + }); } } { const signature = await signatureOf("STDICT"); - expect(signature).toBeDefined(); + await step("Signature should be defined", () => { + expect(signature).toBeDefined(); + }); if (signature) { - expect(signatureString(signature)).toEqual( - "D:Cell|Null b:Builder -> b2:Builder", - ); + await step("Signature should match expected", () => { + expect(signatureString(signature)).toEqual( + "D:Cell|Null b:Builder -> b2:Builder", + ); + }); } } { const signature = await signatureOf("STUXQ"); - expect(signature).toBeDefined(); + await step("Signature should be defined", () => { + expect(signature).toBeDefined(); + }); if (signature) { - expect(signatureString(signature)).toEqual( - "x:Int b:Builder l:Int -> (b2:Builder 0)|(x:Int b:Builder -1)|(x:Int b:Builder 1) status:Int", - ); + await step("Signature should match expected", () => { + expect(signatureString(signature)).toEqual( + "x:Int b:Builder l:Int -> (b2:Builder 0)|(x:Int b:Builder -1)|(x:Int b:Builder 1) status:Int", + ); + }); } } }); diff --git a/src/asm/runtime/test/tests-boc.spec.ts b/src/asm/runtime/test/tests-boc.spec.ts index 42bbfa5548..d14c4765d9 100644 --- a/src/asm/runtime/test/tests-boc.spec.ts +++ b/src/asm/runtime/test/tests-boc.spec.ts @@ -10,6 +10,7 @@ import * as path from "node:path"; import * as fs from "node:fs"; import { Cell } from "@ton/core"; import { DefaultExoticCell, hex } from "@/asm/runtime/util"; +import { step } from "@/test/allure/allure"; const readBoc = (filename: string): Cell | undefined => { const filePath = path.join(__dirname, "testdata", filename); @@ -23,12 +24,14 @@ const readHex = (filename: string): Cell => { const test = (instructions: i.Instr[], expectedBoc: Cell | undefined): (() => void) => - () => { + async () => { if (expectedBoc === undefined) { throw new Error("expectedBoc is undefined"); } const compiled = i.compileCell(instructions); - expect(compiled.toString()).toEqual(expectedBoc.toString()); + await step("Compiled cell should be expected", () => { + expect(compiled.toString()).toEqual(expectedBoc.toString()); + }); }; describe("tests", () => { diff --git a/src/asm/runtime/test/tests-run.spec.ts b/src/asm/runtime/test/tests-run.spec.ts index e6bb34413f..dd3a5a5dd8 100644 --- a/src/asm/runtime/test/tests-run.spec.ts +++ b/src/asm/runtime/test/tests-run.spec.ts @@ -43,6 +43,7 @@ import * as u from "@/asm/runtime/util"; import { execute } from "@/asm/helpers/helpers"; import { code, dictMap } from "@/asm/runtime/util"; import { print } from "@/asm/text/printer"; +import { step } from "@/test/allure/allure"; const emptyData = () => beginCell().endCell(); @@ -70,13 +71,15 @@ const test = ( const openContract = blockchain.openContract(contract); // Deploy - await openContract.send( - treasury.getSender(), - { - value: toNano("10"), - }, - new Cell(), - ); + await step("Send contract", async () => { + await openContract.send( + treasury.getSender(), + { + value: toNano("10"), + }, + new Cell(), + ); + }); const data = await openContract.getAny(methodId); compareResult(data); diff --git a/src/asm/runtime/test/tests-runvm.spec.ts b/src/asm/runtime/test/tests-runvm.spec.ts index 4e0992f9b7..49c191bfcd 100644 --- a/src/asm/runtime/test/tests-runvm.spec.ts +++ b/src/asm/runtime/test/tests-runvm.spec.ts @@ -26,6 +26,7 @@ import type { SandboxContract, TreasuryContract } from "@ton/sandbox"; import { Blockchain } from "@ton/sandbox"; import { call, measureGas2, when } from "@/asm/helpers"; import { dictMap } from "@/asm/runtime/util"; +import { step } from "@/test/allure/allure"; describe("runvm-helper", () => { it(`should correctly execute instructions inside runvm`, async () => { @@ -76,13 +77,15 @@ describe("runvm-helper", () => { const openContract = blockchain.openContract(contract); // Deploy - await openContract.send( - treasure.getSender(), - { - value: toNano("10"), - }, - new Cell(), - ); + await step("Send contract", async () => { + await openContract.send( + treasure.getSender(), + { + value: toNano("10"), + }, + new Cell(), + ); + }); }); }); diff --git a/src/asm/text/test/parser.spec.ts b/src/asm/text/test/parser.spec.ts index 53f8c7f71c..513b518aa0 100644 --- a/src/asm/text/test/parser.spec.ts +++ b/src/asm/text/test/parser.spec.ts @@ -3,47 +3,57 @@ import { print } from "@/asm/text/printer"; import { readFileSync } from "node:fs"; import { parse } from "@/asm/text/parse"; import { boc } from "@/asm/runtime/util"; +import { attachment, step } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; describe("assembly-parser", () => { - it("should parse simple assembly", () => { + it("should parse simple assembly", async () => { const code = ` PUSHINT 10 PUSHINT 5 ADD `; + await attachment("Code", code, ContentType.TEXT); const res = parse("test.asm", code); if (res.$ === "ParseFailure") { throw new Error("unexpected parser error"); } - - expect(print(res.instructions)).toMatchSnapshot(); + await step("Instructions should match snapshot", () => { + expect(print(res.instructions)).toMatchSnapshot(); + }); }); - it("should parse assembly with raw pushref", () => { + it("should parse assembly with raw pushref", async () => { const code = ` PUSHREF x{71} `; + await attachment("Code", code, ContentType.TEXT); const res = parse("test.asm", code); if (res.$ === "ParseFailure") { throw new Error("unexpected parser error"); } - expect(print(res.instructions)).toMatchSnapshot(); + await step("Instructions should match snapshot", () => { + expect(print(res.instructions)).toMatchSnapshot(); + }); }); - it("should parse assembly with invalid raw pushref", () => { + it("should parse assembly with invalid raw pushref", async () => { const code = ` PUSHREF x{22221} `; + await attachment("Code", code, ContentType.TEXT); const res = parse("test.asm", code); if (res.$ === "ParseFailure") { throw new Error("unexpected parser error"); } - expect(print(res.instructions)).toMatchSnapshot(); + await step("Instructions should match snapshot", () => { + expect(print(res.instructions)).toMatchSnapshot(); + }); }); - it("should parse and print assembly", () => { + it("should parse and print assembly", async () => { const instructions = decompileCell( boc( readFileSync( @@ -60,34 +70,49 @@ describe("assembly-parser", () => { const assembly2 = print(res.instructions); - expect(assembly2).toEqual(assembly); + await attachment("Decompiled Assembly", assembly, ContentType.TEXT); + await attachment( + "Parsed and Re-Printed Assembly", + assembly2, + ContentType.TEXT, + ); + + await step("Should match decompiled and re-parsed assembly", () => { + expect(assembly2).toEqual(assembly); + }); }); - it("should not parse assembly with error", () => { + it("should not parse assembly with error", async () => { const code = ` PUSHINT 10 , PUSHINT 5 ADD `; + await attachment("Code", code, ContentType.TEXT); const res = parse("test.asm", code); if (res.$ === "ParseSuccess") { throw new Error("unexpected parser success"); } - expect(res.error.toString()).toMatchSnapshot(); + await step("Should produce parse error", () => { + expect(res.error.toString()).toMatchSnapshot(); + }); }); - it("should give an error for malformed assembly", () => { + it("should give an error for malformed assembly", async () => { const code = ` PUSHINT // no arg PUSHINT 5 ADD `; + await attachment("Code", code, ContentType.TEXT); const res = parse("test.asm", code); if (res.$ === "ParseSuccess") { throw new Error("unexpected parser success"); } - expect(res.error.toString()).toMatchSnapshot(); + await step("Should produce parse error", () => { + expect(res.error.toString()).toMatchSnapshot(); + }); }); }); diff --git a/src/asm/text/test/printer.spec.ts b/src/asm/text/test/printer.spec.ts index 989c0790f2..56e4ff86d3 100644 --- a/src/asm/text/test/printer.spec.ts +++ b/src/asm/text/test/printer.spec.ts @@ -2,15 +2,18 @@ import { ADD, decompileCell, PUSHINT } from "@/asm/runtime"; import { print } from "@/asm/text/printer"; import { readFileSync } from "node:fs"; import { boc } from "@/asm/runtime/util"; +import { step } from "@/test/allure/allure"; describe("assembly-printer", () => { - it("should print simple assembly", () => { + it("should print simple assembly", async () => { const instructions = [PUSHINT(10), PUSHINT(5), ADD()]; - expect(print(instructions)).toMatchSnapshot(); + await step("Instructions should match snapshot", () => { + expect(print(instructions)).toMatchSnapshot(); + }); }); - it("should print assembly", () => { + it("should print assembly", async () => { const instructions = decompileCell( boc( readFileSync( @@ -19,6 +22,8 @@ describe("assembly-printer", () => { ).asCell(), ); - expect(print(instructions)).toMatchSnapshot(); + await step("Instructions should match snapshot", () => { + expect(print(instructions)).toMatchSnapshot(); + }); }); }); diff --git a/src/ast/ast-printer.spec.ts b/src/ast/ast-printer.spec.ts index 89054357b2..2e2e9c7186 100644 --- a/src/ast/ast-printer.spec.ts +++ b/src/ast/ast-printer.spec.ts @@ -4,6 +4,8 @@ import { getParser } from "@/grammar"; import { join } from "path"; import * as assert from "assert"; import { getAstFactory } from "@/ast/ast-helpers"; +import { attachment, step } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; const contractsDir = join(__dirname, "contracts"); @@ -40,11 +42,13 @@ describe.each(contracts)("%s", (_, path) => { ); }); - it("shouldn't change AST", () => { + it("shouldn't change AST", async () => { const Ast = getAstFactory(); const { parse } = getParser(Ast); const code = fs.readFileSync(path, "utf-8"); const ast = parse({ code, path, origin: "user" }); + await attachment("Code", code, ContentType.TEXT); + //TODO: change for proper recursive removal const astStr = stringify(ast).replace(/"id":[0-9]+,/g, ""); @@ -54,11 +58,22 @@ describe.each(contracts)("%s", (_, path) => { path, origin: "user", }); + + await attachment("Formatted code", formattedCode, ContentType.TEXT); + await attachment("AST of Code", astStr, ContentType.JSON); + //TODO: change for proper recursive removal const astFormattedStr = stringify(astFormatted).replace( /"id":[0-9]+,/g, "", ); - expect(astFormattedStr).toEqual(astStr); + await attachment( + "AST of Formatted Code", + astFormattedStr, + ContentType.JSON, + ); + await step("AST string should be equal", () => { + expect(astFormattedStr).toEqual(astStr); + }); }); }); diff --git a/src/benchmarks/escrow/escrow.spec.ts b/src/benchmarks/escrow/escrow.spec.ts index 62ed9e6a6e..94f37c2df9 100644 --- a/src/benchmarks/escrow/escrow.spec.ts +++ b/src/benchmarks/escrow/escrow.spec.ts @@ -1,31 +1,32 @@ import "@ton/test-utils"; -import { Cell, beginCell, toNano, contractAddress } from "@ton/core"; -import type { Sender, Address } from "@ton/core"; -import { Blockchain } from "@ton/sandbox"; +import type { Address, Sender } from "@ton/core"; +import { beginCell, Cell, contractAddress, toNano } from "@ton/core"; import type { SandboxContract, TreasuryContract } from "@ton/sandbox"; +import { Blockchain } from "@ton/sandbox"; import { + type BenchmarkResult, + type CodeSizeResult, + generateCodeSizeResults, generateResults, getStateSizeForAccount, - generateCodeSizeResults, getUsedGas, printBenchmarkTable, - type BenchmarkResult, - type CodeSizeResult, } from "@/benchmarks/utils/gas"; -import { join, resolve } from "path"; +import { resolve } from "path"; import { readFileSync } from "fs"; import { posixNormalize } from "@/utils/filePath"; -import { type Step, writeLog } from "@/test/utils/write-vm-log"; -import { Escrow } from "@/benchmarks/escrow/tact/output/escrow_Escrow"; import type { - UpdateJettonWalletCode, - Funding, Approve, Cancel, + Funding, + UpdateJettonWalletCode, } from "@/benchmarks/escrow/tact/output/escrow_Escrow"; +import { Escrow } from "@/benchmarks/escrow/tact/output/escrow_Escrow"; import benchmarkResults from "@/benchmarks/escrow/results_gas.json"; import benchmarkCodeSizeResults from "@/benchmarks/escrow/results_code_size.json"; +import { attachment, parameter, step } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; const loadFunCEscrowBoc = () => { const bocEscrow = readFileSync( @@ -59,8 +60,6 @@ function testEscrow( const jettonWalletCode = beginCell().storeUint(0, 1).endCell(); - let step: Step; - const dealAmount = toNano(1); async function deployContract( @@ -80,21 +79,30 @@ function testEscrow( assetAddress ? jettonWalletCode : null, ); - const contract = blockchain.openContract(contractInit); + const contract = await step("Open contract", () => { + return blockchain.openContract(contractInit); + }); - const deployResult = await contract.send( - deployer.getSender(), - { value: toNano("0.1") }, - { - $$type: "ProvideEscrowData", + const deployResult = await step( + "Send 'ProvideEscrowData'", + async () => { + return await contract.send( + deployer.getSender(), + { value: toNano("0.1") }, + { + $$type: "ProvideEscrowData", + }, + ); }, ); - expect(deployResult.transactions).toHaveTransaction({ - from: deployer.address, - to: contract.address, - success: true, - deploy: true, + await step("Should have deploy transaction", () => { + expect(deployResult.transactions).toHaveTransaction({ + from: deployer.address, + to: contract.address, + success: true, + deploy: true, + }); }); return contract; @@ -107,10 +115,10 @@ function testEscrow( buyer = await blockchain.treasury("buyer"); guarantor = await blockchain.treasury("guarantor"); - step = writeLog({ - path: join(__dirname, "output", "log.yaml"), - blockchain, - }); + await parameter("deployer", deployer.address.toString()); + await parameter("seller", seller.address.toString()); + await parameter("buyer", buyer.address.toString()); + await parameter("guarantor", guarantor.address.toString()); }); const sendFunding = async ( @@ -122,7 +130,10 @@ function testEscrow( $$type: "Funding", }; - return await escrowContract.send(from, { value }, msg); + return await step("sendFunding", async () => { + await attachment("value", value.toString(), ContentType.TEXT); + return await escrowContract.send(from, { value }, msg); + }); }; const sendChangeCode = async ( @@ -136,7 +147,10 @@ function testEscrow( newJettonWalletCode: newCode, }; - return await escrowContract.send(from, { value }, msg); + return await step("sendChangeCode", async () => { + await attachment("value", value.toString(), ContentType.TEXT); + return await escrowContract.send(from, { value }, msg); + }); }; const sendApprove = async ( @@ -148,7 +162,10 @@ function testEscrow( $$type: "Approve", }; - return await escrowContract.send(from, { value }, msg); + return await step("sendApprove", async () => { + await attachment("value", value.toString(), ContentType.TEXT); + return await escrowContract.send(from, { value }, msg); + }); }; const sendCancel = async ( @@ -160,7 +177,10 @@ function testEscrow( $$type: "Cancel", }; - return await escrowContract.send(from, { value }, msg); + return await step("sendCancel", async () => { + await attachment("value", value.toString(), ContentType.TEXT); + return await escrowContract.send(from, { value }, msg); + }); }; it("fundingTon", async () => { @@ -170,12 +190,16 @@ function testEscrow( sendFunding(contract, buyer.getSender(), toNano(1)), ); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail transaction", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); const gasUsed = getUsedGas(sendResult, "internal"); - expect(gasUsed).toEqual(benchmarkResults.gas["fundingTon"]); + await step("Gas used should match benchmark", () => { + expect(gasUsed).toEqual(benchmarkResults.gas["fundingTon"]); + }); }); it("changeCode", async () => { @@ -195,12 +219,16 @@ function testEscrow( ), ); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail transaction", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); const gasUsed = getUsedGas(sendResult, "internal"); - expect(gasUsed).toEqual(benchmarkResults.gas["changeCode"]); + await step("Gas used should match benchmark", () => { + expect(gasUsed).toEqual(benchmarkResults.gas["changeCode"]); + }); }); it("approveTon", async () => { @@ -218,12 +246,16 @@ function testEscrow( sendApprove(contract, guarantor.getSender(), toNano("0.05")), ); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail transaction", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); const gasUsed = getUsedGas(sendResult, "internal"); - expect(gasUsed).toEqual(benchmarkResults.gas["approveTon"]); + await step("Gas used should match benchmark", () => { + expect(gasUsed).toEqual(benchmarkResults.gas["approveTon"]); + }); }); it("cancelTon", async () => { @@ -241,27 +273,37 @@ function testEscrow( sendCancel(contract, guarantor.getSender(), toNano("0.05")), ); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail transaction", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); const gasUsed = getUsedGas(sendResult, "internal"); - expect(gasUsed).toEqual(benchmarkResults.gas["cancelTon"]); + await step("Gas used should match benchmark", () => { + expect(gasUsed).toEqual(benchmarkResults.gas["cancelTon"]); + }); }); it("cells", async () => { const contract = await deployContract(null, dealAmount, 1n); - expect( - (await getStateSizeForAccount(blockchain, contract.address)).cells, - ).toEqual(codeSizeResults.size["cells"]); + await step("Cell size should match benchmark", async () => { + expect( + (await getStateSizeForAccount(blockchain, contract.address)) + .cells, + ).toEqual(codeSizeResults.size["cells"]); + }); }); it("bits", async () => { const contract = await deployContract(null, dealAmount, 1n); - expect( - (await getStateSizeForAccount(blockchain, contract.address)).bits, - ).toEqual(codeSizeResults.size["bits"]); + await step("Bit size should match benchmark", async () => { + expect( + (await getStateSizeForAccount(blockchain, contract.address)) + .bits, + ).toEqual(codeSizeResults.size["bits"]); + }); }); } diff --git a/src/benchmarks/jetton/jetton.spec.ts b/src/benchmarks/jetton/jetton.spec.ts index 86ad62938a..991775ce48 100644 --- a/src/benchmarks/jetton/jetton.spec.ts +++ b/src/benchmarks/jetton/jetton.spec.ts @@ -82,28 +82,36 @@ function testJetton( content: new Cell(), }; - jettonMinter = blockchain.openContract( - await fromInit(0n, deployer.address, defaultContent), - ); + jettonMinter = await step("Open contract", async () => { + return blockchain.openContract( + await fromInit(0n, deployer.address, defaultContent), + ); + }); - const deployResult = await jettonMinter.send( - deployer.getSender(), - { value: toNano("0.1") }, - msg, - ); + const deployResult = await step("JettonMinter: send", () => { + return jettonMinter.send( + deployer.getSender(), + { value: toNano("0.1") }, + msg, + ); + }); - expect(deployResult.transactions).toHaveTransaction({ - from: deployer.address, - to: jettonMinter.address, - deploy: true, - success: true, + await step("Should have transaction", () => { + expect(deployResult.transactions).toHaveTransaction({ + from: deployer.address, + to: jettonMinter.address, + deploy: true, + success: true, + }); }); - deployerJettonWallet = blockchain.openContract( - await JettonWallet.fromAddress( - await jettonMinter.getGetWalletAddress(deployer.address), - ), - ); + deployerJettonWallet = await step("Open contract", async () => { + return blockchain.openContract( + await JettonWallet.fromAddress( + await jettonMinter.getGetWalletAddress(deployer.address), + ), + ); + }); }); const sendFunding = async ( @@ -218,11 +226,13 @@ function testJetton( ), ); - expect(mintResult.transactions).toHaveTransaction({ - from: jettonMinter.address, - to: deployerJettonWallet.address, - success: true, - endStatus: "active", + await step("Should mint tokens", () => { + expect(mintResult.transactions).toHaveTransaction({ + from: jettonMinter.address, + to: deployerJettonWallet.address, + success: true, + endStatus: "active", + }); }); const someAddress = Address.parse( @@ -243,18 +253,24 @@ function testJetton( ), ); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail transfer", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); - expect(sendResult.transactions).toHaveTransaction({ - from: deployerJettonWallet.address, - success: true, - exitCode: 0, + await step("Should transfer tokens", () => { + expect(sendResult.transactions).toHaveTransaction({ + from: deployerJettonWallet.address, + success: true, + exitCode: 0, + }); }); const transferGasUsed = await getUsedGas(sendResult, "internal"); - expect(transferGasUsed).toEqual(benchmarkResults.gas["transfer"]); + await step("Transfer gas used should match benchmark", () => { + expect(transferGasUsed).toEqual(benchmarkResults.gas["transfer"]); + }); }); it("burn", async () => { @@ -271,14 +287,18 @@ function testJetton( ), ); - expect(burnResult.transactions).toHaveTransaction({ - from: deployerJettonWallet.address, - to: jettonMinter.address, - exitCode: 0, + await step("Should burn tokens", () => { + expect(burnResult.transactions).toHaveTransaction({ + from: deployerJettonWallet.address, + to: jettonMinter.address, + exitCode: 0, + }); }); const burnGasUsed = getUsedGas(burnResult, "internal"); - expect(burnGasUsed).toEqual(benchmarkResults.gas["burn"]); + await step("Burn gas used should match benchmark", () => { + expect(burnGasUsed).toEqual(benchmarkResults.gas["burn"]); + }); }); it("discovery", async () => { @@ -292,50 +312,62 @@ function testJetton( ), ); - expect(discoveryResult.transactions).toHaveTransaction({ - from: deployer.address, - to: jettonMinter.address, - success: true, + await step("Should provide wallet address", () => { + expect(discoveryResult.transactions).toHaveTransaction({ + from: deployer.address, + to: jettonMinter.address, + success: true, + }); }); const discoveryGasUsed = getUsedGas(discoveryResult, "internal"); - expect(discoveryGasUsed).toEqual(benchmarkResults.gas["discovery"]); + await step("Discovery gas used should match benchmark", () => { + expect(discoveryGasUsed).toEqual(benchmarkResults.gas["discovery"]); + }); }); it("minter cells", async () => { - expect( - (await getStateSizeForAccount(blockchain, jettonMinter.address)) - .cells, - ).toEqual(codeSizeResults.size["minter cells"]); + await step("Minter cells should match benchmark", async () => { + expect( + (await getStateSizeForAccount(blockchain, jettonMinter.address)) + .cells, + ).toEqual(codeSizeResults.size["minter cells"]); + }); }); it("minter bits", async () => { - expect( - (await getStateSizeForAccount(blockchain, jettonMinter.address)) - .bits, - ).toEqual(codeSizeResults.size["minter bits"]); + await step("Minter bits should match benchmark", async () => { + expect( + (await getStateSizeForAccount(blockchain, jettonMinter.address)) + .bits, + ).toEqual(codeSizeResults.size["minter bits"]); + }); }); it("wallet cells", async () => { - expect( - ( - await getStateSizeForAccount( - blockchain, - deployerJettonWallet.address, - ) - ).cells, - ).toEqual(codeSizeResults.size["wallet cells"]); + await step("Wallet cells should match benchmark", async () => { + expect( + ( + await getStateSizeForAccount( + blockchain, + deployerJettonWallet.address, + ) + ).cells, + ).toEqual(codeSizeResults.size["wallet cells"]); + }); }); it("wallet bits", async () => { - expect( - ( - await getStateSizeForAccount( - blockchain, - deployerJettonWallet.address, - ) - ).bits, - ).toEqual(codeSizeResults.size["wallet bits"]); + await step("Wallet bits should match benchmark", async () => { + expect( + ( + await getStateSizeForAccount( + blockchain, + deployerJettonWallet.address, + ) + ).bits, + ).toEqual(codeSizeResults.size["wallet bits"]); + }); }); } diff --git a/src/benchmarks/nft/nft.spec.ts b/src/benchmarks/nft/nft.spec.ts index 0ae5185c1e..f773f03943 100644 --- a/src/benchmarks/nft/nft.spec.ts +++ b/src/benchmarks/nft/nft.spec.ts @@ -20,10 +20,9 @@ import { type BenchmarkResult, type CodeSizeResult, } from "@/benchmarks/utils/gas"; -import { join, resolve } from "path"; +import { resolve } from "path"; import { readFileSync } from "fs"; import { posixNormalize } from "@/utils/filePath"; -import { type Step, writeLog } from "@/test/utils/write-vm-log"; import { NFTCollection, ReportStaticData, @@ -44,6 +43,7 @@ import { import benchmarkResults from "@/benchmarks/nft/results_gas.json"; import benchmarkCodeSizeResults from "@/benchmarks/nft/results_code_size.json"; +import { parameter, step } from "@/test/allure/allure"; type dictDeployNFT = { amount: bigint; @@ -106,13 +106,14 @@ function testNFT( let defaultNFTContent: Cell; let royaltyParams: RoyaltyParams; - let step: Step; - beforeAll(async () => { blockchain = await Blockchain.create(); owner = await blockchain.treasury("owner"); notOwner = await blockchain.treasury("notOwner"); + await parameter("owner", owner.address.toString()); + await parameter("notOwner", notOwner.address.toString()); + defaultCommonContent = beginCell().storeStringTail("common").endCell(); defaultCollectionContent = beginCell() .storeStringTail("collectionContent") @@ -130,41 +131,44 @@ function testNFT( owner: owner.address, }; - step = writeLog({ - path: join(__dirname, "output", "log.yaml"), - blockchain, - }); - // Deploy Collection - collectionNFT = blockchain.openContract( - await fromInitCollection( - owner.address, - 0n, - defaultContent, - royaltyParams, - ), - ); + collectionNFT = await step("Open contract", async () => { + return blockchain.openContract( + await fromInitCollection( + owner.address, + 0n, + defaultContent, + royaltyParams, + ), + ); + }); - const deployResult = await collectionNFT.send( - owner.getSender(), - { value: toNano("0.1") }, - { - $$type: "GetRoyaltyParams", - queryId: 0n, - }, - ); + const deployResult = await step("Send 'GetRoyaltyParams'", async () => { + return await collectionNFT.send( + owner.getSender(), + { value: toNano("0.1") }, + { + $$type: "GetRoyaltyParams", + queryId: 0n, + }, + ); + }); - expect(deployResult.transactions).toHaveTransaction({ - from: owner.address, - to: collectionNFT.address, - deploy: true, - success: true, + await step("Should have deploy transaction", () => { + expect(deployResult.transactions).toHaveTransaction({ + from: owner.address, + to: collectionNFT.address, + deploy: true, + success: true, + }); }); // Deploy Item - itemNFT = blockchain.openContract( - await fromInitItem(null, null, owner.address, 0n), - ); + itemNFT = await step("ItemNFT: Open contract", async () => { + return blockchain.openContract( + await fromInitItem(null, null, owner.address, 0n), + ); + }); const deployItemMsg: InitNFTBody = { $$type: "InitNFTBody", @@ -172,17 +176,26 @@ function testNFT( content: defaultNFTContent, }; - const deployItemResult = await itemNFT.send( - owner.getSender(), - { value: toNano("0.1") }, - beginCell().store(storeInitNFTBody(deployItemMsg)).asSlice(), + const deployItemResult = await step( + "ItemNFT: send 'InitNFTBody'", + async () => { + return await itemNFT.send( + owner.getSender(), + { value: toNano("0.1") }, + beginCell() + .store(storeInitNFTBody(deployItemMsg)) + .asSlice(), + ); + }, ); - expect(deployItemResult.transactions).toHaveTransaction({ - from: owner.address, - to: itemNFT.address, - deploy: true, - success: true, + await step("Should have deploy transaction", () => { + expect(deployItemResult.transactions).toHaveTransaction({ + from: owner.address, + to: itemNFT.address, + deploy: true, + success: true, + }); }); }); @@ -220,12 +233,16 @@ function testNFT( ), ); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail transfer", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); const gasUsed = getUsedGas(sendResult, "internal"); - expect(gasUsed).toEqual(benchmarkResults.gas["transfer"]); + await step("Transfer gas used should match benchmark", () => { + expect(gasUsed).toEqual(benchmarkResults.gas["transfer"]); + }); }); it("get static data", async () => { @@ -246,24 +263,30 @@ function testNFT( sendGetStaticData(itemNFT, owner.getSender(), toNano(1)), ); - expect(sendResult.transactions).toHaveTransaction({ - from: itemNFT.address, - to: owner.address, - body: beginCell() - .storeUint(ReportStaticData, 32) - .storeUint(0n, 64) - .storeUint(0n, 256) - .storeAddress(owner.address) - .endCell(), - success: true, + await step("Should have response transaction with correct body", () => { + expect(sendResult.transactions).toHaveTransaction({ + from: itemNFT.address, + to: owner.address, + body: beginCell() + .storeUint(ReportStaticData, 32) + .storeUint(0n, 64) + .storeUint(0n, 256) + .storeAddress(owner.address) + .endCell(), + success: true, + }); }); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail get static data", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); const gasUsed = getUsedGas(sendResult, "internal"); - expect(gasUsed).toEqual(benchmarkResults.gas["get static data"]); + await step("Get static data gas used should match benchmark", () => { + expect(gasUsed).toEqual(benchmarkResults.gas["get static data"]); + }); }); it("deploy nft", async () => { @@ -295,17 +318,23 @@ function testNFT( sendDeployNFT(collectionNFT, owner.getSender(), toNano(1)), ); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail deploy nft", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); - expect(sendResult.transactions).toHaveTransaction({ - from: collectionNFT.address, - deploy: true, + await step("Should have deploy transaction from collectionNFT", () => { + expect(sendResult.transactions).toHaveTransaction({ + from: collectionNFT.address, + deploy: true, + }); }); const gasUsed = getUsedGas(sendResult, "internal"); - expect(gasUsed).toEqual(benchmarkResults.gas["deploy nft"]); + await step("Deploy nft gas used should match benchmark", () => { + expect(gasUsed).toEqual(benchmarkResults.gas["deploy nft"]); + }); }); it("batch deploy nft", async () => { @@ -342,54 +371,80 @@ function testNFT( deployList: beginCell().storeDictDirect(dct).endCell(), }; - return await collectionNFT.send( - sender.getSender(), - { value: toNano("100") * (count + 10n) }, - batchMintNFT, - ); + return await step(`Send collection`, async () => { + return await collectionNFT.send( + sender.getSender(), + { value: toNano("100") * (count + 10n) }, + batchMintNFT, + ); + }); }; const sendResult = await step("batch deploy nft", async () => batchMintNFTProcess(collectionNFT, owner, owner, 100n), ); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail batch deploy nft", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); - expect(sendResult.transactions).toHaveTransaction({ - from: collectionNFT.address, - deploy: true, + await step("Should have deploy transaction from collectionNFT", () => { + expect(sendResult.transactions).toHaveTransaction({ + from: collectionNFT.address, + deploy: true, + }); }); const gasUsed = getUsedGas(sendResult, "internal"); - expect(gasUsed).toEqual(benchmarkResults.gas["batch deploy nft"]); + await step("Batch deploy nft gas used should match benchmark", () => { + expect(gasUsed).toEqual(benchmarkResults.gas["batch deploy nft"]); + }); }); it("collection cells", async () => { - expect( - (await getStateSizeForAccount(blockchain, collectionNFT.address)) - .cells, - ).toEqual(codeSizeResults.size["collection cells"]); + await step("Collection cells size should match benchmark", async () => { + expect( + ( + await getStateSizeForAccount( + blockchain, + collectionNFT.address, + ) + ).cells, + ).toEqual(codeSizeResults.size["collection cells"]); + }); }); it("collection bits", async () => { - expect( - (await getStateSizeForAccount(blockchain, collectionNFT.address)) - .bits, - ).toEqual(codeSizeResults.size["collection bits"]); + await step("Collection bits size should match benchmark", async () => { + expect( + ( + await getStateSizeForAccount( + blockchain, + collectionNFT.address, + ) + ).bits, + ).toEqual(codeSizeResults.size["collection bits"]); + }); }); it("item cells", async () => { - expect( - (await getStateSizeForAccount(blockchain, itemNFT.address)).cells, - ).toEqual(codeSizeResults.size["item cells"]); + await step("Item cells size should match benchmark", async () => { + expect( + (await getStateSizeForAccount(blockchain, itemNFT.address)) + .cells, + ).toEqual(codeSizeResults.size["item cells"]); + }); }); it("item bits", async () => { - expect( - (await getStateSizeForAccount(blockchain, itemNFT.address)).bits, - ).toEqual(codeSizeResults.size["item bits"]); + await step("Item bits size should match benchmark", async () => { + expect( + (await getStateSizeForAccount(blockchain, itemNFT.address)) + .bits, + ).toEqual(codeSizeResults.size["item bits"]); + }); }); } diff --git a/src/benchmarks/notcoin/notcoin.spec.ts b/src/benchmarks/notcoin/notcoin.spec.ts index 275ca0cbe6..328c3a224b 100644 --- a/src/benchmarks/notcoin/notcoin.spec.ts +++ b/src/benchmarks/notcoin/notcoin.spec.ts @@ -19,10 +19,10 @@ import { type BenchmarkResult, type CodeSizeResult, } from "@/benchmarks/utils/gas"; -import { join, resolve } from "path"; +import { resolve } from "path"; import { readFileSync } from "fs"; import { posixNormalize } from "@/utils/filePath"; -import { type Step, writeLog } from "@/test/utils/write-vm-log"; +import { parameter, step } from "@/test/allure/allure"; import { JettonMinterNotcoin, type JettonUpdateContent, @@ -68,7 +68,6 @@ function testNotcoin( let deployer: SandboxContract; let notDeployer: SandboxContract; const defaultContent: Cell = beginCell().endCell(); - let step: Step; let jettonMinterNotcoin: SandboxContract; let deployerJettonWalletNotcoin: SandboxContract; @@ -77,10 +76,8 @@ function testNotcoin( deployer = await blockchain.treasury("deployer"); notDeployer = await blockchain.treasury("notDeployer"); - step = writeLog({ - path: join(__dirname, "output", "log.yaml"), - blockchain, - }); + await parameter("deployer", deployer.address.toString()); + await parameter("notDeployer", notDeployer.address.toString()); const msg: JettonUpdateContent = { $$type: "JettonUpdateContent", @@ -97,17 +94,24 @@ function testNotcoin( ), ); - const deployResult = await jettonMinterNotcoin.send( - deployer.getSender(), - { value: toNano("0.1") }, - msg, + const deployResult = await step( + "Send JettonUpdateContent", + async () => { + return await jettonMinterNotcoin.send( + deployer.getSender(), + { value: toNano("0.1") }, + msg, + ); + }, ); - expect(deployResult.transactions).toHaveTransaction({ - from: deployer.address, - to: jettonMinterNotcoin.address, - deploy: true, - success: true, + await step("Should have deploy transaction", () => { + expect(deployResult.transactions).toHaveTransaction({ + from: deployer.address, + to: jettonMinterNotcoin.address, + deploy: true, + success: true, + }); }); deployerJettonWalletNotcoin = blockchain.openContract( @@ -229,11 +233,13 @@ function testNotcoin( ), ); - expect(mintResult.transactions).toHaveTransaction({ - from: jettonMinterNotcoin.address, - to: deployerJettonWalletNotcoin.address, - success: true, - endStatus: "active", + await step("Should have mint transaction", () => { + expect(mintResult.transactions).toHaveTransaction({ + from: jettonMinterNotcoin.address, + to: deployerJettonWalletNotcoin.address, + success: true, + endStatus: "active", + }); }); const someAddress = Address.parse( @@ -254,18 +260,29 @@ function testNotcoin( ), ); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail transfer", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); - expect(sendResult.transactions).toHaveTransaction({ - from: deployerJettonWalletNotcoin.address, - success: true, - exitCode: 0, + await step("Should have successful transfer transaction", () => { + expect(sendResult.transactions).toHaveTransaction({ + from: deployerJettonWalletNotcoin.address, + success: true, + exitCode: 0, + }); }); - const transferGasUsed = await getUsedGas(sendResult, "internal"); - expect(transferGasUsed).toEqual(benchmarkResults.gas["transfer"]); + const transferGasUsed = await step( + "Get transfer gas used", + async () => { + return await getUsedGas(sendResult, "internal"); + }, + ); + await step("Transfer gas used should match benchmark", () => { + expect(transferGasUsed).toEqual(benchmarkResults.gas["transfer"]); + }); }); it("burn", async () => { @@ -282,14 +299,20 @@ function testNotcoin( ), ); - expect(burnResult.transactions).toHaveTransaction({ - from: deployerJettonWalletNotcoin.address, - to: jettonMinterNotcoin.address, - exitCode: 0, + await step("Should have burn transaction", () => { + expect(burnResult.transactions).toHaveTransaction({ + from: deployerJettonWalletNotcoin.address, + to: jettonMinterNotcoin.address, + exitCode: 0, + }); }); - const burnGasUsed = getUsedGas(burnResult, "internal"); - expect(burnGasUsed).toEqual(benchmarkResults.gas["burn"]); + const burnGasUsed = await step("Get burn gas used", () => { + return getUsedGas(burnResult, "internal"); + }); + await step("Burn gas used should match benchmark", () => { + expect(burnGasUsed).toEqual(benchmarkResults.gas["burn"]); + }); }); it("discovery", async () => { @@ -303,58 +326,72 @@ function testNotcoin( ), ); - expect(discoveryResult.transactions).toHaveTransaction({ - from: deployer.address, - to: jettonMinterNotcoin.address, - success: true, + await step("Should have discovery transaction", () => { + expect(discoveryResult.transactions).toHaveTransaction({ + from: deployer.address, + to: jettonMinterNotcoin.address, + success: true, + }); }); - const discoveryGasUsed = getUsedGas(discoveryResult, "internal"); - expect(discoveryGasUsed).toEqual(benchmarkResults.gas["discovery"]); + const discoveryGasUsed = await step("Get discovery gas used", () => { + return getUsedGas(discoveryResult, "internal"); + }); + await step("Discovery gas used should match benchmark", () => { + expect(discoveryGasUsed).toEqual(benchmarkResults.gas["discovery"]); + }); }); it("minter cells", async () => { - expect( - ( - await getStateSizeForAccount( - blockchain, - jettonMinterNotcoin.address, - ) - ).cells, - ).toEqual(codeSizeResults.size["minter cells"]); + await step("Minter cells size should match benchmark", async () => { + expect( + ( + await getStateSizeForAccount( + blockchain, + jettonMinterNotcoin.address, + ) + ).cells, + ).toEqual(codeSizeResults.size["minter cells"]); + }); }); it("minter bits", async () => { - expect( - ( - await getStateSizeForAccount( - blockchain, - jettonMinterNotcoin.address, - ) - ).bits, - ).toEqual(codeSizeResults.size["minter bits"]); + await step("Minter bits size should match benchmark", async () => { + expect( + ( + await getStateSizeForAccount( + blockchain, + jettonMinterNotcoin.address, + ) + ).bits, + ).toEqual(codeSizeResults.size["minter bits"]); + }); }); it("wallet cells", async () => { - expect( - ( - await getStateSizeForAccount( - blockchain, - deployerJettonWalletNotcoin.address, - ) - ).cells, - ).toEqual(codeSizeResults.size["wallet cells"]); + await step("Wallet cells size should match benchmark", async () => { + expect( + ( + await getStateSizeForAccount( + blockchain, + deployerJettonWalletNotcoin.address, + ) + ).cells, + ).toEqual(codeSizeResults.size["wallet cells"]); + }); }); it("wallet bits", async () => { - expect( - ( - await getStateSizeForAccount( - blockchain, - deployerJettonWalletNotcoin.address, - ) - ).bits, - ).toEqual(codeSizeResults.size["wallet bits"]); + await step("Wallet bits size should match benchmark", async () => { + expect( + ( + await getStateSizeForAccount( + blockchain, + deployerJettonWalletNotcoin.address, + ) + ).bits, + ).toEqual(codeSizeResults.size["wallet bits"]); + }); }); } diff --git a/src/benchmarks/sbt/sbt.spec.ts b/src/benchmarks/sbt/sbt.spec.ts index 8a58517dc4..27b140f8ad 100644 --- a/src/benchmarks/sbt/sbt.spec.ts +++ b/src/benchmarks/sbt/sbt.spec.ts @@ -13,10 +13,9 @@ import { type CodeSizeResult, type BenchmarkResult, } from "@/benchmarks/utils/gas"; -import { join, resolve } from "path"; +import { resolve } from "path"; import { readFileSync } from "fs"; import { posixNormalize } from "@/utils/filePath"; -import { type Step, writeLog } from "@/test/utils/write-vm-log"; import { SBTItem, storeReportStaticData, @@ -40,6 +39,7 @@ import type { import benchmarkResults from "@/benchmarks/sbt/results_gas.json"; import benchmarkCodeSizeResults from "@/benchmarks/sbt/results_code_size.json"; +import { parameter, step } from "@/test/allure/allure"; const loadFunCSBTBoc = () => { const bocItem = readFileSync( @@ -66,37 +66,39 @@ function testSBT( let itemSBT: SandboxContract; let snapshot: BlockchainSnapshot; - let step: Step; beforeAll(async () => { blockchain = await Blockchain.create(); owner = await blockchain.treasury("owner"); - step = writeLog({ - path: join(__dirname, "output", "log.yaml"), - blockchain, - }); + await parameter("owner", owner.address.toString()); itemSBT = blockchain.openContract( - await fromInitItem(0n, owner.address, null, null, null, 0n), + await step("Initialize SBT item contract", async () => + fromInitItem(0n, owner.address, null, null, null, 0n), + ), ); - const deployItemResult = await itemSBT.send( - owner.getSender(), - { value: toNano("0.1") }, - beginCell() - .storeAddress(owner.address) - .storeRef(beginCell().endCell()) - .storeAddress(owner.address) - .storeUint(0n, 64) - .asSlice(), + const deployItemResult = await step("Deploy SBT item", () => + itemSBT.send( + owner.getSender(), + { value: toNano("0.1") }, + beginCell() + .storeAddress(owner.address) + .storeRef(beginCell().endCell()) + .storeAddress(owner.address) + .storeUint(0n, 64) + .asSlice(), + ), ); - expect(deployItemResult.transactions).toHaveTransaction({ - from: owner.address, - to: itemSBT.address, - deploy: true, - success: true, + await step("Should deploy SBT item", () => { + expect(deployItemResult.transactions).toHaveTransaction({ + from: owner.address, + to: itemSBT.address, + deploy: true, + success: true, + }); }); snapshot = blockchain.snapshot(); @@ -109,7 +111,9 @@ function testSBT( it("deploy", async () => { const runDeployTest = async () => { const newItemSBT = blockchain.openContract( - await fromInitItem(1n, owner.address, null, null, null, 0n), + await step("Initialize new SBT item contract", async () => + fromInitItem(1n, owner.address, null, null, null, 0n), + ), ); const sendDeploy = async ( @@ -129,21 +133,31 @@ function testSBT( ); }; - const sendResult = await step("request owner", async () => - sendDeploy(newItemSBT, owner.getSender(), toNano(1)), + const sendResult = await step( + "Send deploy for new SBT item", + async () => + sendDeploy(newItemSBT, owner.getSender(), toNano(1)), ); - expect(sendResult.transactions).toHaveTransaction({ - deploy: true, + await step("Should return expected deploy transaction", () => { + expect(sendResult.transactions).toHaveTransaction({ + deploy: true, + }); }); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail transaction", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); - return getUsedGas(sendResult, "internal"); + return await step("Get used gas", () => + getUsedGas(sendResult, "internal"), + ); }; const deployGasUsed = await runDeployTest(); - expect(deployGasUsed).toEqual(benchmarkResults.gas["deploy"]); + await step("Should match deploy gas benchmark", () => { + expect(deployGasUsed).toEqual(benchmarkResults.gas["deploy"]); + }); }); it("request owner", async () => { const sendRequestOwner = async ( @@ -165,7 +179,7 @@ function testSBT( const runRequestOwnerTest = async ( scopeItemSBT: SandboxContract, ) => { - const sendResult = await step("request owner", async () => + const sendResult = await step("Send request owner", async () => sendRequestOwner(scopeItemSBT, owner.getSender(), toNano(1)), ); @@ -180,27 +194,35 @@ function testSBT( content: beginCell().endCell(), }; - expect(sendResult.transactions).toHaveTransaction({ - from: scopeItemSBT.address, - to: owner.address, - body: beginCell() - .store(storeRequestOwnerOut(expectedBody)) - .endCell(), - inMessageBounceable: true, + await step("Should return expected RequestOwnerOut", () => { + expect(sendResult.transactions).toHaveTransaction({ + from: scopeItemSBT.address, + to: owner.address, + body: beginCell() + .store(storeRequestOwnerOut(expectedBody)) + .endCell(), + inMessageBounceable: true, + }); }); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail transaction", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); - return getUsedGas(sendResult, "internal"); + return await step("Get used gas", () => + getUsedGas(sendResult, "internal"), + ); }; const requestOwnerGasUsedTact = await runRequestOwnerTest(itemSBT); - expect(requestOwnerGasUsedTact).toEqual( - benchmarkResults.gas["request owner"], - ); + await step("Should match request owner gas benchmark", () => { + expect(requestOwnerGasUsedTact).toEqual( + benchmarkResults.gas["request owner"], + ); + }); }); it("prove ownership", async () => { @@ -223,7 +245,7 @@ function testSBT( const runProveOwnershipTest = async ( scopeItemSBT: SandboxContract, ) => { - const sendResult = await step("prove ownership", async () => + const sendResult = await step("Send prove ownership", async () => sendProveOwnership(scopeItemSBT, owner.getSender(), toNano(1)), ); @@ -237,26 +259,34 @@ function testSBT( content: beginCell().endCell(), }; - expect(sendResult.transactions).toHaveTransaction({ - from: scopeItemSBT.address, - to: owner.address, - body: beginCell() - .store(storeProveOwnershipOut(expectedBody)) - .endCell(), - inMessageBounceable: true, + await step("Should return expected ProveOwnershipOut", () => { + expect(sendResult.transactions).toHaveTransaction({ + from: scopeItemSBT.address, + to: owner.address, + body: beginCell() + .store(storeProveOwnershipOut(expectedBody)) + .endCell(), + inMessageBounceable: true, + }); }); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail transaction", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); - return getUsedGas(sendResult, "internal"); + return await step("Get used gas", () => + getUsedGas(sendResult, "internal"), + ); }; const proveOwnershipGasUsedTact = await runProveOwnershipTest(itemSBT); - expect(proveOwnershipGasUsedTact).toEqual( - benchmarkResults.gas["prove ownership"], - ); + await step("Should match prove ownership gas benchmark", () => { + expect(proveOwnershipGasUsedTact).toEqual( + benchmarkResults.gas["prove ownership"], + ); + }); }); it("get static data", async () => { @@ -276,7 +306,7 @@ function testSBT( const runGetStaticTest = async ( scopeItemSBT: SandboxContract, ) => { - const sendResult = await step("get static data", async () => + const sendResult = await step("Send get static data", async () => sendGetStaticData(scopeItemSBT, owner.getSender(), toNano(1)), ); @@ -287,27 +317,35 @@ function testSBT( collectionAddress: owner.address, }; - expect(sendResult.transactions).toHaveTransaction({ - from: scopeItemSBT.address, - to: owner.address, - body: beginCell() - .store(storeReportStaticData(expectedBody)) - .endCell(), - inMessageBounceable: false, + await step("Should return expected ReportStaticData", () => { + expect(sendResult.transactions).toHaveTransaction({ + from: scopeItemSBT.address, + to: owner.address, + body: beginCell() + .store(storeReportStaticData(expectedBody)) + .endCell(), + inMessageBounceable: false, + }); }); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail transaction", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); - return getUsedGas(sendResult, "internal"); + return await step("Get used gas", () => + getUsedGas(sendResult, "internal"), + ); }; const getStaticGasUsedTact = await runGetStaticTest(itemSBT); - expect(getStaticGasUsedTact).toEqual( - benchmarkResults.gas["get static data"], - ); + await step("Should match get static data gas benchmark", () => { + expect(getStaticGasUsedTact).toEqual( + benchmarkResults.gas["get static data"], + ); + }); }); it("take excess", async () => { @@ -327,7 +365,7 @@ function testSBT( const runTakeExcessTest = async ( scopeItemSBT: SandboxContract, ) => { - const sendResult = await step("take excess", async () => + const sendResult = await step("Send take excess", async () => sendTakeExcess(scopeItemSBT, owner.getSender(), toNano(1)), ); @@ -336,24 +374,34 @@ function testSBT( queryId: 0n, }; - expect(sendResult.transactions).toHaveTransaction({ - from: scopeItemSBT.address, - to: owner.address, - body: beginCell().store(storeExcessOut(expectedBody)).endCell(), - inMessageBounceable: false, + await step("Should return expected ExcessOut", () => { + expect(sendResult.transactions).toHaveTransaction({ + from: scopeItemSBT.address, + to: owner.address, + body: beginCell() + .store(storeExcessOut(expectedBody)) + .endCell(), + inMessageBounceable: false, + }); }); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail transaction", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); - return getUsedGas(sendResult, "internal"); + return await step("Get used gas", () => + getUsedGas(sendResult, "internal"), + ); }; const takeExcessGasUsedTact = await runTakeExcessTest(itemSBT); - expect(takeExcessGasUsedTact).toEqual( - benchmarkResults.gas["take excess"], - ); + await step("Should match take excess gas benchmark", () => { + expect(takeExcessGasUsedTact).toEqual( + benchmarkResults.gas["take excess"], + ); + }); }); it("destroy", async () => { @@ -373,7 +421,7 @@ function testSBT( const runDestroyTest = async ( scopeItemSBT: SandboxContract, ) => { - const sendResult = await step("destroy", async () => + const sendResult = await step("Send destroy", async () => sendDestroy(scopeItemSBT, owner.getSender(), toNano(1)), ); @@ -382,22 +430,32 @@ function testSBT( queryId: 0n, }; - expect(sendResult.transactions).toHaveTransaction({ - from: scopeItemSBT.address, - to: owner.address, - body: beginCell().store(storeExcessOut(expectedBody)).endCell(), - inMessageBounceable: false, + await step("Should return expected ExcessOut", () => { + expect(sendResult.transactions).toHaveTransaction({ + from: scopeItemSBT.address, + to: owner.address, + body: beginCell() + .store(storeExcessOut(expectedBody)) + .endCell(), + inMessageBounceable: false, + }); }); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail transaction", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); - return getUsedGas(sendResult, "internal"); + return await step("Get used gas", () => + getUsedGas(sendResult, "internal"), + ); }; const destroyGasUsedTact = await runDestroyTest(itemSBT); - expect(destroyGasUsedTact).toEqual(benchmarkResults.gas["destroy"]); + await step("Should match destroy gas benchmark", () => { + expect(destroyGasUsedTact).toEqual(benchmarkResults.gas["destroy"]); + }); }); it("revoke", async () => { @@ -417,36 +475,50 @@ function testSBT( const runRevokeTest = async ( scopeItemSBT: SandboxContract, ) => { - const sendResult = await step("revoke", async () => + const sendResult = await step("Send revoke", async () => sendRevoke(scopeItemSBT, owner.getSender(), toNano(1)), ); - expect(sendResult.transactions).not.toHaveTransaction({ - from: scopeItemSBT.address, + await step("Should not send transaction from SBT item", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + from: scopeItemSBT.address, + }); }); - expect(sendResult.transactions).not.toHaveTransaction({ - success: false, + await step("Should not fail transaction", () => { + expect(sendResult.transactions).not.toHaveTransaction({ + success: false, + }); }); - return getUsedGas(sendResult, "internal"); + return await step("Get used gas", () => + getUsedGas(sendResult, "internal"), + ); }; const revokeGasUsedTact = await runRevokeTest(itemSBT); - expect(revokeGasUsedTact).toEqual(benchmarkResults.gas["revoke"]); + await step("Should match revoke gas benchmark", () => { + expect(revokeGasUsedTact).toEqual(benchmarkResults.gas["revoke"]); + }); }); it("item cells", async () => { - expect( - (await getStateSizeForAccount(blockchain, itemSBT.address)).cells, - ).toEqual(codeSizeResults.size["item cells"]); + await step("Item cells should match benchmark", async () => { + expect( + (await getStateSizeForAccount(blockchain, itemSBT.address)) + .cells, + ).toEqual(codeSizeResults.size["item cells"]); + }); }); it("item bits", async () => { - expect( - (await getStateSizeForAccount(blockchain, itemSBT.address)).bits, - ).toEqual(codeSizeResults.size["item bits"]); + await step("Item bits should match benchmark", async () => { + expect( + (await getStateSizeForAccount(blockchain, itemSBT.address)) + .bits, + ).toEqual(codeSizeResults.size["item bits"]); + }); }); } diff --git a/src/benchmarks/tests/wallet-v5-test.spec.ts b/src/benchmarks/tests/wallet-v5-test.spec.ts index cd9fe34be4..2a45d96547 100644 --- a/src/benchmarks/tests/wallet-v5-test.spec.ts +++ b/src/benchmarks/tests/wallet-v5-test.spec.ts @@ -5,9 +5,7 @@ import type { } from "@ton/sandbox"; import { Blockchain } from "@ton/sandbox"; import type { Address, Cell } from "@ton/core"; -import { external } from "@ton/core"; -import { SendMode } from "@ton/core"; -import { beginCell, Dictionary, toNano } from "@ton/core"; +import { beginCell, Dictionary, external, SendMode, toNano } from "@ton/core"; import "@ton/test-utils"; import type { KeyPair } from "@ton/crypto"; @@ -20,6 +18,7 @@ import { validUntil, } from "@/benchmarks/wallet-v5/utils"; import { WalletV5 } from "@/benchmarks/wallet-v5/tact/output/wallet-v5_WalletV5"; +import { parameter, step } from "@/test/allure/allure"; export function packAddress(address: Address) { return bufferToBigInt(address.hash); @@ -97,39 +96,58 @@ describe("Wallet v5 correctness tests", () => { deployer = await blockchain.treasury("deployer"); receiver = await blockchain.treasury("receiver"); + await parameter("Deployer", deployer.address.toString()); + await parameter("Receiver", receiver.address.toString()); + seqno = createSeqno(); - walletV5 = blockchain.openContract( - await WalletV5.fromInit( - true, - 0n, - SUBWALLET_ID, - bufferToBigInt(keypair.publicKey), - Dictionary.empty(), - ), - ); + await step("Open contract WalletV5", async () => { + walletV5 = blockchain.openContract( + await WalletV5.fromInit( + true, + 0n, + SUBWALLET_ID, + bufferToBigInt(keypair.publicKey), + Dictionary.empty(), + ), + ); + }); - const deployResult = await walletV5.send( - deployer.getSender(), - { - value: toNano("0.05"), + await parameter("WalletV5", walletV5.address.toString()); + + const deployResult = await step( + "WalletV5: Send transaction", + async () => { + return await walletV5.send( + deployer.getSender(), + { + value: toNano("0.05"), + }, + beginCell().endCell().asSlice(), + ); }, - beginCell().endCell().asSlice(), ); - expect(deployResult.transactions).toHaveTransaction({ - from: deployer.address, - to: walletV5.address, - deploy: true, - success: true, + await step("Should have transaction from deployer to walletV5", () => { + expect(deployResult.transactions).toHaveTransaction({ + from: deployer.address, + to: walletV5.address, + deploy: true, + success: true, + }); }); // top up wallet balance - await deployer.send({ - to: walletV5.address, - value: toNano("10"), - sendMode: SendMode.PAY_GAS_SEPARATELY, - }); + await step( + "Deployer: Send transaction (top-up balance) to walletV5", + async () => { + await deployer.send({ + to: walletV5.address, + value: toNano("10"), + sendMode: SendMode.PAY_GAS_SEPARATELY, + }); + }, + ); snapshot = blockchain.snapshot(); }); @@ -160,22 +178,28 @@ describe("Wallet v5 correctness tests", () => { }, ); - expect(extensionTransferResult.transactions).toHaveTransaction({ - from: deployer.address, - to: walletV5.address, - success: true, - exitCode: 0, // return instead of throw + await step("Should have transaction from deployer to walletV5", () => { + expect(extensionTransferResult.transactions).toHaveTransaction({ + from: deployer.address, + to: walletV5.address, + success: true, + exitCode: 0, // return instead of throw + }); }); // external to extension + internal with return, no send message - expect(extensionTransferResult.transactions.length).toEqual(2); + await step("Should have 2 transactions", () => { + expect(extensionTransferResult.transactions.length).toEqual(2); + }); const receiverBalanceAfter = ( await blockchain.getContract(testReceiver) ).balance; // did not change - expect(receiverBalanceAfter).toEqual(receiverBalanceBefore); + await step("Receiver balance should not change", () => { + expect(receiverBalanceAfter).toEqual(receiverBalanceBefore); + }); }); it("should throw on attempt to add extension that already exists", async () => { @@ -197,10 +221,12 @@ describe("Wallet v5 correctness tests", () => { "internal", ); - expect(secondAddExtensionResult.transactions).toHaveTransaction({ - from: deployer.address, - to: walletV5.address, - exitCode: 139, // throw on map.exist check + await step("Should have transaction from deployer to walletV5", () => { + expect(secondAddExtensionResult.transactions).toHaveTransaction({ + from: deployer.address, + to: walletV5.address, + exitCode: 139, // throw on map.exist check + }); }); }); }); diff --git a/src/benchmarks/wallet-v4/wallet-v4.spec.ts b/src/benchmarks/wallet-v4/wallet-v4.spec.ts index 00ad4530f4..5ab8632ce1 100644 --- a/src/benchmarks/wallet-v4/wallet-v4.spec.ts +++ b/src/benchmarks/wallet-v4/wallet-v4.spec.ts @@ -22,7 +22,6 @@ import { import { resolve } from "path"; import { readFileSync } from "fs"; import { posixNormalize } from "@/utils/filePath"; -import { type Step, writeLog } from "@/test/utils/write-vm-log"; import type { KeyPair } from "@ton/crypto"; import { getSecureRandomBytes, keyPairFromSeed, sign } from "@ton/crypto"; import type { PluginRequestFunds } from "@/benchmarks/wallet-v4/tact/output/wallet-v4_WalletV4"; @@ -38,6 +37,7 @@ import { } from "@/benchmarks/wallet-v5/utils"; import benchmarkResults from "@/benchmarks/wallet-v4/results_gas.json"; +import { parameter, step } from "@/test/allure/allure"; function createSimpleTransferBody(testReceiver: Address, forwardValue: bigint) { const msg = beginCell().storeUint(0, 8); @@ -80,7 +80,6 @@ function testWalletV4( let wallet: SandboxContract; let seqno: () => bigint; let keypair: KeyPair; - let step: Step; const SUBWALLET_ID = 0n; @@ -122,10 +121,8 @@ function testWalletV4( deployer = await blockchain.treasury("deployer"); receiver = await blockchain.treasury("receiver"); - step = writeLog({ - path: resolve(__dirname, "output", "log.yaml"), - blockchain, - }); + await parameter("Deployer", deployer.address.toString()); + await parameter("Receiver", receiver.address.toString()); seqno = createSeqnoCounter(); @@ -140,37 +137,47 @@ function testWalletV4( ); // Deploy wallet - const deployResult = await wallet.send( - deployer.getSender(), - { - value: toNano("0.05"), - }, - beginCell().endCell().asSlice(), + const deployResult = await step("Deploy wallet", () => + wallet.send( + deployer.getSender(), + { + value: toNano("0.05"), + }, + beginCell().endCell().asSlice(), + ), ); - expect(deployResult.transactions).toHaveTransaction({ - from: deployer.address, - to: wallet.address, - deploy: true, - success: true, + await step("Should deploy wallet", () => { + expect(deployResult.transactions).toHaveTransaction({ + from: deployer.address, + to: wallet.address, + deploy: true, + success: true, + }); }); // Top up wallet balance - await deployer.send({ - to: wallet.address, - value: toNano("10"), - sendMode: SendMode.PAY_GAS_SEPARATELY, - }); + await step("Top up wallet balance", () => + deployer.send({ + to: wallet.address, + value: toNano("10"), + sendMode: SendMode.PAY_GAS_SEPARATELY, + }), + ); }); it("check correctness of deploy", async () => { const walletSeqno = await wallet.getSeqno(); - expect(walletSeqno).toBe(0n); + await step("Seqno should be zero", () => { + expect(walletSeqno).toBe(0n); + }); const walletPublicKey = await wallet.getGetPublicKey(); - expect(walletPublicKey).toBe(bufferToBigInt(keypair.publicKey)); + await step("Public key should match", () => { + expect(walletPublicKey).toBe(bufferToBigInt(keypair.publicKey)); + }); }); it("externalTransfer", async () => { @@ -190,18 +197,24 @@ function testWalletV4( sendSignedActionBody(wallet, sendTxActionsList), ); - expect(externalTransferSendResult.transactions).toHaveTransaction({ - to: wallet.address, - success: true, - exitCode: 0, + await step("Should have wallet transaction", () => { + expect(externalTransferSendResult.transactions).toHaveTransaction({ + to: wallet.address, + success: true, + exitCode: 0, + }); }); - expect(externalTransferSendResult.transactions.length).toEqual(2); + await step("Should have 2 transactions", () => { + expect(externalTransferSendResult.transactions.length).toEqual(2); + }); - expect(externalTransferSendResult.transactions).toHaveTransaction({ - from: wallet.address, - to: testReceiver, - value: forwardValue, + await step("Should transfer to testReceiver", () => { + expect(externalTransferSendResult.transactions).toHaveTransaction({ + from: wallet.address, + to: testReceiver, + value: forwardValue, + }); }); const fee = externalTransferSendResult.transactions[1]!.totalFees.coins; @@ -209,17 +222,20 @@ function testWalletV4( await blockchain.getContract(testReceiver) ).balance; - expect(receiverBalanceAfter).toEqual( - receiverBalanceBefore + forwardValue - fee, - ); + await step("Receiver balance should be correct", () => { + expect(receiverBalanceAfter).toEqual( + receiverBalanceBefore + forwardValue - fee, + ); + }); - const externalTransferGasUsed = getUsedGas( - externalTransferSendResult, - "external", - ); - expect(externalTransferGasUsed).toEqual( - benchmarkResult.gas["externalTransfer"], + const externalTransferGasUsed = await step("Get gas used", () => + getUsedGas(externalTransferSendResult, "external"), ); + await step("Gas usage should match benchmark", () => { + expect(externalTransferGasUsed).toEqual( + benchmarkResult.gas["externalTransfer"], + ); + }); }); it("addPlugin", async () => { @@ -230,15 +246,16 @@ function testWalletV4( testPlugin, 10000000n, ); - const addPluginSendResult = await sendSignedActionBody( - wallet, - addExtActionsList, + const addPluginSendResult = await step("Send addPlugin", async () => + sendSignedActionBody(wallet, addExtActionsList), ); - expect(addPluginSendResult.transactions).toHaveTransaction({ - to: wallet.address, - success: true, - exitCode: 0, + await step("Should have wallet addPlugin transaction", () => { + expect(addPluginSendResult.transactions).toHaveTransaction({ + to: wallet.address, + success: true, + exitCode: 0, + }); }); const isPluginInstalled = await wallet.getIsPluginInstalled( @@ -246,13 +263,21 @@ function testWalletV4( bufferToBigInt(testPlugin.hash), ); - expect(isPluginInstalled).toBeTruthy(); - return getUsedGas(addPluginSendResult, "external"); + await step("Plugin should be installed", () => { + expect(isPluginInstalled).toBeTruthy(); + }); + return await step("Get gas used", () => + getUsedGas(addPluginSendResult, "external"), + ); }; const addPluginGasUsedTact = await runAddPluginTest(wallet); - expect(addPluginGasUsedTact).toEqual(benchmarkResult.gas["addPlugin"]); + await step("Gas usage should match benchmark", () => { + expect(addPluginGasUsedTact).toEqual( + benchmarkResult.gas["addPlugin"], + ); + }); }); it("pluginTransfer", async () => { // add deployer as plugin @@ -262,7 +287,9 @@ function testWalletV4( deployerAsPlugin, 10000000n, ); - await sendSignedActionBody(wallet, addExtActionsList); + await step("Add deployer as plugin", async () => + sendSignedActionBody(wallet, addExtActionsList), + ); const walletTest = blockchain.openContract( WalletV4.fromAddress(wallet.address), @@ -272,7 +299,9 @@ function testWalletV4( bufferToBigInt(deployerAsPlugin.hash), ); - expect(isPluginInstalled).toBeTruthy(); + await step("Plugin should be installed", () => { + expect(isPluginInstalled).toBeTruthy(); + }); const forwardValue = toNano(1); @@ -283,7 +312,7 @@ function testWalletV4( extra: null, }; - const pluginTransferResult = await step("pluginTransfer", () => + const pluginTransferResult = await step("Send plugin transfer", () => deployer.send({ to: wallet.address, value: toNano("0.1"), @@ -292,19 +321,22 @@ function testWalletV4( }), ); - expect(pluginTransferResult.transactions).toHaveTransaction({ - from: wallet.address, - to: deployer.address, - value: (v) => v! >= forwardValue, // we care about received amount being greater or equal to requested + await step("Should send funds to deployer", () => { + expect(pluginTransferResult.transactions).toHaveTransaction({ + from: wallet.address, + to: deployer.address, + value: (v) => v! >= forwardValue, // we care about received amount being greater or equal to requested + }); }); - const pluginTransferGasUsed = getUsedGas( - pluginTransferResult, - "internal", - ); - expect(pluginTransferGasUsed).toEqual( - benchmarkResult.gas["pluginTransfer"], + const pluginTransferGasUsed = await step("Get gas used", () => + getUsedGas(pluginTransferResult, "internal"), ); + await step("Gas usage should match benchmark", () => { + expect(pluginTransferGasUsed).toEqual( + benchmarkResult.gas["pluginTransfer"], + ); + }); }); } diff --git a/src/benchmarks/wallet-v5/wallet-v5.spec.ts b/src/benchmarks/wallet-v5/wallet-v5.spec.ts index 23b55c8030..4a02bae78c 100644 --- a/src/benchmarks/wallet-v5/wallet-v5.spec.ts +++ b/src/benchmarks/wallet-v5/wallet-v5.spec.ts @@ -34,6 +34,7 @@ import { import benchmarkResults from "@/benchmarks/wallet-v5/results_gas.json"; import { WalletV5 } from "@/benchmarks/wallet-v5/tact/output/wallet-v5_WalletV5"; +import { step } from "@/test/allure/allure"; export function packAddress(address: Address) { return bufferToBigInt(address.hash); @@ -107,37 +108,47 @@ function testWalletV5( await fromInit(true, bufferToBigInt(keypair.publicKey)), ); - const deployResult = await wallet.send( - deployer.getSender(), - { - value: toNano("0.05"), - }, - beginCell().endCell().asSlice(), - ); + const deployResult = await step("Deploy wallet", async () => { + return await wallet.send( + deployer.getSender(), + { + value: toNano("0.05"), + }, + beginCell().endCell().asSlice(), + ); + }); - expect(deployResult.transactions).toHaveTransaction({ - from: deployer.address, - to: wallet.address, - deploy: true, - success: true, + await step("Should deploy wallet", () => { + expect(deployResult.transactions).toHaveTransaction({ + from: deployer.address, + to: wallet.address, + deploy: true, + success: true, + }); }); // Top up wallet balance - await deployer.send({ - to: wallet.address, - value: toNano("10"), - sendMode: SendMode.PAY_GAS_SEPARATELY, + await step("Top up wallet balance", async () => { + await deployer.send({ + to: wallet.address, + value: toNano("10"), + sendMode: SendMode.PAY_GAS_SEPARATELY, + }); }); }); it("check correctness of deploy", async () => { const walletSeqno = await wallet.getSeqno(); - expect(walletSeqno).toBe(0n); + await step("Seqno should be zero", () => { + expect(walletSeqno).toBe(0n); + }); const walletPublicKey = await wallet.getGetPublicKey(); - expect(walletPublicKey).toBe(bufferToBigInt(keypair.publicKey)); + await step("Public key should match", () => { + expect(walletPublicKey).toBe(bufferToBigInt(keypair.publicKey)); + }); }); it("externalTransfer", async () => { @@ -153,24 +164,35 @@ function testWalletV5( forwardValue, ); - const externalTransferSendResult = await sendSignedActionBody( - wallet.address, - sendTxActionsList, - "external", + const externalTransferSendResult = await step( + "Send external transfer", + async () => { + return await sendSignedActionBody( + wallet.address, + sendTxActionsList, + "external", + ); + }, ); - expect(externalTransferSendResult.transactions).toHaveTransaction({ - to: wallet.address, - success: true, - exitCode: 0, + await step("Should succeed external transfer", () => { + expect(externalTransferSendResult.transactions).toHaveTransaction({ + to: wallet.address, + success: true, + exitCode: 0, + }); }); - expect(externalTransferSendResult.transactions.length).toEqual(2); + await step("Should have two transactions", () => { + expect(externalTransferSendResult.transactions.length).toEqual(2); + }); - expect(externalTransferSendResult.transactions).toHaveTransaction({ - from: wallet.address, - to: testReceiver, - value: forwardValue, + await step("Should send funds to receiver", () => { + expect(externalTransferSendResult.transactions).toHaveTransaction({ + from: wallet.address, + to: testReceiver, + value: forwardValue, + }); }); const fee = externalTransferSendResult.transactions[1]!.totalFees.coins; @@ -178,17 +200,21 @@ function testWalletV5( await blockchain.getContract(testReceiver) ).balance; - expect(receiverBalanceAfter).toEqual( - receiverBalanceBefore + forwardValue - fee, - ); + await step("Receiver balance should be correct", () => { + expect(receiverBalanceAfter).toEqual( + receiverBalanceBefore + forwardValue - fee, + ); + }); const externalTransferGasUsed = getUsedGas( externalTransferSendResult, "external", ); - expect(externalTransferGasUsed).toEqual( - benchmarkResult.gas["externalTransfer"], - ); + await step("Gas usage should match benchmark", () => { + expect(externalTransferGasUsed).toEqual( + benchmarkResult.gas["externalTransfer"], + ); + }); }); it("internalTransfer", async () => { @@ -204,25 +230,36 @@ function testWalletV5( forwardValue, ); - const internalTransferSendResult = await sendSignedActionBody( - wallet.address, - sendTxActionsList, - "internal", + const internalTransferSendResult = await step( + "Send internal transfer", + async () => { + return await sendSignedActionBody( + wallet.address, + sendTxActionsList, + "internal", + ); + }, ); - expect(internalTransferSendResult.transactions).toHaveTransaction({ - from: deployer.address, - to: wallet.address, - success: true, - exitCode: 0, + await step("Should succeed internal transfer", () => { + expect(internalTransferSendResult.transactions).toHaveTransaction({ + from: deployer.address, + to: wallet.address, + success: true, + exitCode: 0, + }); }); - expect(internalTransferSendResult.transactions.length).toEqual(3); + await step("Should have three transactions", () => { + expect(internalTransferSendResult.transactions.length).toEqual(3); + }); - expect(internalTransferSendResult.transactions).toHaveTransaction({ - from: wallet.address, - to: testReceiver, - value: forwardValue, + await step("Should send funds to receiver as transactions", () => { + expect(internalTransferSendResult.transactions).toHaveTransaction({ + from: wallet.address, + to: testReceiver, + value: forwardValue, + }); }); const fee = internalTransferSendResult.transactions[2]!.totalFees.coins; @@ -230,46 +267,63 @@ function testWalletV5( await blockchain.getContract(testReceiver) ).balance; - expect(receiverBalanceAfter).toEqual( - receiverBalanceBefore + forwardValue - fee, - ); + await step("Receiver balance should be correct", () => { + expect(receiverBalanceAfter).toEqual( + receiverBalanceBefore + forwardValue - fee, + ); + }); const internalTransferGasUsed = getUsedGas( internalTransferSendResult, "internal", ); - expect(internalTransferGasUsed).toEqual( - benchmarkResult.gas["internalTransfer"], - ); + await step("Gas usage should match benchmark", () => { + expect(internalTransferGasUsed).toEqual( + benchmarkResult.gas["internalTransfer"], + ); + }); }); it("addExtension", async () => { const testExtension = receiver.address; const addExtActionsList = createAddExtActionMsg(testExtension); - const addExtensionSendResult = await sendSignedActionBody( - wallet.address, - addExtActionsList, - "external", + const addExtensionSendResult = await step( + "Send addExtension", + async () => { + return await sendSignedActionBody( + wallet.address, + addExtActionsList, + "external", + ); + }, ); - expect(addExtensionSendResult.transactions).toHaveTransaction({ - to: wallet.address, - success: true, - exitCode: 0, + await step("Should update extension list", () => { + expect(addExtensionSendResult.transactions).toHaveTransaction({ + to: wallet.address, + success: true, + exitCode: 0, + }); }); const extensions = await wallet.getGetExtensions(); - expect(extensions.size).toEqual(1); - expect(extensions.get(packAddress(testExtension))).toEqual(true); + await step("Extensions size should be 1", () => { + expect(extensions.size).toEqual(1); + }); + await step("Extension should be present", () => { + expect(extensions.get(packAddress(testExtension))).toEqual(true); + }); const addExtensionGasUsed = getUsedGas( addExtensionSendResult, "external", ); - expect(addExtensionGasUsed).toEqual( - benchmarkResult.gas["addExtension"], - ); + await step("Gas usage should match benchmark", () => { + expect(addExtensionGasUsed).toEqual( + benchmarkResult.gas["addExtension"], + ); + }); }); it("extensionTransfer", async () => { @@ -282,45 +336,63 @@ function testWalletV5( // add deployer as extension const actionsListAddExt = createAddExtActionMsg(deployer.address); - await sendSignedActionBody( - wallet.address, - actionsListAddExt, - "internal", - ); + await step("Add deployer as extension", async () => { + await sendSignedActionBody( + wallet.address, + actionsListAddExt, + "internal", + ); + }); const extensions = await wallet.getGetExtensions(); - expect(extensions.get(packAddress(deployer.address))).toEqual(true); + await step("Extension should be present for deployer", () => { + expect(extensions.get(packAddress(deployer.address))).toEqual(true); + }); const sendTxActionsList = createSendTxActionMsg( testReceiver, forwardValue, ); - const extensionTransferResult = await sendInternalMessageFromExtension( - deployer, - wallet.address, - { - body: sendTxActionsList, - value: toNano("0.1"), + const extensionTransferResult = await step( + "Send extension transfer", + async () => { + return await sendInternalMessageFromExtension( + deployer, + wallet.address, + { + body: sendTxActionsList, + value: toNano("0.1"), + }, + ); }, ); - expect(extensionTransferResult.transactions).toHaveTransaction({ - from: deployer.address, - to: wallet.address, - success: true, - exitCode: 0, + await step("Should succeed extension transfer", () => { + expect(extensionTransferResult.transactions).toHaveTransaction({ + from: deployer.address, + to: wallet.address, + success: true, + exitCode: 0, + }); }); - // external to ext + internal + ext transfer action - expect(extensionTransferResult.transactions.length).toEqual(3); + await step( + "Should have three transactions (extension transfer)", + () => { + // external to ext + internal + ext transfer action + expect(extensionTransferResult.transactions.length).toEqual(3); + }, + ); - expect(extensionTransferResult.transactions).toHaveTransaction({ - from: wallet.address, - to: testReceiver, - success: true, - value: forwardValue, - exitCode: 0, + await step("Should send funds to receiver (extension)", () => { + expect(extensionTransferResult.transactions).toHaveTransaction({ + from: wallet.address, + to: testReceiver, + success: true, + value: forwardValue, + exitCode: 0, + }); }); const fee = extensionTransferResult.transactions[2]!.totalFees.coins; @@ -328,17 +400,21 @@ function testWalletV5( await blockchain.getContract(testReceiver) ).balance; - expect(receiverBalanceAfter).toEqual( - receiverBalanceBefore + forwardValue - fee, - ); + await step("Receiver balance should be correct (extension)", () => { + expect(receiverBalanceAfter).toEqual( + receiverBalanceBefore + forwardValue - fee, + ); + }); const extensionTransferGasUsed = getUsedGas( extensionTransferResult, "internal", ); - expect(extensionTransferGasUsed).toEqual( - benchmarkResult.gas["extensionTransfer"], - ); + await step("Gas usage should match benchmark (extension)", () => { + expect(extensionTransferGasUsed).toEqual( + benchmarkResult.gas["extensionTransfer"], + ); + }); }); } diff --git a/src/cli/logger.spec.ts b/src/cli/logger.spec.ts index 4d5a0b32c6..5c8f6d5659 100644 --- a/src/cli/logger.spec.ts +++ b/src/cli/logger.spec.ts @@ -6,6 +6,7 @@ import { getAnsiMarkup } from "@/cli/colors"; import { TerminalLogger } from "@/cli/logger"; import pathWindows from "path/win32"; import pathPosix from "path/posix"; +import { step } from "@/test/allure/allure"; const catchProcessExit = (fn: () => T): T | string => { const exitSpy = jest.spyOn(process, "exit").mockImplementation((code) => { @@ -40,7 +41,7 @@ const os = [ describe.each(os)("TerminalLogger %s", (_, pathApi) => { const ansi = getAnsiMarkup(false); - test("only first error without error recovery", () => { + test("only first error without error recovery", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const result = catchProcessExit(() => { return TerminalLogger(pathApi, "info", ansi, (log) => { @@ -48,12 +49,16 @@ describe.each(os)("TerminalLogger %s", (_, pathApi) => { log.error(log.text`Error 2`); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); logSpy.mockRestore(); }); - test("all info logs are logged", () => { + test("all info logs are logged", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const result = catchProcessExit(() => { return TerminalLogger(pathApi, "info", ansi, (log) => { @@ -61,12 +66,16 @@ describe.each(os)("TerminalLogger %s", (_, pathApi) => { log.info(log.text`Info 2`); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); logSpy.mockRestore(); }); - test("warn verbosity does not show info", () => { + test("warn verbosity does not show info", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const result = catchProcessExit(() => { return TerminalLogger(pathApi, "warn", ansi, (log) => { @@ -74,12 +83,16 @@ describe.each(os)("TerminalLogger %s", (_, pathApi) => { log.info(log.text`Info`); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); logSpy.mockRestore(); }); - test("path is resolved relative to cwd", () => { + test("path is resolved relative to cwd", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const cwdSpy = jest.spyOn(process, "cwd").mockReturnValue("/foo"); const result = catchProcessExit(() => { @@ -87,37 +100,49 @@ describe.each(os)("TerminalLogger %s", (_, pathApi) => { log.error(log.text`See ${log.path("/foo/bar")}.`); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); cwdSpy.mockRestore(); logSpy.mockRestore(); }); - test("raw internal error", () => { + test("raw internal error", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const result = catchProcessExit(() => { return TerminalLogger(pathApi, "info", ansi, (_log) => { throwInternal(`OMG`); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); logSpy.mockRestore(); }); - test("logger internal error", () => { + test("logger internal error", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const result = catchProcessExit(() => { return TerminalLogger(pathApi, "info", ansi, (log) => { log.internal(log.text`OMG`); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); logSpy.mockRestore(); }); - test("raw internal error in source", () => { + test("raw internal error in source", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const cwdSpy = jest.spyOn(process, "cwd").mockReturnValue("/foo"); const result = catchProcessExit(() => { @@ -127,13 +152,17 @@ describe.each(os)("TerminalLogger %s", (_, pathApi) => { }); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); cwdSpy.mockRestore(); logSpy.mockRestore(); }); - test("logger internal error in source", () => { + test("logger internal error in source", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const cwdSpy = jest.spyOn(process, "cwd").mockReturnValue("/foo"); const result = catchProcessExit(() => { @@ -144,13 +173,17 @@ describe.each(os)("TerminalLogger %s", (_, pathApi) => { }); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); cwdSpy.mockRestore(); logSpy.mockRestore(); }); - test("internal error in source at range", () => { + test("internal error in source at range", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const cwdSpy = jest.spyOn(process, "cwd").mockReturnValue("/foo"); const result = catchProcessExit(() => { @@ -161,25 +194,33 @@ describe.each(os)("TerminalLogger %s", (_, pathApi) => { }); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); cwdSpy.mockRestore(); logSpy.mockRestore(); }); - test("uncaught error", () => { + test("uncaught error", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const result = catchProcessExit(() => { return TerminalLogger(pathApi, "info", ansi, () => { throw new Error("Uncaught!"); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); logSpy.mockRestore(); }); - test("uncaught error in source", () => { + test("uncaught error in source", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const cwdSpy = jest.spyOn(process, "cwd").mockReturnValue("/foo"); const result = catchProcessExit(() => { @@ -189,13 +230,17 @@ describe.each(os)("TerminalLogger %s", (_, pathApi) => { }); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); cwdSpy.mockRestore(); logSpy.mockRestore(); }); - test("multiple errors", () => { + test("multiple errors", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const result = catchProcessExit(() => { return TerminalLogger(pathApi, "info", ansi, (log) => { @@ -205,12 +250,16 @@ describe.each(os)("TerminalLogger %s", (_, pathApi) => { }); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); logSpy.mockRestore(); }); - test("exit on error", () => { + test("exit on error", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const result = catchProcessExit(() => { return TerminalLogger(pathApi, "info", ansi, (log) => { @@ -222,12 +271,16 @@ describe.each(os)("TerminalLogger %s", (_, pathApi) => { log.error(log.text`impossible`); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); logSpy.mockRestore(); }); - test("multiple errors inside source", () => { + test("multiple errors inside source", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const cwdSpy = jest.spyOn(process, "cwd").mockReturnValue("/foo"); const result = catchProcessExit(() => { @@ -242,13 +295,17 @@ describe.each(os)("TerminalLogger %s", (_, pathApi) => { }); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); cwdSpy.mockRestore(); logSpy.mockRestore(); }); - test("source inside multiple errors", () => { + test("source inside multiple errors", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const cwdSpy = jest.spyOn(process, "cwd").mockReturnValue("/foo"); const result = catchProcessExit(() => { @@ -263,13 +320,17 @@ describe.each(os)("TerminalLogger %s", (_, pathApi) => { }); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); cwdSpy.mockRestore(); logSpy.mockRestore(); }); - test("typed errors", () => { + test("typed errors", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const cwdSpy = jest.spyOn(process, "cwd").mockReturnValue("/foo"); const fooBarSchema = (l: Logger) => ({ @@ -285,13 +346,17 @@ describe.each(os)("TerminalLogger %s", (_, pathApi) => { }); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); cwdSpy.mockRestore(); logSpy.mockRestore(); }); - test("typed errors for source", () => { + test("typed errors for source", async () => { const logSpy = jest.spyOn(console, "log").mockImplementation(() => {}); const cwdSpy = jest.spyOn(process, "cwd").mockReturnValue("/foo"); const fooBarSchemaSrc = (l: SourceLogger) => ({ @@ -309,8 +374,12 @@ describe.each(os)("TerminalLogger %s", (_, pathApi) => { }); }); }); - expect(result).toMatchSnapshot(); - expect(logSpy.mock.calls).toMatchSnapshot(); + await step("Result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); + await step("Console log calls should match snapshot", () => { + expect(logSpy.mock.calls).toMatchSnapshot(); + }); cwdSpy.mockRestore(); logSpy.mockRestore(); }); diff --git a/src/cli/tact-fmt/e2e.spec.ts b/src/cli/tact-fmt/e2e.spec.ts index deed739ab6..97a9e593ac 100644 --- a/src/cli/tact-fmt/e2e.spec.ts +++ b/src/cli/tact-fmt/e2e.spec.ts @@ -2,6 +2,8 @@ import { join, normalize } from "path"; import { runCommand } from "@/cli/test-util.build"; import { readFileSync, rmSync, writeFileSync } from "fs"; import { mkdir } from "fs/promises"; +import { step, attachment } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; // disable tests on windows const testExceptWindows = @@ -45,9 +47,12 @@ describe("tact-fmt foo.tact", () => { testExceptWindows("Exits with correct code", async () => { await mkdir(outputDir, { recursive: true }); const file = join(outputDir, "contract.tact"); + await attachment("Code", goodContract, ContentType.TEXT); writeFileSync(file, goodContract); const result = await tactFmt(file); - expect(result).toMatchObject({ kind: "exited", code: 0 }); + await step("Result exited with code 0", () => { + expect(result).toMatchObject({ kind: "exited", code: 0 }); + }); rmSync(file); }); @@ -55,9 +60,12 @@ describe("tact-fmt foo.tact", () => { testExceptWindows("Default run", async () => { await mkdir(outputDir, { recursive: true }); const file = join(outputDir, "contract.tact"); + await attachment("Code", goodContract, ContentType.TEXT); writeFileSync(file, goodContract); const result = await tactFmt(file); - expect(result).toMatchSnapshot(); + await step("CLI output should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); rmSync(file); }); @@ -65,11 +73,14 @@ describe("tact-fmt foo.tact", () => { testExceptWindows("Default run with write to file", async () => { await mkdir(outputDir, { recursive: true }); const file = join(outputDir, "contract.tact"); + await attachment("Code", goodContract, ContentType.TEXT); writeFileSync(file, goodContract); await tactFmt(file, "-w"); const newContent = readFileSync(file, "utf8"); - expect(newContent).toMatchSnapshot(); + await step("Formatted file should match snapshot", () => { + expect(newContent).toMatchSnapshot(); + }); rmSync(file); }); @@ -98,9 +109,15 @@ describe("tact-fmt foo.tact", () => { await tactFmt(innerDir, "-w"); - expect(readFileSync(file1, "utf8")).toMatchSnapshot(); - expect(readFileSync(file2, "utf8")).toMatchSnapshot(); - expect(readFileSync(file3, "utf8")).toMatchSnapshot(); + await step("file1.tact should match snapshot", () => { + expect(readFileSync(file1, "utf8")).toMatchSnapshot(); + }); + await step("file2.tact should match snapshot", () => { + expect(readFileSync(file2, "utf8")).toMatchSnapshot(); + }); + await step("file3.tact should match snapshot", () => { + expect(readFileSync(file3, "utf8")).toMatchSnapshot(); + }); rmSync(innerDir, { recursive: true }); }); @@ -128,7 +145,9 @@ describe("tact-fmt foo.tact", () => { writeFileSync(file3, "fun foo3() {}\n"); const result = await tactFmt(innerDir, "--check"); - expect(result).toMatchSnapshot(); + await step("Check-only CLI output should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); rmSync(innerDir, { recursive: true }); }); @@ -158,7 +177,9 @@ describe("tact-fmt foo.tact", () => { writeFileSync(file3, "fun foo3() { }\n"); const result = await tactFmt(innerDir, "--check"); - expect(result).toMatchSnapshot(); + await step("Check-only CLI output should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); rmSync(innerDir, { recursive: true }); }, @@ -187,7 +208,12 @@ describe("tact-fmt foo.tact", () => { writeFileSync(file3, "fun foo3() {}\n"); const result = await tactFmt("--check", file1, file3); - expect(result).toMatchSnapshot(); + await step( + "Check multiple files CLI output should match snapshot", + () => { + expect(result).toMatchSnapshot(); + }, + ); rmSync(innerDir, { recursive: true }); }); @@ -218,7 +244,12 @@ describe("tact-fmt foo.tact", () => { writeFileSync(file3, "fun foo3() {}\n"); const result = await tactFmt("--check", innerInnerDir, innerInnerDir2); - expect(result).toMatchSnapshot(); + await step( + "Check multiple directories CLI output should match snapshot", + () => { + expect(result).toMatchSnapshot(); + }, + ); rmSync(innerDir, { recursive: true }); }); @@ -226,9 +257,12 @@ describe("tact-fmt foo.tact", () => { testExceptWindows("With error", async () => { await mkdir(outputDir, { recursive: true }); const file = join(outputDir, "contract.tact"); + await attachment("Code", badContract, ContentType.TEXT); writeFileSync(file, badContract); const result = await tactFmt(file); - expect(result).toMatchObject({ kind: "exited", code: 1 }); + await step("Result exited with code 1", () => { + expect(result).toMatchObject({ kind: "exited", code: 1 }); + }); rmSync(file); }); @@ -236,9 +270,12 @@ describe("tact-fmt foo.tact", () => { testExceptWindows("With syntax error", async () => { await mkdir(outputDir, { recursive: true }); const file = join(outputDir, "contact.tact"); + await attachment("Code", contractWithSyntaxError, ContentType.TEXT); writeFileSync(file, contractWithSyntaxError); const result = await tactFmt(file, "-w"); - expect(result).toMatchSnapshot(); + await step("Syntax error output should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); rmSync(file); }); @@ -246,9 +283,12 @@ describe("tact-fmt foo.tact", () => { testExceptWindows("Check and write flags simultaneously", async () => { await mkdir(outputDir, { recursive: true }); const file = join(outputDir, "contact.tact"); + await attachment("Code", contractWithSyntaxError, ContentType.TEXT); writeFileSync(file, contractWithSyntaxError); const result = await tactFmt(file, "-w", "--check"); - expect(result).toMatchSnapshot(); + await step("Formatted code should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); rmSync(file); }); diff --git a/src/cli/tact/e2e.spec.ts b/src/cli/tact/e2e.spec.ts index 2dbae737da..13225e1e46 100644 --- a/src/cli/tact/e2e.spec.ts +++ b/src/cli/tact/e2e.spec.ts @@ -1,6 +1,8 @@ import { stat } from "fs/promises"; import { makeCodegen, runCommand } from "@/cli/test-util.build"; -import { join, normalize, dirname } from "path"; +import { dirname, join, normalize } from "path"; +import { attachment, step } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; // disable tests on windows const testExceptWindows = @@ -20,16 +22,22 @@ describe("tact --version", () => { testExceptWindows("Exits with correct code", async () => { const result = await tact("--version"); - expect(result).toMatchObject({ kind: "exited", code: 0 }); + await step("Result exited with code 0", () => { + expect(result).toMatchObject({ kind: "exited", code: 0 }); + }); }); testExceptWindows("Returns correct stdout", async () => { const result = await tact("--version"); - expect(result).toHaveProperty( - "stdout", - expect.stringMatching(/^\d+\.\d+\.\d+\ngit commit: [0-9a-f]+\n$/), - ); + await step("Stdout contains version string", () => { + expect(result).toHaveProperty( + "stdout", + expect.stringMatching( + /^\d+\.\d+\.\d+\ngit commit: [0-9a-f]+\n$/, + ), + ); + }); }); }); @@ -62,10 +70,12 @@ describe("tact foo.tact", () => { await codegen.contract(`no-err-${name}`, code), ); - expect(result).toHaveProperty( - "stdout", - expect.not.stringMatching(/.*at \w+ \(.*/), - ); + await step("Stdout contains no stacktrace", () => { + expect(result).toHaveProperty( + "stdout", + expect.not.stringMatching(/.*at \w+ \(.*/), + ); + }); }, ); @@ -97,99 +107,131 @@ describe("tact foo.tact --...", () => { testExceptWindows( "Check single-contract compilation without flags", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const path = await codegen.contract(`single`, goodContract); const result = await tact(path); - expect(result).toMatchObject({ kind: "exited", code: 0 }); + await step("Result exited with code 0", () => { + expect(result).toMatchObject({ kind: "exited", code: 0 }); + }); }, ); testExceptWindows( "Check single-contract compilation with custom output directory", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const path = await codegen.contract(`single-output`, goodContract); const customOutput = "custom-output"; const result = await tact(`${path} --output ${customOutput}`); - expect(result).toMatchObject({ kind: "exited", code: 0 }); + await step("Result exited with code 0", () => { + expect(result).toMatchObject({ kind: "exited", code: 0 }); + }); const statPromise = stat(join(dirname(path), customOutput)); - await expect(statPromise).resolves.not.toThrow(); + await step("Custom output directory exists", async () => { + await expect(statPromise).resolves.not.toThrow(); + }); }, ); testExceptWindows( "Check single-contract compilation with --check", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const path = await codegen.contract(`single-check`, goodContract); const result = await tact(`--check ${path}`); - expect(result).toMatchObject({ kind: "exited", code: 0 }); + await step("Result exited with code 0", () => { + expect(result).toMatchObject({ kind: "exited", code: 0 }); + }); }, ); testExceptWindows( "Check single-contract compilation with --func", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const path = await codegen.contract(`single-func`, goodContract); const result = await tact(`--func ${path}`); - expect(result).toMatchObject({ kind: "exited", code: 0 }); + await step("Result exited with code 0", () => { + expect(result).toMatchObject({ kind: "exited", code: 0 }); + }); }, ); testExceptWindows( "Check single-contract compilation with --with-decompilation", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const path = await codegen.contract( `single-decompile`, goodContract, ); const result = await tact(`--with-decompilation ${path}`); - expect(result).toMatchObject({ kind: "exited", code: 0 }); + await step("Result exited with code 0", () => { + expect(result).toMatchObject({ kind: "exited", code: 0 }); + }); }, ); }); describe("tact --config config.json", () => { testExceptWindows("Complete results", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const r = await codegen.config("complete", goodContract, { options: { external: true }, mode: "full", }); const result = await tact(`--config ${r.config}`); - expect(result).toMatchObject({ kind: "exited", code: 0 }); + await step("Result exited with code 0", () => { + expect(result).toMatchObject({ kind: "exited", code: 0 }); + }); const statPromise = stat(r.outputPath("pkg")); - await expect(statPromise).resolves.not.toThrow(); + await step("Package directory exists", async () => { + await expect(statPromise).resolves.not.toThrow(); + }); }); testExceptWindows("With decompiled binary", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const r = await codegen.config("decompile", goodContract, { options: { external: true }, mode: "fullWithDecompilation", }); const result = await tact(`--config ${r.config}`); - expect(result).toMatchObject({ kind: "exited", code: 0 }); + await step("Result exited with code 0", () => { + expect(result).toMatchObject({ kind: "exited", code: 0 }); + }); const statPromise = stat(r.outputPath("rev.fif")); - await expect(statPromise).resolves.toMatchObject({}); + await step("Reverse binary file exists", async () => { + await expect(statPromise).resolves.toMatchObject({}); + }); }); testExceptWindows("Mode passed as parameter takes priority", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const r = await codegen.config("priority", goodContract, { options: { external: true }, mode: "full", }); const result = await tact(`--check --config ${r.config}`); - expect(result).toMatchObject({ kind: "exited", code: 0 }); + await step("Result exited with code 0", () => { + expect(result).toMatchObject({ kind: "exited", code: 0 }); + }); const statPromise = stat(r.outputPath("pkg")); - await expect(statPromise).rejects.toThrow(); + await step("Package directory should not exist", async () => { + await expect(statPromise).rejects.toThrow(); + }); }); }); @@ -201,50 +243,66 @@ describe("tact -q foo.tact", () => { ); const result = await tact(`-q ${path}`); - expect( - result.kind === "exited" && - result.stderr.includes("Cannot find 'A'"), - ).toBe(true); - expect(result).toMatchObject({ kind: "exited", code: 30 }); + await step("Stderr reports compilation error", () => { + expect( + result.kind === "exited" && + result.stderr.includes("Cannot find 'A'"), + ).toBe(true); + }); + await step("Result exited with code 30", () => { + expect(result).toMatchObject({ kind: "exited", code: 30 }); + }); }); }); describe("Wrong flags", () => { testExceptWindows("--func --check are mutually exclusive ", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const path = await codegen.contract(`func-check`, goodContract); const result = await tact(`${path} --func --check`); - expect(result).toMatchObject({ kind: "exited", code: 30 }); + await step("Result exited with code 30", () => { + expect(result).toMatchObject({ kind: "exited", code: 30 }); + }); }); testExceptWindows( "--with-decompilation --check are mutually exclusive", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const path = await codegen.contract( `decompile-check`, goodContract, ); const result = await tact(`${path} --with-decompilation --check`); - expect(result).toMatchObject({ kind: "exited", code: 30 }); + await step("Result exited with code 30", () => { + expect(result).toMatchObject({ kind: "exited", code: 30 }); + }); }, ); testExceptWindows( "--func --with-decompilation are mutually exclusive", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const path = await codegen.contract(`func-decompile`, goodContract); const result = await tact(`${path} --func --with-decompilation`); - expect(result).toMatchObject({ kind: "exited", code: 30 }); + await step("Result exited with code 30", () => { + expect(result).toMatchObject({ kind: "exited", code: 30 }); + }); }, ); testExceptWindows("Unknown flag throws error", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const path = await codegen.contract(`func-decompile`, goodContract); const result = await tact(`${path} --unknownOption`); - expect(result).toMatchObject({ kind: "exited", code: 30 }); + await step("Result exited with code 30", () => { + expect(result).toMatchObject({ kind: "exited", code: 30 }); + }); }); }); @@ -264,7 +322,9 @@ describe("Compilation failures", () => { }); const result = await tact(`--config ${r.config}`); - expect(result).toMatchObject({ kind: "exited", code: 30 }); + await step("Result exited with code 30", () => { + expect(result).toMatchObject({ kind: "exited", code: 30 }); + }); }); }); @@ -274,7 +334,12 @@ describe("tact --eval", () => { '-e "(1 + 2 * (pow(3,4) - 2) << 1 & 0x54 | 33 >> 1) * 2 + 2"', ); - expect(result).toHaveProperty("stdout", expect.stringMatching("42\n")); + await step("Stdout evaluates to 42", () => { + expect(result).toHaveProperty( + "stdout", + expect.stringMatching("42\n"), + ); + }); }); testExceptWindows( @@ -282,10 +347,12 @@ describe("tact --eval", () => { async () => { const result = await tact('-e "0x2A"'); - expect(result).toHaveProperty( - "stdout", - expect.stringMatching("42\n"), - ); + await step("Stdout evaluates to 42", () => { + expect(result).toHaveProperty( + "stdout", + expect.stringMatching("42\n"), + ); + }); }, ); @@ -294,10 +361,12 @@ describe("tact --eval", () => { async () => { const result = await tact('-e "42"'); - expect(result).toHaveProperty( - "stdout", - expect.stringMatching("42\n"), - ); + await step("Stdout evaluates to 42", () => { + expect(result).toHaveProperty( + "stdout", + expect.stringMatching("42\n"), + ); + }); }, ); @@ -306,10 +375,12 @@ describe("tact --eval", () => { async () => { const result = await tact('-e "0o52"'); - expect(result).toHaveProperty( - "stdout", - expect.stringMatching("42\n"), - ); + await step("Stdout evaluates to 42", () => { + expect(result).toHaveProperty( + "stdout", + expect.stringMatching("42\n"), + ); + }); }, ); @@ -318,10 +389,12 @@ describe("tact --eval", () => { async () => { const result = await tact('-e "0b101010"'); - expect(result).toHaveProperty( - "stdout", - expect.stringMatching("42\n"), - ); + await step("Stdout evaluates to 42", () => { + expect(result).toHaveProperty( + "stdout", + expect.stringMatching("42\n"), + ); + }); }, ); }); diff --git a/src/cli/unboc/e2e.spec.ts b/src/cli/unboc/e2e.spec.ts index 0eef836194..631e04df0f 100644 --- a/src/cli/unboc/e2e.spec.ts +++ b/src/cli/unboc/e2e.spec.ts @@ -1,5 +1,7 @@ import { join, normalize } from "path"; import { makeCodegen, runCommand } from "@/cli/test-util.build"; +import { step, attachment } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; // disable tests on windows const testWin = @@ -32,40 +34,55 @@ contract Test { describe("unboc foo.boc", () => { testWin("Exits with correct code", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const r = await codegen.config(`unboc`, goodContract, {}); await tact(`-c ${r.config}`); const result = await unboc(r.outputPath("code.boc")); - expect(result).toMatchObject({ kind: "exited", code: 0 }); + await step("Result exited with code 0", () => { + expect(result).toMatchObject({ kind: "exited", code: 0 }); + }); }); testWin("Default run", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const r = await codegen.config(`unboc`, goodContract, {}); await tact(`-c ${r.config}`); const result = await unboc(r.outputPath("code.boc")); - expect(result).toMatchSnapshot(); + await step("CLI output should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); testWin("Without aliases", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const r = await codegen.config(`unboc`, goodContract, {}); await tact(`-c ${r.config}`); const result = await unboc("--no-aliases", r.outputPath("code.boc")); - expect(result).toMatchSnapshot(); + await step("CLI output without aliases should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); testWin("Without refs", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const r = await codegen.config(`unboc`, goodContract, {}); await tact(`-c ${r.config}`); const result = await unboc( "--no-compute-refs", r.outputPath("code.boc"), ); - expect(result).toMatchSnapshot(); + await step("CLI output without refs should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); testWin("With bitcode", async () => { + await attachment("Code", goodContract, ContentType.TEXT); const r = await codegen.config(`unboc`, goodContract, {}); await tact(`-c ${r.config}`); const result = await unboc("--show-bitcode", r.outputPath("code.boc")); - expect(result).toMatchSnapshot(); + await step("CLI output with bitcode should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); }); diff --git a/src/error/async-util.spec.ts b/src/error/async-util.spec.ts index 22517225aa..e3ca6d7f4f 100644 --- a/src/error/async-util.spec.ts +++ b/src/error/async-util.spec.ts @@ -1,23 +1,30 @@ import { catchUncolored, thenUncolored } from "@/error/async-util"; +import { step } from "@/test/allure/allure"; describe("catchUncolored", () => { - it("should return the result of a synchronous function when it does not throw an error", () => { + it("should return the result of a synchronous function when it does not throw an error", async () => { const result = catchUncolored( () => 1, () => { throw new Error("Rethrow"); }, ); - expect(result).toBe(1); + await step("Result should be 1", () => { + expect(result).toBe(1); + }); }); - it("should call onError callback when the synchronous function throws an error", () => { + it("should call onError callback when the synchronous function throws an error", async () => { const onError = jest.fn(() => "Handled"); const result = catchUncolored(() => { throw new Error("Test Error"); }, onError); - expect(result).toBe("Handled"); - expect(onError).toHaveBeenCalledWith(expect.any(Error)); + await step("Result should be 'Handled'", () => { + expect(result).toBe("Handled"); + }); + await step("onError should be called with Error", () => { + expect(onError).toHaveBeenCalledWith(expect.any(Error)); + }); }); it("should return the result of an asynchronous function", async () => { @@ -27,7 +34,9 @@ describe("catchUncolored", () => { throw new Error("Rethrow"); }, ); - expect(result).toBe(3); + await step("Result should be 3", () => { + expect(result).toBe(3); + }); }); it("should handle error in asynchronous function and return result from synchronous onError", async () => { @@ -37,8 +46,12 @@ describe("catchUncolored", () => { throw new Error("Async Error"); // eslint-disable-next-line @typescript-eslint/no-explicit-any }, onError as any); - expect(result).toBe("Handled"); - expect(onError).toHaveBeenCalledWith(expect.any(Error)); + await step("Result should be 'Handled'", () => { + expect(result).toBe("Handled"); + }); + await step("onError should be called with Error", () => { + expect(onError).toHaveBeenCalledWith(expect.any(Error)); + }); }); it("should handle error in asynchronous function and return result from asynchronous onError", async () => { @@ -48,8 +61,12 @@ describe("catchUncolored", () => { const result = await catchUncolored(async () => { throw new Error("Async Error"); }, onError); - expect(result).toBe("Handled"); - expect(onError).toHaveBeenCalledWith(expect.any(Error)); + await step("Result should be 'Handled'", () => { + expect(result).toBe("Handled"); + }); + await step("onError should be called with Error", () => { + expect(onError).toHaveBeenCalledWith(expect.any(Error)); + }); }); it("should return result from synchronous onError when async function throws an error", async () => { @@ -59,8 +76,12 @@ describe("catchUncolored", () => { throw new Error("Async Error"); // eslint-disable-next-line @typescript-eslint/no-explicit-any }, onError as any); - expect(result).toBe("Handled"); - expect(onError).toHaveBeenCalledWith(expect.any(Error)); + await step("Result should be 'Handled'", () => { + expect(result).toBe("Handled"); + }); + await step("onError should be called with Error", () => { + expect(onError).toHaveBeenCalledWith(expect.any(Error)); + }); }); it("should return result from asynchronous onError when async function throws an error", async () => { @@ -70,19 +91,27 @@ describe("catchUncolored", () => { const result = await catchUncolored(async () => { throw new Error("Async Error"); }, onError); - expect(result).toBe("Handled"); - expect(onError).toHaveBeenCalledWith(expect.any(Error)); + await step("Result should be 'Handled'", () => { + expect(result).toBe("Handled"); + }); + await step("onError should be called with Error", () => { + expect(onError).toHaveBeenCalledWith(expect.any(Error)); + }); }); }); describe("thenUncolored", () => { - it("should return the result of a synchronous function when function is passed", () => { + it("should return the result of a synchronous function when function is passed", async () => { const result = thenUncolored(3, () => 2); - expect(result).toBe(2); + await step("Result should be 2", () => { + expect(result).toBe(2); + }); }); it("should return a Promise that resolves to the result when a Promise is passed", async () => { const result = thenUncolored(Promise.resolve(5), () => 2); - await expect(result).resolves.toBe(2); + await step("Promise result should resolve to 2", async () => { + await expect(result).resolves.toBe(2); + }); }); }); diff --git a/src/error/logger-util.spec.ts b/src/error/logger-util.spec.ts index fe00b7e737..2c438d1e37 100644 --- a/src/error/logger-util.spec.ts +++ b/src/error/logger-util.spec.ts @@ -6,6 +6,7 @@ import { handleTopLevelErrors, rethrowWithPath, } from "@/error/logger-util"; +import { step } from "@/test/allure/allure"; describe("handleTopLevelErrors", () => { const mockLogger = { @@ -21,16 +22,20 @@ describe("handleTopLevelErrors", () => { jest.clearAllMocks(); }); - it("should return result when no error is thrown", () => { + it("should return result when no error is thrown", async () => { const cb = jest.fn(() => "result"); const result = handleTopLevelErrors(mockLogger, cb, exit); - expect(result).toBe("result"); - expect(cb).toHaveBeenCalledTimes(1); + await step("Result should equal 'result'", () => { + expect(result).toBe("result"); + }); + await step("Callback should be called once", () => { + expect(cb).toHaveBeenCalledTimes(1); + }); }); - it("should handle _ExitError and call process.exit(30)", () => { + it("should handle _ExitError and call process.exit(30)", async () => { const cb = jest.fn(() => { throw new _ExitError(); }); @@ -50,17 +55,22 @@ describe("handleTopLevelErrors", () => { caughtError = error; } - // Now we check the caught error at the top level of the test - expect(caughtError).not.toBeNull(); - expect(caughtError).toMatchObject({ - message: "process.exit called with code 30", + await step("Error should be caught", () => { + expect(caughtError).not.toBeNull(); + }); + await step("Caught error message should include exit code 30", () => { + expect(caughtError).toMatchObject({ + message: "process.exit called with code 30", + }); + }); + await step("Callback should be called once", () => { + expect(cb).toHaveBeenCalledTimes(1); }); - expect(cb).toHaveBeenCalledTimes(1); exitSpy.mockRestore(); }); - it("should handle TactInternalError and call process.exit(30)", () => { + it("should handle TactInternalError and call process.exit(30)", async () => { const internalError = new TactInternalError("Internal error"); const cb = jest.fn(() => { throw internalError; @@ -80,16 +90,22 @@ describe("handleTopLevelErrors", () => { caughtError = error; } - expect(caughtError).not.toBeNull(); - expect(caughtError).toMatchObject({ - message: "process.exit called with code 30", + await step("Error should be caught", () => { + expect(caughtError).not.toBeNull(); + }); + await step("Caught error message should include exit code 30", () => { + expect(caughtError).toMatchObject({ + message: "process.exit called with code 30", + }); + }); + await step("Callback should be called once", () => { + expect(cb).toHaveBeenCalledTimes(1); }); - expect(cb).toHaveBeenCalledTimes(1); exitSpy.mockRestore(); }); - it("should handle general error and call process.exit(30)", () => { + it("should handle general error and call process.exit(30)", async () => { const generalError = new Error("General error"); const cb = jest.fn(() => { throw generalError; @@ -109,11 +125,17 @@ describe("handleTopLevelErrors", () => { caughtError = error; } - expect(caughtError).not.toBeNull(); - expect(caughtError).toMatchObject({ - message: "process.exit called with code 30", + await step("Error should be caught", () => { + expect(caughtError).not.toBeNull(); + }); + await step("Caught error message should include exit code 30", () => { + expect(caughtError).toMatchObject({ + message: "process.exit called with code 30", + }); + }); + await step("Callback should be called once", () => { + expect(cb).toHaveBeenCalledTimes(1); }); - expect(cb).toHaveBeenCalledTimes(1); exitSpy.mockRestore(); }); @@ -123,9 +145,15 @@ describe("handleTopLevelErrors", () => { const result = await handleTopLevelErrors(mockLogger, cb, exit); - expect(result).toBe("resolved value"); - expect(cb).toHaveBeenCalledTimes(1); - expect(mockLogger.internal).not.toHaveBeenCalled(); + await step("Promise result should equal 'resolved value'", () => { + expect(result).toBe("resolved value"); + }); + await step("Callback should be called once", () => { + expect(cb).toHaveBeenCalledTimes(1); + }); + await step("Logger.internal should not be called", () => { + expect(mockLogger.internal).not.toHaveBeenCalled(); + }); }); it("should handle Promise rejecting with _ExitError", async () => { @@ -146,12 +174,17 @@ describe("handleTopLevelErrors", () => { caughtError = error; } - // Now we check the caught error at the top level of the test - expect(caughtError).not.toBeNull(); - expect(caughtError).toMatchObject({ - message: "process.exit called with code 30", + await step("Error should be caught", () => { + expect(caughtError).not.toBeNull(); + }); + await step("Caught error message should include exit code 30", () => { + expect(caughtError).toMatchObject({ + message: "process.exit called with code 30", + }); + }); + await step("Callback should be called once", () => { + expect(cb).toHaveBeenCalledTimes(1); }); - expect(cb).toHaveBeenCalledTimes(1); exitSpy.mockRestore(); }); @@ -174,11 +207,17 @@ describe("handleTopLevelErrors", () => { caughtError = error; } - expect(caughtError).not.toBeNull(); - expect(caughtError).toMatchObject({ - message: "process.exit called with code 30", + await step("Error should be caught", () => { + expect(caughtError).not.toBeNull(); + }); + await step("Caught error message should include exit code 30", () => { + expect(caughtError).toMatchObject({ + message: "process.exit called with code 30", + }); + }); + await step("Callback should be called once", () => { + expect(cb).toHaveBeenCalledTimes(1); }); - expect(cb).toHaveBeenCalledTimes(1); exitSpy.mockRestore(); }); @@ -201,41 +240,55 @@ describe("handleTopLevelErrors", () => { caughtError = error; } - expect(caughtError).not.toBeNull(); - expect(caughtError).toMatchObject({ - message: "process.exit called with code 30", + await step("Error should be caught", () => { + expect(caughtError).not.toBeNull(); + }); + await step("Caught error message should include exit code 30", () => { + expect(caughtError).toMatchObject({ + message: "process.exit called with code 30", + }); + }); + await step("Callback should be called once", () => { + expect(cb).toHaveBeenCalledTimes(1); }); - expect(cb).toHaveBeenCalledTimes(1); exitSpy.mockRestore(); }); }); describe("rethrowWithPath", () => { - it("should append path to TactInternalError message", () => { + it("should append path to TactInternalError message", async () => { const error = new TactInternalError("Internal error"); const path = "/some/path"; - expect(() => rethrowWithPath(error, path)).toThrow( - new TactInternalError("Internal error\nwhile compiling /some/path"), - ); + await step("Throws TactInternalError with appended path", () => { + expect(() => rethrowWithPath(error, path)).toThrow( + new TactInternalError( + "Internal error\nwhile compiling /some/path", + ), + ); + }); }); - it("should append path to general Error message", () => { + it("should append path to general Error message", async () => { const error = new Error("General error"); const path = "/some/path"; - expect(() => rethrowWithPath(error, path)).toThrow( - new Error("General error\nwhile compiling /some/path"), - ); + await step("Throws Error with appended path", () => { + expect(() => rethrowWithPath(error, path)).toThrow( + new Error("General error\nwhile compiling /some/path"), + ); + }); }); - it("should not modify the error message for non-Error instances", () => { + it("should not modify the error message for non-Error instances", async () => { const error = "Some non-error string"; const path = "/some/path"; - expect(() => rethrowWithPath(error, path)).toThrow( - "Some non-error string", - ); + await step("Throws original non-error value", () => { + expect(() => rethrowWithPath(error, path)).toThrow( + "Some non-error string", + ); + }); }); }); diff --git a/src/fmt/test/helpers.ts b/src/fmt/test/helpers.ts index 0b0d2dc7f2..1c8e0c029e 100644 --- a/src/fmt/test/helpers.ts +++ b/src/fmt/test/helpers.ts @@ -1,5 +1,7 @@ import { parseCode } from "@/fmt/cst/cst-helpers"; import { format } from "@/fmt/formatter/formatter"; +import { attachment, step } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; function normalizeIndentation(input: string): string { const lines = input.split("\n"); @@ -27,19 +29,27 @@ function normalizeIndentation(input: string): string { } export const test = (input: string, output: string) => { - return (): void => { + return async (): Promise => { const normalizedInput = normalizeIndentation(input).trim(); const normalizedOutput = normalizeIndentation(output).trim(); + await attachment("normalizedInput", normalizedInput, ContentType.TEXT); + await attachment( + "normalizedOutput", + normalizedOutput, + ContentType.TEXT, + ); const root = parseCode(normalizedInput); if (root === undefined) { throw new Error("cannot parse code"); } const formatted = format(root); - expect(formatted.trim()).toBe(normalizedOutput); + await step("Formatted code should match output", () => { + expect(formatted.trim()).toBe(normalizedOutput); + }); }; }; -export const intact = (input: string): (() => void) => { +export const intact = (input: string): (() => Promise) => { return test(input, input); }; diff --git a/src/func/funcCompile.spec.ts b/src/func/funcCompile.spec.ts index ee74d5c197..f1ecee6619 100644 --- a/src/func/funcCompile.spec.ts +++ b/src/func/funcCompile.spec.ts @@ -3,6 +3,8 @@ import path from "path"; import { Logger } from "@/context/logger"; import { funcCompile } from "@/func/funcCompile"; import * as Stdlib from "@/stdlib/stdlib"; +import { attachment, step } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; describe("funcCompile", () => { it("should compile small contract", async () => { @@ -24,6 +26,9 @@ describe("funcCompile", () => { ], logger: new Logger(), }); - expect(res.ok).toBe(true); + await attachment("Source", source, ContentType.TEXT); + await step("Compilation should succeed", () => { + expect(res.ok).toBe(true); + }); }); }); diff --git a/src/generator/writers/resolveFuncType.spec.ts b/src/generator/writers/resolveFuncType.spec.ts index 4e797583c2..744c34f822 100644 --- a/src/generator/writers/resolveFuncType.spec.ts +++ b/src/generator/writers/resolveFuncType.spec.ts @@ -6,6 +6,8 @@ import { openContext, parseModules } from "@/context/store"; import { CompilerContext } from "@/context/context"; import { getParser } from "@/grammar"; import type { Source } from "@/imports/source"; +import { attachment, step } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; const primitiveCode = ` primitive Int; @@ -15,7 +17,7 @@ primitive Cell; primitive Slice; trait BaseTrait { - + } struct Struct1 { @@ -32,7 +34,7 @@ contract Contract1 { c2: Int; init() { - + } } @@ -47,11 +49,12 @@ contract Contract2 { `; describe("resolveFuncType", () => { - it("should process primitive types", () => { + it("should process primitive types", async () => { const ast = getAstFactory(); const sources: Source[] = [ { code: primitiveCode, path: "", origin: "user" }, ]; + await attachment("Code", primitiveCode, ContentType.TEXT); let ctx = openContext( new CompilerContext(), sources, @@ -60,70 +63,94 @@ describe("resolveFuncType", () => { ); ctx = resolveDescriptors(ctx, ast); const wCtx = new WriterContext(ctx, "Contract1"); - expect( - resolveFuncType( - { kind: "ref", name: "Int", optional: false }, - wCtx, - ), - ).toBe("int"); - expect( - resolveFuncType( - { kind: "ref", name: "Bool", optional: false }, - wCtx, - ), - ).toBe("int"); - expect( - resolveFuncType( - { kind: "ref", name: "Cell", optional: false }, - wCtx, - ), - ).toBe("cell"); - expect( - resolveFuncType( - { kind: "ref", name: "Slice", optional: false }, - wCtx, - ), - ).toBe("slice"); - expect( - resolveFuncType( - { kind: "ref", name: "Builder", optional: false }, - wCtx, - ), - ).toBe("builder"); - expect( - resolveFuncType({ kind: "ref", name: "Int", optional: true }, wCtx), - ).toBe("int"); - expect( - resolveFuncType( - { kind: "ref", name: "Bool", optional: true }, - wCtx, - ), - ).toBe("int"); - expect( - resolveFuncType( - { kind: "ref", name: "Cell", optional: true }, - wCtx, - ), - ).toBe("cell"); - expect( - resolveFuncType( - { kind: "ref", name: "Slice", optional: true }, - wCtx, - ), - ).toBe("slice"); - expect( - resolveFuncType( - { kind: "ref", name: "Builder", optional: true }, - wCtx, - ), - ).toBe("builder"); + await step("Int should resolve to int", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Int", optional: false }, + wCtx, + ), + ).toBe("int"); + }); + await step("Bool should resolve to int", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Bool", optional: false }, + wCtx, + ), + ).toBe("int"); + }); + await step("Cell should resolve to cell", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Cell", optional: false }, + wCtx, + ), + ).toBe("cell"); + }); + await step("Slice should resolve to slice", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Slice", optional: false }, + wCtx, + ), + ).toBe("slice"); + }); + await step("Builder should resolve to builder", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Builder", optional: false }, + wCtx, + ), + ).toBe("builder"); + }); + await step("Optional Int should resolve to int", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Int", optional: true }, + wCtx, + ), + ).toBe("int"); + }); + await step("Optional Bool should resolve to int", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Bool", optional: true }, + wCtx, + ), + ).toBe("int"); + }); + await step("Optional Cell should resolve to cell", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Cell", optional: true }, + wCtx, + ), + ).toBe("cell"); + }); + await step("Optional Slice should resolve to slice", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Slice", optional: true }, + wCtx, + ), + ).toBe("slice"); + }); + await step("Optional Builder should resolve to builder", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Builder", optional: true }, + wCtx, + ), + ).toBe("builder"); + }); }); - it("should process contract and struct types", () => { + it("should process contract and struct types", async () => { const ast = getAstFactory(); const sources: Source[] = [ { code: primitiveCode, path: "", origin: "user" }, ]; + await attachment("Code", primitiveCode, ContentType.TEXT); let ctx = openContext( new CompilerContext(), sources, @@ -132,53 +159,69 @@ describe("resolveFuncType", () => { ); ctx = resolveDescriptors(ctx, ast); const wCtx = new WriterContext(ctx, "Contract1"); - expect( - resolveFuncType( - { kind: "ref", name: "Struct1", optional: false }, - wCtx, - ), - ).toBe("(int, int)"); - expect( - resolveFuncType( - { kind: "ref", name: "Struct2", optional: false }, - wCtx, - ), - ).toBe("(int)"); - expect( - resolveFuncType( - { kind: "ref", name: "Contract1", optional: false }, - wCtx, - ), - ).toBe("(int, int)"); - expect( - resolveFuncType( - { kind: "ref", name: "Contract2", optional: false }, - wCtx, - ), - ).toBe("(int, (int, int))"); - expect( - resolveFuncType( - { kind: "ref", name: "Struct1", optional: true }, - wCtx, - ), - ).toBe("tuple"); - expect( - resolveFuncType( - { kind: "ref", name: "Struct2", optional: true }, - wCtx, - ), - ).toBe("tuple"); - expect( - resolveFuncType( - { kind: "ref", name: "Contract1", optional: true }, - wCtx, - ), - ).toBe("tuple"); - expect( - resolveFuncType( - { kind: "ref", name: "Contract2", optional: true }, - wCtx, - ), - ).toBe("tuple"); + await step("Struct1 should resolve to (int, int)", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Struct1", optional: false }, + wCtx, + ), + ).toBe("(int, int)"); + }); + await step("Struct2 should resolve to (int)", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Struct2", optional: false }, + wCtx, + ), + ).toBe("(int)"); + }); + await step("Contract1 should resolve to (int, int)", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Contract1", optional: false }, + wCtx, + ), + ).toBe("(int, int)"); + }); + await step("Contract2 should resolve to (int, (int, int))", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Contract2", optional: false }, + wCtx, + ), + ).toBe("(int, (int, int))"); + }); + await step("Optional Struct1 should resolve to tuple", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Struct1", optional: true }, + wCtx, + ), + ).toBe("tuple"); + }); + await step("Optional Struct2 should resolve to tuple", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Struct2", optional: true }, + wCtx, + ), + ).toBe("tuple"); + }); + await step("Optional Contract1 should resolve to tuple", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Contract1", optional: true }, + wCtx, + ), + ).toBe("tuple"); + }); + await step("Optional Contract2 should resolve to tuple", () => { + expect( + resolveFuncType( + { kind: "ref", name: "Contract2", optional: true }, + wCtx, + ), + ).toBe("tuple"); + }); }); }); diff --git a/src/generator/writers/writeExpression.spec.ts b/src/generator/writers/writeExpression.spec.ts index e82332d8ae..6ad660103d 100644 --- a/src/generator/writers/writeExpression.spec.ts +++ b/src/generator/writers/writeExpression.spec.ts @@ -10,6 +10,8 @@ import { CompilerContext } from "@/context/context"; import { getParser } from "@/grammar"; import { getAstFactory } from "@/ast/ast-helpers"; import type { Source } from "@/imports/source"; +import { step, attachment } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; const code = ` @@ -70,8 +72,9 @@ const golden: string[] = [ ]; describe("writeExpression", () => { - it("should write expression", () => { + it("should write expression", async () => { const ast = getAstFactory(); + await attachment("Code", code, ContentType.TEXT); const sources: Source[] = [ { code: code, path: "", origin: "user" }, ]; @@ -93,13 +96,18 @@ describe("writeExpression", () => { throw Error("Unexpected statement kind"); } const wCtx = new WriterContext(ctx, "Contract1"); - wCtx.fun("$main", () => { - wCtx.body(() => { - expect(writeExpression(s.expression, wCtx)).toBe( - golden[i]!, - ); - }); - }); + await step( + `Expression ${i} should match expected result: ${golden[i]}`, + () => { + wCtx.fun("$main", () => { + wCtx.body(() => { + expect(writeExpression(s.expression, wCtx)).toBe( + golden[i]!, + ); + }); + }); + }, + ); i++; } }); diff --git a/src/grammar/expr-equality.spec.ts b/src/grammar/expr-equality.spec.ts index e85426c909..0791227c52 100644 --- a/src/grammar/expr-equality.spec.ts +++ b/src/grammar/expr-equality.spec.ts @@ -1,5 +1,6 @@ import { eqExpressions, getAstFactory } from "@/ast/ast-helpers"; import { getParser } from "@/grammar/index"; +import { step } from "@/test/allure/allure"; type Test = { expr1: string; expr2: string; equality: boolean }; @@ -365,53 +366,55 @@ const initOfExpressions: Test[] = [ { expr1: "initOf a(b,c,d)", expr2: "s.a(b,c,d)", equality: false }, ]; -function testEquality(expr1: string, expr2: string, equal: boolean) { +async function testEquality(expr1: string, expr2: string, equal: boolean) { const ast = getAstFactory(); const { parseExpression } = getParser(ast); - expect(eqExpressions(parseExpression(expr1), parseExpression(expr2))).toBe( - equal, - ); + await step(`"${expr1}" vs "${expr2}" should be ${equal}`, () => { + expect( + eqExpressions(parseExpression(expr1), parseExpression(expr2)), + ).toBe(equal); + }); } describe("expression-equality", () => { - it("should correctly determine if two expressions involving values are equal or not.", () => { - valueExpressions.forEach((test) => { - testEquality(test.expr1, test.expr2, test.equality); - }); + it("should correctly determine if two expressions involving values are equal or not.", async () => { + for (const t of valueExpressions) { + await testEquality(t.expr1, t.expr2, t.equality); + } }); - it("should correctly determine if two expressions involving function calls are equal or not.", () => { - functionCallExpressions.forEach((test) => { - testEquality(test.expr1, test.expr2, test.equality); - }); + it("should correctly determine if two expressions involving function calls are equal or not.", async () => { + for (const t of functionCallExpressions) { + await testEquality(t.expr1, t.expr2, t.equality); + } }); - it("should correctly determine if two expressions involving unary operators are equal or not.", () => { - unaryOpExpressions.forEach((test) => { - testEquality(test.expr1, test.expr2, test.equality); - }); + it("should correctly determine if two expressions involving unary operators are equal or not.", async () => { + for (const t of unaryOpExpressions) { + await testEquality(t.expr1, t.expr2, t.equality); + } }); - it("should correctly determine if two expressions involving binary operators are equal or not.", () => { - binaryOpExpressions.forEach((test) => { - testEquality(test.expr1, test.expr2, test.equality); - }); + it("should correctly determine if two expressions involving binary operators are equal or not.", async () => { + for (const t of binaryOpExpressions) { + await testEquality(t.expr1, t.expr2, t.equality); + } }); - it("should correctly determine if two expressions involving conditionals are equal or not.", () => { - conditionalExpressions.forEach((test) => { - testEquality(test.expr1, test.expr2, test.equality); - }); + it("should correctly determine if two expressions involving conditionals are equal or not.", async () => { + for (const t of conditionalExpressions) { + await testEquality(t.expr1, t.expr2, t.equality); + } }); - it("should correctly determine if two expressions involving structs are equal or not.", () => { - structExpressions.forEach((test) => { - testEquality(test.expr1, test.expr2, test.equality); - }); + it("should correctly determine if two expressions involving structs are equal or not.", async () => { + for (const t of structExpressions) { + await testEquality(t.expr1, t.expr2, t.equality); + } }); - it("should correctly determine if two expressions involving field accesses are equal or not.", () => { - fieldAccessExpressions.forEach((test) => { - testEquality(test.expr1, test.expr2, test.equality); - }); + it("should correctly determine if two expressions involving field accesses are equal or not.", async () => { + for (const t of fieldAccessExpressions) { + await testEquality(t.expr1, t.expr2, t.equality); + } }); - it("should correctly determine if two expressions involving initOf are equal or not.", () => { - initOfExpressions.forEach((test) => { - testEquality(test.expr1, test.expr2, test.equality); - }); + it("should correctly determine if two expressions involving initOf are equal or not.", async () => { + for (const t of initOfExpressions) { + await testEquality(t.expr1, t.expr2, t.equality); + } }); }); diff --git a/src/grammar/expr-is-value.spec.ts b/src/grammar/expr-is-value.spec.ts index 22d1b6d200..06e85107d2 100644 --- a/src/grammar/expr-is-value.spec.ts +++ b/src/grammar/expr-is-value.spec.ts @@ -2,6 +2,7 @@ import { getAstFactory, isLiteral } from "@/ast/ast-helpers"; import { getParser } from "@/grammar/index"; +import { step } from "@/test/allure/allure"; const valueExpressions: string[] = ["1", "true", "false", "null"]; @@ -49,21 +50,27 @@ const notValueExpressions: string[] = [ "initOf a(0,1,null)", ]; -function testIsValue(expr: string, testResult: boolean) { +async function testIsValue(expr: string, expected: boolean): Promise { const ast = getAstFactory(); const { parseExpression } = getParser(ast); - expect(isLiteral(parseExpression(expr))).toBe(testResult); + await step( + `'${expr}' should ${expected ? "" : "NOT "}be identified as a value`, + () => { + expect(isLiteral(parseExpression(expr))).toBe(expected); + }, + ); } describe("expression-is-value", () => { - valueExpressions.forEach((test) => { - it(`should correctly determine that '${test}' is a value expression.`, () => { - testIsValue(test, true); - }); + it("should correctly identify value expressions", async () => { + for (const expr of valueExpressions) { + await testIsValue(expr, true); + } }); - notValueExpressions.forEach((test) => { - it(`should correctly determine that '${test}' is NOT a value expression.`, () => { - testIsValue(test, false); - }); + + it("should correctly identify non‑value expressions", async () => { + for (const expr of notValueExpressions) { + await testIsValue(expr, false); + } }); }); diff --git a/src/grammar/grammar.spec.ts b/src/grammar/grammar.spec.ts index 577f089e8a..09e4143e82 100644 --- a/src/grammar/grammar.spec.ts +++ b/src/grammar/grammar.spec.ts @@ -3,6 +3,8 @@ import { loadCases } from "@/utils/loadCases.infra"; import type { SrcInfo } from "@/grammar/src-info"; import { isSrcInfo } from "@/grammar/src-info"; import { getParser } from "@/grammar/index"; +import { step, attachment } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; expect.addSnapshotSerializer({ test: (src) => isSrcInfo(src), @@ -11,17 +13,22 @@ expect.addSnapshotSerializer({ for (const r of loadCases(__dirname + "/test/")) { const isNeg = r.name.endsWith(".fail"); - it("should " + (isNeg ? "fail" : "parse") + " " + r.name, () => { + it("should " + (isNeg ? "fail" : "parse") + " " + r.name, async () => { + await attachment("Code", r.code, ContentType.TEXT); const ast = getAstFactory(); const { parse } = getParser(ast); if (isNeg) { - expect(() => - parse({ code: r.code, path: "", origin: "user" }), - ).toThrowErrorMatchingSnapshot(); + await step("Parse should fail and match snapshot", () => { + expect(() => + parse({ code: r.code, path: "", origin: "user" }), + ).toThrowErrorMatchingSnapshot(); + }); } else { - expect( - parse({ code: r.code, path: "", origin: "user" }), - ).toMatchSnapshot(); + await step("Parse output should match snapshot", () => { + expect( + parse({ code: r.code, path: "", origin: "user" }), + ).toMatchSnapshot(); + }); } }); } @@ -37,201 +44,245 @@ describe("parse imports", () => { }); }; - it("should reject non-relative imports", () => { - expect(() => parse('import "some_name";')).toThrow(); + it("should reject non-relative imports", async () => { + await step("Should reject non-relative imports", () => { + expect(() => parse('import "some_name";')).toThrow(); + }); }); - it("should reject folder imports", () => { - expect(() => parse('import "./some_name/";')).toThrow(); + it("should reject folder imports", async () => { + await step("Should reject folder imports", () => { + expect(() => parse('import "./some_name/";')).toThrow(); + }); }); - it("should reject windows imports", () => { - expect(() => parse('import ".\\some_name";')).toThrow(); + it("should reject windows imports", async () => { + await step("Should reject windows imports", () => { + expect(() => parse('import ".\\some_name";')).toThrow(); + }); }); - it("should parse relative imports", () => { - expect(parse('import "./import";')).toMatchObject({ - imports: [ - { - importPath: { - type: "relative", - language: "tact", - path: { - segments: ["import.tact"], - stepsUp: 0, + it("should parse relative imports", async () => { + const result = parse('import "./import";'); + await step("Should parse relative imports", () => { + expect(result).toMatchObject({ + imports: [ + { + importPath: { + type: "relative", + language: "tact", + path: { + segments: ["import.tact"], + stepsUp: 0, + }, }, }, - }, - ], + ], + }); }); }); - it("should parse step-up imports", () => { - expect(parse('import "../import";')).toMatchObject({ - imports: [ - { - importPath: { - type: "relative", - language: "tact", - path: { - segments: ["import.tact"], - stepsUp: 1, + it("should parse step-up imports", async () => { + const result = parse('import "../import";'); + await step("Should parse step-up imports", () => { + expect(result).toMatchObject({ + imports: [ + { + importPath: { + type: "relative", + language: "tact", + path: { + segments: ["import.tact"], + stepsUp: 1, + }, }, }, - }, - ], + ], + }); }); }); - it("should parse deep imports", () => { - expect(parse('import "./import/second";')).toMatchObject({ - imports: [ - { - importPath: { - type: "relative", - language: "tact", - path: { - segments: ["import", "second.tact"], - stepsUp: 0, + it("should parse deep imports", async () => { + const result = parse('import "./import/second";'); + await step("Should parse deep imports", () => { + expect(result).toMatchObject({ + imports: [ + { + importPath: { + type: "relative", + language: "tact", + path: { + segments: ["import", "second.tact"], + stepsUp: 0, + }, }, }, - }, - ], + ], + }); }); }); - it("should not add .tact second time", () => { - expect(parse('import "./import.tact";')).toMatchObject({ - imports: [ - { - importPath: { - type: "relative", - language: "tact", - path: { - segments: ["import.tact"], - stepsUp: 0, + it("should not add .tact second time", async () => { + const result = parse('import "./import.tact";'); + await step("Should not add .tact second time", () => { + expect(result).toMatchObject({ + imports: [ + { + importPath: { + type: "relative", + language: "tact", + path: { + segments: ["import.tact"], + stepsUp: 0, + }, }, }, - }, - ], + ], + }); }); }); - it("should detect .fc imports", () => { - expect(parse('import "./import.fc";')).toMatchObject({ - imports: [ - { - importPath: { - type: "relative", - language: "func", - path: { - segments: ["import.fc"], - stepsUp: 0, + it("should detect .fc imports", async () => { + const result = parse('import "./import.fc";'); + await step("Should detect .fc imports", () => { + expect(result).toMatchObject({ + imports: [ + { + importPath: { + type: "relative", + language: "func", + path: { + segments: ["import.fc"], + stepsUp: 0, + }, }, }, - }, - ], + ], + }); }); }); - it("should detect .func imports", () => { - expect(parse('import "./import.func";')).toMatchObject({ - imports: [ - { - importPath: { - type: "relative", - language: "func", - path: { - segments: ["import.func"], - stepsUp: 0, + it("should detect .func imports", async () => { + const result = parse('import "./import.func";'); + await step("Should detect .func imports", () => { + expect(result).toMatchObject({ + imports: [ + { + importPath: { + type: "relative", + language: "func", + path: { + segments: ["import.func"], + stepsUp: 0, + }, }, }, - }, - ], + ], + }); }); }); - it("should parse absolute stdlib imports", () => { - expect(parse('import "@stdlib/foo";')).toMatchObject({ - imports: [ - { - importPath: { - type: "stdlib", - language: "tact", - path: { - segments: ["foo.tact"], - stepsUp: 0, + it("should parse absolute stdlib imports", async () => { + const result = parse('import "@stdlib/foo";'); + await step("Should parse absolute stdlib imports", () => { + expect(result).toMatchObject({ + imports: [ + { + importPath: { + type: "stdlib", + language: "tact", + path: { + segments: ["foo.tact"], + stepsUp: 0, + }, }, }, - }, - ], + ], + }); }); }); - it("should parse relative stdlib imports", () => { - expect(parse('import "@stdlib/foo/../bar";')).toMatchObject({ - imports: [ - { - importPath: { - type: "stdlib", - language: "tact", - path: { - segments: ["bar.tact"], - stepsUp: 0, + it("should parse relative stdlib imports", async () => { + const result = parse('import "@stdlib/foo/../bar";'); + await step("Should parse relative stdlib imports", () => { + expect(result).toMatchObject({ + imports: [ + { + importPath: { + type: "stdlib", + language: "tact", + path: { + segments: ["bar.tact"], + stepsUp: 0, + }, }, }, - }, - ], + ], + }); }); }); - it("should parse stdlib tact imports with extension", () => { - expect(parse('import "@stdlib/foo.tact";')).toMatchObject({ - imports: [ - { - importPath: { - type: "stdlib", - language: "tact", - path: { - segments: ["foo.tact"], - stepsUp: 0, + it("should parse stdlib tact imports with extension", async () => { + const result = parse('import "@stdlib/foo.tact";'); + await step("Should parse stdlib tact imports with extension", () => { + expect(result).toMatchObject({ + imports: [ + { + importPath: { + type: "stdlib", + language: "tact", + path: { + segments: ["foo.tact"], + stepsUp: 0, + }, }, }, - }, - ], + ], + }); }); }); - it("should parse stdlib func imports with extension", () => { - expect(parse('import "@stdlib/foo.fc";')).toMatchObject({ - imports: [ - { - importPath: { - type: "stdlib", - language: "func", - path: { - segments: ["foo.fc"], - stepsUp: 0, + it("should parse stdlib func imports with extension", async () => { + const result = parse('import "@stdlib/foo.fc";'); + await step("Should parse stdlib func imports with extension", () => { + expect(result).toMatchObject({ + imports: [ + { + importPath: { + type: "stdlib", + language: "func", + path: { + segments: ["foo.fc"], + stepsUp: 0, + }, }, }, - }, - ], + ], + }); }); }); - it("should reject stdlib root import", () => { - expect(() => parse('import "@stdlib";')).toThrow(); + it("should reject stdlib root import", async () => { + await step("Should reject stdlib root import", () => { + expect(() => parse('import "@stdlib";')).toThrow(); + }); }); - it("should reject stdlib root import as folder", () => { - expect(() => parse('import "@stdlib/";')).toThrow(); + it("should reject stdlib root import as folder", async () => { + await step("Should reject stdlib root import as folder", () => { + expect(() => parse('import "@stdlib/";')).toThrow(); + }); }); - it("should reject stdlib folder import", () => { - expect(() => parse('import "@stdlib/foo/";')).toThrow(); + it("should reject stdlib folder import", async () => { + await step("Should reject stdlib folder import", () => { + expect(() => parse('import "@stdlib/foo/";')).toThrow(); + }); }); - it("should reject stdlib import up from stdlib root", () => { - expect(() => parse('import "@stdlib/../foo";')).toThrow(); + it("should reject stdlib import up from stdlib root", async () => { + await step("Should reject stdlib import up from stdlib root", () => { + expect(() => parse('import "@stdlib/../foo";')).toThrow(); + }); }); }); diff --git a/src/imports/path.spec.ts b/src/imports/path.spec.ts index 64598e8409..eaeef5693d 100644 --- a/src/imports/path.spec.ts +++ b/src/imports/path.spec.ts @@ -1,79 +1,108 @@ import { asString, fromString } from "@/imports/path"; +import { step } from "@/test/allure/allure"; describe("fromString", () => { - it("empty path", () => { - expect(fromString("")).toMatchObject({ stepsUp: 0, segments: [] }); + it("empty path", async () => { + await step("RelativePath should match expected", () => { + expect(fromString("")).toMatchObject({ stepsUp: 0, segments: [] }); + }); }); - it("empty path segments: .///foo", () => { - expect(fromString(".///foo")).toMatchObject({ - stepsUp: 0, - segments: ["foo"], + it("empty path segments: .///foo", async () => { + await step("RelativePath should match expected", () => { + expect(fromString(".///foo")).toMatchObject({ + stepsUp: 0, + segments: ["foo"], + }); }); }); - it("dot segments: /./foo", () => { - expect(fromString("/./foo")).toMatchObject({ - stepsUp: 0, - segments: ["foo"], + it("dot segments: /./foo", async () => { + await step("RelativePath should match expected", () => { + expect(fromString("/./foo")).toMatchObject({ + stepsUp: 0, + segments: ["foo"], + }); }); }); - it("double dot segments: ../foo", () => { - expect(fromString("../foo")).toMatchObject({ - stepsUp: 1, - segments: ["foo"], + it("double dot segments: ../foo", async () => { + await step("RelativePath should match expected", () => { + expect(fromString("../foo")).toMatchObject({ + stepsUp: 1, + segments: ["foo"], + }); }); }); - it("two double dot segments: ../../foo", () => { - expect(fromString("../../foo")).toMatchObject({ - stepsUp: 2, - segments: ["foo"], + it("two double dot segments: ../../foo", async () => { + await step("RelativePath should match expected", () => { + expect(fromString("../../foo")).toMatchObject({ + stepsUp: 2, + segments: ["foo"], + }); }); }); - it("removed part: ../foo/../bar", () => { - expect(fromString("../foo/../bar")).toMatchObject({ - stepsUp: 1, - segments: ["bar"], + it("removed part: ../foo/../bar", async () => { + await step("RelativePath should match expected", () => { + expect(fromString("../foo/../bar")).toMatchObject({ + stepsUp: 1, + segments: ["bar"], + }); }); }); - it("removed parts: ../foo/../../bar", () => { - expect(fromString("../foo/../../bar")).toMatchObject({ - stepsUp: 2, - segments: ["bar"], + it("removed parts: ../foo/../../bar", async () => { + await step("RelativePath should match expected", () => { + expect(fromString("../foo/../../bar")).toMatchObject({ + stepsUp: 2, + segments: ["bar"], + }); }); }); }); describe("asString", () => { - it("empty path", () => { - expect(asString(fromString(""))).toBe(""); + it("empty path", async () => { + await step("RelativePath should match expected string", () => { + expect(asString(fromString(""))).toBe(""); + }); }); - it("empty path segments: .///foo", () => { - expect(asString(fromString(".///foo"))).toBe("foo"); + it("empty path segments: .///foo", async () => { + await step("RelativePath should match expected string", () => { + expect(asString(fromString(".///foo"))).toBe("foo"); + }); }); - it("dot segments: /./foo", () => { - expect(asString(fromString("/./foo"))).toBe("foo"); + it("dot segments: /./foo", async () => { + await step("RelativePath should match expected string", () => { + expect(asString(fromString("/./foo"))).toBe("foo"); + }); }); - it("double dot segments: ../foo", () => { - expect(asString(fromString("../foo"))).toBe("../foo"); + it("double dot segments: ../foo", async () => { + await step("RelativePath should match expected string", () => { + expect(asString(fromString("../foo"))).toBe("../foo"); + }); }); - it("two double dot segments: ../../foo", () => { - expect(asString(fromString("../../foo"))).toBe("../../foo"); + it("two double dot segments: ../../foo", async () => { + await step("RelativePath should match expected string", () => { + expect(asString(fromString("../../foo"))).toBe("../../foo"); + }); }); - it("removed part: ../foo/../bar", () => { - expect(asString(fromString("../foo/../bar"))).toBe("../bar"); + it("removed part: ../foo/../bar", async () => { + await step("RelativePath should match expected string", () => { + expect(asString(fromString("../foo/../bar"))).toBe("../bar"); + }); }); - it("removed parts: ../foo/../../bar", () => { - expect(asString(fromString("../foo/../../bar"))).toBe("../../bar"); + it("removed parts: ../foo/../../bar", async () => { + await step("RelativePath should match expected string", () => { + expect(asString(fromString("../foo/../../bar"))).toBe("../../bar"); + }); }); }); diff --git a/src/imports/resolveImports.spec.ts b/src/imports/resolveImports.spec.ts index d318d61124..afb01b81f5 100644 --- a/src/imports/resolveImports.spec.ts +++ b/src/imports/resolveImports.spec.ts @@ -3,9 +3,10 @@ import { createNodeFileSystem } from "@/vfs/createNodeFileSystem"; import path from "path"; import { getParser } from "@/grammar"; import { getAstFactory } from "@/ast/ast-helpers"; +import { step } from "@/test/allure/allure"; describe("resolveImports", () => { - it("should resolve imports", () => { + it("should resolve imports", async () => { const project = createNodeFileSystem( path.resolve(__dirname, "__testdata", "project"), ); @@ -19,68 +20,70 @@ describe("resolveImports", () => { entrypoint: "./main.tact", parser: getParser(ast), }); - expect(resolved).toMatchObject({ - func: [ - { - code: "", - path: path.resolve( - __dirname, - "__testdata", - "stdlib", - "std", - "stdlib2.fc", - ), - }, - ], - tact: [ - { - code: 'import "./stdlib2.fc";', - path: path.resolve( - __dirname, - "__testdata", - "stdlib", - "std", - "stdlib.tact", - ), - }, - { - code: "", - path: path.resolve( - __dirname, - "__testdata", - "project", - "imported.tact", - ), - }, - { - code: 'import "../imported_from_subfolder";', - path: path.resolve( - __dirname, - "__testdata", - "project", - "subfolder", - "import_from_parent.tact", - ), - }, - { - code: "", - path: path.resolve( - __dirname, - "__testdata", - "project", - "imported_from_subfolder.tact", - ), - }, - { - code: 'import "./imported"; import "./subfolder/import_from_parent";', - path: path.resolve( - __dirname, - "__testdata", - "project", - "main.tact", - ), - }, - ], + await step("Resolved imports should match expected object", () => { + expect(resolved).toMatchObject({ + func: [ + { + code: "", + path: path.resolve( + __dirname, + "__testdata", + "stdlib", + "std", + "stdlib2.fc", + ), + }, + ], + tact: [ + { + code: 'import "./stdlib2.fc";', + path: path.resolve( + __dirname, + "__testdata", + "stdlib", + "std", + "stdlib.tact", + ), + }, + { + code: "", + path: path.resolve( + __dirname, + "__testdata", + "project", + "imported.tact", + ), + }, + { + code: 'import "../imported_from_subfolder";', + path: path.resolve( + __dirname, + "__testdata", + "project", + "subfolder", + "import_from_parent.tact", + ), + }, + { + code: "", + path: path.resolve( + __dirname, + "__testdata", + "project", + "imported_from_subfolder.tact", + ), + }, + { + code: 'import "./imported"; import "./subfolder/import_from_parent";', + path: path.resolve( + __dirname, + "__testdata", + "project", + "main.tact", + ), + }, + ], + }); }); }); }); diff --git a/src/imports/resolveLibrary.spec.ts b/src/imports/resolveLibrary.spec.ts index 94094c82c6..35e5df1227 100644 --- a/src/imports/resolveLibrary.spec.ts +++ b/src/imports/resolveLibrary.spec.ts @@ -2,6 +2,7 @@ import { createVirtualFileSystem } from "@/vfs/createVirtualFileSystem"; import { fromString } from "@/imports/path"; import { resolveLibrary } from "@/imports/resolveLibrary"; import type { Source } from "@/imports/source"; +import { step } from "@/test/allure/allure"; const project = createVirtualFileSystem("/project", { ["main.tact"]: "", @@ -26,7 +27,7 @@ const stdlibSource: Source = { code: "", }; -it("project file, stdlib import", () => { +it("project file, stdlib import", async () => { const resolved = resolveLibrary({ sourceFrom: mainSource, importPath: { @@ -37,15 +38,17 @@ it("project file, stdlib import", () => { project, stdlib, }); - expect(resolved).toMatchObject({ - ok: true, - path: "@stdlib/libs/config.tact", - origin: "stdlib", - language: "tact", + await step("Resolved library should match expected object", () => { + expect(resolved).toMatchObject({ + ok: true, + path: "@stdlib/libs/config.tact", + origin: "stdlib", + language: "tact", + }); }); }); -it("project file, relative import, func", () => { +it("project file, relative import, func", async () => { const resolved = resolveLibrary({ sourceFrom: mainSource, importPath: { @@ -56,15 +59,17 @@ it("project file, relative import, func", () => { project, stdlib, }); - expect(resolved).toMatchObject({ - ok: true, - path: "/project/main.fc", - origin: "user", - language: "func", + await step("Resolved library should match expected object", () => { + expect(resolved).toMatchObject({ + ok: true, + path: "/project/main.fc", + origin: "user", + language: "func", + }); }); }); -it("project file, relative import, tact", () => { +it("project file, relative import, tact", async () => { const resolved = resolveLibrary({ sourceFrom: mainSource, importPath: { @@ -75,15 +80,17 @@ it("project file, relative import, tact", () => { project, stdlib, }); - expect(resolved).toMatchObject({ - ok: true, - path: "/project/import.tact", - origin: "user", - language: "tact", + await step("Resolved library should match expected object", () => { + expect(resolved).toMatchObject({ + ok: true, + path: "/project/import.tact", + origin: "user", + language: "tact", + }); }); }); -it("stdlib file, relative import, tact", () => { +it("stdlib file, relative import, tact", async () => { const resolved = resolveLibrary({ sourceFrom: stdlibSource, importPath: { @@ -95,10 +102,12 @@ it("stdlib file, relative import, tact", () => { stdlib, }); - expect(resolved).toMatchObject({ - ok: true, - path: "@stdlib/libs/import.tact", - origin: "stdlib", - language: "tact", + await step("Resolved library should match expected object", () => { + expect(resolved).toMatchObject({ + ok: true, + path: "@stdlib/libs/import.tact", + origin: "stdlib", + language: "tact", + }); }); }); diff --git a/src/optimizer/test/interpreter-eval-failed.spec.ts b/src/optimizer/test/interpreter-eval-failed.spec.ts index 18b0809ffb..22467b6c36 100644 --- a/src/optimizer/test/interpreter-eval-failed.spec.ts +++ b/src/optimizer/test/interpreter-eval-failed.spec.ts @@ -8,26 +8,31 @@ import { resolveDescriptors } from "@/types/resolveDescriptors"; import { resolveSignatures } from "@/types/resolveSignatures"; import { resolveStatements } from "@/types/resolveStatements"; import { loadCases } from "@/utils/loadCases.infra"; +import { step, attachment } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; describe("interpreter-evaluation", () => { for (const r of loadCases(__dirname + "/failed/")) { - it(`${r.name} should fail compilation`, () => { + it(`${r.name} should fail compilation`, async () => { const Ast = getAstFactory(); const sources: Source[] = [ { code: r.code, path: "", origin: "user" }, ]; + await attachment("Code", r.code, ContentType.TEXT); let ctx = openContext( new CompilerContext(), sources, [], parseModules(sources, getParser(Ast)), ); - expect(() => { - ctx = resolveDescriptors(ctx, Ast); - ctx = resolveStatements(ctx); - ctx = resolveSignatures(ctx, Ast); - evalComptimeExpressions(ctx, Ast); - }).toThrowErrorMatchingSnapshot(); + await step("Compilation should fail and match snapshot", () => { + expect(() => { + ctx = resolveDescriptors(ctx, Ast); + ctx = resolveStatements(ctx); + ctx = resolveSignatures(ctx, Ast); + evalComptimeExpressions(ctx, Ast); + }).toThrowErrorMatchingSnapshot(); + }); }); } }); diff --git a/src/optimizer/test/interpreter-eval-success.spec.ts b/src/optimizer/test/interpreter-eval-success.spec.ts index 5874343003..b9dac7ecb9 100644 --- a/src/optimizer/test/interpreter-eval-success.spec.ts +++ b/src/optimizer/test/interpreter-eval-success.spec.ts @@ -9,14 +9,17 @@ import { getAllExpressionTypes } from "@/types/resolveExpression"; import { resolveSignatures } from "@/types/resolveSignatures"; import { resolveStatements } from "@/types/resolveStatements"; import { loadCases } from "@/utils/loadCases.infra"; +import { step, attachment } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; describe("interpreter-evaluation", () => { for (const r of loadCases(__dirname + "/success/")) { - it(`${r.name} should pass compilation`, () => { + it(`${r.name} should pass compilation`, async () => { const Ast = getAstFactory(); const sources: Source[] = [ { code: r.code, path: "", origin: "user" }, ]; + await attachment("Code", r.code, ContentType.TEXT); let ctx = openContext( new CompilerContext(), sources, @@ -27,7 +30,9 @@ describe("interpreter-evaluation", () => { ctx = resolveStatements(ctx); ctx = resolveSignatures(ctx, Ast); evalComptimeExpressions(ctx, Ast); - expect(getAllExpressionTypes(ctx)).toMatchSnapshot(); + await step("Expression types should match snapshot", () => { + expect(getAllExpressionTypes(ctx)).toMatchSnapshot(); + }); }); } }); diff --git a/src/optimizer/test/partial-eval.spec.ts b/src/optimizer/test/partial-eval.spec.ts index 203ff36c65..0f28df6667 100644 --- a/src/optimizer/test/partial-eval.spec.ts +++ b/src/optimizer/test/partial-eval.spec.ts @@ -10,6 +10,7 @@ import { getParser } from "@/grammar"; import { throwInternalCompilerError } from "@/error/errors"; import type { FactoryAst } from "@/ast/ast-helpers"; import { eqExpressions, getAstFactory, isLiteral } from "@/ast/ast-helpers"; +import { step } from "@/test/allure/allure"; const MAX: string = "115792089237316195423570985008687907853269984665640564039457584007913129639935"; @@ -311,7 +312,7 @@ const booleanExpressions = [ ]; function testExpression(original: string, simplified: string) { - it(`should simplify ${original} to ${simplified}`, () => { + it(`should simplify ${original} to ${simplified}`, async () => { const ast = getAstFactory(); const { parseExpression } = getParser(ast); const util = getAstUtil(ast); @@ -322,7 +323,9 @@ function testExpression(original: string, simplified: string) { ); const simplifiedValue = dummyEval(parseExpression(simplified), ast); const areMatching = eqExpressions(originalValue, simplifiedValue); - expect(areMatching).toBe(true); + await step(`Expression should simplify to expected form`, () => { + expect(areMatching).toBe(true); + }); }); } @@ -331,7 +334,7 @@ function testExpressionWithOptimizer( simplified: string, optimizer: ExpressionTransformer, ) { - it(`should simplify ${original} to ${simplified}`, () => { + it(`should simplify ${original} to ${simplified}`, async () => { const ast = getAstFactory(); const { parseExpression } = getParser(ast); const originalValue = optimizer.applyRules( @@ -339,7 +342,9 @@ function testExpressionWithOptimizer( ); const simplifiedValue = dummyEval(parseExpression(simplified), ast); const areMatching = eqExpressions(originalValue, simplifiedValue); - expect(areMatching).toBe(true); + await step("Expression should simplify to expected form", () => { + expect(areMatching).toBe(true); + }); }); } diff --git a/src/pipeline/packaging.spec.ts b/src/pipeline/packaging.spec.ts index 4a2304ad4d..6807be03ba 100644 --- a/src/pipeline/packaging.spec.ts +++ b/src/pipeline/packaging.spec.ts @@ -5,6 +5,7 @@ import * as Stdlib from "@/stdlib/stdlib"; import { createNodeFileSystem } from "@/vfs/createNodeFileSystem"; import path from "path"; import * as fs from "fs"; +import { step } from "@/test/allure/allure"; it(`should correctly generate .pkg file: Windows uses Unix-like paths`, async () => { const output = "./output/"; @@ -31,5 +32,7 @@ it(`should correctly generate .pkg file: Windows uses Unix-like paths`, async () const pathAbiFile = project.resolve(output, "packaging_Echo.pkg"); const content = fs.readFileSync(pathAbiFile).toString(); - expect(content).toMatchSnapshot(); + await step("Generated package content should match snapshot", () => { + expect(content).toMatchSnapshot(); + }); }); diff --git a/src/server/run-server.spec.ts b/src/server/run-server.spec.ts index 70263430f6..dca3e42062 100644 --- a/src/server/run-server.spec.ts +++ b/src/server/run-server.spec.ts @@ -2,94 +2,115 @@ import { throwInternal } from "@/error/errors"; import type { Logger, SourceLogger } from "@/error/logger-util"; import type { Range } from "@/error/range"; import { runServer } from "@/server/run-server"; +import { step } from "@/test/allure/allure"; describe("runServer", () => { - test("only first error without error recovery", () => { + test("only first error without error recovery", async () => { const result = runServer((log) => { log.error(log.text`Error 1`); log.error(log.text`Error 2`); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); - test("all info logs are logged", () => { + test("all info logs are logged", async () => { const result = runServer((log) => { log.info(log.text`Info 1`); log.info(log.text`Info 2`); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); - test("raw internal error", () => { + test("raw internal error", async () => { const result = runServer((_log) => { throwInternal(`OMG`); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); - test("logger internal error", () => { + test("logger internal error", async () => { const result = runServer((log) => { log.internal(log.text`OMG`); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); - test("raw internal error in source", () => { + test("raw internal error in source", async () => { const result = runServer((log) => { log.source("/foo/bar", "Hello, world", () => { throwInternal(`OMG`); }); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); - test("logger internal error in source", () => { + test("logger internal error in source", async () => { const result = runServer((log) => { log.source("/foo/bar", "Hello, world", (log) => { log.internal(log.text`OMG`); log.info(log.text`Impossible`); }); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); - test("internal error in source at range", () => { + test("internal error in source at range", async () => { const result = runServer((log) => { log.source("/foo/bar", "Hello, world", (log) => { log.at({ start: 3, end: 5 }).internal(log.text`OMG`); log.info(log.text`Impossible`); }); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); - test("uncaught error", () => { + test("uncaught error", async () => { const result = runServer(() => { throw new Error("Uncaught!"); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); - test("uncaught error in source", () => { + test("uncaught error in source", async () => { const result = runServer((log) => { return log.source("/foo/bar", "Hello, world", () => { throw new Error("hehe"); }); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); - test("multiple errors", () => { + test("multiple errors", async () => { const result = runServer((log) => { log.recover((log) => { log.error(log.text`foo`); log.error(log.text`bar`); }); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); - test("exit on error", () => { + test("exit on error", async () => { const result = runServer((log) => { log.recover((log) => { log.error(log.text`foo`); @@ -98,10 +119,12 @@ describe("runServer", () => { }); log.error(log.text`impossible`); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); - test("multiple errors inside source", () => { + test("multiple errors inside source", async () => { const result = runServer((log) => { log.source("/foo/bar", "Hello, world", (log) => { log.recover((log) => { @@ -112,10 +135,12 @@ describe("runServer", () => { log.error(log.text`impossible`); }); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); - test("source inside multiple errors", () => { + test("source inside multiple errors", async () => { const result = runServer((log) => { log.recover((log) => { log.source("/foo/bar", "Hello, world", (log) => { @@ -126,10 +151,12 @@ describe("runServer", () => { log.error(log.text`impossible`); }); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); - test("typed errors", () => { + test("typed errors", async () => { const fooBarSchema = (l: Logger) => ({ fooError: () => l.error(l.text`Foo!`), barError: () => l.error(l.text`Bar!`), @@ -141,10 +168,12 @@ describe("runServer", () => { l.barError(); }); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); - test("typed errors for source", () => { + test("typed errors for source", async () => { const fooBarSchemaSrc = (l: SourceLogger) => ({ fooError: (at: Range) => l.at(at).error(l.text`Foo!`), barError: () => l.error(l.text`Bar!`), @@ -158,6 +187,8 @@ describe("runServer", () => { }); }); }); - expect(result).toMatchSnapshot(); + await step("RunServer result should match snapshot", () => { + expect(result).toMatchSnapshot(); + }); }); }); diff --git a/src/storage/resolveAllocation.spec.ts b/src/storage/resolveAllocation.spec.ts index 51be32310c..a6a3bd9b8f 100644 --- a/src/storage/resolveAllocation.spec.ts +++ b/src/storage/resolveAllocation.spec.ts @@ -13,6 +13,8 @@ import { getParser } from "@/grammar"; import { getAstFactory } from "@/ast/ast-helpers"; import { stdlibPath } from "@/stdlib/path"; import type { Source } from "@/imports/source"; +import { step, attachment } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; const primitivesPath = path.join(stdlibPath, "/std/internal/primitives.tact"); const stdlib = fs.readFileSync(primitivesPath, "utf-8"); @@ -67,12 +69,13 @@ contract Sample { `; describe("resolveAllocation", () => { - it("should write program", () => { + it("should write program", async () => { const ast = getAstFactory(); const sources: Source[] = [ { code: stdlib, path: primitivesPath, origin: "stdlib" }, { code: src, path: "", origin: "user" }, ]; + await attachment("Code", src, ContentType.TEXT); let ctx = openContext( new CompilerContext(), sources, @@ -83,6 +86,8 @@ describe("resolveAllocation", () => { ctx = resolveSignatures(ctx, ast); ctx = resolveStatements(ctx); ctx = resolveAllocations(ctx); - expect(getAllocations(ctx)).toMatchSnapshot(); + await step("Allocations should match snapshot", () => { + expect(getAllocations(ctx)).toMatchSnapshot(); + }); }); }); diff --git a/src/storage/resolveErrors.spec.ts b/src/storage/resolveErrors.spec.ts index 549de85b45..4311a22e70 100644 --- a/src/storage/resolveErrors.spec.ts +++ b/src/storage/resolveErrors.spec.ts @@ -10,12 +10,14 @@ import { getAstFactory } from "@/ast/ast-helpers"; import { stdlibPath } from "@/stdlib/path"; import type { Source } from "@/imports/source"; import { resolveErrors } from "@/types/resolveErrors"; +import { step, attachment } from "@/test/allure/allure"; +import { ContentType } from "allure-js-commons"; const primitivesPath = path.join(stdlibPath, "/std/internal/primitives.tact"); const stdlib = fs.readFileSync(primitivesPath, "utf-8"); describe("resolveErrors", () => { - it("should throw an error", () => { + it("should throw an error", async () => { const src = ` trait BaseTrait {} @@ -26,6 +28,8 @@ contract Test { } `; + await attachment("Code", src, ContentType.TEXT); + const expectedErrors = `:6:23: The second parameter of "require()" must be evaluated at compile time 5 | get fun foo(cond: Bool, error: String) { > 6 | require(cond, error); @@ -47,6 +51,8 @@ contract Test { ctx = resolveDescriptors(ctx, ast); ctx = resolveSignatures(ctx, ast); ctx = resolveStatements(ctx); - expect(() => resolveErrors(ctx, ast)).toThrow(expectedErrors); + await step("resolveErrors should throw expected error", () => { + expect(() => resolveErrors(ctx, ast)).toThrow(expectedErrors); + }); }); });