Skip to content

Commit

Permalink
fix(main): add "common" variable to beforeAll and afterAll hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
dnotes committed Nov 11, 2024
1 parent 361624a commit ffa86bf
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 18 deletions.
21 changes: 21 additions & 0 deletions .changeset/lucky-beers-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
"@quickpickle/playwright": patch
"quickpickle": patch
---

Added common variable should to BeforeAll and AfterAll hooks.
In your support files, you can get or set variables that should be
common (shared) across all the tests in the Feature file.

In general it is not good testing practice to use this for anything
other than setup and teardown; for example, you might start a server
and save the port in BeforeAll, then tear it down in AfterAll.

Here is a ridiculous example demonstrating the functionality:

```ts
import { BeforeAll, AfterAll, AfterStep } from 'quickpickle'
BeforeAll(async (common) => { common.totalSteps = 0 })
AfterStep(async (world) => { world.common.totalSteps++ })
AfterAll(async(common) => { expect(common.totalSteps).not.toBeFalsy() })
```
2 changes: 1 addition & 1 deletion packages/main/gherkin-example/example.feature.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions packages/main/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ const addHook = (hooksName: string, opts: string | Hook | ((state:any) => any),
};


export const BeforeAll = (opts: string | (() => any), f?: () => any): void => { addHook('beforeAll', opts, f) };
export const BeforeAll = (opts: string | ((common:any) => void), f?: (common:any) => void): void => { addHook('beforeAll', opts, f) };

export const Before = (opts: string | Hook | ((state:any) => any), f?: (state:any) => any): void => { addHook('before', opts, f) };
export const Before = (opts: string | Hook | ((state:any) => void), f?: (state:any) => void): void => { addHook('before', opts, f) };

export const BeforeStep = (opts: string | Hook | ((state:any) => any), f?: (state:any) => any): void => { addHook('beforeStep', opts, f) };
export const BeforeStep = (opts: string | Hook | ((state:any) => void), f?: (state:any) => void): void => { addHook('beforeStep', opts, f) };

export const AfterAll = (opts: string | (() => any), f?: () => any): void => { addHook('afterAll', opts, f) };
export const AfterAll = (opts: string | ((common:any) => void), f?: (common:any) => void): void => { addHook('afterAll', opts, f) };

export const After = (opts: string | Hook | ((state:any) => any), f?: (state:any) => any): void => { addHook('after', opts, f) };
export const After = (opts: string | Hook | ((state:any) => void), f?: (state:any) => void): void => { addHook('after', opts, f) };

