jest-bdd-generator
Version:
Jest code generator empowered with Gherkin and BDD
179 lines (178 loc) • 8.9 kB
JavaScript
function _define_property(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
import { AstBuilder, GherkinClassicTokenMatcher, Parser } from "@cucumber/gherkin";
import { IdGenerator } from "@cucumber/messages";
import { JestToGherkin } from "../jest-to-gherkin/JestToGherkin";
export * from "../types";
const newId = IdGenerator.uuid();
const builder = new AstBuilder(newId);
const matcher = new GherkinClassicTokenMatcher();
const parser = new Parser(builder, matcher);
class TestGeneratorFromSource {
/**
* Read Gherkin source code and generate GherkinDocument object.
* @param gherkinDoc string of Gherkin soucecode.
* @returns GherkinDocument object.
*/
compileGherkinFromSource(gherkinDoc) {
const gherkinDocument = parser.parse(gherkinDoc);
return gherkinDocument;
}
/**
* Get steps details from source code. Includes statements, position, accessed variables, defined indentifiers
* @param source TypeScript source code in Jest
* @returns Array of Steps extracted from the source code.
*/
compileKnownStepsFromSource(source) {
this.source = source;
const _piler = new JestToGherkin();
_piler.fileName = "pathTestInput.test.ts";
const o = _piler.transpile(source, {
fileName: "pathTestInput.test.ts"
}).outputText;
this.transpiler = _piler;
return _piler.output;
}
/**
* @deprecated
* Generate Jest testing. Use `generateJestFromGherkin` instead.
* If a step is given source code `statements`, it will use it.
* If there are same step definitions in other scenarios without source code, it will reference them with comments.
* @param steps array of Steps extracted from source code.
* @param gherkinSource source Gherkin text.
* @returns Jest testing code in TypeScript, or undefined if cannot find the Feature definition.
*/
generateGherkinFromSource(steps, gherkinSource) {
return this.generateJestFromGherkin(steps, gherkinSource);
}
/**
* Generate Jest testing.
* If a step is given source code `statements`, it will use it.
* If there are same step definitions in other scenarios without source code, it will reference them with comments.
* @param steps array of Steps extracted from source code.
* @param gherkinSource source Gherkin text.
* @returns Jest testing code in TypeScript, or undefined if cannot find the Feature definition.
*/
generateJestFromGherkin(steps, gherkinSource) {
var _plan_feature, _feature_sourceCode;
const plan = this.compileGherkinFromSource(gherkinSource);
const statements = (_plan_feature = plan.feature) === null || _plan_feature === void 0 ? void 0 : _plan_feature.children.map((p) => {
var _p_scenario, _p_scenario1, _p_scenario2;
let lastKeyword;
const sourceSteps = (_p_scenario = p.scenario) === null || _p_scenario === void 0 ? void 0 : _p_scenario.steps.map((pStep) => {
const criteria = pStep.text;
const keyword = pStep.keyword === "And " ? lastKeyword : pStep.keyword;
lastKeyword = keyword;
const source = steps.find((s) => {
var _p_scenario3;
return s.value === criteria && s.parent === ((_p_scenario3 = p.scenario) === null || _p_scenario3 === void 0 ? void 0 : _p_scenario3.name);
});
if (source === null || source === void 0 ? void 0 : source.sourceCode) {
var _source_sourceCode_statements;
var _source_sourceCode_statements_map;
return [
// `//@${source.key} ${source.value}`,
// ` // import [${source.sourceCode?.imports}]`,
// ` // export [${source.sourceCode?.exports}]`,
` ${((_source_sourceCode_statements_map = (_source_sourceCode_statements = source.sourceCode.statements) === null || _source_sourceCode_statements === void 0 ? void 0 : _source_sourceCode_statements.map((a) => a.getFullText())) !== null && _source_sourceCode_statements_map !== void 0 ? _source_sourceCode_statements_map : []).join("")}`
].join("");
}
const sourceInOtherScenario = steps.find((s) => s.value === criteria);
if (sourceInOtherScenario) {
var _sourceInOtherScenario_sourceCode, _sourceInOtherScenario_sourceCode1, _sourceInOtherScenario_sourceCode_statements, _sourceInOtherScenario_sourceCode2;
var _sourceInOtherScenario_sourceCode_statements_map;
return [
`/**`,
`* FOUND: @${keyword}${pStep.text}`,
`* FROM: (Scenario)${sourceInOtherScenario.parent} - [Step]${sourceInOtherScenario.key} ${sourceInOtherScenario.value}`,
`* referenced: [${(_sourceInOtherScenario_sourceCode = sourceInOtherScenario.sourceCode) === null || _sourceInOtherScenario_sourceCode === void 0 ? void 0 : _sourceInOtherScenario_sourceCode.imports}]`,
`* declared: [${(_sourceInOtherScenario_sourceCode1 = sourceInOtherScenario.sourceCode) === null || _sourceInOtherScenario_sourceCode1 === void 0 ? void 0 : _sourceInOtherScenario_sourceCode1.exports}]`,
`* (remove this comments block with: /^\\s*?\\/\\*\\*\\n\\*\\s*?FOUND: @.+\\n(\\*.+\\n)+/g )`,
`${((_sourceInOtherScenario_sourceCode_statements_map = (_sourceInOtherScenario_sourceCode2 = sourceInOtherScenario.sourceCode) === null || _sourceInOtherScenario_sourceCode2 === void 0 ? void 0 : (_sourceInOtherScenario_sourceCode_statements = _sourceInOtherScenario_sourceCode2.statements) === null || _sourceInOtherScenario_sourceCode_statements === void 0 ? void 0 : _sourceInOtherScenario_sourceCode_statements.map((a) => a.getFullText())) !== null && _sourceInOtherScenario_sourceCode_statements_map !== void 0 ? _sourceInOtherScenario_sourceCode_statements_map : []).join("")}`,
`*/`
].join("\n");
}
return [
`//@${keyword}${pStep.text}`,
` // TODO: implement assertions here;`,
` return 'pending';`,
``
].join(" \n");
}).join("\n");
let fnTitle = "it";
let sourceParams = [];
if ((_p_scenario1 = p.scenario) === null || _p_scenario1 === void 0 ? void 0 : _p_scenario1.examples) {
const exp = p.scenario.examples.map((ex) => {
var _ex_tableHeader;
const ret = [];
var _ex_tableHeader_cells_map;
sourceParams = (_ex_tableHeader_cells_map = (_ex_tableHeader = ex.tableHeader) === null || _ex_tableHeader === void 0 ? void 0 : _ex_tableHeader.cells.map((cell) => cell.value)) !== null && _ex_tableHeader_cells_map !== void 0 ? _ex_tableHeader_cells_map : [];
ex.tableBody.forEach((line) => {
const row = {};
line.cells.forEach((v, i) => {
row[sourceParams[i]] = JSON.parse(v.value);
});
const lineExpression = Object.keys(row).map((k) => {
const valueExpression = JSON.stringify(row[k]);
if (k.match(/^[a-zA-Z][0-9a-zA-Z]*?$/)) {
return `${k}: ${valueExpression}`;
} else {
return `"${k}": ${valueExpression}`;
}
}).join(", ");
ret.push(`{${lineExpression}}`);
});
return ret.join(",\n ");
}).join("");
fnTitle = `test.each([
${exp}
])`;
sourceParams = sourceParams.map((strVariable) => {
if (strVariable.match(/^[a-zA-Z][0-9a-zA-Z]*?$/)) {
return strVariable;
} else {
return `'${strVariable}': ${strVariable.replace(/-(\w)/g, (x) => x[1].toUpperCase())}`;
}
});
}
return `${fnTitle}('${(_p_scenario2 = p.scenario) === null || _p_scenario2 === void 0 ? void 0 : _p_scenario2.name}', async ({ ${sourceParams} }) => {
${sourceSteps}
});`;
}).flat().join("\n");
const feature = steps.find((s) => {
var _plan_feature2;
return s.key === "Feature" && s.value === ((_plan_feature2 = plan.feature) === null || _plan_feature2 === void 0 ? void 0 : _plan_feature2.name);
});
if (feature === null || feature === void 0 ? void 0 : (_feature_sourceCode = feature.sourceCode) === null || _feature_sourceCode === void 0 ? void 0 : _feature_sourceCode.statements) {
const contents = feature.sourceCode.statements;
const head = this.source.substring(0, contents[0].getFullStart());
const tail = this.source.substring(contents[contents.length - 1].end);
return [
head,
statements,
tail
].join("\n");
} else {
return statements;
}
}
constructor() {
_define_property(this, "transpiler", void 0);
_define_property(this, "source", void 0);
}
}
export {
TestGeneratorFromSource
};
//# sourceMappingURL=TestGeneratorFromSource.js.map