Skip to content

Commit 705b584

Browse files
committed
cucumber interface for intern
1 parent 30619b1 commit 705b584

File tree

10 files changed

+4058
-1
lines changed

10 files changed

+4058
-1
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
_build

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
BSD 3-Clause License
22

3-
Copyright (c) 2018, rhpijnacker
3+
Copyright (c) 2018, Ronald Pijnacker
44
All rights reserved.
55

66
Redistribution and use in source and binary forms, with or without

intern.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"capabilities": { "name": "intern-cucumber" },
3+
"environments": [
4+
"node",
5+
{
6+
"browserName": "chrome",
7+
"fixSessionCapabilities": false
8+
}
9+
],
10+
"node": {
11+
"suites": [
12+
"tests/unit/**/*.js"
13+
],
14+
"plugins": [
15+
"tests/support/nodeMocking.js"
16+
]
17+
},
18+
// "coverage": "src/**/*.js",
19+
"filterErrorStack": true
20+
}

package-lock.json

+3,544
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "intern-cucumber",
3+
"version": "0.0.1",
4+
"description": "cucumber-js interface for intern",
5+
"main": "src/plugin.js",
6+
"scripts": {
7+
"test": "NODE_PATH=. intern"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/rhpijnacker/intern-cucumber.git"
12+
},
13+
"keywords": [
14+
"intern",
15+
"cucumber-js",
16+
"bdd"
17+
],
18+
"author": "Ronald Pijnacker",
19+
"license": "BSD-3-Clause",
20+
"bugs": {
21+
"url": "https://github.com/rhpijnacker/intern-cucumber/issues"
22+
},
23+
"homepage": "https://github.com/rhpijnacker/intern-cucumber#readme",
24+
"devDependencies": {
25+
"sinon": "^4.1.6",
26+
"umd-compat-loader": "^2.1.1",
27+
"webpack": "^3.10.0"
28+
},
29+
"dependencies": {
30+
"cucumber": "^3.2.1",
31+
"intern": "^4.1.5"
32+
}
33+
}

src/interface/cucumber.js