export const AfterStep = (opts: string | Hook | ((state:any) => any), f?: (state:any) => any): void => { addHook('afterStep', opts, f) };
export const AfterStep = (opts: string | Hook | ((state:any) => void), f?: (state:any) => void): void => { addHook('afterStep', opts, f) };
2 changes: 1 addition & 1 deletion packages/main/src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function renderFeature(feature:Feature, config:QuickPickleConfig) {
// Render the initScenario function, which will be called at the beginning of each scenario
return`
const initScenario = async(context, scenario, tags, steps) => {
let state = new World(context, { feature:'${featureName}', scenario, tags, steps, config:${JSON.stringify(config)}}, ${JSON.stringify(config.worldConfig)});
let state = new World(context, { feature:'${featureName}', scenario, tags, steps, common, config:${JSON.stringify(config)}}, ${JSON.stringify(config.worldConfig)});
await state.init();
state.common = common;
state.info.feature = '${featureName}';
Expand Down
13 changes: 8 additions & 5 deletions packages/main/src/world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,21 @@ export interface QuickPickleWorldInterface {
isComplete: boolean // (read only) whether the Scenario is on the last step
config: QuickPickleConfig // (read only) configuration for QuickPickle
worldConfig: QuickPickleConfig['worldConfig'] // (read only) configuration for the World
common: {[key: string]: any} // Common data shared across tests --- USE SPARINGLY
common: {[key: string]: any} // Common data shared across tests in one Feature file --- USE SPARINGLY
init: () => Promise<void> // function called by QuickPickle when the world is created
tagsMatch(tags: string[]): string[]|null // function to check if the Scenario tags match the given tags
}

export type InfoConstructor = Omit<QuickPickleWorldInterface['info'], 'errors'> & { common:{[key:string]:any} }

export class QuickPickleWorld implements QuickPickleWorldInterface {
info: QuickPickleWorldInterface['info']
common: QuickPickleWorldInterface['common'] = {}
common: QuickPickleWorldInterface['common']
context: TestContext
constructor(context:TestContext, info:Omit<QuickPickleWorldInterface['info'], 'errors'>) {
constructor(context:TestContext, info:InfoConstructor) {
this.context = context
this.info = {...info, errors:[]}
this.common = info.common
this.info = { ...info, errors:[] }
}
async init() {}
get config() { return this.info.config }
Expand All @@ -52,7 +55,7 @@ export class QuickPickleWorld implements QuickPickleWorldInterface {

export type WorldConstructor = new (
context: TestContext,
info: QuickPickleWorldInterface['info'],
info: InfoConstructor,
) => QuickPickleWorldInterface;

let worldConstructor:WorldConstructor = QuickPickleWorld
Expand Down
3 changes: 3 additions & 0 deletions packages/main/tests/hooks/hooks.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Feature: Testing hooks
As a behavioral test writer
I need a consistent way to run code before and after steps, scenarios, and test runs

Scenario: Hooks: The BeforeAll hook can set things in common
Then the variable "common.beforeAll" should be "beforeAll"

Scenario: Hooks: All hooks should work
Given I run the tests
Then the tests should pass
Expand Down
11 changes: 9 additions & 2 deletions packages/main/tests/hooks/hooks.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import { Given, When, Then, BeforeAll, Before, BeforeStep, AfterStep, After, Aft

const log:any = {}

BeforeAll(async () => {
BeforeAll(async (common) => {
common.beforeAll = 'beforeAll'
common.totalSteps = 0
log.tests = {}
})

Before(async (state) => {
state.common.before = true
log.tests[state.info.scenario] = []
log.tests[state.info.scenario].push('Before')
state.hooks = {}
Expand All @@ -23,6 +26,7 @@ AfterStep({ tags:'clearErrorsAfterStep' }, async (state) => {
})

AfterStep(async (state) => {
state.common.totalSteps++
log.tests[state.info.scenario].push('errors: ' + state.info.errors.length)
log.tests[state.info.scenario].push('AfterStep')
})
Expand Down Expand Up @@ -56,8 +60,11 @@ const testWithError = [
'After',
]

AfterAll(async () => {
AfterAll(async (common) => {
console.log('AfterAll')
expect(common.beforeAll).toBe('beforeAll')
expect(common.before).toBe(true)
expect(common.totalSteps).not.toBeFalsy()
expect(log.tests).toMatchObject({
'Hooks: All hooks should work': testWithNoErrors,
'Hooks: Hooks also work on @soft tests': testWithNoErrors,
Expand Down
3 changes: 2 additions & 1 deletion packages/playwright/src/PlaywrightWorld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { normalizeTags, QuickPickleWorld, QuickPickleWorldInterface } from 'quic
import { After } from 'quickpickle';
import type { TestContext } from 'vitest';
import { defaultsDeep } from 'lodash-es'
import { InfoConstructor } from 'quickpickle/dist/world';

const browsers = { chromium, firefox, webkit }

Expand Down Expand Up @@ -53,7 +54,7 @@ export class PlaywrightWorld extends QuickPickleWorld {
browserContext!: BrowserContext
page!: Page

constructor(context:TestContext, info:QuickPickleWorldInterface['info']) {
constructor(context:TestContext, info:InfoConstructor) {
super(context, info)
this.setConfig(info.config.worldConfig)
}
Expand Down
18 changes: 16 additions & 2 deletions vitest.workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,34 @@ export default defineWorkspace([
root: './packages/main',
extends: './packages/main/vite.config.ts',
test: {
include : [ './tests/*.feature' ],
name: 'features',
include : [ 'tests/*.feature' ],
setupFiles: [ 'tests/tests.steps.ts' ],
},
},
{
root: './packages/main',
extends: './packages/main/vite.config.ts',
test: {
include : [ 'tests/*.test.ts' ],
name: 'hooks',
include : [ 'tests/hooks/*.feature' ],
setupFiles: [ 'tests/tests.steps.ts','tests/hooks/hooks.steps.ts' ],
},
},
{
root: './packages/main',
extends: './packages/main/vite.config.ts',
test: {
name: 'unit',
include : [ './tests/*.test.ts' ],
}
},
{
root: './packages/playwright',
extends: './packages/playwright/vite.config.ts',
test: {
name: 'playwright',
},
// @ts-ignore
quickpickle: {
skipTags: ['@skip','@wip','@skip-ci'],
Expand Down

0 comments on commit ffa86bf

Please sign in to comment.