racejar 1.1.3 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.1](https://github.com/portabletext/editor/compare/racejar-v1.2.0...racejar-v1.2.1) (2025-03-03)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * make hooks optional in Playwright Feature ([b02b7fd](https://github.com/portabletext/editor/commit/b02b7fd86c1ef77a4928f8997244b18c16972e9c))
9
+
10
+ ## [1.2.0](https://github.com/portabletext/editor/compare/racejar-v1.1.3...racejar-v1.2.0) (2025-02-10)
11
+
12
+
13
+ ### Features
14
+
15
+ * add support for `Before` and `After` hooks ([#784](https://github.com/portabletext/editor/issues/784)) ([0cee059](https://github.com/portabletext/editor/commit/0cee059aff7bb799ed51b608d8068490bc379d12))
16
+ * Add support for Data Tables and Doc Strings ([#783](https://github.com/portabletext/editor/issues/783)) ([2083aab](https://github.com/portabletext/editor/commit/2083aab7637ef5c5f95e25b5b4373b63869dde74))
17
+
3
18
  ## [1.1.3](https://github.com/portabletext/editor/compare/racejar-v1.1.2...racejar-v1.1.3) (2025-02-05)
4
19
 
5
20
 
@@ -2,11 +2,12 @@ import {expect} from 'vitest'
2
2
  import {Given, Then, When} from '../src/step-definitions'
3
3
  import {Feature} from '../src/vitest'
4
4
 
5
- function greet(name: string) {
6
- return `Hello, ${name}!`
5
+ function greet(name: string, greeting: string) {
6
+ return `Hello ${name}, ${greeting}`
7
7
  }
8
8
 
9
9
  type Context = {
10
+ greetingPrefix: string
10
11
  person: string
11
12
  greeting: string
12
13
  }
@@ -16,15 +17,19 @@ Feature({
16
17
  Feature: Greeting
17
18
  Scenario: Greeting a person
18
19
  Given the person "Herman"
19
- When greeting the person
20
- Then the greeting is "Hello, Herman!"`,
20
+ When greeting the person with:
21
+ | how are you? |
22
+ Then the greeting is "Hello Herman, how are you?"`,
21
23
  stepDefinitions: [
22
24
  Given('the person {string}', (context: Context, person: string) => {
23
25
  context.person = person
24
26
  }),
25
- When('greeting the person', (context: Context) => {
26
- context.greeting = greet(context.person)
27
- }),
27
+ When(
28
+ 'greeting the person with:',
29
+ (context: Context, greeting: string[][]) => {
30
+ context.greeting = greet(context.person, greeting[0][0])
31
+ },
32
+ ),
28
33
  Then('the greeting is {string}', (context: Context, greeting: string) => {
29
34
  expect(context.greeting).toBe(greeting)
30
35
  }),
@@ -0,0 +1,42 @@
1
+ import {expect} from 'vitest'
2
+ import {After, Before} from '../src/hooks'
3
+ import {Given, Then, When} from '../src/step-definitions'
4
+ import {Feature} from '../src/vitest'
5
+
6
+ function greet(prefix: string, name: string) {
7
+ return `${prefix} ${name}`
8
+ }
9
+
10
+ type Context = {
11
+ greetingPrefix: string
12
+ person: string
13
+ greeting: string
14
+ }
15
+
16
+ Feature({
17
+ featureText: `
18
+ Feature: Greeting
19
+ Scenario: Greeting a person
20
+ Given the person "Herman"
21
+ When greeting the person
22
+ Then the greeting is "Hello Herman"`,
23
+ hooks: [
24
+ Before((context: Context) => {
25
+ context.greetingPrefix = 'Hello'
26
+ }),
27
+ After((context: Context) => {
28
+ expect(context.greeting).toBe('Hello Herman')
29
+ }),
30
+ ],
31
+ stepDefinitions: [
32
+ Given('the person {string}', (context: Context, person: string) => {
33
+ context.person = person
34
+ }),
35
+ When('greeting the person', (context: Context) => {
36
+ context.greeting = greet(context.greetingPrefix, context.person)
37
+ }),
38
+ Then('the greeting is {string}', (context: Context, greeting: string) => {
39
+ expect(context.greeting).toBe(greeting)
40
+ }),
41
+ ],
42
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "racejar",
3
- "version": "1.1.3",
3
+ "version": "1.2.1",
4
4
  "description": "A testing framework agnostic Gherkin driver",
5
5
  "keywords": [
6
6
  "cucumber",
@@ -41,18 +41,18 @@
41
41
  },
42
42
  "devDependencies": {
43
43
  "@cucumber/cucumber-expressions": "^18.0.1",
44
- "@cucumber/gherkin": "^31.0.0",
44
+ "@cucumber/gherkin": "^32.0.0",
45
45
  "@cucumber/messages": "^27.2.0",
46
46
  "@jest/globals": "^29.7.0",
47
47
  "@playwright/test": "^1.50.1",
48
48
  "@sanity/pkg-utils": "^7.0.4",
49
49
  "typescript": "5.7.3",
50
- "vitest": "^3.0.5"
50
+ "vitest": "^3.0.7"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "@jest/globals": "^29.7.0",
54
54
  "@playwright/test": "^1.50.1",
55
- "vitest": "^3.0.5"
55
+ "vitest": "^3.0.7"
56
56
  },
57
57
  "scripts": {
58
58
  "check:lint": "biome lint .",
@@ -5,6 +5,7 @@ import {
5
5
  } from '@cucumber/cucumber-expressions'
6
6
  import * as Gherkin from '@cucumber/gherkin'
7
7
  import * as Messages from '@cucumber/messages'
8
+ import type {Hook} from './hooks'
8
9
  import type {StepDefinition} from './step-definitions'
9
10
 
10
11
  /**
@@ -18,6 +19,8 @@ export type CompiledFeature<TStepContext extends Record<string, any> = object> =
18
19
  name: string
19
20
  tag?: 'only' | 'skip'
20
21
  steps: Array<(stepContext?: TStepContext) => Promise<void> | void>
22
+ beforeHooks: Array<(stepContext?: TStepContext) => Promise<void> | void>
23
+ afterHooks: Array<(stepContext?: TStepContext) => Promise<void> | void>
21
24
  }>
22
25
  }
23
26
 
@@ -29,10 +32,12 @@ export function compileFeature<
29
32
  TStepContext extends Record<string, any> = object,
30
33
  >({
31
34
  featureText,
35
+ hooks,
32
36
  stepDefinitions,
33
37
  parameterTypes,
34
38
  }: {
35
39
  featureText: string
40
+ hooks?: Array<Hook<TStepContext>>
36
41
  stepDefinitions: Array<StepDefinition<TContext, any, any, any>>
37
42
  parameterTypes?: Array<ParameterType<unknown>>
38
43
  }): CompiledFeature<TStepContext> {
@@ -118,6 +123,16 @@ export function compileFeature<
118
123
  }
119
124
 
120
125
  const args = matchingStep.args.map((arg) => arg.getValue(matchingStep))
126
+ if (step.argument?.dataTable) {
127
+ args.push(
128
+ step.argument.dataTable.rows.map((row) =>
129
+ row.cells.map((cell) => cell.value),
130
+ ),
131
+ )
132
+ }
133
+ if (step.argument?.docString) {
134
+ args.push(step.argument.docString.content)
135
+ }
121
136
 
122
137
  return (stepContext: TStepContext | undefined) =>
123
138
  matchingStep.callback(
@@ -138,6 +153,18 @@ export function compileFeature<
138
153
  ? ('only' as const)
139
154
  : undefined,
140
155
  steps,
156
+ beforeHooks: (hooks ?? [])
157
+ .filter((hook) => hook.type === 'Before')
158
+ .map(
159
+ (hook) => (stepContext: TStepContext | undefined) =>
160
+ hook.callback(Object.assign(context, stepContext)),
161
+ ),
162
+ afterHooks: (hooks ?? [])
163
+ .filter((hook) => hook.type === 'After')
164
+ .map(
165
+ (hook) => (stepContext: TStepContext | undefined) =>
166
+ hook.callback(Object.assign(context, stepContext)),
167
+ ),
141
168
  }
142
169
  })
143
170
 
package/src/hooks.ts ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @public
3
+ */
4
+ export type Hook<TContext extends Record<string, any> = object> = {
5
+ type: 'Before' | 'After'
6
+ callback: HookCallback<TContext>
7
+ }
8
+
9
+ /**
10
+ * @public
11
+ */
12
+ export type HookCallback<TContext extends Record<string, any> = object> = (
13
+ context: TContext,
14
+ ) => Promise<void> | void
15
+
16
+ /**
17
+ * @public
18
+ */
19
+ export function Before<TContext extends Record<string, any> = object>(
20
+ callback: HookCallback<TContext>,
21
+ ): Hook<TContext> {
22
+ return {type: 'Before', callback}
23
+ }
24
+
25
+ /**
26
+ * @public
27
+ */
28
+ export function After<TContext extends Record<string, any> = object>(
29
+ callback: HookCallback<TContext>,
30
+ ): Hook<TContext> {
31
+ return {type: 'After', callback}
32
+ }
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './compile-feature'
2
2
  export * from './create-parameter-type'
3
3
  export * from './step-definitions'
4
+ export * from './hooks'
@@ -1,6 +1,7 @@
1
1
  import type {ParameterType} from '@cucumber/cucumber-expressions'
2
- import {describe, test} from '@jest/globals'
2
+ import {afterEach, beforeEach, describe, test} from '@jest/globals'
3
3
  import {compileFeature} from '../compile-feature'
4
+ import type {Hook} from '../hooks'
4
5
  import type {StepDefinition} from '../step-definitions'
5
6
 
6
7
  /**
@@ -8,15 +9,18 @@ import type {StepDefinition} from '../step-definitions'
8
9
  */
9
10
  export function Feature<TContext extends Record<string, any> = object>({
10
11
  featureText,
12
+ hooks,
11
13
  stepDefinitions,
12
14
  parameterTypes,
13
15
  }: {
14
16
  featureText: string
17
+ hooks: Array<Hook<TContext>>
15
18
  stepDefinitions: Array<StepDefinition<TContext, any, any, any>>
16
19
  parameterTypes?: Array<ParameterType<unknown>>
17
20
  }) {
18
21
  const feature = compileFeature({
19
22
  featureText,
23
+ hooks,
20
24
  stepDefinitions,
21
25
  parameterTypes,
22
26
  })
@@ -30,6 +34,13 @@ export function Feature<TContext extends Record<string, any> = object>({
30
34
 
31
35
  describeFn(feature.name, () => {
32
36
  for (const scenario of feature.scenarios) {
37
+ for (const before of scenario.beforeHooks) {
38
+ beforeEach(before)
39
+ }
40
+
41
+ for (const after of scenario.afterHooks) {
42
+ afterEach(after)
43
+ }
33
44
  const testFn =
34
45
  scenario.tag === 'only'
35
46
  ? test.only
@@ -38,6 +49,9 @@ export function Feature<TContext extends Record<string, any> = object>({
38
49
  : test
39
50
 
40
51
  testFn(scenario.name, async () => {
52
+ for (const before of scenario.beforeHooks) {
53
+ await before()
54
+ }
41
55
  for (const step of scenario.steps) {
42
56
  await step()
43
57
  }
@@ -1,16 +1,25 @@
1
1
  import type {ParameterType} from '@cucumber/cucumber-expressions'
2
- import {test, type BrowserContext, type Page} from '@playwright/test'
2
+ import {
3
+ test,
4
+ type PlaywrightTestArgs,
5
+ type PlaywrightTestOptions,
6
+ type PlaywrightWorkerArgs,
7
+ type PlaywrightWorkerOptions,
8
+ } from '@playwright/test'
3
9
  import {compileFeature} from '../compile-feature'
10
+ import type {Hook} from '../hooks'
4
11
  import type {StepDefinition} from '../step-definitions'
5
12
 
13
+ type PlaywrightOptions = PlaywrightTestArgs &
14
+ PlaywrightTestOptions &
15
+ PlaywrightWorkerArgs &
16
+ PlaywrightWorkerOptions
17
+
6
18
  /**
7
19
  * @public
8
20
  */
9
- export type PlaywrightContext = {
10
- playwright: {
11
- page: Page
12
- context: BrowserContext
13
- }
21
+ export type PlaywrightContext = Record<'string', any> & {
22
+ playwright: PlaywrightOptions
14
23
  }
15
24
 
16
25
  /**
@@ -20,15 +29,18 @@ export function Feature<
20
29
  TContext extends PlaywrightContext = PlaywrightContext,
21
30
  >({
22
31
  featureText,
32
+ hooks,
23
33
  stepDefinitions,
24
34
  parameterTypes,
25
35
  }: {
26
36
  featureText: string
37
+ hooks?: Array<Hook<TContext>>
27
38
  stepDefinitions: Array<StepDefinition<TContext, any, any, any>>
28
39
  parameterTypes?: Array<ParameterType<unknown>>
29
40
  }) {
30
41
  const feature = compileFeature({
31
42
  featureText,
43
+ hooks,
32
44
  stepDefinitions,
33
45
  parameterTypes,
34
46
  })
@@ -42,6 +54,22 @@ export function Feature<
42
54
 
43
55
  describeFn(feature.name, () => {
44
56
  for (const scenario of feature.scenarios) {
57
+ for (const before of scenario.beforeHooks) {
58
+ test.beforeEach(async (playwrightOptions) => {
59
+ await before({
60
+ playwright: playwrightOptions,
61
+ } as TContext)
62
+ })
63
+ }
64
+
65
+ for (const after of scenario.afterHooks) {
66
+ test.afterEach(async (playwrightOptions) => {
67
+ await after({
68
+ playwright: playwrightOptions,
69
+ } as TContext)
70
+ })
71
+ }
72
+
45
73
  const testFn =
46
74
  scenario.tag === 'only'
47
75
  ? test.only
@@ -49,14 +77,11 @@ export function Feature<
49
77
  ? test.skip
50
78
  : test
51
79
 
52
- testFn(scenario.name, async ({page, context}) => {
80
+ testFn(scenario.name, async (playwrightOptions) => {
53
81
  for (const step of scenario.steps) {
54
82
  await step({
55
- playwright: {
56
- page,
57
- context,
58
- },
59
- })
83
+ playwright: playwrightOptions,
84
+ } as TContext)
60
85
  }
61
86
  })
62
87
  }
@@ -1,6 +1,7 @@
1
1
  import type {ParameterType} from '@cucumber/cucumber-expressions'
2
- import {describe, test} from 'vitest'
2
+ import {afterEach, beforeEach, describe, test} from 'vitest'
3
3
  import {compileFeature} from '../compile-feature'
4
+ import type {Hook} from '../hooks'
4
5
  import type {StepDefinition} from '../step-definitions'
5
6
 
6
7
  /**
@@ -8,15 +9,18 @@ import type {StepDefinition} from '../step-definitions'
8
9
  */
9
10
  export function Feature<TContext extends Record<string, any> = object>({
10
11
  featureText,
12
+ hooks,
11
13
  stepDefinitions,
12
14
  parameterTypes,
13
15
  }: {
14
16
  featureText: string
17
+ hooks?: Array<Hook<TContext>>
15
18
  stepDefinitions: Array<StepDefinition<TContext, any, any, any>>
16
19
  parameterTypes?: Array<ParameterType<unknown>>
17
20
  }) {
18
21
  const feature = compileFeature({
19
22
  featureText,
23
+ hooks,
20
24
  stepDefinitions,
21
25
  parameterTypes,
22
26
  })
@@ -30,6 +34,14 @@ export function Feature<TContext extends Record<string, any> = object>({
30
34
 
31
35
  describeFn(feature.name, () => {
32
36
  for (const scenario of feature.scenarios) {
37
+ for (const before of scenario.beforeHooks) {
38
+ beforeEach(before)
39
+ }
40
+
41
+ for (const after of scenario.afterHooks) {
42
+ afterEach(after)
43
+ }
44
+
33
45
  const testFn =
34
46
  scenario.tag === 'only'
35
47
  ? test.only
package/tsconfig.json CHANGED
@@ -3,5 +3,5 @@
3
3
  "compilerOptions": {
4
4
  "noEmit": true
5
5
  },
6
- "include": ["src"]
6
+ "include": ["src", "example", "example-playwright"]
7
7
  }
@@ -9,11 +9,4 @@ export default defineWorkspace([
9
9
  exclude: ['example-playwright'],
10
10
  },
11
11
  },
12
- {
13
- plugins: [],
14
- test: {
15
- name: 'browser (chromium)',
16
- exclude: ['example-playwright'],
17
- },
18
- },
19
12
  ])