Skip to content

Commit

Permalink
Add stepHooks (without tests)
Browse files Browse the repository at this point in the history
  • Loading branch information
Andras-Marozsi committed May 29, 2020
1 parent a4716c0 commit 210f318
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 3 deletions.
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ export { default as UsageFormatter } from './formatter/usage_formatter'
export { default as UsageJsonFormatter } from './formatter/usage_json_formatter'
export { formatterHelpers }

// Support Code Fuctions
// Support Code Functions
const { methods } = supportCodeLibraryBuilder
export const After = methods.After
export const AfterAll = methods.AfterAll
export const AfterStep = methods.AfterStep
export const Before = methods.Before
export const BeforeAll = methods.BeforeAll
export const BeforeStep = methods.BeforeStep
export const defineParameterType = methods.defineParameterType
export const defineStep = methods.defineStep
export const Given = methods.Given
Expand Down
34 changes: 34 additions & 0 deletions src/models/test_step_hook_definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { PickleTagFilter } from '../pickle_filter'
import Definition, {
IDefinition,
IGetInvocationDataResponse,
IGetInvocationDataRequest,
IDefinitionParameters,
IHookDefinitionOptions,
} from './definition'
import { messages } from 'cucumber-messages'

export default class TestStepHookDefinition extends Definition
implements IDefinition {
private readonly pickleTagFilter: PickleTagFilter

constructor(data: IDefinitionParameters<IHookDefinitionOptions>) {
super(data)
this.pickleTagFilter = new PickleTagFilter(data.options.tags)
}

appliesToTestCase(pickle: messages.IPickle): boolean {
return this.pickleTagFilter.matchesAllTagExpressions(pickle)
}

async getInvocationParameters({
hookParameter,
}: IGetInvocationDataRequest): Promise<IGetInvocationDataResponse> {
return Promise.resolve({
getInvalidCodeLengthMessage: () =>
this.buildInvalidCodeLengthMessage('0 or 1', '2'),
parameters: [hookParameter],
validCodeLengths: [0, 1, 2],
})
}
}
105 changes: 103 additions & 2 deletions src/runtime/pickle_runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import _ from 'lodash'
import { getAmbiguousStepException } from './helpers'
import AttachmentManager from './attachment_manager'
import StepRunner from './step_runner'
import { messages, IdGenerator } from 'cucumber-messages'
import { IdGenerator, messages } from 'cucumber-messages'
import { addDurations, getZeroDuration } from '../time'
import { EventEmitter } from 'events'
import {
ISupportCodeLibrary,
ITestCaseHookParameter,
} from '../support_code_library_builder/types'
import TestCaseHookDefinition from '../models/test_case_hook_definition'
import TestStepHookDefinition from '../models/test_step_hook_definition'
import StepDefinition from '../models/step_definition'
import { IDefinition } from '../models/definition'
import { doesNotHaveValue } from '../value_checker'
Expand All @@ -21,6 +22,7 @@ interface ITestStep {
isBeforeHook?: boolean
isHook: boolean
hookDefinition?: TestCaseHookDefinition
stepHookDefinition?: TestStepHookDefinition
pickleStep?: messages.Pickle.IPickleStep
stepDefinitions?: StepDefinition[]
}
Expand Down Expand Up @@ -171,6 +173,18 @@ export default class PickleRunner {
)
}

getBeforeStepHookDefinitions(): TestStepHookDefinition[] {
return this.supportCodeLibrary.beforeTestStepHookDefinitions.filter(
hookDefinition => hookDefinition.appliesToTestCase(this.pickle)
)
}

getAfterStepHookDefinitions(): TestStepHookDefinition[] {
return this.supportCodeLibrary.afterTestStepHookDefinitions.filter(
hookDefinition => hookDefinition.appliesToTestCase(this.pickle)
)
}

