testem 2.10.0 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/app.js CHANGED
@@ -259,7 +259,9 @@ module.exports = class App extends EventEmitter {
259
259
  id: id,
260
260
  protocol: 'browser'
261
261
  }, this.config).create();
262
- browser = new BrowserTestRunner(launcher, this.reporter, this.runnerIndex++, this.config);
262
+ const singleRun = this.config.get('single_run');
263
+
264
+ browser = new BrowserTestRunner(launcher, this.reporter, this.runnerIndex++, singleRun, this.config);
263
265
  this.addRunner(browser);
264
266
  }
265
267
 
package/lib/launcher.js CHANGED
@@ -1,16 +1,15 @@
1
1
  'use strict';
2
2
 
3
3
  const log = require('npmlog');
4
- const rimraf = require('rimraf');
5
- const mkdirp = require('mkdirp');
4
+ const tmp = require('tmp');
6
5
  const path = require('path');
7
6
  const Bluebird = require('bluebird');
8
7
 
9
8
  const template = require('./strutils').template;
10
9
  const ProcessCtl = require('./process-ctl');
11
10
 
12
- const rimrafAsync = Bluebird.promisify(rimraf);
13
- const mkdirpAsync = Bluebird.promisify(mkdirp);
11
+ // setup graceful cleanup: removes the created directories when an uncaught exception occurs.
12
+ tmp.setGracefulCleanup();
14
13
 
