UNPKG

testeranto

Version:

the AI powered BDD test framework for typescript projects

873 lines (862 loc) 22.2 kB
import { createRequire } from 'module';const require = createRequire(import.meta.url); var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // dist/cjs-shim.js import path from "node:path"; import url from "node:url"; globalThis.__filename = url.fileURLToPath(import.meta.url); globalThis.__dirname = path.dirname(__filename); // src/lib/pmProxy.ts var baseProxy = function(pm, mappings) { return new Proxy(pm, { get: (target, prop, receiver) => { for (const mapping of mappings) { const method = mapping[0]; const arger = mapping[1]; if (prop === method) { return (...x) => { const modifiedArgs = arger(...x); return target[prop](...modifiedArgs); }; } } return (...x) => { return target[prop](...x); }; } }); }; var butThenProxy = (pm, filepath, addArtifact) => { return baseProxy(pm, [ [ "screencast", (opts, p) => { const path2 = `${filepath}/butThen/${opts.path}`; addArtifact(path2); return [ { ...opts, path: path2 }, p ]; } ], [ "createWriteStream", (fp) => { const path2 = `${filepath}/butThen/${fp}`; addArtifact(path2); return [path2]; } ], [ "writeFileSync", (fp, contents, testName) => { console.log( `[DEBUG] butThenProxy writeFileSync: fp="${fp}" (type: ${typeof fp}), contents="${contents}" (type: ${typeof contents}), testName="${testName}" (type: ${typeof testName})` ); if (typeof fp !== "string") { console.log(`[ERROR] fp is not a string:`, fp); if (Array.isArray(fp)) { fp = fp.join(""); console.log(`[DEBUG] Converted array to string: "${fp}"`); } else { fp = String(fp); console.log(`[DEBUG] Converted to string: "${fp}"`); } } const path2 = `${filepath}/butThen/${fp}`; console.log(`[DEBUG] Generated path: "${path2}"`); addArtifact(path2); return [path2, contents, testName]; } ], [ "customScreenShot", (opts, p) => { const path2 = `${filepath}/butThen/${opts.path}`; addArtifact(path2); return [ { ...opts, path: path2 }, p ]; } ] ]); }; var andWhenProxy = (pm, filepath, addArtifact) => { return baseProxy(pm, [ [ "screencast", (opts, p) => { const path2 = `${filepath}/andWhen/${opts.path}`; addArtifact(path2); return [ { ...opts, path: path2 }, p ]; } ], [ "createWriteStream", (fp) => { const path2 = `${filepath}/andWhen/${fp}`; addArtifact(path2); return [path2]; } ], [ "writeFileSync", (fp, contents, testName) => { const path2 = `${filepath}/andWhen/${fp}`; addArtifact(path2); return [path2, contents, testName]; } ], [ "customScreenShot", (opts, p) => { const path2 = `${filepath}/andWhen/${opts.path}`; addArtifact(path2); return [ { ...opts, path: path2 }, p ]; } ] ]); }; var afterEachProxy = (pm, suite, given, addArtifact) => { return baseProxy(pm, [ [ "screencast", (opts, p) => { const path2 = `suite-${suite}/given-${given}/afterEach/${opts.path}`; addArtifact(path2); return [ { ...opts, path: path2 }, p ]; } ], [ "createWriteStream", (fp) => { const path2 = `suite-${suite}/afterEach/${fp}`; addArtifact(path2); return [path2]; } ], [ "writeFileSync", (fp, contents, testName) => { const path2 = `suite-${suite}/given-${given}/afterEach/${fp}`; addArtifact(path2); return [path2, contents, testName]; } ], [ "customScreenShot", (opts, p) => { const path2 = `suite-${suite}/given-${given}/afterEach/${opts.path}`; addArtifact(path2); return [ { ...opts, path: path2 }, p ]; } ] ]); }; var beforeEachProxy = (pm, suite, addArtifact) => { return baseProxy(pm, [ [ "screencast", (opts, p) => { const path2 = `suite-${suite}/beforeEach/${opts.path}`; addArtifact(path2); return [ { ...opts, path: path2 }, p ]; } ], [ "writeFileSync", (fp, contents, testName) => { const path2 = `suite-${suite}/beforeEach/${fp}`; addArtifact(path2); return [path2, contents, testName]; } ], [ "customScreenShot", (opts, p) => { const path2 = `suite-${suite}/beforeEach/${opts.path}`; addArtifact(path2); return [ { ...opts, path: path2 }, p ]; } ], [ "createWriteStream", (fp) => { const path2 = `suite-${suite}/beforeEach/${fp}`; addArtifact(path2); return [path2]; } ] ]); }; var beforeAllProxy = (pm, suite, addArtifact) => { return baseProxy(pm, [ [ "writeFileSync", (fp, contents, testName) => { const path2 = `suite-${suite}/beforeAll/${fp}`; addArtifact(path2); return [path2, contents, testName]; } ], [ "customScreenShot", (opts, p) => { const path2 = `suite-${suite}/beforeAll/${opts.path}`; addArtifact(path2); return [ { ...opts, path: path2 }, p ]; } ], [ "createWriteStream", (fp) => { const path2 = `suite-${suite}/beforeAll/${fp}`; addArtifact(path2); return [path2]; } ] ]); }; var afterAllProxy = (pm, suite, addArtifact) => { return baseProxy(pm, [ [ "createWriteStream", (fp) => { const path2 = `suite-${suite}/afterAll/${fp}`; addArtifact(path2); return [path2]; } ], [ "writeFileSync", (fp, contents, testName) => { const path2 = `suite-${suite}/afterAll/${fp}`; addArtifact(path2); return [path2, contents, testName]; } ], [ "customScreenShot", (opts, p) => { const path2 = `suite-${suite}/afterAll/${opts.path}`; addArtifact(path2); return [ { ...opts, path: path2 }, p ]; } ] ]); }; // src/lib/basebuilder.ts var BaseBuilder = class { constructor(input, suitesOverrides, givenOverides, whenOverides, thenOverides, testResourceRequirement, testSpecification) { this.artifacts = []; this.artifacts = []; this.testResourceRequirement = testResourceRequirement; this.suitesOverrides = suitesOverrides; this.givenOverides = givenOverides; this.whenOverides = whenOverides; this.thenOverides = thenOverides; this.testSpecification = testSpecification; this.specs = testSpecification( this.Suites(), this.Given(), this.When(), this.Then() ); this.testJobs = this.specs.map((suite) => { const suiteRunner = (suite2) => async (puppetMaster, tLog) => { try { const x = await suite2.run( input, puppetMaster.testResourceConfiguration, (fPath, value) => puppetMaster.testArtiFactoryfileWriter( tLog, (p) => { this.artifacts.push(p); } )( puppetMaster.testResourceConfiguration.fs + "/" + fPath, value ), tLog, puppetMaster ); return x; } catch (e) { console.error(e.stack); } }; const runner = suiteRunner(suite); return { test: suite, toObj: () => { return suite.toObj(); }, runner, receiveTestResourceConfig: async function(puppetMaster) { const tLog = async (...l) => { }; try { const suiteDone = await runner(puppetMaster, tLog); const fails = suiteDone.fails; await puppetMaster.writeFileSync( `tests.json`, JSON.stringify(this.toObj(), null, 2), "test" ); return { failed: fails > 0, fails, artifacts: this.artifacts || [], features: suiteDone.features() }; } catch (e) { console.error(e.stack); return { failed: true, fails: -1, artifacts: this.artifacts || [], features: [] }; } } }; }); } // testsJson() { // puppetMaster.writeFileSync( // `tests.json`, // JSON.stringify({ features: suiteDone.features() }, null, 2) // ); // } Specs() { return this.specs; } Suites() { return this.suitesOverrides; } Given() { return this.givenOverides; } When() { return this.whenOverides; } Then() { return this.thenOverides; } }; // src/lib/classBuilder.ts var ClassBuilder = class extends BaseBuilder { constructor(testImplementation, testSpecification, input, suiteKlasser, givenKlasser, whenKlasser, thenKlasser, testResourceRequirement) { const classySuites = Object.entries(testImplementation.suites).reduce( (a, [key], index) => { a[key] = (somestring, givens) => { return new suiteKlasser.prototype.constructor( somestring, index, givens ); }; return a; }, {} ); const classyGivens = Object.entries(testImplementation.givens).reduce( (a, [key, g]) => { a[key] = (features, whens, thens, ...initialValues) => { return new givenKlasser.prototype.constructor( key, features, whens, thens, testImplementation.givens[key], initialValues ); }; return a; }, {} ); const classyWhens = Object.entries(testImplementation.whens).reduce( (a, [key, whEn]) => { a[key] = (...payload) => { return new whenKlasser.prototype.constructor( `${key}: ${payload && payload.toString()}`, whEn(...payload) ); }; return a; }, {} ); const classyThens = Object.entries(testImplementation.thens).reduce( (a, [key, thEn]) => { a[key] = (...args) => { return new thenKlasser.prototype.constructor( `${key}: ${args && args.toString()}`, thEn(...args) ); }; return a; }, {} ); super( input, classySuites, classyGivens, classyWhens, classyThens, testResourceRequirement, testSpecification ); } }; // src/lib/BaseSuite.ts var BaseSuite = class { constructor(name, index, givens = {}) { this.artifacts = []; const suiteName = name || "testSuite"; if (!suiteName) { throw new Error("BaseSuite requires a non-empty name"); } this.name = suiteName; this.index = index; this.givens = givens; this.fails = 0; } addArtifact(path2) { const normalizedPath = path2.replace(/\\/g, "/"); this.artifacts.push(normalizedPath); } features() { try { const features = Object.keys(this.givens).map((k) => this.givens[k].features).flat().filter((value, index, array) => { return array.indexOf(value) === index; }); return features || []; } catch (e) { console.error("[ERROR] Failed to extract features:", JSON.stringify(e)); return []; } } toObj() { const givens = Object.keys(this.givens).map((k) => this.givens[k].toObj()); return { name: this.name, givens, fails: this.fails, failed: this.failed, features: this.features() }; } setup(s, artifactory, tr, pm) { return new Promise((res) => res(s)); } assertThat(t) { return !!t; } afterAll(store, artifactory, pm) { return store; } async run(input, testResourceConfiguration, artifactory, tLog, pm) { this.testResourceConfiguration = testResourceConfiguration; const suiteArtifactory = (fPath, value) => artifactory(`suite-${this.index}-${this.name}/${fPath}`, value); const sNdx = this.index; const addArtifact = this.addArtifact.bind(this); const proxiedPm = beforeAllProxy(pm, sNdx.toString(), addArtifact); const subject = await this.setup( input, suiteArtifactory, testResourceConfiguration, proxiedPm ); for (const [gKey, g] of Object.entries(this.givens)) { const giver = this.givens[gKey]; this.store = await giver.give( subject, gKey, testResourceConfiguration, this.assertThat, suiteArtifactory, tLog, pm, sNdx ).catch((e) => { this.failed = true; this.fails = this.fails + 1; throw e; }); } try { const addArtifact2 = this.addArtifact.bind(this); const afterAllPm = afterAllProxy(pm, sNdx.toString(), addArtifact2); this.afterAll(this.store, artifactory, afterAllPm); } catch (e) { console.error(JSON.stringify(e)); } return this; } }; // src/lib/index.ts var BaseAdapter = () => ({ beforeAll: async (input, testResource, pm) => { return input; }, beforeEach: async function(subject, initializer, testResource, initialValues, pm) { return subject; }, afterEach: async (store, key, pm) => Promise.resolve(store), afterAll: (store, pm) => void 0, butThen: async (store, thenCb, testResource, pm) => { return thenCb(store, pm); }, andWhen: async (store, whenCB, testResource, pm) => { return whenCB(store, pm); }, assertThis: (x) => x }); var DefaultAdapter = (p) => { const base = BaseAdapter(); return { ...base, ...p }; }; var defaultTestResourceRequirement = { ports: 0 }; // src/lib/abstractBase.ts var BaseGiven = class { constructor(name, features, whens, thens, givenCB, initialValues) { this.artifacts = []; this.name = name; this.features = features; this.whens = whens; this.thens = thens; this.givenCB = givenCB; this.initialValues = initialValues; } addArtifact(path2) { const normalizedPath = path2.replace(/\\/g, "/"); this.artifacts.push(normalizedPath); } beforeAll(store) { return store; } toObj() { return { key: this.key, name: this.name, whens: this.whens.map((w) => { if (w && w.toObj) return w.toObj(); console.error("w is not as expected!", JSON.stringify(w)); return {}; }), thens: this.thens.map((t) => t.toObj()), error: this.error ? [this.error, this.error.stack] : null, failed: this.failed, features: this.features, artifacts: this.artifacts }; } async afterEach(store, key, artifactory, pm) { return store; } async give(subject, key, testResourceConfiguration, tester, artifactory, tLog, pm, suiteNdx) { this.key = key; tLog(` ${this.key}`); tLog(` Given: ${this.name}`); const givenArtifactory = (fPath, value) => artifactory(`given-${key}/${fPath}`, value); this.uberCatcher((e) => { console.error(e.toString()); this.error = e.error; tLog(e.stack); }); try { const addArtifact = this.addArtifact.bind(this); const proxiedPm = beforeEachProxy(pm, suiteNdx.toString(), addArtifact); this.store = await this.givenThat( subject, testResourceConfiguration, givenArtifactory, this.givenCB, this.initialValues, proxiedPm ); } catch (e) { this.failed = true; this.error = e.stack; } try { for (const [whenNdx, whenStep] of this.whens.entries()) { await whenStep.test( this.store, testResourceConfiguration, tLog, pm, `suite-${suiteNdx}/given-${key}/when/${whenNdx}` ); } for (const [thenNdx, thenStep] of this.thens.entries()) { const t = await thenStep.test( this.store, testResourceConfiguration, tLog, pm, `suite-${suiteNdx}/given-${key}/then-${thenNdx}` ); tester(t); } } catch (e) { this.error = e.stack; this.failed = true; } finally { try { const addArtifact = this.addArtifact.bind(this); const proxiedPm = afterEachProxy( pm, suiteNdx.toString(), key, addArtifact ); await this.afterEach(this.store, this.key, givenArtifactory, proxiedPm); } catch (e) { this.failed = e; throw e; } } return this.store; } }; var BaseWhen = class { constructor(name, whenCB) { this.artifacts = []; this.name = name; this.whenCB = whenCB; } addArtifact(path2) { const normalizedPath = path2.replace(/\\/g, "/"); this.artifacts.push(normalizedPath); } toObj() { const obj = { name: this.name, error: this.error ? `${this.error.name}: ${this.error.message} ${this.error.stack}` : null, artifacts: this.artifacts || [] }; console.log( `[TOOBJ] Serializing ${this.constructor.name} with artifacts:`, obj.artifacts ); return obj; } async test(store, testResourceConfiguration, tLog, pm, filepath) { try { const addArtifact = this.addArtifact.bind(this); const proxiedPm = andWhenProxy(pm, filepath, addArtifact); const result = await this.andWhen( store, this.whenCB, testResourceConfiguration, proxiedPm ); return result; } catch (e) { console.error( "[ERROR] When step failed:", this.name.toString(), e.toString() ); this.error = e; throw e; } } }; var BaseThen = class { constructor(name, thenCB) { this.artifacts = []; this.name = name; this.thenCB = thenCB; this.error = false; this.artifacts = []; } addArtifact(path2) { const normalizedPath = path2.replace(/\\/g, "/"); this.artifacts.push(normalizedPath); } toObj() { const obj = { name: this.name, error: this.error, artifacts: this.artifacts }; return obj; } async test(store, testResourceConfiguration, tLog, pm, filepath) { const addArtifact = this.addArtifact.bind(this); const proxiedPm = butThenProxy(pm, filepath, addArtifact); return this.butThen( store, async (s) => { try { if (typeof this.thenCB === "function") { const wrappedPm = new Proxy(proxiedPm, { get: (target, prop, receiver) => { if (prop === "writeFileSync") { return (...args) => { console.log( `[DEBUG] writeFileSync called with args:`, args ); return target[prop](...args); }; } return target[prop]; } }); const result = await this.thenCB(s, wrappedPm); return result; } else { return this.thenCB; } } catch (e) { console.error(e.stack); } }, testResourceConfiguration, proxiedPm ).catch((e) => { this.error = e.stack; }); } }; // src/lib/core.ts var TesterantoCore = class extends ClassBuilder { constructor(input, testSpecification, testImplementation, testResourceRequirement = defaultTestResourceRequirement, testAdapter, uberCatcher) { const fullAdapter = DefaultAdapter(testAdapter); super( testImplementation, testSpecification, input, class extends BaseSuite { afterAll(store, artifactory, pm) { return fullAdapter.afterAll(store, pm); } assertThat(t) { return fullAdapter.assertThis(t); } async setup(s, artifactory, tr, pm) { return fullAdapter.beforeAll?.(s, tr, pm) ?? s; } }, class Given extends BaseGiven { constructor() { super(...arguments); this.uberCatcher = uberCatcher; } async givenThat(subject, testResource, artifactory, initializer, initialValues, pm) { return fullAdapter.beforeEach( subject, initializer, testResource, initialValues, pm ); } afterEach(store, key, artifactory, pm) { return Promise.resolve(fullAdapter.afterEach(store, key, pm)); } }, class When extends BaseWhen { async andWhen(store, whenCB, testResource, pm) { return await fullAdapter.andWhen(store, whenCB, testResource, pm); } }, class Then extends BaseThen { async butThen(store, thenCB, testResource, pm) { return await fullAdapter.butThen(store, thenCB, testResource, pm); } }, testResourceRequirement ); } }; // src/PM/index.ts var PM = class { }; export { __export, defaultTestResourceRequirement, butThenProxy, andWhenProxy, BaseGiven, BaseWhen, BaseThen, BaseBuilder, ClassBuilder, BaseSuite, TesterantoCore, PM };