testeranto
Version:
the AI powered BDD test framework for typescript projects
873 lines (862 loc) • 22.2 kB
JavaScript
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
};