jasmine-browser-runner 3.0.0-beta.2 → 4.0.0-beta.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/MIT.LICENSE CHANGED
@@ -1,5 +1,5 @@
1
1
  Copyright (c) 2019 Pivotal Labs
2
- Copyright (c) 2020-2024 The Jasmine developers
2
+ Copyright (c) 2020-2025 The Jasmine developers
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining
5
5
  a copy of this software and associated documentation files (the
package/README.md CHANGED
@@ -56,7 +56,7 @@ To use a browser other than Firefox, add a `browser` field to
56
56
  ```javascript
57
57
  export default {
58
58
  // ...
59
- "browser": "chrome"
59
+ browser: "chrome"
60
60
  }
61
61
  ```
62
62
 
@@ -71,8 +71,8 @@ in PEM format in `jasmine-browser.mjs`:
71
71
  ```javascript
72
72
  export default {
73
73
  // ...
74
- "tlsKey": "/path/to/tlsKey.pem",
75
- "tlsCert": "/path/to/tlsCert.pem",
74
+ tlsKey: "/path/to/tlsKey.pem",
75
+ tlsCert: "/path/to/tlsCert.pem",
76
76
  // ...
77
77
  }
78
78
  ```
@@ -85,38 +85,41 @@ or command line options may be necessary to use an invalid TLS certificate.
85
85
 
86
86
  ## Controlling which network interfaces are listened to
87
87
 
88
- By default, jasmine-browser-runner listens to all available network interfaces.
89
- You might need that if you're using a remote grid such as Saucelabs. If you
90
- don't need that, you can improve security by listening only to localhost.
88
+ **Note: This behavior differs between 2.x and 3.x. If you are using 2.x, please
89
+ consult the README for the version you're using.**
90
+
91
+ By default, jasmine-browser-runner listens to the network interface that
92
+ corresponds to localhost. To listen on a different interface, set `listenAddress`
93
+ to the corresponding hostname or IP address. To listen on all available network
94
+ interfaces, set `listenAddress` to `"*"`. You might need to do that if you're
95
+ using a remote grid such as Saucelabs.
91
96
 
92
97
  ```javascript
93
98
  export default {
94
99
  // ...
95
- "listenAddress": "localhost",
100
+ listenAddress: "*",
96
101
  // ...
97
102
  }
98
103
  ```
99
104
 
100
105
  ## Hostname support
101
106
 
107
+ **Note: This behavior differs between 2.x and 3.x. If you are using 2.x, please
108
+ consult the README for the version you're using.**
109
+
102
110
  If you need to access your tests via a specific hostname, you can do that by
103
111
  setting the `hostname` configuration property:
104
112
 
105
113
  ```javascript
106
114
  export default {
107
115
  // ...
108
- "hostname": "mymachine.mynetwork",
116
+ hostname: "mymachine.mynetwork",
109
117
  // ...
110
118
  }
111
119
  ```
112
120
 
113
121
  This can also be specified on the command line with `--hostname`.
114
122
 
115
- Setting `hostname` but not `listenAddress` has the same effect as setting
116
- `listenAddress` to the same value as `hostname`. If you need to set a hostname
117
- but retain the default behavior of listening to all network interfaces, you can
118
- do that by setting `listenAddress` to `"*"`.
119
-
120
123
  There are a few important caveats when doing this:
121
124
 
122
125
  1. This name must either be an IP or a name that can really be resolved on your
@@ -145,7 +148,7 @@ If a source, spec, or helper file's name ends in `.mjs`, it will be loaded as
145
148
  an ES module rather than a regular script. Note that ES modules can only be
146
149
  loaded from other ES modules. So if your source files are ES modules, your
147
150
  spec files need to be ES modules too. Want to use a different extension than
148
- `.esm`? Just set the `esmFilenameExtension` config property, e.g.
151
+ `.mjs`? Just set the `esmFilenameExtension` config property, e.g.
149
152
  `"esmFilenameExtension": ".js"`.
150
153
 
151
154
  To allow spec files to import source files via relative paths, set the `specDir`
@@ -153,8 +156,11 @@ config field to something that's high enough up to include both spec and source
153
156
  files, and set `srcFiles` to `[]`. You can autogenerate such a configuration by
154
157
  running `npx jasmine-browser-runner init --esm`.
155
158
 
159
+ If you want to load ES module source directly on load instead of loading it from
160
+ the corresponding spec, set the `modulesWithSideEffectsInSrcFiles` config property to `true`.
161
+
156
162
  If you have specs or helper files that use top-level await, set the
157
- `enableTopLevelAwait` config property is set to `true`.
163
+ `enableTopLevelAwait` config property to `true`.
158
164
 
159
165
  [Import maps](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap)
160
166
  are also supported:
@@ -294,7 +300,7 @@ export default {
294
300
  "url": "https://ondemand.saucelabs.com/wd/hub",
295
301
  "platformName": "macOS 12",
296
302
  "sauce:options": {
297
- "tunnel-identifier": "tunnel ID",
303
+ "tunnelName": "the same tunnel name that was provided to Sauce Connect",
298
304
  "userName": "your Saucelabs username",
299
305
  "accessKey": "your Saucelabs access key"
300
306
  }
@@ -326,7 +332,7 @@ import jasmineBrowser from 'jasmine-browser-runner';
326
332
  import config from './spec/support/jasmine-browser.mjs';
327
333
 
328
334
  config.projectBaseDir = path.resolve('some/path');
329
- jasmineBrowser.startServer(config, { port: 4321 });
335
+ jasmineBrowser.startServer(config);
330
336
 
331
337
 
332
338
  // CommonJS
@@ -336,7 +342,7 @@ const jasmineBrowser = require('jasmine-browser-runner');
336
342
  import('./spec/support/jasmine-browser.mjs')
337
343
  .then(function({default: config}) {
338
344
  config.projectBaseDir = path.resolve('some/path');
339
- jasmineBrowser.startServer(config, { port: 4321 });
345
+ jasmineBrowser.startServer(config);
340
346
  });
341
347
  ```
342
348
 
@@ -347,8 +353,8 @@ Firefox, and Microsoft Edge) as well as Node.
347
353
 
348
354
  | Environment | Supported versions |
349
355
  |-------------------|----------------------------|
350
- | Node | 18, 20, 22 |
351
- | Safari | 15*, 16, 17 |
356
+ | Node | 20, 22, 24 |
357
+ | Safari | 16*, 17* |
352
358
  | Chrome | Evergreen |
353
359
  | Firefox | Evergreen, 102*, 115*, 128 |
354
360
  | Edge | Evergreen |
@@ -359,13 +365,13 @@ browsers, as well as older & newer versions of some supported browsers, are
359
365
  likely to work. However, jasmine-browser-runner isn't tested against them and
360
366
  they aren't actively supported.
361
367
 
362
- \* Environments that are past end of life are supported on a best-effort
363
- basis. They may be dropped in a future minor release if continued support
364
- becomes impractical.
368
+ \* Supported on a best-effort basis. Support for these versions may be dropped
369
+ if it becomes impractical, and bugs affecting only these versions may not be
370
+ treated as release blockers.
365
371
 
366
372
 
367
373
  To find out what environments work with a particular Jasmine release, see the [release notes](https://github.com/jasmine/jasmine/tree/main/release_notes).
368
374
 
369
375
  Copyright (c) 2019 Pivotal Labs<br>
370
- Copyright (c) 2020-2024 The Jasmine developers<br>
376
+ Copyright (c) 2020-2025 The Jasmine developers<br>
371
377
  This software is licensed under the MIT License.
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- const ConsoleReporter = require('./lib/console_reporter');
1
+ const ConsoleReporter = require('@jasminejs/reporters/console');
2
2
  const webdriverModule = require('./lib/webdriver');
3
3
  const Server = require('./lib/server');
4
4
  const Runner = require('./lib/runner');
@@ -11,9 +11,12 @@ async function createReporters(options, deps) {
11
11
  deps = deps || {};
12
12
  const ReporterCtor = deps.ConsoleReporter || ConsoleReporter;
13
13
  const consoleReporter = new ReporterCtor();
14
- consoleReporter.setOptions({
14
+ consoleReporter.configure({
15
15
  color: options.color,
16
16
  alwaysListPendingSpecs: options.alwaysListPendingSpecs,
17
+ randomSeedReproductionCmd(seed) {
18
+ return 'jasmine-browser-runner runSpecs --seed=' + seed;
19
+ },
17
20
  });
18
21
  result.push(consoleReporter);
19
22
  }
@@ -11,6 +11,8 @@ export default {
11
11
  "spec/helpers/**/*.?(m)js"
12
12
  ],
13
13
  esmFilenameExtension: ".mjs",
14
+ // Set to true if you need to load module src files instead of loading via the spec files.
15
+ modulesWithSideEffectsInSrcFiles: false,
14
16
  // Allows the use of top-level await in src/spec/helper files. This is off by
15
17
  // default because it makes files load more slowly.
16
18
  enableTopLevelAwait: false,
package/lib/runner.js CHANGED
@@ -8,11 +8,6 @@ function getBatch(driver) {
8
8
  'if (results[i][1].passedExpectations) {\n' +
9
9
  'expectations = expectations.concat(results[i][1].passedExpectations);\n' +
10
10
  '}\n' +
11
- 'for (var j = 0; j < expectations.length; j++) {\n' +
12
- 'var expectation = expectations[j];\n' +
13
- "try { JSON.stringify(expectation.expected); } catch (e) { expectation.expected = '<circular expected>'; }\n" +
14
- "try { JSON.stringify(expectation.actual); } catch (e) { expectation.actual = '<circular actual>'; }\n" +
15
- '}\n' +
16
11
  '}\n' +
17
12
  'return results;'
18
13
  );
package/lib/server.js CHANGED
@@ -82,8 +82,11 @@ class Server {
82
82
  this.options.srcFiles,
83
83
  '/__src__'
84
84
  ).filter(url => {
85
- // Exclude ES modules. These will be loaded by other ES modules.
86
- return !url.endsWith(this.options.esmFilenameExtension);
85
+ // Exclude ES modules by default. These will be loaded by other ES modules.
86
+ return (
87
+ this.options.modulesWithSideEffectsInSrcFiles ||
88
+ !url.endsWith(this.options.esmFilenameExtension)
89
+ );
87
90
  });
88
91
  const helperUrls = this.getUrls(
89
92
  this.options.specDir,
@@ -111,14 +114,22 @@ class Server {
111
114
  const resultMap = {};
112
115
 
113
116
  if (importMap.imports) {
114
- resultMap.imports = reifyRawSpecifierMap(importMap.imports);
117
+ resultMap.imports = reifyRawSpecifierMap(
118
+ importMap.imports,
119
+ this.options,
120
+ this.projectBaseDir
121
+ );
115
122
  }
116
123
 
117
124
  if (importMap.scopes) {
118
125
  resultMap.scopes = {};
119
126
 
120
127
  for (const [scope, map] of Object.entries(importMap.scopes)) {
121
- resultMap.scopes[scope] = reifyRawSpecifierMap(map);
128
+ resultMap.scopes[scope] = reifyRawSpecifierMap(
129
+ map,
130
+ this.options,
131
+ this.projectBaseDir
132
+ );
122
133
  }
123
134
  }
124
135
 
@@ -191,6 +202,8 @@ class Server {
191
202
  esmFilenameExtension: self.options.esmFilenameExtension,
192
203
  importMap: self.importMap(),
193
204
  enableTopLevelAwait: self.options.enableTopLevelAwait || false,
205
+ modulesWithSideEffectsInSrcFiles:
206
+ self.options.modulesWithSideEffectsInSrcFiles || false,
194
207
  })
195
208
  );
196
209
  } catch (error) {
@@ -372,14 +385,32 @@ function unWindows(filePath) {
372
385
  // paths that the run.html.ejs file will contain. The `rawSpecifierMap` is not
373
386
  // the entire importMap. It is a key/value map that may be the "imports" value
374
387
  // or an individual map inside of "scopes"[someScope].
375
- function reifyRawSpecifierMap(rawSpecifierMap) {
388
+ function reifyRawSpecifierMap(rawSpecifierMap, options, projectBaseDir) {
376
389
  const concreteMap = {};
390
+ const absoluteModuleRootDir = options.importMap.moduleRootDir
391
+ ? path.resolve(projectBaseDir, options.importMap.moduleRootDir)
392
+ : projectBaseDir;
377
393
 
378
394
  for (const [key, value] of Object.entries(rawSpecifierMap)) {
379
395
  if (value.match(/^https?:\/\//)) {
380
396
  concreteMap[key] = value; // pass through unchanged
381
- } else {
397
+ continue;
398
+ }
399
+
400
+ const absolutePath = path.join(absoluteModuleRootDir, value);
401
+ const endsWithSeparator = value.endsWith('/') || value.endsWith('\\');
402
+
403
+ const resolvedSrcDir = path.join(absoluteModuleRootDir, options.srcDir);
404
+ const relativeFromSrcDir = path.relative(resolvedSrcDir, absolutePath);
405
+
406
+ if (relativeFromSrcDir.startsWith('..')) {
382
407
  concreteMap[key] = './' + unWindows(path.join('__moduleRoot__', value));
408
+ } else {
409
+ // We are under srcDir: Reuse existing __src__ path
410
+ concreteMap[key] =
411
+ './' +
412
+ unWindows(path.join('__src__', relativeFromSrcDir)) +
413
+ (endsWithSeparator ? '/' : '');
383
414
  }
384
415
  }
385
416
 
@@ -21,16 +21,31 @@ function BatchReporter() {
21
21
  };
22
22
 
23
23
  this.jasmineDone = function(info) {
24
+ deleteExpectedAndActual(info);
24
25
  events.push(['jasmineDone', info]);
25
26
  };
26
27
 
27
28
  this.suiteDone = function(info) {
29
+ deleteExpectedAndActual(info);
28
30
  events.push(['suiteDone', info]);
29
31
  };
30
32
 
31
33
  this.specDone = function(info) {
34
+ deleteExpectedAndActual(info);
32
35
  events.push(['specDone', info]);
33
36
  };
37
+
38
+ function deleteExpectedAndActual(info) {
39
+ for (const e of info.failedExpectations) {
40
+ // Delete expected and actual. Not all JS objects are serializable, we
41
+ // don't have a reliable way to determine what Selenium can and can't
42
+ // serialize, not everything that's serializable survives the process
43
+ // intact, and there are no known reporters that use the expected or
44
+ // actual properties of expectation results.
45
+ delete e.expected;
46
+ delete e.actual;
47
+ }
48
+ }
34
49
  }
35
50
 
36
51
  window.batchReporter = new BatchReporter();
@@ -1,24 +1,5 @@
1
1
  /* eslint-env browser, jasmine */
2
2
 
3
- window._jasmine_loadEsModule = function(src) {
4
- const script = document.createElement('script');
5
- script.type = 'module';
6
-
7
- // Safari reports syntax errors in ES modules as a script element error
8
- // event rather than a global error event. Rethrow so that Jasmine can
9
- // pick it up and fail the suite.
10
- script.addEventListener('error', function(event) {
11
- const msg =
12
- 'An error occurred while loading ' +
13
- src +
14
- '. Check the browser console for details.';
15
- throw new Error(msg);
16
- });
17
-
18
- script.src = src;
19
- document.head.appendChild(script);
20
- };
21
-
22
3
  window._jasmine_loadWithTopLevelAwaitSupport = async function(
23
4
  scriptUrls,
24
5
  esmFilenameExtension
package/lib/types.js CHANGED
@@ -153,6 +153,16 @@
153
153
  * @type boolean | undefined
154
154
  * @default false
155
155
  */
156
+ /**
157
+ * If set to true jasmine loads also ES Modules which are included in SrcFiles.
158
+ * This option is off by default because in most scenarios it is better to load the
159
+ * module under test from the test itself.
160
+ * But if the module has wanted side effects (like for example polyfills) you can
161
+ * interleave ES module and classic scripts in your SrcFiles.
162
+ * @name Configuration#modulesWithSideEffectsInSrcFiles
163
+ * @type boolean | undefined
164
+ * @default false
165
+ */
156
166
  /**
157
167
  * <p>An optional map from paths to Express application middleware to mount on
158
168
  * those paths. This can be used to serve static files, proxy requests to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jasmine-browser-runner",
3
- "version": "3.0.0-beta.2",
3
+ "version": "4.0.0-beta.0",
4
4
  "description": "Serve and run your Jasmine specs in a browser",
5
5
  "bin": "bin/jasmine-browser-runner",
6
6
  "exports": "./index.js",
@@ -17,7 +17,7 @@
17
17
  "lib/examples/default_esm_config.mjs"
18
18
  ],
19
19
  "scripts": {
20
- "posttest": "eslint bin/* lib spec index.js --ignore-path=.styleIgnore && prettier --check --ignore-path=.styleIgnore \"lib/**/*.js\" \"spec/**/*.js\" index.js",
20
+ "posttest": "eslint && prettier --check --ignore-path=.styleIgnore \"lib/**/*.js\" \"spec/**/*.js\" index.js",
21
21
  "test": "jasmine",
22
22
  "cleanup": "prettier --write --ignore-path=.styleIgnore \"lib/**/*.js\" \"spec/**/*.js\" index.js"
23
23
  },
@@ -37,94 +37,27 @@
37
37
  },
38
38
  "homepage": "https://github.com/jasmine/jasmine-browser-runner#readme",
39
39
  "dependencies": {
40
+ "@jasminejs/reporters": "^0.1.0",
40
41
  "ejs": "^3.1.6",
41
42
  "express": "^5.0.0",
42
- "glob": "^10.0.0",
43
+ "glob": "^10.2.2 || ^11.0.3 || ^12.0.0 || ^13.0.0",
43
44
  "selenium-webdriver": "^4.12.0"
44
45
  },
45
46
  "peerDependencies": {
46
- "jasmine-core": "^5.5.0"
47
+ "jasmine-core": "6.0.0-beta.1"
47
48
  },
48
49
  "devDependencies": {
49
- "eslint": "^8.50.0",
50
- "eslint-plugin-jasmine": "^4.1.3",
51
- "jasmine": "^5.0.0",
52
- "jasmine-core": "^5.5.0",
53
- "prettier": "^1.17.1",
54
- "shelljs": "^0.8.3",
55
- "temp": "^0.9.4"
50
+ "@eslint/eslintrc": "^3.3.1",
51
+ "@eslint/js": "^9.29.0",
52
+ "eslint": "^9.29.0",
53
+ "eslint-plugin-jasmine": "^4.2.2",
54
+ "globals": "^16.2.0",
55
+ "jasmine": "github:jasmine/jasmine-npm",
56
+ "jasmine-core": "6.0.0-beta.1",
57
+ "prettier": "^1.17.1"
56
58
  },
57
59
  "prettier": {
58
60
  "singleQuote": true,
59
61
  "trailingComma": "es5"
60
- },
61
- "eslintConfig": {
62
- "env": {
63
- "commonjs": true,
64
- "es6": true,
65
- "node": true
66
- },
67
- "extends": "eslint:recommended",
68
- "globals": {
69
- "Atomics": "readonly",
70
- "SharedArrayBuffer": "readonly",
71
- "expectAsync": "readonly"
72
- },
73
- "parserOptions": {
74
- "ecmaVersion": 2022
75
- },
76
- "plugins": [
77
- "jasmine"
78
- ],
79
- "rules": {
80
- "quotes": [
81
- "error",
82
- "single",
83
- {
84
- "avoidEscape": true
85
- }
86
- ],
87
- "no-unused-vars": [
88
- "error",
89
- {
90
- "args": "none"
91
- }
92
- ],
93
- "block-spacing": "error",
94
- "comma-dangle": [
95
- "error",
96
- {
97
- "arrays": "always-multiline",
98
- "objects": "always-multiline",
99
- "imports": "always-multiline",
100
- "exports": "always-multiline",
101
- "functions": "never"
102
- }
103
- ],
104
- "eqeqeq": "error",
105
- "func-call-spacing": [
106
- "error",
107
- "never"
108
- ],
109
- "key-spacing": "error",
110
- "no-tabs": "error",
111
- "no-trailing-spaces": "error",
112
- "no-whitespace-before-property": "error",
113
- "semi": [
114
- "error",
115
- "always"
116
- ],
117
- "space-before-blocks": "error",
118
- "no-console": "off",
119
- "no-var": "error"
120
- },
121
- "overrides": [
122
- {
123
- "files": "spec/**/*.js",
124
- "env": {
125
- "jasmine": true
126
- }
127
- }
128
- ]
129
62
  }
130
63
  }
package/run.html.ejs CHANGED
@@ -29,7 +29,9 @@
29
29
  <% } else { %>
30
30
  <% userJsFiles.forEach(function(jsFile) { %>
31
31
  <% if (jsFile.endsWith(esmFilenameExtension)) { %>
32
- <script type="module">_jasmine_loadEsModule('<%= jsFile %>')</script>
32
+ <script src="<%= jsFile %>" type="module"></script>
33
+ <% } else if (modulesWithSideEffectsInSrcFiles) { %>
34
+ <script src="<%= jsFile %>" type="text/javascript" defer></script>
33
35
  <% } else { %>
34
36
  <script src="<%= jsFile %>" type="text/javascript"></script>
35
37
  <% } %>
@@ -1,317 +0,0 @@
1
- const util = require('util');
2
- module.exports = exports = ConsoleReporter;
3
-
4
- /**
5
- * @classdesc A reporter that prints spec and suite results to the console.
6
- * A ConsoleReporter is installed unless {@link Configuration#useConsoleReporter}
7
- * is set to false.
8
- *
9
- * @constructor
10
- * @example
11
- * const {ConsoleReporter} = require('jasmine');
12
- * const reporter = new ConsoleReporter();
13
- */
14
- function ConsoleReporter() {
15
- let print = function() {
16
- process.stdout.write(util.format.apply(this, arguments));
17
- },
18
- showColors = true,
19
- jasmineCorePath = null,
20
- specCount,
21
- executableSpecCount,
22
- failureCount,
23
- failedSpecs = [],
24
- pendingSpecs = [],
25
- alwaysListPendingSpecs = true,
26
- ansi = {
27
- green: '\x1B[32m',
28
- red: '\x1B[31m',
29
- yellow: '\x1B[33m',
30
- none: '\x1B[0m',
31
- },
32
- failedSuites = [],
33
- stackFilter = defaultStackFilter;
34
-
35
- /**
36
- * Configures the reporter.
37
- * @function
38
- * @name ConsoleReporter#setOptions
39
- * @param {ConsoleReporterOptions} options
40
- */
41
- this.setOptions = function(options) {
42
- if (options.print) {
43
- print = options.print;
44
- }
45
-
46
- /**
47
- * @interface ConsoleReporterOptions
48
- */
49
- /**
50
- * Whether to colorize the output
51
- * @name ConsoleReporterOptions#color
52
- * @type Boolean|undefined
53
- * @default true
54
- */
55
- if (options.color !== undefined) {
56
- showColors = options.color;
57
- }
58
-
59
- if (options.jasmineCorePath) {
60
- jasmineCorePath = options.jasmineCorePath;
61
- }
62
- if (options.stackFilter) {
63
- stackFilter = options.stackFilter;
64
- }
65
- if (options.alwaysListPendingSpecs !== undefined) {
66
- alwaysListPendingSpecs = options.alwaysListPendingSpecs;
67
- }
68
- };
69
-
70
- this.jasmineStarted = function(options) {
71
- specCount = 0;
72
- executableSpecCount = 0;
73
- failureCount = 0;
74
- if (options && options.order && options.order.random) {
75
- print('Randomized with seed ' + options.order.seed);
76
- printNewline();
77
- }
78
- print('Started');
79
- printNewline();
80
- };
81
-
82
- this.jasmineDone = function(result) {
83
- if (result.failedExpectations) {
84
- failureCount += result.failedExpectations.length;
85
- }
86
-
87
- printNewline();
88
- printNewline();
89
- if (failedSpecs.length > 0) {
90
- print('Failures:');
91
- }
92
- for (let i = 0; i < failedSpecs.length; i++) {
93
- specFailureDetails(failedSpecs[i], i + 1);
94
- }
95
-
96
- for (let i = 0; i < failedSuites.length; i++) {
97
- suiteFailureDetails(failedSuites[i]);
98
- }
99
-
100
- if (
101
- result &&
102
- result.failedExpectations &&
103
- result.failedExpectations.length > 0
104
- ) {
105
- suiteFailureDetails(result);
106
- }
107
-
108
- if (alwaysListPendingSpecs || result.overallStatus === 'passed') {
109
- if (pendingSpecs.length > 0) {
110
- print('Pending:');
111
- }
112
- for (let i = 0; i < pendingSpecs.length; i++) {
113
- pendingSpecDetails(pendingSpecs[i], i + 1);
114
- }
115
- }
116
-
117
- if (specCount > 0) {
118
- printNewline();
119
-
120
- if (executableSpecCount !== specCount) {
121
- print(
122
- 'Ran ' +
123
- executableSpecCount +
124
- ' of ' +
125
- specCount +
126
- plural(' spec', specCount)
127
- );
128
- printNewline();
129
- }
130
- let specCounts =
131
- executableSpecCount +
132
- ' ' +
133
- plural('spec', executableSpecCount) +
134
- ', ' +
135
- failureCount +
136
- ' ' +
137
- plural('failure', failureCount);
138
-
139
- if (pendingSpecs.length) {
140
- specCounts +=
141
- ', ' +
142
- pendingSpecs.length +
143
- ' pending ' +
144
- plural('spec', pendingSpecs.length);
145
- }
146
-
147
- print(specCounts);
148
- } else {
149
- print('No specs found');
150
- }
151
-
152
- printNewline();
153
- const seconds = result ? result.totalTime / 1000 : 0;
154
- print('Finished in ' + seconds + ' ' + plural('second', seconds));
155
- printNewline();
156
-
157
- if (result && result.overallStatus === 'incomplete') {
158
- print('Incomplete: ' + result.incompleteReason);
159
- printNewline();
160
- }
161
-
162
- if (result && result.order && result.order.random) {
163
- print('Randomized with seed ' + result.order.seed);
164
- print(
165
- ' (jasmine-browser-runner runSpecs --seed=' + result.order.seed + ')'
166
- );
167
- printNewline();
168
- }
169
- };
170
-
171
- this.specDone = function(result) {
172
- specCount++;
173
-
174
- if (result.status === 'pending') {
175
- pendingSpecs.push(result);
176
- executableSpecCount++;
177
- print(colored('yellow', '*'));
178
- return;
179
- }
180
-
181
- if (result.status === 'passed') {
182
- executableSpecCount++;
183
- print(colored('green', '.'));
184
- return;
185
- }
186
-
187
- if (result.status === 'failed') {
188
- failureCount++;
189
- failedSpecs.push(result);
190
- executableSpecCount++;
191
- print(colored('red', 'F'));
192
- }
193
- };
194
-
195
- this.suiteDone = function(result) {
196
- if (result.failedExpectations && result.failedExpectations.length > 0) {
197
- failureCount++;
198
- failedSuites.push(result);
199
- }
200
- };
201
-
202
- return this;
203
-
204
- function printNewline() {
205
- print('\n');
206
- }
207
-
208
- function colored(color, str) {
209
- return showColors ? ansi[color] + str + ansi.none : str;
210
- }
211
-
212
- function plural(str, count) {
213
- return count === 1 ? str : str + 's';
214
- }
215
-
216
- function repeat(thing, times) {
217
- const arr = [];
218
- for (let i = 0; i < times; i++) {
219
- arr.push(thing);
220
- }
221
- return arr;
222
- }
223
-
224
- function indent(str, spaces) {
225
- const lines = (str || '').split('\n');
226
- const newArr = [];
227
- for (let i = 0; i < lines.length; i++) {
228
- newArr.push(repeat(' ', spaces).join('') + lines[i]);
229
- }
230
- return newArr.join('\n');
231
- }
232
-
233
- function defaultStackFilter(stack) {
234
- if (!stack) {
235
- return '';
236
- }
237
-
238
- const filteredStack = stack
239
- .split('\n')
240
- .filter(function(stackLine) {
241
- return stackLine.indexOf(jasmineCorePath) === -1;
242
- })
243
- .join('\n');
244
- return filteredStack;
245
- }
246
-
247
- function specFailureDetails(result, failedSpecNumber) {
248
- printNewline();
249
- print(failedSpecNumber + ') ');
250
- print(result.fullName);
251
- printFailedExpectations(result);
252
-
253
- if (result.debugLogs) {
254
- printNewline();
255
- print(indent('Debug logs:', 2));
256
- printNewline();
257
-
258
- for (const entry of result.debugLogs) {
259
- print(indent(`${entry.timestamp}ms: ${entry.message}`, 4));
260
- printNewline();
261
- }
262
- }
263
- }
264
-
265
- function suiteFailureDetails(result) {
266
- printNewline();
267
- print('Suite error: ' + result.fullName);
268
- printFailedExpectations(result);
269
- }
270
-
271
- function printFailedExpectations(result) {
272
- for (let i = 0; i < result.failedExpectations.length; i++) {
273
- const failedExpectation = result.failedExpectations[i];
274
- printNewline();
275
- print(indent('Message:', 2));
276
- printNewline();
277
- print(colored('red', indent(failedExpectation.message, 4)));
278
- printNewline();
279
- print(indent('Stack:', 2));
280
- printNewline();
281
- print(indent(stackFilter(failedExpectation.stack), 4));
282
- }
283
-
284
- // When failSpecWithNoExpectations = true and a spec fails because of no expectations found,
285
- // jasmine-core reports it as a failure with no message.
286
- //
287
- // Therefore we assume that when there are no failed or passed expectations,
288
- // the failure was because of our failSpecWithNoExpectations setting.
289
- //
290
- // Same logic is used by jasmine.HtmlReporter, see https://github.com/jasmine/jasmine/blob/main/src/html/HtmlReporter.js
291
- if (
292
- result.failedExpectations.length === 0 &&
293
- result.passedExpectations.length === 0
294
- ) {
295
- printNewline();
296
- print(indent('Message:', 2));
297
- printNewline();
298
- print(colored('red', indent('Spec has no expectations', 4)));
299
- }
300
-
301
- printNewline();
302
- }
303
-
304
- function pendingSpecDetails(result, pendingSpecNumber) {
305
- printNewline();
306
- printNewline();
307
- print(pendingSpecNumber + ') ');
308
- print(result.fullName);
309
- printNewline();
310
- let pendingReason = 'No reason given';
311
- if (result.pendingReason && result.pendingReason !== '') {
312
- pendingReason = result.pendingReason;
313
- }
314
- print(indent(colored('yellow', pendingReason), 2));
315
- printNewline();
316
- }
317
- }