Skip to content

Commit

Permalink
Add stepHook functionality - without tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Andras-Marozsi committed Nov 25, 2019
1 parent 76d50cd commit 3afe03b
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 5 deletions.
4 changes: 3 additions & 1 deletion src/index.js
Expand Up @@ -27,12 +27,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 defineSupportCode = methods.defineSupportCode
Expand Down
27 changes: 27 additions & 0 deletions src/models/test_step_hook_definition.js
@@ -0,0 +1,27 @@
import PickleFilter from '../pickle_filter'
import Definition from './definition'

export default class TestStepHookDefinition extends Definition {
constructor(...data) {
super(...data)
this.pickleFilter = new PickleFilter({
tagExpression: this.options.tags,
})
}

appliesToTestCase({ pickle, uri }) {
return this.pickleFilter.matches({ pickle, uri })
}

getInvalidCodeLengthMessage() {
return this.buildInvalidCodeLengthMessage('0 or 1', '2')
}

getInvocationParameters({ hookParameter }) {
return [hookParameter]
}

getValidCodeLengths() {
return [0, 1, 2]
}
}
100 changes: 96 additions & 4 deletions src/runtime/test_case_runner.js
Expand Up @@ -35,6 +35,8 @@ export default class TestCaseRunner {
attach: ::attachmentManager.create,
parameters: worldParameters,
})
this.beforeStepHookDefinitions = this.getBeforeStepHookDefinitions()
this.afterStepHookDefinitions = this.getAfterStepHookDefinitions()
this.beforeHookDefinitions = this.getBeforeHookDefinitions()
this.afterHookDefinitions = this.getAfterHookDefinitions()
this.maxTestStepIndex =
Expand Down Expand Up @@ -113,6 +115,18 @@ export default class TestCaseRunner {
)
}

getBeforeStepHookDefinitions() {
return this.supportCodeLibrary.beforeTestStepHookDefinitions.filter(
hookDefinition => hookDefinition.appliesToTestCase(this.testCase)
)
}

getAfterStepHookDefinitions() {
return this.supportCodeLibrary.afterTestStepHookDefinitions.filter(
hookDefinition => hookDefinition.appliesToTestCase(this.testCase)
)
}

