diff --git a/CHANGELOG.md b/CHANGELOG.md index 78e73f8f..8d708e97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## Unreleased + +- Support config file locations other than project root, fixes [#1243](https://github.com/badeball/cypress-cucumber-preprocessor/discussions/1243). + ## v21.0.0 Breaking changes: diff --git a/declarations.d.ts b/declarations.d.ts index e0ee0f65..a2867bf8 100644 --- a/declarations.d.ts +++ b/declarations.d.ts @@ -4,9 +4,14 @@ declare module "find-cypress-specs" { export function getSpecs( config: Cypress.ConfigOptions, type: Cypress.TestingType, + returnAbsolute?: boolean, ): string[]; - export function getSpecs(config: Cypress.PluginConfigOptions): string[]; + export function getSpecs( + config: Cypress.PluginConfigOptions, + type: Cypress.TestingType, + returnAbsolute?: boolean, + ): string[]; export function getConfig(): Cypress.ConfigOptions; } diff --git a/features/issues/1243.feature b/features/issues/1243.feature new file mode 100644 index 00000000..eecb58d2 --- /dev/null +++ b/features/issues/1243.feature @@ -0,0 +1,21 @@ +# https://github.com/badeball/cypress-cucumber-preprocessor/issues/1243 + +Feature: custom config location + Scenario: custom config location + Given a file named "cypress/e2e/a.feature" with: + """ + Feature: a feature + Scenario: a scenario + Given a step + """ + And a file named "cypress/support/step_definitions/steps.js" with: + """ + const { Given } = require("@badeball/cypress-cucumber-preprocessor"); + Given("a step", function() {}) + """ + And a file named "config/cypress.config.js" with: + """ + module.exports = require("../cypress.config.js"); + """ + When I run cypress with "--config-file config/cypress.config.js" + Then it passes diff --git a/lib/add-cucumber-preprocessor-plugin.ts b/lib/add-cucumber-preprocessor-plugin.ts index 5b7b19b9..7d6da74a 100644 --- a/lib/add-cucumber-preprocessor-plugin.ts +++ b/lib/add-cucumber-preprocessor-plugin.ts @@ -138,46 +138,48 @@ export async function addCucumberPreprocessorPlugin( const node = parse(tags); - const testFiles = getSpecs(config).filter((testFile) => { - if (!testFile.endsWith(".feature")) { - switch (preprocessor.filterSpecsMixedMode) { - case "hide": - return false; - case "show": - return true; - case "empty-set": - return node.evaluate([]); - default: - assertNever(preprocessor.filterSpecsMixedMode); + const testFiles = getSpecs(config, "foobar" as any, true).filter( + (testFile) => { + if (!testFile.endsWith(".feature")) { + switch (preprocessor.filterSpecsMixedMode) { + case "hide": + return false; + case "show": + return true; + case "empty-set": + return node.evaluate([]); + default: + assertNever(preprocessor.filterSpecsMixedMode); + } } - } - - const content = fs.readFileSync(testFile).toString("utf-8"); - - const options = { - includeSource: false, - includeGherkinDocument: false, - includePickles: true, - newId: IdGenerator.incrementing(), - }; - - const envelopes = generateMessages( - content, - testFile, - SourceMediaType.TEXT_X_CUCUMBER_GHERKIN_PLAIN, - options, - ); - - const pickles = envelopes - .map((envelope) => envelope.pickle) - .filter(notNull); - - return pickles.some((pickle) => - node.evaluate( - pickle.tags?.map((tag) => tag.name).filter(notNull) ?? [], - ), - ); - }); + + const content = fs.readFileSync(testFile).toString("utf-8"); + + const options = { + includeSource: false, + includeGherkinDocument: false, + includePickles: true, + newId: IdGenerator.incrementing(), + }; + + const envelopes = generateMessages( + content, + testFile, + SourceMediaType.TEXT_X_CUCUMBER_GHERKIN_PLAIN, + options, + ); + + const pickles = envelopes + .map((envelope) => envelope.pickle) + .filter(notNull); + + return pickles.some((pickle) => + node.evaluate( + pickle.tags?.map((tag) => tag.name).filter(notNull) ?? [], + ), + ); + }, + ); debug(`Resolved specs ${inspect(testFiles)}`); diff --git a/lib/step-definitions.ts b/lib/step-definitions.ts index 3e7fc0ba..72979693 100644 --- a/lib/step-definitions.ts +++ b/lib/step-definitions.ts @@ -56,6 +56,22 @@ export function getStepDefinitionPatterns( >, filepath: string, ): string[] { + /** + * The reason for these assertions is that when giving relative paths to path.relative, the result + * will depend on CWD, which is affected by EG. the --config-file parameter [1]. + * + * [1] https://github.com/badeball/cypress-cucumber-preprocessor/issues/1243 + */ + assert( + path.isAbsolute(configuration.implicitIntegrationFolder), + `Expected an absolute path for implicit integration folder but got ${configuration.implicitIntegrationFolder}`, + ); + + assert( + path.isAbsolute(filepath), + `Expected an absolute path for spec but got ${filepath}`, + ); + const filepathReplacement = glob.escape( trimFeatureExtension( path.relative(configuration.implicitIntegrationFolder, filepath), diff --git a/lib/subpath-entrypoints/esbuild.ts b/lib/subpath-entrypoints/esbuild.ts index 88d2b53a..f7cc6538 100644 --- a/lib/subpath-entrypoints/esbuild.ts +++ b/lib/subpath-entrypoints/esbuild.ts @@ -75,8 +75,6 @@ export function createEsbuildPlugin( if (needPrettify) { debug("esbuild: prettifying sources"); - /** - */ sourceMap.sources = sourceMap.sources.map((source: string) => { return path.relative( configuration.projectRoot, @@ -95,9 +93,17 @@ export function createEsbuildPlugin( "base64", ); + /** + * Why `${"sourceMappingURL"}` you may ask. This is so esbuild doesn't crap itself upon + * errors, where it would search for source maps and find THIS code line, which is not a + * valid source map (obvously). + * + * Without this, esbuild would error with "Unexpected token z in JSON at position 0" every + * time an error occurred during build time. + */ await fs.appendFile( outfile, - `//# sourceMappingURL=data:application/json;base64,${encoded}\n`, + `//# ${"sourceMappingURL"}=data:application/json;base64,${encoded}\n`, ); }); } diff --git a/lib/template.ts b/lib/template.ts index 3dcfe76c..6064d2e4 100644 --- a/lib/template.ts +++ b/lib/template.ts @@ -71,7 +71,11 @@ export async function compile( const pickles = envelopes.map((envelope) => envelope.pickle).filter(notNull); const implicitIntegrationFolder = assertAndReturn( - ancestor(...getSpecs(configuration).map(path.dirname).map(path.normalize)), + ancestor( + ...getSpecs(configuration, "foobar" as any, true) + .map(path.dirname) + .map(path.normalize), + ), "Expected to find a common ancestor path", );