Skip to content

Commit

Permalink
feature: show step locations in unused export and in duplicate steps …
Browse files Browse the repository at this point in the history
…error (#113)
  • Loading branch information
vitalets committed Apr 12, 2024
1 parent 1816bcc commit 848205a
Show file tree
Hide file tree
Showing 11 changed files with 141 additions and 62 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## dev
* feature: show step locations in unused export and in duplicate steps error ([#113](https://github.com/vitalets/playwright-bdd/issues/113))
* feature: add `$step` fixture ([#133](https://github.com/vitalets/playwright-bdd/issues/133))
* feature: add experimental support of `steps` option (related to [#94](https://github.com/vitalets/playwright-bdd/issues/94))

Expand Down
38 changes: 28 additions & 10 deletions src/cucumber/loadSteps.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { IRunConfiguration, IRunEnvironment, loadSupport } from '@cucumber/cucumber/api';
import { installTransform } from '../playwright/transform';
import { exit } from '../utils/exit';
import { ISupportCodeLibrary } from './types';
import { ISupportCodeLibrary, StepDefinition } from './types';
import { getStepConfig } from '../steps/stepConfig';
import path from 'node:path';

const cache = new Map<string, Promise<ISupportCodeLibrary>>();

Expand Down Expand Up @@ -35,24 +37,40 @@ export async function loadSteps(
export function findStepDefinition(
supportCodeLibrary: ISupportCodeLibrary,
stepText: string,
file: string,
featureFile: string,
) {
const matchedSteps = supportCodeLibrary.stepDefinitions.filter((step) => {
return step.matchesStepName(stepText);
});
if (matchedSteps.length === 0) return;
if (matchedSteps.length > 1)
exit(
[
`Multiple step definitions matched for text: "${stepText}" (${file})`,
// todo: print location of every step definition (as in cucumber)
...matchedSteps.map((s) => ` ${s.pattern}`),
].join('\n'),
);
if (matchedSteps.length > 1) {
exit(formtDuplicateStepsError(stepText, featureFile, matchedSteps));
}

return matchedSteps[0];
}

export function hasTsNodeRegister(runConfiguration: IRunConfiguration) {
return runConfiguration.support.requireModules.includes('ts-node/register');
}

function formtDuplicateStepsError(
stepText: string,
featureFile: string,
matchedSteps: StepDefinition[],
) {
const stepLines = matchedSteps.map(formatDuplicateStep);
return [
`Multiple step definitions matched for text: "${stepText}" (${featureFile})`,
...stepLines,
].join('\n');
}

function formatDuplicateStep(step: StepDefinition) {
const { pattern } = step;
const patternText = typeof pattern === 'string' ? pattern : pattern.source;
const { location } = getStepConfig(step) || {};
const file = location ? path.relative(process.cwd(), location.file) : '';
const locationStr = location ? ` - ${file}:${location.line}` : '';
return ` ${patternText}${locationStr}`;
}
2 changes: 2 additions & 0 deletions src/cucumber/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import type { ParameterTypeRegistry } from '@cucumber/cucumber-expressions';
import type StepDefinition from '@cucumber/cucumber/lib/models/step_definition';

export { StepDefinition };

export interface ISupportCodeLibrary {
parameterTypeRegistry: ParameterTypeRegistry;
stepDefinitions: StepDefinition[];
Expand Down
4 changes: 0 additions & 4 deletions test/duplicate-steps/features/one.feature

This file was deleted.

13 changes: 13 additions & 0 deletions test/duplicate-steps/features/sample.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Feature: duplicate steps

@duplicate-regular-steps
Scenario: duplicate regular steps
Given duplicate step

@duplicate-decorator-steps
Scenario: duplicate decorator steps
Given duplicate decorator step

@no-duplicates
Scenario: no duplicates
Given unique step
4 changes: 0 additions & 4 deletions test/duplicate-steps/features/two.feature

This file was deleted.

62 changes: 43 additions & 19 deletions test/duplicate-steps/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,49 @@
import { defineConfig } from '@playwright/test';
import { defineConfig, Project } from '@playwright/test';
import { defineBddConfig } from 'playwright-bdd';

const PROJECTS = (process.env.PROJECTS || '').split(',');

export default defineConfig({
projects: [
{
name: 'no duplicate steps',
testDir: defineBddConfig({
outputDir: `.features-gen/one`,
paths: ['features/one.feature'],
importTestFrom: 'steps/fixtures.ts',
}),
},
{
// important to have duplicate steps in the second project
// that runs in a worker process
name: 'duplicates',
testDir: defineBddConfig({
outputDir: `.features-gen/two`,
paths: ['features/two.feature'],
importTestFrom: 'steps/fixtures.ts',
}),
},
...(PROJECTS.includes('no-duplicates') ? [noDuplicates()] : []),
...(PROJECTS.includes('duplicate-regular-steps') ? [duplicateRegularSteps()] : []),
...(PROJECTS.includes('duplicate-decorator-steps') ? [duplicateDecoratorSteps()] : []),
],
});

function noDuplicates(): Project {
return {
// this project must be first and is needed to run the second project in a worker
name: 'no-duplicates',
testDir: defineBddConfig({
outputDir: `.features-gen/no-duplicates`,
paths: ['features/*.feature'],
require: ['steps/steps.ts'],
tags: '@no-duplicates',
}),
};
}

function duplicateRegularSteps(): Project {
return {
name: 'duplicate-regular-steps',
testDir: defineBddConfig({
outputDir: `.features-gen/regular`,
paths: ['features/*.feature'],
require: ['steps/steps.ts'],
tags: '@duplicate-regular-steps',
}),
};
}

function duplicateDecoratorSteps(): Project {
return {
name: 'duplicate-decorator-steps',
testDir: defineBddConfig({
outputDir: `.features-gen/decorator`,
paths: ['features/*.feature'],
importTestFrom: 'steps/fixtures.ts',
tags: '@duplicate-decorator-steps',
}),
};
}
6 changes: 3 additions & 3 deletions test/duplicate-steps/steps/TodoPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { test } from './fixtures';
export
@Fixture<typeof test>('todoPage')
class TodoPage {
@Given('unique step')
@Given('duplicate decorator step')
async step1() {}

@Given('duplicate step')
@Given('duplicate decorator step')
async step2() {}

@Given('duplicate step')
@Given(/duplicate decorator step/)
async step3() {}
}
6 changes: 1 addition & 5 deletions test/duplicate-steps/steps/fixtures.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { test as base } from 'playwright-bdd';
import { TodoPage } from './TodoPage';

type Fixtures = {
todoPage: TodoPage;
};

export const test = base.extend<Fixtures>({
export const test = base.extend<{ todoPage: TodoPage }>({
todoPage: ({}, use) => use(new TodoPage()),
});
8 changes: 8 additions & 0 deletions test/duplicate-steps/steps/steps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createBdd } from 'playwright-bdd';

const { Given } = createBdd();

Given('unique step', async () => {});
Given('duplicate step', async () => {});
Given('duplicate step', async () => {});
Given(/duplicate step/, async () => {});
59 changes: 42 additions & 17 deletions test/duplicate-steps/test.mjs
Original file line number Diff line number Diff line change
@@ -1,24 +1,49 @@
import { normalize } from 'node:path';
import { test, TestDir, execPlaywrightTestWithError, DEFAULT_CMD } from '../_helpers/index.mjs';
import {
test,
TestDir,
normalize,
execPlaywrightTestWithError,
BDDGEN_CMD,
} from '../_helpers/index.mjs';

const testDir = new TestDir(import.meta);
const featureFile = normalize('features/sample.feature');

test(`${testDir.name} (main thread)`, () => {
execPlaywrightTestWithError(
testDir.name,
DUPLICATE_STEPS_ERROR,
`${DEFAULT_CMD} --project duplicates`,
);
test(`${testDir.name} (main thread - regular steps)`, () => {
const error = [
`Multiple step definitions matched for text: "duplicate step" (${featureFile})`,
` duplicate step - steps/steps.ts:6`,
` duplicate step - steps/steps.ts:7`,
` duplicate step - steps/steps.ts:8`,
].join('\n');
execPlaywrightTestWithError(testDir.name, error, {
cmd: BDDGEN_CMD,
env: { PROJECTS: 'duplicate-regular-steps' },
});
});

test(`${testDir.name} (worker)`, () => {
execPlaywrightTestWithError(testDir.name, DUPLICATE_STEPS_ERROR);
test(`${testDir.name} (main thread - decorator steps)`, () => {
const error = [
`Multiple step definitions matched for text: "duplicate decorator step" (${featureFile})`,
` duplicate decorator step - steps/TodoPage.ts:7`,
` duplicate decorator step - steps/TodoPage.ts:10`,
` duplicate decorator step - steps/TodoPage.ts:13`,
].join('\n');
execPlaywrightTestWithError(testDir.name, error, {
cmd: BDDGEN_CMD,
env: { PROJECTS: 'duplicate-decorator-steps' },
});
});

const DUPLICATE_STEPS_ERROR = [
`Multiple step definitions matched for text: "duplicate step" (${normalize(
'features/two.feature',
)})`,
' duplicate step',
' duplicate step',
].join('\n');
test(`${testDir.name} (worker - regular steps)`, () => {
const error = [
`Multiple step definitions matched for text: "duplicate step" (${featureFile})`,
` duplicate step - steps/steps.ts:6`,
` duplicate step - steps/steps.ts:7`,
` duplicate step - steps/steps.ts:8`,
].join('\n');
execPlaywrightTestWithError(testDir.name, error, {
cmd: BDDGEN_CMD,
env: { PROJECTS: 'no-duplicates,duplicate-regular-steps' },
});
});

0 comments on commit 848205a

Please sign in to comment.