15
14
  module.exports = class Launcher {
16
15
  constructor(name, settings, config) {
@@ -52,10 +51,10 @@ module.exports = class Launcher {
52
51
  }
53
52
 
54
53
  launch() {
55
- let settings = this.settings;
56
- let dir = this.browserTmpDir(this.config, this.id);
54
+ const settings = this.settings;
55
+ this.setupBrowserTmpDir();
57
56
 
58
- return rimrafAsync(dir).then(() => mkdirpAsync(dir)).then(() => {
57
+ return Bluebird.resolve().asCallback(() => {
59
58
  if (settings.setup) {
60
59
  return Bluebird.fromCallback(setupCallback => {
61
60
  settings.setup.call(this, this.config, setupCallback);
@@ -129,9 +128,21 @@ module.exports = class Launcher {
129
128
  }
130
129
  }
131
130
 
131
+ setupBrowserTmpDir() {
132
+ const userDataDir = this.config.getUserDataDir();
133
+ const tmpPath = path.join(userDataDir, 'testem-' + this.id);
134
+
135
+ this.browserTmpDirectory = tmp.dirSync({
136
+ template: `${tmpPath}-XXXXXX`,
137
+ unsafeCleanup: true
138
+ });
139
+ }
140
+
132
141
  browserTmpDir() {
133
- let userDataDir = this.config.getUserDataDir();
142
+ if (!this.browserTmpDirectory) {
143
+ setupBrowserTmpDir();
144
+ }
134
145
 
135
- return path.join(userDataDir, 'testem-' + this.id);
146
+ return this.browserTmpDirectory.name;
136
147
  }
137
148
  };
@@ -128,6 +128,14 @@ module.exports = class BrowserTestRunner {
128
128
  socket.on('all-test-results', this.onAllTestResults.bind(this));
129
129
  socket.on('after-tests-complete', this.onAfterTests.bind(this));
130
130
 
131
+ const customBrowserSocketEvents = this.launcher.config.get('custom_browser_socket_events');
132
+
133
+ if (customBrowserSocketEvents) {
134
+ Object.keys(customBrowserSocketEvents).forEach((key) => {
135
+ socket.on(key, customBrowserSocketEvents[key].bind(this));
136
+ });
137
+ }
138
+
131
139
  let tap = new BrowserTapConsumer(socket);
132
140
  tap.on('test-result', this.onTestResult.bind(this));
133
141
  tap.on('all-test-results', this.onAllTestResults.bind(this));
@@ -169,7 +177,8 @@ module.exports = class BrowserTestRunner {
169
177
  launcherId: this.launcherId,
170
178
  failed: result.failed,
171
179
  pending: result.pending,
172
- items: result.items
180
+ items: result.items,
181
+ originalResultObj: result
173
182
  });
174
183
  this.logs = [];
175
184
  this.currentTestContext.state = 'complete';
@@ -252,8 +261,8 @@ module.exports = class BrowserTestRunner {
252
261
  if (this.finished) { return; }
253
262
 
254
263
  this.onProcessExitTimer = setTimeout(() => {
255
- if (this.finished) {
256
- return;
264
+ if (this.finished) {
265
+ return;
257
266
  }
258
267
 
259
268
  this.reportResults(new Error(this.exitRequested
@@ -145,6 +145,7 @@ class Server extends EventEmitter {
145
145
  configureExpress(app) {
146
146
  app.engine('mustache', Mustache);
147
147
  app.set('view options', {layout: false});
148
+ app.set('etag', 'strong');
148
149
  app.use((req, res, next) => {
149
150
  if (this.ieCompatMode) {
150
151
  res.setHeader('X-UA-Compatible', `IE=${this.ieCompatMode}`);
@@ -234,9 +235,6 @@ class Server extends EventEmitter {
234
235
  }
235
236
 
236
237
  renderDefaultTestPage(req, res) {
237
- res.header('Cache-Control', 'No-cache');
238
- res.header('Pragma', 'No-cache');
239
-
240
238
  var config = this.config;
241
239
  var test_page = config.get('test_page')[0];
242
240
 
@@ -299,13 +297,6 @@ class Server extends EventEmitter {
299
297
  });
300
298
  }
301
299
 
302
- killTheCache(req, res) {
303
- res.setHeader('Cache-Control', 'No-cache');
304
- res.setHeader('Pragma', 'No-cache');
305
- delete req.headers['if-modified-since'];
306
- delete req.headers['if-none-match'];
307
- }
308
-
309
300
  route(uri) {
310
301
  var config = this.config;
311
302
  var routes = config.get('routes') || config.get('route') || {};
@@ -338,7 +329,6 @@ class Server extends EventEmitter {
338
329
  var routeRes = this.route(uri);
339
330
  uri = routeRes.uri;
340
331
  var wasRouted = routeRes.routed;
341
- this.killTheCache(req, res);
342
332
  var allowUnsafeDirs = config.get('unsafe_file_serving');
343
333
  var filePath = path.resolve(config.resolvePath(uri));
344
334
  var ext = path.extname(filePath);
@@ -34,6 +34,16 @@ function chromeArgs(browserTmpDir, url) {
34
34
  ];
35
35
  }
36
36
 
37
+ function headlessChromeArgs(browserTmpDir, url) {
38
+ return [
39
+ '--headless',
40
+ '--disable-gpu',
41
+ '--disable-software-rasterizer',
42
+ '--mute-audio',
43
+ '--remote-debugging-port=0',
44
+ '--window-size=1440,900'].concat(chromeArgs(browserTmpDir, url));
45
+ }
46
+
37
47
  // Return the catalogue of the browsers that Testem supports for the platform. Each 'browser object'
38
48
  // will contain these fields:
39
49
  //
@@ -96,6 +106,18 @@ module.exports = function knownBrowsers(platform, config) {
96
106
  return chromeArgs(this.browserTmpDir(), url);
97
107
  }
98
108
  },
109
+ {
110
+ name: 'Headless Chrome',
111
+ possiblePath: chromeWinPaths(userHomeDir, 'Chrome').concat(chromeOSXPaths('Google Chrome')),
112
+ possibleExe: [
113
+ 'google-chrome',
114
+ 'google-chrome-stable',
115
+ 'chrome'
116
+ ],
117
+ args(config, url) {
118
+ return headlessChromeArgs(this.browserTmpDir(), url);
119
+ }
120
+ },
99
121
 
100
122
  // On Windows, Chrome Stable/Beta/Dev are installed on the same path
101
123
  {
@@ -108,6 +130,16 @@ module.exports = function knownBrowsers(platform, config) {
108
130
  return chromeArgs(this.browserTmpDir(), url);
109
131
  }
110
132
  },
133
+ {
134
+ name: 'Headless Chrome Beta',
135
+ possiblePath: chromeOSXPaths('Google Chrome Beta'),
136
+ possibleExe: [
137
+ 'google-chrome-beta'
138
+ ],
139
+ args(config, url) {
140
+ return headlessChromeArgs(this.browserTmpDir(), url);
141
+ }
142
+ },
111
143
  {
112
144
  name: 'Chrome Dev',
113
145
  possiblePath: chromeOSXPaths('Google Chrome Dev'),
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "unittest",
8
8
  "browser"
9
9
  ],
10
- "version": "2.10.0",
10
+ "version": "2.14.0",
11
11
  "author": "Toby Ho <airportyh@gmail.com>",
12
12
  "repository": {
13
13
  "type": "git",
@@ -26,7 +26,7 @@
26
26
  "charm": "^1.0.0",
27
27
  "commander": "^2.6.0",
28
28
  "consolidate": "^0.15.1",
29
- "execa": "^0.10.0",
29
+ "execa": "^1.0.0",
30
30
  "express": "^4.10.7",
31
31
  "fireworm": "^0.7.0",
32
32
  "glob": "^7.0.4",
@@ -38,15 +38,16 @@
38
38
  "lodash.find": "^4.5.1",
39
39
  "lodash.uniqby": "^4.7.0",
40
40
  "mkdirp": "^0.5.1",
41
- "mustache": "^2.2.1",
41
+ "mustache": "^3.0.0",
42
42
  "node-notifier": "^5.0.1",
43
43
  "npmlog": "^4.0.0",
44
- "printf": "^0.3.0",
44
+ "printf": "^0.5.1",
45
45
  "rimraf": "^2.4.4",
46
46
  "socket.io": "^2.1.0",
47
47
  "spawn-args": "^0.2.0",
48
48
  "styled_string": "0.0.1",
49
49
  "tap-parser": "^7.0.0",
50
+ "tmp": "0.0.33",
50
51
  "xmldom": "^0.1.19"
51
52
  },
52
53
  "files": [
@@ -84,8 +85,7 @@
84
85
  "shelljs": "^0.8.2",
85
86
  "sinon": "^6.0.0",
86
87
  "sinon-chai": "^3.2.0",
87
- "tape": "^4.9.1",
88
- "tmp": "0.0.33"
88
+ "tape": "^4.9.1"
89
89
  },
90
90
  "bin": {
91
91
  "testem": "./testem.js"
@@ -93,6 +93,7 @@ function qunitAdapter() {
93
93
  currentTest.skipped = params.skipped;
94
94
  currentTest.total = params.total;
95
95
  currentTest.runDuration = params.runtime;
96
+ currentTest.testId = params.testId;
96
97
 
97
98
  results.total++;
98
99
 
@@ -175,6 +175,7 @@ var Testem = {
175
175
  handler.apply(this, argsWithoutFirst);
176
176
  }
177
177
  }
178
+
178
179
  this.emitMessage.apply(this, arguments);
179
180
  },
180
181
  on: function(evt, callback) {
@@ -266,6 +267,11 @@ var Testem = {
266
267
  case 'stop-run':
267
268
  self.emit('after-tests-complete');
268
269
  break;
270
+ default:
271
+ if (type.indexOf('testem:') === 0) {
272
+ self.emit(type, message.data);
273
+ }
274
+ break;
269
275
  }
270
276
  });
271
277
  },
@@ -286,6 +292,21 @@ var Testem = {
286
292
  // stringify for clients that only can handle string postMessages (IE <= 10)
287
293
  return JSON.stringify(message);
288
294
  },
295
+ removeEventCallbacks: function(evt, callback) {
296
+ var handlers = this.evtHandlers[evt];
297
+ var removeIdx = [];
298
+ if (typeof handlers === "undefined") {
299
+ return;
300
+ }
301
+ for (var i = 0; i < handlers.length; i++) {
302
+ if (handlers[i] === callback) {
303
+ removeIdx.push(i);
304
+ }
305
+ }
306
+ for (var j = 0; j < removeIdx.length; j++) {
307
+ handlers.splice(j, 1);
308
+ }
309
+ },
289
310
  runAfterTests: function() {
290
311
  if (Testem.afterTestsQueue.length) {
291
312
  var afterTestsCallback = Testem.afterTestsQueue.shift();
@@ -1,4 +1,5 @@
1
- /* globals document, parent, window, io, navigator */
1
+ /* globals document, parent, io, window, navigator */
2
+ /* globals module */
2
3
  'use strict';
3
4
 
4
5
  var socket;
@@ -37,11 +38,13 @@ function sendMessageToParent(type, data) {
37
38
  parent.postMessage(message, '*');
38
39
  }
39
40
 
40
- var addListener = window.addEventListener ?
41
- function(obj, evt, cb) { obj.addEventListener(evt, cb, false); } :
42
- function(obj, evt, cb) { obj.attachEvent('on' + evt, cb); };
43
-
44
- addListener(window, 'message', handleMessage);
41
+ var addListener;
42
+ if (typeof window !== 'undefined') {
43
+ addListener = window.addEventListener ?
44
+ function(obj, evt, cb) { obj.addEventListener(evt, cb, false); } :
45
+ function(obj, evt, cb) { obj.attachEvent('on' + evt, cb); };
46
+ addListener(window, 'message', handleMessage);
47
+ }
45
48
 
46
49
  var messageListeners = {};
47
50
  function handleMessage(event) {
@@ -158,8 +161,21 @@ function init() {
158
161
  });
159
162
  }
160
163
 
164
+ function patchEmitterForWildcard(socket) {
165
+ var emit = io.Manager.prototype.emit;
166
+
167
+ function onevent (packet) {
168
+ var args = packet.data || [];
169
+ emit.call(this, '*', packet);
170
+ return emit.apply(this, args);
171
+ }
172
+ socket.onevent = onevent;
173
+ }
174
+
161
175
  function initSocket(id) {
162
176
  socket = io.connect({ reconnectionDelayMax: 1000, randomizationFactor: 0 });
177
+ patchEmitterForWildcard(socket);
178
+
163
179
  socket.emit('browser-login', getBrowserName(navigator.userAgent), id);
164
180
  socket.on('connect', function() {
165
181
  connectStatus = 'connected';
@@ -176,6 +192,21 @@ function initSocket(id) {
176
192
  socket.on('stop-run', function() {
177
193
  sendMessageToParent('stop-run');
178
194
  });
195
+ socket.on('*', function(event) {
196
+ if (event.data && event.data[0].indexOf('testem:') === 0 ) {
197
+ var eventName = event.data[0];
198
+ var eventData = event.data[1];
199
+ sendMessageToParent(eventName, eventData);
200
+ }
201
+ });
202
+ }
203
+
204
+ // We should only call init() if it ran in browser.
205
+ if (typeof window !== 'undefined') {
206
+ init();
179
207
  }
180
208
 
181
- init();
209
+ // Exporting this as a module so that it can be unit tested in Node.
210
+ if (typeof module !== 'undefined') {
211
+ module.exports = patchEmitterForWildcard;
212
+ }