Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

typescript: type this as IWorld in user functions #1690

Merged
merged 10 commits into from
Jun 4, 2021
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CO

### Fixed

* `this` now has correct TypeScript type in support code functions ([#1667](https://github.com/cucumber/cucumber-js/issues/1667) [#1690](https://github.com/cucumber/cucumber-js/pull/1690))
* Progress bar formatter now reports total step count correctly ([#1579](https://github.com/cucumber/cucumber-js/issues/1579)
[#1669](https://github.com/cucumber/cucumber-js/pull/1669))
* Rerun functionality will now run nothing if the rerun file is empty from the previous run ([#1302](https://github.com/cucumber/cucumber-js/issues/1302) [#1568](https://github.com/cucumber/cucumber-js/pull/1568))
Expand Down
1 change: 1 addition & 0 deletions dependency-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ requiredModules:
dev:
- '{compatibility,features,scripts,test}/**/*'
- '**/*_spec.ts'
- 'test-d/**/*.ts'
- 'example/index.ts'
- '**/test_helpers.ts'
ignore:
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const Then = methods.Then
export const When = methods.When
export {
default as World,
IWorld,
IWorldOptions,
} from './support_code_library_builder/world'
export const Status = messages.TestStepResultStatus
36 changes: 24 additions & 12 deletions src/support_code_library_builder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,19 @@ export class SupportCodeLibraryBuilder {

defineTestCaseHook(
getCollection: () => ITestCaseHookDefinitionConfig[]
): (
options: string | IDefineTestCaseHookOptions | TestCaseHookFunction,
code?: TestCaseHookFunction
): <WorldType>(
options:
| string
| IDefineTestCaseHookOptions
| TestCaseHookFunction<WorldType>,
code?: TestCaseHookFunction<WorldType>
) => void {
return (
options: string | IDefineTestCaseHookOptions | TestCaseHookFunction,
code?: TestCaseHookFunction
return <WorldType>(
options:
| string
| IDefineTestCaseHookOptions
| TestCaseHookFunction<WorldType>,
code?: TestCaseHookFunction<WorldType>
) => {
if (typeof options === 'string') {
options = { tags: options }
Expand All @@ -180,13 +186,19 @@ export class SupportCodeLibraryBuilder {

defineTestStepHook(
getCollection: () => ITestStepHookDefinitionConfig[]
): (
options: string | IDefineTestStepHookOptions | TestStepHookFunction,
code?: TestStepHookFunction
): <WorldType>(
options:
| string
| IDefineTestStepHookOptions
| TestStepHookFunction<WorldType>,
code?: TestStepHookFunction<WorldType>
) => void {
return (
options: string | IDefineTestStepHookOptions | TestStepHookFunction,
code?: TestStepHookFunction
return <WorldType>(
options:
| string
| IDefineTestStepHookOptions
| TestStepHookFunction<WorldType>,
code?: TestStepHookFunction<WorldType>
) => {
if (typeof options === 'string') {
options = { tags: options }
Expand Down
114 changes: 78 additions & 36 deletions src/support_code_library_builder/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import TestStepHookDefinition from '../models/test_step_hook_definition'
import TestRunHookDefinition from '../models/test_run_hook_definition'
import StepDefinition from '../models/step_definition'
import { ParameterTypeRegistry } from '@cucumber/cucumber-expressions'
import { IWorld } from './world'

export type DefineStepPattern = string | RegExp

Expand All @@ -22,21 +23,20 @@ export interface ITestStepHookParameter {
testStepId: string
}

export type TestCaseHookFunctionWithoutParameter = () => any | Promise<any>
export type TestCaseHookFunctionWithParameter = (
arg: ITestCaseHookParameter
export type TestCaseHookFunction<WorldType> = (
this: WorldType,
arg?: ITestCaseHookParameter
) => any | Promise<any>
export type TestCaseHookFunction =
| TestCaseHookFunctionWithoutParameter
| TestCaseHookFunctionWithParameter

export type TestStepHookFunctionWithoutParameter = () => void
export type TestStepHookFunctionWithParameter = (
arg: ITestStepHookParameter
export type TestStepHookFunction<WorldType> = (
this: WorldType,
arg?: ITestStepHookParameter
) => void
export type TestStepHookFunction =
| TestStepHookFunctionWithoutParameter
| TestStepHookFunctionWithParameter

export type TestStepFunction<WorldType> = (
this: WorldType,
...args: any[]
) => any | Promise<any>

export interface IDefineStepOptions {
timeout?: number
Expand Down Expand Up @@ -67,48 +67,90 @@ export interface IParameterTypeDefinition<T> {

export interface IDefineSupportCodeMethods {
defineParameterType: (options: IParameterTypeDefinition<any>) => void
defineStep: ((pattern: DefineStepPattern, code: Function) => void) &
((
defineStep: (<WorldType = IWorld>(
pattern: DefineStepPattern,
code: TestStepFunction<WorldType>
) => void) &
(<WorldType = IWorld>(
pattern: DefineStepPattern,
options: IDefineStepOptions,
code: Function
code: TestStepFunction<WorldType>
) => void)
setDefaultTimeout: (milliseconds: number) => void
setDefinitionFunctionWrapper: (fn: Function) => void
setWorldConstructor: (fn: any) => void
After: ((code: TestCaseHookFunction) => void) &
((tags: string, code: TestCaseHookFunction) => void) &
((options: IDefineTestCaseHookOptions, code: TestCaseHookFunction) => void)
AfterStep: ((code: TestStepHookFunction) => void) &
((tags: string, code: TestStepHookFunction) => void) &
((options: IDefineTestStepHookOptions, code: TestStepHookFunction) => void)
After: (<WorldType = IWorld>(code: TestCaseHookFunction<WorldType>) => void) &
(<WorldType = IWorld>(
tags: string,
code: TestCaseHookFunction<WorldType>
) => void) &
(<WorldType = IWorld>(
options: IDefineTestCaseHookOptions,
code: TestCaseHookFunction<WorldType>
) => void)
AfterStep: (<WorldType = IWorld>(
code: TestStepHookFunction<WorldType>
) => void) &
(<WorldType = IWorld>(
tags: string,
code: TestStepHookFunction<WorldType>
) => void) &
(<WorldType = IWorld>(
options: IDefineTestStepHookOptions,
code: TestStepHookFunction<WorldType>
) => void)
AfterAll: ((code: Function) => void) &
((options: IDefineTestRunHookOptions, code: Function) => void)
Before: ((code: TestCaseHookFunction) => void) &
((tags: string, code: TestCaseHookFunction) => void) &
((options: IDefineTestCaseHookOptions, code: TestCaseHookFunction) => void)
BeforeStep: ((code: TestStepHookFunction) => void) &
((tags: string, code: TestStepHookFunction) => void) &
((options: IDefineTestStepHookOptions, code: TestStepHookFunction) => void)
Before: (<WorldType = IWorld>(
code: TestCaseHookFunction<WorldType>
) => void) &
(<WorldType = IWorld>(
tags: string,
code: TestCaseHookFunction<WorldType>
) => void) &
(<WorldType = IWorld>(
options: IDefineTestCaseHookOptions,
code: TestCaseHookFunction<WorldType>
) => void)
BeforeStep: (<WorldType = IWorld>(
code: TestStepHookFunction<WorldType>
) => void) &
(<WorldType = IWorld>(
tags: string,
code: TestStepHookFunction<WorldType>
) => void) &
(<WorldType = IWorld>(
options: IDefineTestStepHookOptions,
code: TestStepHookFunction<WorldType>
) => void)
BeforeAll: ((code: Function) => void) &
((options: IDefineTestRunHookOptions, code: Function) => void)
Given: ((pattern: DefineStepPattern, code: Function) => void) &
((
Given: (<WorldType = IWorld>(
pattern: DefineStepPattern,
code: TestStepFunction<WorldType>
) => void) &
(<WorldType = IWorld>(
pattern: DefineStepPattern,
options: IDefineStepOptions,
code: Function
code: TestStepFunction<WorldType>
) => void)
Then: ((pattern: DefineStepPattern, code: Function) => void) &
((
Then: (<WorldType = IWorld>(
pattern: DefineStepPattern,
code: TestStepFunction<WorldType>
) => void) &
(<WorldType = IWorld>(
pattern: DefineStepPattern,
options: IDefineStepOptions,
code: Function
code: TestStepFunction<WorldType>
) => void)
When: ((pattern: DefineStepPattern, code: Function) => void) &
((
When: (<WorldType = IWorld>(
pattern: DefineStepPattern,
code: TestStepFunction<WorldType>
) => void) &
(<WorldType = IWorld>(
pattern: DefineStepPattern,
options: IDefineStepOptions,
code: Function
code: TestStepFunction<WorldType>
) => void)
}

Expand Down
9 changes: 8 additions & 1 deletion src/support_code_library_builder/world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ export interface IWorldOptions {
parameters: any
}

export default class World {
export interface IWorld {
readonly attach: ICreateAttachment
readonly log: ICreateLog
readonly parameters: any
aurelien-reeves marked this conversation as resolved.
Show resolved Hide resolved
[key: string]: any
davidjgoss marked this conversation as resolved.
Show resolved Hide resolved
}

export default class World implements IWorld {
public readonly attach: ICreateAttachment
public readonly log: ICreateLog
public readonly parameters: any
Expand Down
46 changes: 46 additions & 0 deletions test-d/world.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Before, setWorldConstructor, When, World } from '../'
import { expectError } from 'tsd'

// should allow us to read parameters and add attachments
Before(async function () {
await this.attach(this.parameters.foo)
})
When('stuff happens', async function () {
await this.attach(this.parameters.foo)
})

// should prevent reassignment of parameters
expectError(
Before(async function () {
this.parameters = null
})
)
expectError(
When('stuff happens', async function () {
this.parameters = null
})
)

// should allow us to set and get arbitrary properties
Before(async function () {
this.bar = 'baz'
await this.log(this.baz)
})
When('stuff happens', async function () {
this.bar = 'baz'
await this.log(this.baz)
})

// should allow us to use a custom world class
class CustomWorld extends World {
doThing(): string {
return 'foo'
}
}
setWorldConstructor(CustomWorld)
Before(async function (this: CustomWorld) {
this.doThing()
})
When('stuff happens', async function (this: CustomWorld) {
this.doThing()
})