UNPKG

playwright-bdd

Version:
112 lines (95 loc) 4 kB
/** * Load features. * * See: https://github.com/cucumber/cucumber-js/blob/main/src/api/load_sources.ts * See: https://github.com/cucumber/cucumber-js/blob/main/src/api/gherkin.ts */ import fs from 'node:fs'; import path from 'node:path'; import { generateMessages } from '@cucumber/gherkin'; import { Query as GherkinQuery } from '@cucumber/gherkin-utils'; import { ParseError, Pickle, IdGenerator, SourceMediaType } from '@cucumber/messages'; import { resolveFiles } from '../utils/paths'; import { toArray } from '../utils'; import { GherkinDocumentWithPickles, PickleWithLocation } from './types'; import { TagsFromPathInjector } from './TagsFromPathInjector'; import { AutofillMap } from '../utils/AutofillMap'; export function resolveFeatureFiles(cwd: string, patterns: string | string[]) { return resolveFiles(cwd, toArray(patterns), 'feature'); } type LoadFeaturesOptions = { // Default is 'en'. // See: https://github.com/cucumber/gherkin-streams/blob/main/src/makeGherkinOptions.ts#L5 defaultDialect?: string; // If relativeTo is provided, uri in gherkin documents will be relative to it. // See: https://github.com/cucumber/gherkin-streams/blob/main/src/SourceMessageStream.ts#L31 relativeTo?: string; }; const newId = IdGenerator.uuid(); export class FeaturesLoader { gherkinQuery = new GherkinQuery(); parseErrors: ParseError[] = []; private tagsFromPathInjector = new TagsFromPathInjector(newId); async load(absFeatureFiles: string[], options: LoadFeaturesOptions) { this.gherkinQuery = new GherkinQuery(); this.parseErrors = []; // Load features sequentially (as it was in gherkin-streams). // Potentially it can be parallelized in the future. // See: https://github.com/cucumber/gherkin-streams/blob/main/src/GherkinStreams.ts#L30 for (const featureFile of absFeatureFiles) { await this.loadFeature(featureFile, options); } } getDocumentsCount() { return this.gherkinQuery.getGherkinDocuments().length; } getDocumentsWithPickles(): GherkinDocumentWithPickles[] { const picklesByUri = new AutofillMap<string, Pickle[]>(); for (const pickle of this.gherkinQuery.getPickles()) { const uri = pickle.uri!; picklesByUri.getOrCreate(uri, () => []).push(pickle); } return this.gherkinQuery.getGherkinDocuments().map((gherkinDocument) => { const pickles = (picklesByUri.get(gherkinDocument.uri!) ?? []).map((pickle) => this.getPickleWithLocation(pickle), ); return this.tagsFromPathInjector.inject({ ...gherkinDocument, pickles }); }); } private async loadFeature(featureFile: string, options: LoadFeaturesOptions) { const uri = getFeatureUri(featureFile, options); const gherkinSource = await fs.promises.readFile(featureFile, 'utf8'); const gherkinOptions = makeGherkinOptions(options); const envelopes = generateMessages( gherkinSource, uri, SourceMediaType.TEXT_X_CUCUMBER_GHERKIN_PLAIN, gherkinOptions, ); envelopes.forEach((envelope) => { this.gherkinQuery.update(envelope); if (envelope.parseError) { this.parseErrors.push(envelope.parseError); } }); } private getPickleWithLocation(pickle: Pickle): PickleWithLocation { const lastAstNodeId = pickle.astNodeIds[pickle.astNodeIds.length - 1]!; // Pickle.location is optional in @cucumber/messages, but @cucumber/gherkin // always populates it (added in gherkin v37). Safe to assert non-null. const location = this.gherkinQuery.getLocation(lastAstNodeId)!; return { ...pickle, location }; } } function makeGherkinOptions(options: LoadFeaturesOptions) { return { defaultDialect: options.defaultDialect, includeSource: true, includeGherkinDocument: true, includePickles: true, newId, }; } function getFeatureUri(featureFile: string, options: LoadFeaturesOptions) { return options.relativeTo ? path.relative(options.relativeTo, featureFile) : featureFile; }