playwright-bdd
Version:
BDD Testing with Playwright runner
144 lines • 6.23 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TestFilesGenerator = void 0;
/**
* Generate playwright test files from Gherkin documents.
*/
const node_fs_1 = __importDefault(require("node:fs"));
const tinyglobby_1 = __importDefault(require("tinyglobby"));
const file_1 = require("./file");
const featuresLoader_1 = require("../gherkin/featuresLoader");
const snippets_1 = require("../snippets");
const tag_expressions_1 = require("@cucumber/tag-expressions");
const exit_1 = require("../utils/exit");
const loader_1 = require("../steps/loader");
const paths_1 = require("../utils/paths");
const stepRegistry_1 = require("../steps/stepRegistry");
const utils_1 = require("../utils");
const logger_1 = require("./logger");
class TestFilesGenerator {
constructor(config) {
this.config = config;
this.featuresLoader = new featuresLoader_1.FeaturesLoader();
this.files = [];
this.logger = new logger_1.TestFilesGeneratorLogger({ verbose: config.verbose });
if (config.tags)
this.tagsExpression = (0, tag_expressions_1.parse)(config.tags);
}
async generate() {
await Promise.all([this.loadFeatures(), this.loadSteps()]);
this.buildFiles();
this.checkStepDefinitionArityErrors();
this.checkMissingSteps();
await this.clearOutputDir();
await this.saveFiles();
}
async extractSteps() {
await this.loadSteps();
return stepRegistry_1.stepDefinitions;
}
// todo: combine with extractSteps
async extractUnusedSteps() {
await Promise.all([this.loadFeatures(), this.loadSteps()]);
this.buildFiles();
const allUsedDefinitions = this.files.reduce((acc, file) => {
file.getUsedDefinitions().forEach((definition) => acc.add(definition));
return acc;
}, new Set());
return stepRegistry_1.stepDefinitions.filter((stepDefinition) => {
return !allUsedDefinitions.has(stepDefinition);
});
}
async loadFeatures() {
const { configDir, features, language } = this.config;
const { files, finalPatterns } = await (0, featuresLoader_1.resolveFeatureFiles)(configDir, features);
this.logger.logLoadingFeatures(files, finalPatterns);
await this.featuresLoader.load(files, {
relativeTo: configDir,
defaultDialect: language,
});
this.handleFeatureParseErrors();
}
async loadSteps() {
const { configDir, steps } = this.config;
const { files, finalPatterns } = await (0, loader_1.resolveStepFiles)(configDir, steps);
this.logger.logLoadingSteps(files, finalPatterns);
await (0, loader_1.loadSteps)(files);
this.logger.logLoadedSteps(files, stepRegistry_1.stepDefinitions);
await this.handleImportTestFrom();
}
buildFiles() {
this.files = this.featuresLoader
.getDocumentsWithPickles()
.map((gherkinDocument) => {
return new file_1.TestFile({
config: this.config,
gherkinDocument,
tagsExpression: this.tagsExpression,
}).build();
})
// render only files that have at least one executable test
.filter((file) => file.hasExecutableTests());
}
checkMissingSteps() {
if (this.config.missingSteps !== 'fail-on-gen')
return;
const missingSteps = [];
this.files.forEach((file) => missingSteps.push(...file.getMissingSteps()));
if (missingSteps.length) {
new snippets_1.Snippets(missingSteps).print();
(0, exit_1.exit)();
}
}
checkStepDefinitionArityErrors() {
const stepDefinitionArityErrors = this.files.flatMap((file) => file.getStepDefinitionArityErrors());
if (stepDefinitionArityErrors.length) {
const uniqueErrors = [...new Set(stepDefinitionArityErrors)];
(0, exit_1.exit)([
`Found step definitions with incorrect arguments: ${uniqueErrors.length}`,
uniqueErrors.join('\n\n'),
].join('\n'));
}
}
async handleImportTestFrom() {
const { importTestFrom } = this.config;
if (!importTestFrom)
return;
// require importTestFrom separately instead of just adding to steps,
// b/c we need additionally check that it exports "test" variable.
// If checking by together loaded files, it's become messy to find correct file by url,
// b/c of win/posix path separator.
const exportedTests = await (0, loader_1.loadStepsFromFile)(importTestFrom.file);
const varName = importTestFrom.varName || 'test';
if (!exportedTests.find((info) => info.varName === varName)) {
(0, exit_1.exit)(`File "${(0, paths_1.relativeToCwd)(importTestFrom.file)}" pointed by "importTestFrom"`, `should export "${varName}" variable.`);
}
}
async saveFiles() {
this.files.forEach((file) => (0, utils_1.saveFileSync)(file.outputPath, file.content));
this.logger.logGeneratedTestFiles(this.files);
}
async clearOutputDir() {
const pattern = `${tinyglobby_1.default.convertPathToPattern(this.config.outputDir)}/**/*.spec.js`;
const testFiles = await tinyglobby_1.default.glob(pattern, { expandDirectories: false });
this.logger.logClearingOutputDir(pattern);
const tasks = testFiles.map((testFile) => node_fs_1.default.promises.rm(testFile));
await Promise.all(tasks);
}
handleFeatureParseErrors() {
const { parseErrors } = this.featuresLoader;
if (parseErrors.length) {
const message = parseErrors
.map((parseError) => {
return `Parse error in "${parseError.source.uri}" ${parseError.message}`;
})
.join('\n');
(0, exit_1.exit)(message);
}
}
}
exports.TestFilesGenerator = TestFilesGenerator;
//# sourceMappingURL=index.js.map