UNPKG

cucumber

Version:

The official JavaScript implementation of Cucumber.

286 lines (238 loc) 8.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _lodash = _interopRequireDefault(require("lodash")); var _helpers = require("./helpers"); var _attachment_manager = _interopRequireDefault(require("./attachment_manager")); var _bluebird = _interopRequireDefault(require("bluebird")); var _status = _interopRequireDefault(require("../status")); var _step_runner = _interopRequireDefault(require("./step_runner")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } class TestCaseRunner { constructor({ eventBroadcaster: eventBroadcaster, retries = 0, skip: skip, testCase: testCase, supportCodeLibrary: supportCodeLibrary, worldParameters: worldParameters }) { this.attachmentManager = new _attachment_manager.default(({ data: data, media: media }) => { if (this.testStepIndex > this.maxTestStepIndex) { throw new Error('Cannot attach after all steps/hooks have finished running. Ensure your step/hook waits for the attach to finish.'); } this.emit('test-step-attachment', { index: this.testStepIndex, data: data, media: media }); }); this.eventBroadcaster = eventBroadcaster; this.maxAttempts = 1 + (skip ? 0 : retries); this.skip = skip; this.testCase = testCase; this.supportCodeLibrary = supportCodeLibrary; this.worldParameters = worldParameters; this.beforeHookDefinitions = this.getBeforeHookDefinitions(); this.afterHookDefinitions = this.getAfterHookDefinitions(); this.maxTestStepIndex = this.beforeHookDefinitions.length + this.testCase.pickle.steps.length + this.afterHookDefinitions.length - 1; this.testCaseSourceLocation = { uri: this.testCase.uri, line: this.testCase.pickle.locations[0].line }; this.resetTestProgressData(); } resetTestProgressData() { var _context; this.world = new this.supportCodeLibrary.World({ attach: (_context = this.attachmentManager).create.bind(_context), parameters: this.worldParameters }); this.testStepIndex = 0; this.result = { duration: 0, status: this.skip ? _status.default.SKIPPED : _status.default.PASSED }; } emit(name, data) { const eventData = { ...data }; const testCaseData = { sourceLocation: this.testCaseSourceLocation }; if (this.currentAttemptNumber) { testCaseData.attemptNumber = this.currentAttemptNumber; } if (_lodash.default.startsWith(name, 'test-case')) { _lodash.default.merge(eventData, testCaseData); } else { eventData.testCase = testCaseData; } this.eventBroadcaster.emit(name, eventData); } emitPrepared() { const steps = []; this.beforeHookDefinitions.forEach(definition => { const actionLocation = { uri: definition.uri, line: definition.line }; steps.push({ actionLocation: actionLocation }); }); this.testCase.pickle.steps.forEach(step => { const actionLocations = this.getStepDefinitions(step).map(definition => ({ uri: definition.uri, line: definition.line })); const sourceLocation = { uri: this.testCase.uri, line: _lodash.default.last(step.locations).line }; const data = { sourceLocation: sourceLocation }; if (actionLocations.length === 1) { data.actionLocation = actionLocations[0]; } steps.push(data); }); this.afterHookDefinitions.forEach(definition => { const actionLocation = { uri: definition.uri, line: definition.line }; steps.push({ actionLocation: actionLocation }); }); this.emit('test-case-prepared', { steps: steps }); } getAfterHookDefinitions() { return this.supportCodeLibrary.afterTestCaseHookDefinitions.filter(hookDefinition => hookDefinition.appliesToTestCase(this.testCase)); } getBeforeHookDefinitions() { return this.supportCodeLibrary.beforeTestCaseHookDefinitions.filter(hookDefinition => hookDefinition.appliesToTestCase(this.testCase)); } getStepDefinitions(step) { return this.supportCodeLibrary.stepDefinitions.filter(stepDefinition => stepDefinition.matchesStepName(step.text)); } invokeStep(step, stepDefinition, hookParameter) { return _step_runner.default.run({ defaultTimeout: this.supportCodeLibrary.defaultTimeout, hookParameter: hookParameter, step: step, stepDefinition: stepDefinition, world: this.world }); } isSkippingSteps() { return this.result.status !== _status.default.PASSED; } shouldSkipHook(isBeforeHook) { return this.skip || this.isSkippingSteps() && isBeforeHook; } shouldUpdateStatus(testStepResult) { switch (testStepResult.status) { case _status.default.UNDEFINED: case _status.default.FAILED: case _status.default.AMBIGUOUS: return !_lodash.default.some([_status.default.FAILED, _status.default.AMBIGUOUS, _status.default.UNDEFINED], this.result.status); default: return this.result.status === _status.default.PASSED; } } async aroundTestStep(runStepFn) { this.emit('test-step-started', { index: this.testStepIndex }); const testStepResult = await runStepFn(); if (testStepResult.duration) { this.result.duration += testStepResult.duration; } if (this.shouldUpdateStatus(testStepResult)) { this.result.status = testStepResult.status; } if (testStepResult.exception) { this.result.exception = testStepResult.exception; } this.emit('test-step-finished', { index: this.testStepIndex, result: testStepResult }); this.testStepIndex += 1; } async run() { this.emitPrepared(); for (this.currentAttemptNumber = 1; this.currentAttemptNumber <= this.maxAttempts; this.currentAttemptNumber++) { this.emit('test-case-started'); await this.runHooks(this.beforeHookDefinitions, { sourceLocation: this.testCaseSourceLocation, pickle: this.testCase.pickle }, true); await this.runSteps(); const shouldRetry = this.result.status === _status.default.FAILED && this.currentAttemptNumber < this.maxAttempts; if (shouldRetry) { this.result.retried = true; } await this.runHooks(this.afterHookDefinitions, { sourceLocation: this.testCaseSourceLocation, pickle: this.testCase.pickle, result: this.result }, false); this.emit('test-case-finished', { result: this.result }); if (!shouldRetry) { break; } this.resetTestProgressData(); } return this.result; } async runHook(hookDefinition, hookParameter, isBeforeHook) { if (this.shouldSkipHook(isBeforeHook)) { return { status: _status.default.SKIPPED }; } return this.invokeStep(null, hookDefinition, hookParameter); } async runHooks(hookDefinitions, hookParameter, isBeforeHook) { await _bluebird.default.each(hookDefinitions, async hookDefinition => { await this.aroundTestStep(() => this.runHook(hookDefinition, hookParameter, isBeforeHook)); }); } async runStep(step) { const stepDefinitions = this.getStepDefinitions(step); if (stepDefinitions.length === 0) { return { status: _status.default.UNDEFINED }; } else if (stepDefinitions.length > 1) { return { exception: (0, _helpers.getAmbiguousStepException)(stepDefinitions), status: _status.default.AMBIGUOUS }; } else if (this.isSkippingSteps()) { return { status: _status.default.SKIPPED }; } return this.invokeStep(step, stepDefinitions[0]); } async runSteps() { await _bluebird.default.each(this.testCase.pickle.steps, async step => { await this.aroundTestStep(() => this.runStep(step)); }); } } exports.default = TestCaseRunner;