getStepDefinitions(
pickleStep: messages.Pickle.IPickleStep
): StepDefinition[] {
Expand Down Expand Up @@ -323,6 +337,21 @@ export default class PickleRunner {
return this.invokeStep(null, hookDefinition, hookParameter)
}

async runStepHook(
stepHookDefinition: TestStepHookDefinition
): Promise<messages.ITestResult> {
if (this.isSkippingSteps()) {
return messages.TestResult.fromObject({ status: Status.SKIPPED })
}
const hookParameter: ITestCaseHookParameter = {
gherkinDocument: this.gherkinDocument,
pickle: this.pickle,
testCaseStartedId: this.currentTestCaseStartedId,
}

return this.invokeStep(null, stepHookDefinition, hookParameter)
}

async runStep(testStep: ITestStep): Promise<messages.ITestResult> {
if (testStep.stepDefinitions.length === 0) {
return messages.TestResult.fromObject({ status: Status.UNDEFINED })
Expand All @@ -334,6 +363,78 @@ export default class PickleRunner {
} else if (this.isSkippingSteps()) {
return messages.TestResult.fromObject({ status: Status.SKIPPED })
}
return this.invokeStep(testStep.pickleStep, testStep.stepDefinitions[0])
let stepResult
let afterStepHooksResult
const beforeStepHooksResult = await this.runStepHooks(
this.getBeforeStepHookDefinitions()
)

if (beforeStepHooksResult.status !== Status.FAILED) {
stepResult = await this.invokeStep(
testStep.pickleStep,
testStep.stepDefinitions[0]
)
if (stepResult.status === Status.PASSED) {
afterStepHooksResult = await this.runStepHooks(
this.getAfterStepHookDefinitions()
)
}
}
let cumulatedStepResult = beforeStepHooksResult

if (stepResult !== undefined) {
cumulatedStepResult = stepResult
if (beforeStepHooksResult.duration !== null) {
cumulatedStepResult.duration = addDurations(
cumulatedStepResult.duration,
beforeStepHooksResult.duration
)
}
if (afterStepHooksResult !== undefined) {
if (afterStepHooksResult.duration !== null) {
cumulatedStepResult.duration = addDurations(
cumulatedStepResult.duration,
afterStepHooksResult.duration
)
}
if (this.shouldUpdateStatus(afterStepHooksResult)) {
cumulatedStepResult.status = afterStepHooksResult.status
}
if (afterStepHooksResult.message !== null) {
cumulatedStepResult.message = afterStepHooksResult.message
}
}
}

return cumulatedStepResult
}

async runStepHooks(
stepHooks: TestStepHookDefinition[]
): Promise<messages.ITestResult> {
const stepHooksResult = messages.TestResult.fromObject({
status:
this.result.status === Status.FAILED
? Status.SKIPPED
: this.result.status,
})

for (const stepHookDefinition of stepHooks) {
const stepHookResult = await this.runStepHook(stepHookDefinition)
if (this.shouldUpdateStatus(stepHookResult)) {
stepHooksResult.status = stepHookResult.status
this.result.status = stepHookResult.status
}
if (stepHookResult.message !== null) {
stepHooksResult.message = stepHookResult.message
}
if (stepHookResult.duration !== null) {
stepHooksResult.duration =
stepHooksResult.duration !== null
? addDurations(stepHooksResult.duration, stepHookResult.duration)
: stepHookResult.duration
}
}
return stepHooksResult
}
}
76 changes: 76 additions & 0 deletions src/support_code_library_builder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import _ from 'lodash'
import { buildParameterType, getDefinitionLineAndUri } from './build_helpers'
import { IdGenerator } from 'cucumber-messages'
import TestCaseHookDefinition from '../models/test_case_hook_definition'
import TestStepHookDefinition from '../models/test_step_hook_definition'
import TestRunHookDefinition from '../models/test_run_hook_definition'
import StepDefinition from '../models/step_definition'
import { formatLocation } from '../formatter/helpers'
Expand All @@ -19,7 +20,9 @@ import {
DefineStepPattern,
IDefineStepOptions,
IDefineTestCaseHookOptions,
IDefineTestStepHookOptions,
TestCaseHookFunction,
TestStepHookFunction,
IDefineTestRunHookOptions,
ISupportCodeLibrary,
IParameterTypeDefinition,
Expand All @@ -41,6 +44,13 @@ interface ITestCaseHookDefinitionConfig {
uri: string
}

interface ITestStepHookDefinitionConfig {
code: any
line: number
options: any
uri: string
}

interface ITestRunHookDefinitionConfig {
code: any
line: number
Expand All @@ -53,8 +63,10 @@ export class SupportCodeLibraryBuilder {

private afterTestCaseHookDefinitionConfigs: ITestCaseHookDefinitionConfig[]
private afterTestRunHookDefinitionConfigs: ITestRunHookDefinitionConfig[]
private afterTestStepHookDefinitionConfigs: ITestStepHookDefinitionConfig[]
private beforeTestCaseHookDefinitionConfigs: ITestCaseHookDefinitionConfig[]
private beforeTestRunHookDefinitionConfigs: ITestRunHookDefinitionConfig[]
private beforeTestStepHookDefinitionConfigs: ITestStepHookDefinitionConfig[]
private cwd: string
private defaultTimeout: number
private definitionFunctionWrapper: any
Expand All @@ -72,12 +84,18 @@ export class SupportCodeLibraryBuilder {
AfterAll: this.defineTestRunHook(
() => this.afterTestRunHookDefinitionConfigs
),
AfterStep: this.defineTestStepHook(
() => this.afterTestStepHookDefinitionConfigs
),
Before: this.defineTestCaseHook(
() => this.beforeTestCaseHookDefinitionConfigs
),
BeforeAll: this.defineTestRunHook(
() => this.beforeTestRunHookDefinitionConfigs
),
BeforeStep: this.defineTestStepHook(
() => this.beforeTestStepHookDefinitionConfigs
),
defineParameterType: this.defineParameterType.bind(this),
defineStep,
Given: defineStep,
Expand Down Expand Up @@ -155,6 +173,37 @@ export class SupportCodeLibraryBuilder {
}
}

defineTestStepHook(
getCollection: () => ITestStepHookDefinitionConfig[]
): (
options: string | IDefineTestStepHookOptions | TestStepHookFunction,
code?: TestStepHookFunction
) => void {
return (
options: string | IDefineTestStepHookOptions | TestStepHookFunction,
code?: TestStepHookFunction
) => {
if (typeof options === 'string') {
options = { tags: options }
} else if (typeof options === 'function') {
code = options
options = {}
}
const { line, uri } = getDefinitionLineAndUri(this.cwd)
validateArguments({
args: { code, options },
fnName: 'defineTestStepHook',
location: formatLocation({ line, uri }),
})
getCollection().push({
code,
line,
options,
uri,
})
}
}

defineTestRunHook(
getCollection: () => ITestRunHookDefinitionConfig[]
): (options: IDefineTestRunHookOptions | Function, code?: Function) => void {
Expand Down Expand Up @@ -215,6 +264,25 @@ export class SupportCodeLibraryBuilder {
})
}

buildTestStepHookDefinitions(
configs: ITestStepHookDefinitionConfig[]
): TestStepHookDefinition[] {
return configs.map(({ code, line, options, uri }) => {
const wrappedCode = this.wrapCode({
code,
wrapperOptions: options.wrapperOptions,
})
return new TestStepHookDefinition({
code: wrappedCode,
id: this.newId(),
line,
options,
unwrappedCode: code,
uri,
})
})
}

buildTestRunHookDefinitions(
configs: ITestRunHookDefinitionConfig[]
): TestRunHookDefinition[] {
Expand Down Expand Up @@ -279,12 +347,18 @@ export class SupportCodeLibraryBuilder {
afterTestRunHookDefinitions: this.buildTestRunHookDefinitions(
this.afterTestRunHookDefinitionConfigs
).reverse(),
afterTestStepHookDefinitions: this.buildTestStepHookDefinitions(
this.afterTestStepHookDefinitionConfigs
).reverse(),
beforeTestCaseHookDefinitions: this.buildTestCaseHookDefinitions(
this.beforeTestCaseHookDefinitionConfigs
),
beforeTestRunHookDefinitions: this.buildTestRunHookDefinitions(
this.beforeTestRunHookDefinitionConfigs
),
beforeTestStepHookDefinitions: this.buildTestStepHookDefinitions(
this.beforeTestStepHookDefinitionConfigs
),
defaultTimeout: this.defaultTimeout,
parameterTypeRegistry: this.parameterTypeRegistry,
stepDefinitions: this.buildStepDefinitions(),
Expand All @@ -297,8 +371,10 @@ export class SupportCodeLibraryBuilder {
this.newId = newId
this.afterTestCaseHookDefinitionConfigs = []
this.afterTestRunHookDefinitionConfigs = []
this.afterTestStepHookDefinitionConfigs = []
this.beforeTestCaseHookDefinitionConfigs = []
this.beforeTestRunHookDefinitionConfigs = []
this.beforeTestStepHookDefinitionConfigs = []
this.definitionFunctionWrapper = null
this.defaultTimeout = 5000
this.parameterTypeRegistry = new ParameterTypeRegistry()
Expand Down
Loading

0 comments on commit 210f318

Please sign in to comment.