testem 2.7.2 → 2.9.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
@@ -150,7 +150,14 @@ module.exports = class App extends EventEmitter {
150
150
 
151
151
  return Bluebird.using(this.runHook('before_tests'), () => {
152
152
  return Bluebird.using(RunTimeout.with(this.config.get('timeout')), timeout => {
153
- timeout.on('timeout', () => this.killRunners());
153
+ timeout.on('timeout', () => {
154
+ let timeoutSeconds = timeout.timeout;
155
+ if (timeoutSeconds) {
156
+ log.info(`Test suite execution has timed out (config.timeout = ${timeoutSeconds} seconds). Terminating all test runners`);
157
+ this.testSuiteTimedOut = true;
158
+ }
159
+ this.killRunners();
160
+ });
154
161
  this.timeoutID = timeout.timeoutID; // TODO Remove, just for the tests
155
162
  this.currentRun = this.singleRun(timeout);
156
163
  this.emit('testRun');
package/lib/config.js CHANGED
@@ -403,6 +403,14 @@ class Config {
403
403
  this.getFileSet(srcFiles, srcFilesIgnore, callback);
404
404
  }
405
405
 
406
+ getFooterScripts(callback) {
407
+ var want = this.get('footer_scripts') || [];
408
+ var dontWant = this.get('src_files_ignore') || '';
409
+
410
+ this.getFileSet(want, dontWant, callback);
411
+ };
412
+
413
+
406
414
  getServeFiles(callback) {
407
415
  let want = this.get('serve_files') || this.get('src_files') || '*.js';
408
416
  let dontWant = this.get('serve_files_ignore') || this.get('src_files_ignore') || '';
@@ -468,9 +476,12 @@ class Config {
468
476
 
469
477
  this.getCSSFiles((err, files) => {
470
478
  ret.css_files = files.map(replaceSlashes);
471
- if (cb) {
472
- cb(err, ret);
473
- }
479
+ this.getFooterScripts((err, files) => {
480
+ ret.footer_scripts = files;
481
+ if (cb) {
482
+ cb(err, ret);
483
+ }
484
+ });
474
485
  });
475
486
  });
476
487
  }
@@ -29,7 +29,7 @@ function failureDisplay(item) {
29
29
  }
30
30
 
31
31
  if (item.expected) {
32
- extra.push(' expected ' + item.expected);
32
+ extra.push(' expected ' + (item.negative ? 'NOT ' : '') + item.expected);
33
33
  }
34
34
 
35
35
  if (item.actual) {
@@ -80,8 +80,8 @@ module.exports = class DotReporter {
80
80
 
81
81
  if ('expected' in error && 'actual' in error) {
82
82
  printf(this.out, '\n' +
83
- ' expected: %O\n' +
84
- ' actual: %O\n', error.expected, error.actual);
83
+ ' expected: %s%O\n' +
84
+ ' actual: %O\n', (error.negative ? 'NOT ' : ''), error.expected, error.actual);
85
85
  }
86
86
 
87
87
  if (error.stack) {
@@ -1,6 +1,47 @@
1
1
  'use strict';
2
2
 
3
- class TeamcityReporter {
3
+ function namify(prefix, result) {
4
+ let line = (prefix ? (prefix + ' - ') : '') +
5
+ result.name.trim();
6
+
7
+ return escape(line);
8
+ }
9
+
10
+ /**
11
+ * Borrowed from https://github.com/travisjeffery/mocha-teamcity-reporter
12
+ * Escape the given `str`.
13
+ */
14
+
15
+ function escape(str) {
16
+ if (!str) {
17
+ return '';
18
+ }
19
+ return str
20
+ .toString()
21
+ .replace(/\|/g, '||')
22
+ .replace(/\n/g, '|n')
23
+ .replace(/\r/g, '|r')
24
+ .replace(/\[/g, '|[')
25
+ .replace(/\]/g, '|]')
26
+ .replace(/\u0085/g, '|x')
27
+ .replace(/\u2028/g, '|l')
28
+ .replace(/\u2029/g, '|p')
29
+ .replace(/'/g, '|\'');
30
+ }
31
+
32
+ function teamcityLine(type, options) {
33
+ const attributes = Object.keys(options)
34
+ .map(attributeName => `${attributeName}='${options[attributeName]}'`)
35
+ .join(' ');
36
+
37
+ return `##teamcity[${type} ${attributes}]\n`;
38
+ }
39
+
40
+ function runDurationAttribute(result) {
41
+ return typeof result.runDuration === 'number' ? {duration: result.runDuration} : undefined;
42
+ }
43
+
44
+ module.exports = class TeamcityReporter {
4
45
  constructor(silent, out) {
5
46
  this.out = out || process.stdout;
6
47
  this.silent = silent;
@@ -14,8 +55,13 @@ class TeamcityReporter {
14
55
  }
15
56
 
16
57
  report(prefix, data) {
17
- this.out.write('##teamcity[testStarted name=\'' + this._namify(prefix, data) + '\']\n');
58
+ const name = namify(prefix, data);
59
+ this.out.write(teamcityLine('testStarted', {name}));
18
60
  this._display(prefix, data);
61
+ this.out.write(teamcityLine('testFinished', Object.assign(
62
+ {name},
63
+ runDurationAttribute(data)
64
+ )));
19
65
  this.total++;
20
66
  if (data.skipped) {
21
67
  this.skipped++;
@@ -30,7 +76,10 @@ class TeamcityReporter {
30
76
  }
31
77
  this.endTime = new Date();
32
78
  this.out.write('\n\n');
33
- this.out.write('##teamcity[testSuiteFinished name=\'testem.suite\' duration=\'' + this._duration() + '\']\n');
79
+ this.out.write(teamcityLine('testSuiteFinished', {
80
+ name: 'testem.suite',
81
+ duration: Math.round((this.endTime - this.startTime)),
82
+ }));
34
83
  this.out.write('\n\n');
35
84
  }
36
85
 
@@ -38,52 +87,36 @@ class TeamcityReporter {
38
87
  if (this.silent) {
39
88
  return;
40
89
  }
90
+ const name = namify(prefix, result);
91
+
41
92
  if (result.skipped) {
42
- this.out.write('##teamcity[testIgnored name=\'' + this._namify(prefix, result) + '\' message=\'pending\']\n');
93
+ this.out.write(teamcityLine('testIgnored', {
94
+ name,
95
+ message: 'pending',
96
+ }));
43
97
  } else if (!result.passed) {
44
- var message = (result.error && result.error.message) || '';
45
- var stack = (result.error && result.error.stack) || '';
46
- this.out.write('##teamcity[testFailed name=\'' + this._namify(prefix, result) + '\' message=\'' + escape(message) + '\' details=\'' + escape(stack) + '\']\n');
47
- }
48
- this.out.write('##teamcity[testFinished name=\'' + this._namify(prefix, result) + '\'' + this._runDurationAttribute(result) + ']\n');
49
-
50
- }
98
+ const hasError = result.error;
99
+ const attributes = {name};
51
100
 
52
- _namify(prefix, result) {
53
- var line = (prefix ? (prefix + ' - ') : '') +
54
- result.name.trim();
55
- return escape(line);
56
- }
101
+ const message = (hasError && result.error.message) || '';
102
+ const stack = (hasError && result.error.stack) || '';
57
103
 
58
- _duration() {
59
- return Math.round((this.endTime - this.startTime));
60
- }
104
+ attributes.message = escape(message);
105
+ attributes.details = escape(stack);
61
106
 
62
- _runDurationAttribute(result) {
63
- return typeof result.runDuration === 'number' ? ' duration=\'' + result.runDuration + '\'' : '';
64
- }
65
- }
107
+ if (
108
+ hasError &&
109
+ result.error.hasOwnProperty('expected') &&
110
+ result.error.hasOwnProperty('actual')
111
+ ) {
112
+ attributes.type = 'comparisonFailure';
113
+ attributes.expected = (result.error.negative ? 'NOT ' : '') + escape(result.error.expected);
114
+ attributes.actual = escape(result.error.actual);
115
+ }
66
116
 
67
- /**
68
- * Borrowed from https://github.com/travisjeffery/mocha-teamcity-reporter
69
- * Escape the given `str`.
70
- */
71
-
72
- function escape(str) {
73
- if (!str) {
74
- return '';
117
+ this.out.write(teamcityLine('testFailed', attributes));
118
+ }
75
119
  }
76
- return str
77
- .toString()
78
- .replace(/\|/g, '||')
79
- .replace(/\n/g, '|n')
80
- .replace(/\r/g, '|r')
81
- .replace(/\[/g, '|[')
82
- .replace(/\]/g, '|]')
83
- .replace(/\u0085/g, '|x')
84
- .replace(/\u2028/g, '|l')
85
- .replace(/\u2029/g, '|p')
86
- .replace(/'/g, '|\'');
87
- }
120
+ };
88
121
 
89
- module.exports = TeamcityReporter;
122
+ module.exports.teamcityLine = teamcityLine;
@@ -21,6 +21,7 @@ module.exports = class BrowserTestRunner {
21
21
 
22
22
  this.pendingTimer = undefined;
23
23
  this.onProcessExitTimer = undefined;
24
+ this.exitRequested = false;
24
25
  }
25
26
 
26
27
  start(onFinish) {
@@ -61,6 +62,11 @@ module.exports = class BrowserTestRunner {
61
62
  return Bluebird.resolve();
62
63
  }
63
64
 
65
+ // mark that somebody (generally app.js) has requested that the
66
+ // test runner exit. This is not an unexpected exit--this setting
67
+ // is to tell the processExit handler that.
68
+ this.exitRequested = true;
69
+
64
70
  log.info(`Closing browser ${this.name()}.`);
65
71
  return this.process.kill().then(() => {
66
72
  this.process = null;
@@ -246,11 +252,15 @@ module.exports = class BrowserTestRunner {
246
252
  if (this.finished) { return; }
247
253
 
248
254
  this.onProcessExitTimer = setTimeout(() => {
249
- if (this.finished) {
250
- return;
255
+ if (this.finished) {
256
+ return;
251
257
  }
252
258
 
253
- this.reportResults(new Error('Browser exited unexpectedly'), code, browserProcess);
259
+ this.reportResults(new Error(this.exitRequested
260
+ ? "Browser exited on request from test driver"
261
+ : "Browser exited unexpectedly"),
262
+ code,
263
+ browserProcess);
254
264
  }, 1000);
255
265
  }
256
266
 
@@ -212,7 +212,7 @@ class Server extends EventEmitter {
212
212
  }
213
213
  }
214
214
 
215
- renderRunnerPage(err, files, res) {
215
+ renderRunnerPage(err, files, footerScripts, res) {
216
216
  var config = this.config;
217
217
  var framework = config.get('framework') || 'jasmine';
218
218
  var css_files = config.get('css_files');
@@ -228,7 +228,8 @@ class Server extends EventEmitter {
228
228
  }[framework] + '.mustache';
229
229
  res.render(`${__dirname}/../../views/${templateFile}`, {
230
230
  scripts: files,
231
- styles: css_files
231
+ styles: css_files,
232
+ footer_scripts: footerScripts
232
233
  });
233
234
  }
234
235
 
@@ -249,7 +250,9 @@ class Server extends EventEmitter {
249
250
  res.redirect(url);
250
251
  } else {
251
252
  config.getServeFiles((err, files) => {
252
- this.renderRunnerPage(err, files, res);
253
+ config.getFooterScripts((err, footerScripts) => {
254
+ this.renderRunnerPage(err, files, footerScripts, res);
255
+ });
253
256
  });
254
257
  }
255
258
  }
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "unittest",
8
8
  "browser"
9
9
  ],
10
- "version": "2.7.2",
10
+ "version": "2.9.0",
11
11
  "author": "Toby Ho <airportyh@gmail.com>",
12
12
  "repository": {
13
13
  "type": "git",
@@ -75,16 +75,16 @@
75
75
  "chai-shallow-deep-equal": "^1.4.4",
76
76
  "cheerio": "^1.0.0-rc.2",
77
77
  "dirty-chai": "^2.0.0",
78
- "eslint": "^4.1.0",
78
+ "eslint": "^5.0.0",
79
79
  "eslint-plugin-chai-expect": "^1.1.1",
80
80
  "eslint-plugin-mocha": "^5.0.0",
81
81
  "mocha": "^3.0.0",
82
82
  "request": "^2.51.0",
83
- "saucie": "^3.0.1",
83
+ "saucie": "^3.3.2",
84
84
  "shelljs": "^0.8.2",
85
- "sinon": "^5.0.10",
86
- "sinon-chai": "^3.0.0",
87
- "tape": "^4.0.0",
85
+ "sinon": "^6.0.0",
86
+ "sinon-chai": "^3.2.0",
87
+ "tape": "^4.9.1",
88
88
  "tmp": "0.0.33"
89
89
  },
90
90
  "bin": {
@@ -71,7 +71,8 @@ function qunitAdapter() {
71
71
  actual: params.actual,
72
72
  expected: params.expected,
73
73
  stack: params.source,
74
- message: params.message
74
+ message: params.message,
75
+ negative: params.negative
75
76
  });
76
77
  }
77
78
 
@@ -409,5 +409,15 @@ function emit() {
409
409
 
410
410
  if (typeof window !== 'undefined') {
411
411
  window.Testem = Testem;
412
+
413
+ // Stub window.alert and window.confirm to throw error so alert and confirm does not get used in tests
414
+ // this will prevent browser disconnect failures
415
+ window.alert = function() {
416
+ throw new Error('[Testem] Calling window.alert() in tests is disabled, because it causes testem to fail with browser disconnect error.');
417
+ }
418
+
419
+ window.confirm = function() {
420
+ throw new Error('[Testem] Calling window.confirm() in tests is disabled, because it causes testem to fail with browser disconnect error.');
421
+ }
412
422
  init();
413
423
  }
@@ -9,4 +9,5 @@
9
9
  {{#scripts}}<script src="{{{src}}}"{{#attrs}} {{&.}}{{/attrs}}></script>{{/scripts}}
10
10
  </head>
11
11
  <body></body>
12
+ {{#footer_scripts}}<script src="{{{src}}}"{{#attrs}} {{&.}}{{/attrs}}></script>{{/footer_scripts}}
12
13
  </html>
@@ -8,4 +8,5 @@
8
8
  </head>
9
9
  <body>
10
10
  </body>
11
+ {{#footer_scripts}}<script src="{{{src}}}"{{#attrs}} {{&.}}{{/attrs}}></script>{{/footer_scripts}}
11
12
  </html>
@@ -16,4 +16,5 @@
16
16
  <body>
17
17
  <div id="jasmine_content"></div>
18
18
  </body>
19
+ {{#footer_scripts}}<script src="{{{src}}}"{{#attrs}} {{&.}}{{/attrs}}></script>{{/footer_scripts}}
19
20
  </html>
@@ -25,4 +25,5 @@
25
25
  <body>
26
26
  <div id="jasmine_content"></div>
27
27
  </body>
28
+ {{#footer_scripts}}<script src="{{{src}}}"{{#attrs}} {{&.}}{{/attrs}}></script>{{/footer_scripts}}
28
29
  </html>
@@ -11,6 +11,7 @@
11
11
  </head>
12
12
  <body>
13
13
  <div id="mocha"></div>
14
+ {{#footer_scripts}}<script src="{{{src}}}"{{#attrs}} {{&.}}{{/attrs}}></script>{{/footer_scripts}}
14
15
  <script>
15
16
  mocha.run()
16
17
  </script>
@@ -11,6 +11,7 @@
11
11
  </head>
12
12
  <body>
13
13
  <div id="mocha"></div>
14
+ {{#footer_scripts}}<script src="{{{src}}}"{{#attrs}} {{&.}}{{/attrs}}></script>{{/footer_scripts}}
14
15
  <script>
15
16
  mocha.run()
16
17
  </script>
@@ -12,4 +12,5 @@
12
12
  <div id="qunit"></div>
13
13
  <div id="qunit-fixture"></div>
14
14
  </body>
15
+ {{#footer_scripts}}<script src="{{{src}}}"{{#attrs}} {{&.}}{{/attrs}}></script>{{/footer_scripts}}
15
16
  </html>
@@ -16,5 +16,6 @@ Testem.handleConsoleMessage = function(msg){
16
16
  }
17
17
  </script>
18
18
  {{#scripts}}<script src="{{{src}}}"{{#attrs}} {{&.}}{{/attrs}}></script>{{/scripts}}
19
+ {{#footer_scripts}}<script src="{{{src}}}"{{#attrs}} {{&.}}{{/attrs}}></script>{{/footer_scripts}}
19
20
  </body>
20
- </html>
21
+ </html>