diff --git a/CHANGELOG.md b/CHANGELOG.md index 3671b6cf..66d17481 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## Dev +* fix default world in Playwright-style steps ([#255](https://github.com/vitalets/playwright-bdd/issues/255)) + ## 8.0.0 * support tags from path ([#217](https://github.com/vitalets/playwright-bdd/issues/217)) * support scoped step definitions by tags ([#205](https://github.com/vitalets/playwright-bdd/issues/205)) diff --git a/src/steps/styles/playwrightStyle.ts b/src/steps/styles/playwrightStyle.ts index 18bf6e2a..ecea5f33 100644 --- a/src/steps/styles/playwrightStyle.ts +++ b/src/steps/styles/playwrightStyle.ts @@ -49,17 +49,27 @@ export function playwrightStepCtor( * See: https://github.com/vitalets/playwright-bdd/issues/110 */ function getCallableStepFn(pattern: StepPattern, fn: StepFn) { - // need Partial<...> here, otherwise TS requires all Playwright fixtures to be passed - return (fixtures: Partial[0]>, ...args: ParametersExceptFirst) => { + // need Partial<...> here, otherwise TS requires _all_ Playwright fixtures to be passed + type StepFnArguments = + Parameters extends [] + ? [] + : [Partial[0]>, ...ParametersExceptFirst]; + + // todo: move default world type somewhere + // eslint-disable-next-line @typescript-eslint/no-explicit-any + type DefaultWorld = any; + + return function (this: DefaultWorld, ...args: StepFnArguments) { + const [fixtures, ...params] = args; assertStepIsCalledWithRequiredFixtures(pattern, fn, fixtures); - return fn(fixtures, ...args); + return fn.call(this, fixtures, ...params); }; } function assertStepIsCalledWithRequiredFixtures( pattern: StepPattern, fn: StepFn, - passedFixtures: Partial[0]>, + passedFixtures: Record = {}, ) { const requiredFixtures = fixtureParameterNames(fn); const missingFixtures = requiredFixtures.filter( diff --git a/test/reuse-step-fn/playwright.config.ts b/test/reuse-step-fn/playwright.config.ts index a8c6d775..3048e812 100644 --- a/test/reuse-step-fn/playwright.config.ts +++ b/test/reuse-step-fn/playwright.config.ts @@ -1,33 +1,36 @@ import { defineConfig } from '@playwright/test'; import { defineBddConfig } from 'playwright-bdd'; +const TAG = process.env.TAG; // @success, @error + export default defineConfig({ projects: [ { - name: 'pw-style-success', + name: 'pw-style', testDir: defineBddConfig({ - outputDir: '.features-gen/pw-style-success', + outputDir: `.features-gen/pw-style-${TAG}`, paths: ['features/*.feature'], require: ['steps-pw-style/*.ts'], - tags: '@success', + tags: TAG, }), }, { - name: 'pw-style-invalid-invocation', + name: 'cucumber-style', testDir: defineBddConfig({ - outputDir: '.features-gen/pw-style-invalid-invocation', + outputDir: `.features-gen/cucumber-style-${TAG}`, paths: ['features/*.feature'], - require: ['steps-pw-style/*.ts'], - tags: '@error', + require: ['steps-cucumber-style/*.ts'], + tags: TAG, }), }, { - name: 'cucumber-style-success', + name: 'pw-style-world', testDir: defineBddConfig({ - outputDir: '.features-gen/cucumber-style-success', + outputDir: `.features-gen/pw-style-world-${TAG}`, paths: ['features/*.feature'], - require: ['steps-cucumber-style/*.ts'], - tags: '@success', + require: ['steps-pw-style-world/*.ts'], + verbose: true, + tags: TAG, }), }, ], diff --git a/test/reuse-step-fn/steps-cucumber-style/index.ts b/test/reuse-step-fn/steps-cucumber-style/index.ts index ec2d2844..33ae2bd6 100644 --- a/test/reuse-step-fn/steps-cucumber-style/index.ts +++ b/test/reuse-step-fn/steps-cucumber-style/index.ts @@ -21,3 +21,21 @@ When('I create 2 todos {string} and {string}', async function (text1: string, te Then('I see todos:', async function (data: DataTable) { expect(this.todos).toEqual(data.rows().flat()); }); + +When( + 'I incorrectly create 2 todos {string} and {string}', + async function (_text1: string, _text2: string) { + // todo: incorrect invocation: forget to pass world + // await createTodo(text1, text2); + }, +); + +// step without parameters - check typing + +const fn = When('a step', async () => {}); + +expectTypeOf(fn).parameters.toEqualTypeOf<[]>(); + +Then('another step', async () => { + fn.call(this); +}); diff --git a/test/reuse-step-fn/steps-pw-style-world/index.ts b/test/reuse-step-fn/steps-pw-style-world/index.ts new file mode 100644 index 00000000..857116ed --- /dev/null +++ b/test/reuse-step-fn/steps-pw-style-world/index.ts @@ -0,0 +1,44 @@ +import { createBdd, DataTable } from 'playwright-bdd'; +import { expect } from '@playwright/test'; +import { expectTypeOf } from 'expect-type'; + +const { When, Then } = createBdd(); + +const createTodo = When('I create todo {string}', async function ({ $testInfo }, text: string) { + this.todos = this.todos || []; + this.todos.push(`${$testInfo.title} - ${text}`); +}); + +expectTypeOf(createTodo).toBeFunction(); +expectTypeOf(createTodo).parameter(0).toHaveProperty('$testInfo'); +expectTypeOf(createTodo).parameter(1).toEqualTypeOf(); + +When( + 'I create 2 todos {string} and {string}', + async function ({ $testInfo }, text1: string, text2: string) { + await createTodo.call(this, { $testInfo }, text1); + await createTodo.call(this, { $testInfo }, text2); + }, +); + +Then('I see todos:', async function ({}, data: DataTable) { + expect(this.todos).toEqual(data.rows().flat()); +}); + +When( + 'I incorrectly create 2 todos {string} and {string}', + async function ({}, text1: string, _text2: string) { + // incorrect invocation, should pass $testInfo + await createTodo.call(this, {}, text1); + }, +); + +// step without parameters - check typing + +const fn = When('a step', async function () {}); + +expectTypeOf(fn).parameters.toEqualTypeOf<[]>(); + +Then('another step', async function () { + fn.call(this); +}); diff --git a/test/reuse-step-fn/steps-pw-style/index.ts b/test/reuse-step-fn/steps-pw-style/index.ts index 1472d4c1..1f128c50 100644 --- a/test/reuse-step-fn/steps-pw-style/index.ts +++ b/test/reuse-step-fn/steps-pw-style/index.ts @@ -33,3 +33,13 @@ When( Then('I see todos:', async ({ todos }, data: DataTable) => { expect(todos).toEqual(data.rows().flat()); }); + +// step without parameters - check typing + +const fn = When('a step', async () => {}); + +// expectTypeOf(fn).parameters.toEqualTypeOf<[]>(); + +Then('another step', async () => { + fn(); +}); diff --git a/test/reuse-step-fn/test.mjs b/test/reuse-step-fn/test.mjs index b7793d28..225f7d17 100644 --- a/test/reuse-step-fn/test.mjs +++ b/test/reuse-step-fn/test.mjs @@ -8,18 +8,37 @@ import { const testDir = new TestDir(import.meta); -test(`${testDir.name} (pw-style-success)`, () => { - execPlaywrightTest(testDir.name, `${DEFAULT_CMD} --project pw-style-success`); +test(`${testDir.name} (pw-style, success)`, () => { + execPlaywrightTest(testDir.name, { + cmd: `${DEFAULT_CMD} --project pw-style`, + env: { TAG: '@success' }, + }); }); -test(`${testDir.name} (cucumber-style-success)`, () => { - execPlaywrightTest(testDir.name, `${DEFAULT_CMD} --project cucumber-style-success`); +test(`${testDir.name} (pw-style, error)`, () => { + execPlaywrightTestWithError(testDir.name, ['Missing fixtures: todos, $testInfo'], { + cmd: `${DEFAULT_CMD} --project pw-style`, + env: { TAG: '@error' }, + }); }); -test(`${testDir.name} (pw-style-invalid-invocation)`, () => { - execPlaywrightTestWithError( - testDir.name, - ['Missing fixtures: todos, $testInfo'], - `${DEFAULT_CMD} --project pw-style-invalid-invocation`, - ); +test(`${testDir.name} (cucumber-style, success)`, () => { + execPlaywrightTest(testDir.name, { + cmd: `${DEFAULT_CMD} --project cucumber-style`, + env: { TAG: '@success' }, + }); +}); + +test(`${testDir.name} (pw-style-world, success)`, () => { + execPlaywrightTest(testDir.name, { + cmd: `${DEFAULT_CMD} --project pw-style-world`, + env: { TAG: '@success' }, + }); +}); + +test(`${testDir.name} (pw-style-world, error)`, () => { + execPlaywrightTestWithError(testDir.name, ['Missing fixtures: $testInfo'], { + cmd: `${DEFAULT_CMD} --project pw-style-world`, + env: { TAG: '@error' }, + }); });