+183
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
const cucumber = require('cucumber');
2+
const global = require('@dojo/shim/global');
3+
const Suite = require('intern/lib/Suite');
4+
const Task = require('@dojo/core/async/Task');
5+
const Test = require('intern/lib/Test');
6+
7+
const events = require('events');
8+
9+
// Make cucumber interface available
10+
exports.Given = cucumber.Given;
11+
exports.Then = cucumber.Then;
12+
exports.When = cucumber.When;
13+
14+
function registerCucumber(featureSource /*, ...stepDefinitionInitializers */) {
15+
let stepDefinitionInitializers = Array.prototype.slice.call(arguments, 1);
16+
return _registerCucumber(global.default.intern, featureSource, stepDefinitionInitializers);
17+
}
18+
exports.default = registerCucumber;
19+
20+
function getInterface(executor) {
21+
return {
22+
registerCucumber: function(featureSource /*, ...stepDefinitionInitializers */) {
23+
let stepDefinitionInitializers = Array.prototype.slice.call(arguments, 1);
24+
return _registerCucumber(executor, featureSource, stepDefinitionInitializers);
25+
},
26+
// Make cucumber interface available
27+
Given: cucumber.Given,
28+
When: cucumber.When,
29+
Then: cucumber.Then
30+
};
31+
}
32+
exports.getInterface = getInterface;
33+
34+
function _registerCucumber(executor, featureSource, stepDefinitionInitializers) {
35+
executor.addSuite(function(parent) {
36+
let suite = _createSuite(featureSource, stepDefinitionInitializers);
37+
parent.add(suite);
38+
});
39+
}
40+
41+
function _createSuite(featureSource, stepDefinitionInitializers) {
42+
let suite = new Suite.default({
43+
name: 'x',
44+
run: () => {
45+
return new Task.default((resolve, reject) => {
46+
try {
47+
let eventBroadcaster = _createEventBroadcaster(suite);
48+
let featureRunner =
49+
_createFeatureRunner(
50+
featureSource,
51+
stepDefinitionInitializers,
52+
eventBroadcaster
53+
);
54+
featureRunner.start().then(() => {
55+
resolve();
56+
}).catch((e) => {
57+
suite.error = e;
58+
// reject(e);
59+
resolve();
60+
});
61+
} catch(e) {
62+
suite.error = e;
63+
console.log(e);
64+
reject();
65+
}
66+
});
67+
}
68+
});
69+
return suite;
70+
}
71+
72+
function _createEventBroadcaster(suite) {
73+
let eventBroadcaster = new events.EventEmitter();
74+
let eventDataCollector = new cucumber.formatterHelpers.EventDataCollector(eventBroadcaster);
75+
let subSuite, test, prevSuiteName, sameSuiteCount;
76+
77+
eventBroadcaster.on('test-run-started', () => {
78+
suite.executor.emit('suiteStart', suite);
79+
});
80+
eventBroadcaster.on('test-run-finished', () => {
81+
suite.executor.emit('suiteEnd', suite);
82+
});
83+
84+
eventBroadcaster.on('test-case-prepared', (event) => {
85+
// console.log('test-case-prepared event:', event);
86+
});
87+
eventBroadcaster.on('test-case-started', (event) => {
88+
// console.log('\ntest-case-started event:', event);
89+
let data = eventDataCollector.getTestCaseData(event.sourceLocation);
90+
// console.log('test-case-started data:', data);
91+
try {
92+
let name = data.pickle.name;
93+
if (name === prevSuiteName) {
94+
sameSuiteCount++;
95+
name = `${name} (${sameSuiteCount})`;
96+
} else {
97+
sameSuiteCount = 1;
98+
}
99+
prevSuiteName = data.pickle.name;
100+
subSuite = new Suite.default({ name: name });
101+
suite.add(subSuite);
102+
suite.executor.emit('suiteStart', subSuite);
103+
} catch(e) {
104+
suite.error = e;
105+
console.log(e);
106+
}
107+
});
108+
eventBroadcaster.on('test-case-finished', () => {
109+
suite.executor.emit('suiteEnd', subSuite);
110+
subSuite = null;
111+
});
112+
113+
eventBroadcaster.on('test-step-attachment', (event) => {
114+
// console.log('test-step-attachment event:', event);
115+
});
116+
eventBroadcaster.on('test-step-started', (event) => {
117+
// console.log('\ntest-step-started event:', event);
118+
// console.log('test-step-started step data:', data);
119+
try {
120+
let data = eventDataCollector.getTestStepData(event);
121+
let name = `${data.gherkinKeyword}${data.pickleStep.text}`
122+
test = new Test.default({ name, test: () => {} });
123+
subSuite.add(test);
124+
suite.executor.emit('testStart', test);
125+
} catch(e) {
126+
suite.error = e;
127+
console.log(e);
128+
}
129+
});
130+
eventBroadcaster.on('test-step-finished', (event) => {
131+
// console.log('\ntest-step-finished event:', event);
132+
try {
133+
test._hasPassed = event.result.status === cucumber.Status.PASSED;
134+
if (event.result.status === cucumber.Status.FAILED) {
135+
let exception = event.result.exception;
136+
let message = `"${test.name}" failed:\n${exception.message}`;
137+
test.error = new Error(message);
138+
} else if (event.result.status == cucumber.Status.UNDEFINED) {
139+
let execption = event.result.exception;
140+
let message = `"${test.name}" does not have a matching step definition`;
141+
test.error = new Error(message);
142+
}
143+
suite.executor.emit('testEnd', test);
144+
test = null;
145+
} catch(e) {
146+
suite.error = e;
147+
console.log(e);
148+
}
149+
});
150+
return eventBroadcaster;
151+
}
152+
153+
function _createFeatureRunner(featureSource, stepDefinitionInitializers, eventBroadcaster) {
154+
let testCases = cucumber.getTestCases({
155+
eventBroadcaster,
156+
pickleFilter: new cucumber.PickleFilter({}),
157+
source: featureSource,
158+
uri: '/feature'
159+
});
160+
161+
cucumber.supportCodeLibraryBuilder.reset('/');
162+
stepDefinitionInitializers.forEach((initializer) => { initializer(); });
163+
let supportCodeLibrary = cucumber.supportCodeLibraryBuilder.finalize();
164+
165+
let eventDataCollector =
166+
new cucumber.formatterHelpers.EventDataCollector(eventBroadcaster);
167+
let formatterOptions = {
168+
cwd: '/',
169+
eventBroadcaster,
170+
eventDataCollector,
171+
log: () => {},
172+
supportCodeLibrary
173+
};
174+
cucumber.FormatterBuilder.build('progress', formatterOptions);
175+
176+
let runner = new cucumber.Runtime({
177+
eventBroadcaster,
178+
options: { failFast: true },
179+
testCases,
180+
supportCodeLibrary
181+
});
182+
return runner;
183+
}

src/plugin.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const getInterface = require('./interface/cucumber').getInterface;
2+
3+
intern.registerInterface('cucumber', getInterface(intern));

tests/support/nodeMocking.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"use strict";
2+
intern.registerPlugin('mockRequire', function () {
3+
function mockRequire(require, mod, mocks) {
4+
var registeredMocks = [];
5+
mod = require.resolve(mod);
6+
registeredMocks.push({ id: mod, original: require.cache[mod] });
7+
delete require.cache[mod];
8+
Object.keys(mocks).forEach(function (name) {
9+
var id = require.resolve(name);
10+
registeredMocks.push({ id: id, original: require.cache[id] });
11+
delete require.cache[id];
12+
if (mocks[name] != null) {
13+
require.cache[id] = {
14+
id: id,
15+
filename: id,
16+
loaded: true,
17+
exports: mocks[name]
18+
};
19+
}
20+
});
21+
return Promise.resolve({
22+
module: require(mod),
23+
remove: function () {
24+
while (registeredMocks.length > 0) {
25+
var _a = registeredMocks.pop(), id = _a.id, original = _a.original;
26+
delete require.cache[id];
27+
if (typeof original !== 'undefined') {
28+
require.cache[id] = original;
29+
}
30+
}
31+
}
32+
});
33+
}
34+
return mockRequire;
35+
});

0 commit comments

Comments
 (0)