getStepDefinitions(step) {
return this.supportCodeLibrary.stepDefinitions.filter(stepDefinition =>
stepDefinition.matchesStepName(step.text)
Expand Down Expand Up @@ -151,9 +165,11 @@ export default class TestCaseRunner {
}
}

async aroundTestStep(runStepFn) {
startStep() {
this.emit('test-step-started', { index: this.testStepIndex })
const testStepResult = await runStepFn()
}

finishStep(testStepResult) {
if (testStepResult.duration) {
this.result.duration += testStepResult.duration
}
Expand All @@ -170,6 +186,44 @@ export default class TestCaseRunner {
this.testStepIndex += 1
}

async aroundTestHook(runStepFn) {
this.startStep()
const testStepResult = await runStepFn()
this.finishStep(testStepResult)
}

async aroundTestStep(runStepFn, step = null) {
this.startStep()
let stepAfterHooksResult
let testStepResult
const stepBeforeHooksResult = await this.runStepHooks(step, true)
if (stepBeforeHooksResult.status !== Status.FAILED) {
testStepResult = await runStepFn()
if (testStepResult.status === Status.PASSED) {
stepAfterHooksResult = await this.runStepHooks(step, false)
}
}
let cumulatedStepResult = stepBeforeHooksResult
if (testStepResult) {
cumulatedStepResult = testStepResult
if (stepBeforeHooksResult.duration !== undefined) {
cumulatedStepResult.duration += stepBeforeHooksResult.duration
}
}
if (stepAfterHooksResult) {
if (stepAfterHooksResult.duration !== undefined) {
cumulatedStepResult.duration += stepAfterHooksResult.duration
}
if (this.shouldUpdateStatus(stepAfterHooksResult)) {
cumulatedStepResult.status = stepAfterHooksResult.status
}
if (stepAfterHooksResult.exception) {
cumulatedStepResult.exception = stepAfterHooksResult.exception
}
}
this.finishStep(cumulatedStepResult)
}

async run() {
this.emitPrepared()
for (
Expand Down Expand Up @@ -220,12 +274,50 @@ export default class TestCaseRunner {

async runHooks(hookDefinitions, hookParameter, isBeforeHook) {
await Promise.each(hookDefinitions, async hookDefinition => {
await this.aroundTestStep(() =>
await this.aroundTestHook(() =>
this.runHook(hookDefinition, hookParameter, isBeforeHook)
)
})
}

async runStepHooks(step, isBeforeHook) {
const status =
this.result.status === Status.FAILED ? Status.SKIPPED : this.result.status
const stepHooksResult = { status: status }
if (step) {
const stepInfo = {
text: step.text,
arguments: step.arguments,
location: Object.assign({ uri: this.testCase.uri }, step.locations[0]),
}
let stepHooks = []
if (isBeforeHook) {
stepHooks = this.beforeStepHookDefinitions
} else {
stepHooks = this.afterStepHookDefinitions
}
await Promise.each(stepHooks, async stepHook => {
if (this.isSkippingSteps()) {
stepHooksResult.status = Status.SKIPPED
return
}
const stepHookResult = await this.invokeStep(null, stepHook, stepInfo)
if (this.shouldUpdateStatus(stepHookResult)) {
stepHooksResult.status = stepHookResult.status
}
if (stepHookResult.exception) {
stepHooksResult.exception = stepHookResult.exception
}
if (stepHookResult.duration) {
stepHooksResult.duration = stepHooksResult.duration
? stepHooksResult.duration + stepHookResult.duration
: stepHookResult.duration
}
})
}
return stepHooksResult
}

async runStep(step) {
const stepDefinitions = this.getStepDefinitions(step)
if (stepDefinitions.length === 0) {
Expand All @@ -243,7 +335,7 @@ export default class TestCaseRunner {

async runSteps() {
await Promise.each(this.testCase.pickle.steps, async step => {
await this.aroundTestStep(() => this.runStep(step))
await this.aroundTestStep(() => this.runStep(step), step)
})
}
}
2 changes: 2 additions & 0 deletions src/runtime/test_case_runner_spec.js
Expand Up @@ -29,7 +29,9 @@ describe('TestCaseRunner', () => {
}
this.supportCodeLibrary = {
afterTestCaseHookDefinitions: [],
afterTestStepHookDefinitions: [],
beforeTestCaseHookDefinitions: [],
beforeTestStepHookDefinitions: [],
defaultTimeout: 5000,
stepDefinitions: [],
parameterTypeRegistry: {},
Expand Down
22 changes: 22 additions & 0 deletions src/support_code_library_builder/build_helpers.js
Expand Up @@ -12,6 +12,7 @@ import { isFileNameInCucumber } from '../stack_trace_filter'
import StepDefinition from '../models/step_definition'
import TestCaseHookDefinition from '../models/test_case_hook_definition'
import TestRunHookDefinition from '../models/test_run_hook_definition'
import TestStepHookDefinition from '../models/test_step_hook_definition'
import validateArguments from './validate_arguments'

export function buildTestCaseHookDefinition({ options, code, cwd }) {
Expand All @@ -35,6 +36,27 @@ export function buildTestCaseHookDefinition({ options, code, cwd }) {
})
}

export function buildTestStepHookDefinition({ options, code, cwd }) {
if (typeof options === 'string') {
options = { tags: options }
} else if (typeof options === 'function') {
code = options
options = {}
}
const { line, uri } = getDefinitionLineAndUri(cwd)
validateArguments({
args: { code, options },
fnName: 'defineTestStepHook',
location: formatLocation({ line, uri }),
})
return new TestStepHookDefinition({
code,
line,
options,
uri,
})
}

export function buildTestRunHookDefinition({ options, code, cwd }) {
if (typeof options === 'string') {
options = { tags: options }
Expand Down
16 changes: 16 additions & 0 deletions src/support_code_library_builder/index.js
Expand Up @@ -6,6 +6,7 @@ import {
buildStepDefinitionConfig,
buildStepDefinitionFromConfig,
buildTestCaseHookDefinition,
buildTestStepHookDefinition,
buildTestRunHookDefinition,
} from './build_helpers'
import { wrapDefinitions } from './finalize_helpers'
Expand All @@ -16,8 +17,10 @@ export class SupportCodeLibraryBuilder {
defineParameterType: this.defineParameterType.bind(this),
After: this.defineTestCaseHook('afterTestCaseHookDefinitions'),
AfterAll: this.defineTestRunHook('afterTestRunHookDefinitions'),
AfterStep: this.defineTestStepHook('afterTestStepHookDefinitions'),
Before: this.defineTestCaseHook('beforeTestCaseHookDefinitions'),
BeforeAll: this.defineTestRunHook('beforeTestRunHookDefinitions'),
BeforeStep: this.defineTestStepHook('beforeTestStepHookDefinitions'),
defineStep: this.defineStep.bind(this),
defineSupportCode: util.deprecate(fn => {
fn(this.methods)
Expand Down Expand Up @@ -61,6 +64,17 @@ export class SupportCodeLibraryBuilder {
}
}

defineTestStepHook(collectionName) {
return (options, code) => {
const hookDefinition = buildTestStepHookDefinition({
options,
code,
cwd: this.cwd,
})
this.options[collectionName].push(hookDefinition)
}
}

defineTestRunHook(collectionName) {
return (options, code) => {
const hookDefinition = buildTestRunHookDefinition({
Expand Down Expand Up @@ -105,8 +119,10 @@ export class SupportCodeLibraryBuilder {
this.options = _.cloneDeep({
afterTestCaseHookDefinitions: [],
afterTestRunHookDefinitions: [],
afterTestStepHookDefinitions: [],
beforeTestCaseHookDefinitions: [],
beforeTestRunHookDefinitions: [],
beforeTestStepHookDefinitions: [],
defaultTimeout: 5000,
definitionFunctionWrapper: null,
stepDefinitionConfigs: [],
Expand Down
12 changes: 12 additions & 0 deletions src/support_code_library_builder/validate_arguments.js
Expand Up @@ -40,6 +40,18 @@ const validations = {
optionsTimeoutValidation,
{ identifier: 'second argument', ...fnValidation },
],
defineTestStepHook: [
{ identifier: 'first argument', ...optionsValidation },
{
identifier: '"options.tags"',
expectedType: 'string',
predicate({ options }) {
return !options.tags || _.isString(options.tags)
},
},
optionsTimeoutValidation,
{ identifier: 'second argument', ...fnValidation },
],
defineStep: [
{
identifier: 'first argument',
Expand Down

0 comments on commit 3afe03b

Please sign in to comment.