jasmine-browser-runner 2.3.0 → 2.4.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,4 +1,5 @@
1
- Copyright (c) 2019-2019 Pivotal Labs
1
+ Copyright (c) 2019 Pivotal Labs
2
+ Copyright (c) 2020-2024 The Jasmine developers
2
3
 
3
4
  Permission is hereby granted, free of charge, to any person obtaining
4
5
  a copy of this software and associated documentation files (the
package/README.md CHANGED
@@ -24,7 +24,9 @@ change the spec files, helpers, and source files that are loaded, specify the
24
24
  [Jasmine env's configuration](https://jasmine.github.io/api/edge/Configuration.html),
25
25
  and more.
26
26
 
27
- You can also use the `--config` option to specify a different file. This file can be a JSON file or a javascript file that exports a object that looks like the JSON above.
27
+ You can also use the `--config` option to specify a different file.
28
+ This file can be a JSON file or a javascript file that exports a object that looks like the JSON above.
29
+ More information about the configuration can be found at the runner [documentation website](https://jasmine.github.io/api/browser-runner/edge/Configuration.html).
28
30
 
29
31
  To start the server so that you can run the specs interactively (particularly
30
32
  useful for debugging):
@@ -52,6 +54,63 @@ To use a browser other than Firefox, add a `browser` field to
52
54
  Its value can be `"firefox"`, `"headlessFirefox"`, `"safari"`,
53
55
  `"MicrosoftEdge"`, `"chrome"`, or `"headlessChrome"`.
54
56
 
57
+ ## TLS support
58
+
59
+ To serve tests over HTTPS instead of HTTP, supply a path to a TLS cert and key
60
+ in PEM format in `jasmine-browser.json`:
61
+
62
+ ```javascript
63
+ {
64
+ // ...
65
+ "tlsKey": "/path/to/tlsKey.pem",
66
+ "tlsCert": "/path/to/tlsCert.pem",
67
+ // ...
68
+ }
69
+ ```
70
+
71
+ These can also be specified on the command line with `--tlsKey` and `--tlsCert`.
72
+
73
+ Note that if you are using a self-signed or otherwise invalid certificate, the
74
+ browser will not allow the connection by default. Additional browser configs
75
+ or command line options may be necessary to use an invalid TLS certificate.
76
+
77
+ ## Hostname support
78
+
79
+ To serve tests on a specific interface or IP, you can specify a hostname in
80
+ `jasmine-browser.json`:
81
+
82
+ ```javascript
83
+ {
84
+ // ...
85
+ "hostname": "mymachine.mynetwork",
86
+ // ...
87
+ }
88
+ ```
89
+
90
+ This can also be specified on the command line with `--hostname`.
91
+
92
+ There are a few important caveats when doing this:
93
+
94
+ 1. This name must either be an IP or a name that can really be resolved on your
95
+ system. Otherwise, you will get `ENOTFOUND` errors.
96
+ 2. This name must correspond to an IP assigned to one of the network interfaces
97
+ on your system. Otherwise, you will get `EADDRNOTAVAIL` errors.
98
+ 3. If this name matches the [HSTS preload list](https://hstspreload.org/),
99
+ browsers will force the connection to HTTPS. If you are not using TLS, you
100
+ will get an error that says `The browser tried to speak HTTPS to an HTTP
101
+ server. Misconfiguration is likely.` You may be surprised by the names on
102
+ that preload list, which include such favorite local network hostnames as:
103
+ - dev
104
+ - foo
105
+ - app
106
+ - nexus
107
+ - windows
108
+ - office
109
+ - dad
110
+ You can see a full list in [Chromium source](https://raw.githubusercontent.com/chromium/chromium/main/net/http/transport_security_state_static.json)
111
+ or query your hostname at the [HSTS preload site](https://hstspreload.org/).
112
+
113
+
55
114
  ## ES module support
56
115
 
57
116
  If a source, spec, or helper file's name ends in `.mjs`, it will be loaded as
@@ -93,7 +152,7 @@ whether you use the Asset Pipeline or Webpacker.
93
152
 
94
153
  ### Webpacker
95
154
 
96
- 1. Run `yarn add --dev jasmine-browser-runner`.
155
+ 1. Run `yarn add --dev jasmine-browser-runner jasmine-core`.
97
156
  2. Run `npx jasmine-browser-runner init`.
98
157
  3. Edit `spec/support/jasmine-browser.json` as follows:
99
158
  ```
@@ -224,38 +283,11 @@ exception of the `url` will be used as `capabilties` sent to the grid hub url.
224
283
  if no value is specified for the `url` then a default of
225
284
  `http://localhost:4445/wd/hub` is used.
226
285
 
227
- ## Saucelabs support (legacy)
228
- > NOTE: the below configuration format only supports using Saucelabs in the US. if connecting from the EU, please use the above specifying a `url` value specific to your region (e.g. `https://ondemand.eu-central-1.saucelabs.com:443/wd/hub`) to avoid a connection error of `WebDriverError: This user is unauthorized to the region. Please try another region, or contact customer support.`
229
-
230
- > WARNING: the below configuration format may be removed in favour of using the above in the future so it is advised that you migrate to the above
231
-
232
- jasmine-browser-runner can run your Jasmine specs on [Saucelabs](https://saucelabs.com/).
233
- To use Saucelabs, set `browser.name`, `browser.useSauce`, and `browser.sauce`
234
- in your config file as follows:
235
-
236
- ```json
237
- {
238
- // ...
239
- "browser": {
240
- "name": "safari",
241
- "useSauce": true,
242
- "sauce": {
243
- "browserVersion": "13",
244
- "os": "OS X 10.15",
245
- "tags": ["your tag", "your other tag"],
246
- "tunnelIdentifier": "tunnel ID",
247
- "username": "your Saucelabs username",
248
- "accessKey": "your Saucelabs access key"
249
- }
250
- }
251
- }
252
- ```
253
-
254
- All properties of `browser.sauce` are optional except for `username` and
255
- `accessKey`. It's best to omit `browser.sauce.os` unless you need to run on a
256
- specific operating system. Setting `browser.sauce.tunnelIdentifier` is strongly
257
- recommended unless you're sure that your account will never have more than one
258
- active tunnel.
286
+ It's common for remote grids to support only a limited set of ports. Check your
287
+ remote grid's documentation to make sure that the port you're using is
288
+ supported. When using a remote grid, `jasmine-browser-runner` will run on port
289
+ 5555 unless you use the `--port` command line option or specify a port in the
290
+ second parameter to`startServer`.
259
291
 
260
292
  ## Want more control?
261
293
 
@@ -277,9 +309,9 @@ Firefox, and Microsoft Edge) as well as Node.
277
309
  | Environment | Supported versions |
278
310
  |-------------------|------------------------|
279
311
  | Node | 18, 20 |
280
- | Safari | 15-16 |
312
+ | Safari | 15-17 |
281
313
  | Chrome | Evergreen |
282
- | Firefox | Evergreen, 102 |
314
+ | Firefox | Evergreen, 102, 115 |
283
315
  | Edge | Evergreen |
284
316
 
285
317
  For evergreen browsers, each version of jasmine-browser-runner is tested against
@@ -290,3 +322,6 @@ they aren't actively supported.
290
322
 
291
323
  To find out what environments work with a particular Jasmine release, see the [release notes](https://github.com/jasmine/jasmine/tree/main/release_notes).
292
324
 
325
+ Copyright (c) 2019 Pivotal Labs<br>
326
+ Copyright (c) 2020-2024 The Jasmine developers<br>
327
+ This software is licensed under the MIT License.
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const path = require('path'),
4
- jasmineCore = require('../lib/jasmineCore'),
5
- Command = require('../lib/command'),
6
- jasmineBrowser = require('../index.js');
3
+ const path = require('path');
4
+ const jasmineCore = require('../lib/jasmineCore');
5
+ const Command = require('../lib/command');
6
+ const jasmineBrowser = require('../index.js');
7
7
  const UsageError = require('../lib/usage_error');
8
8
 
9
- const command = new Command({
9
+ const command = new Command({
10
10
  baseDir: path.resolve(),
11
11
  jasmineCore,
12
12
  jasmineBrowser,
package/index.js CHANGED
@@ -1,8 +1,8 @@
1
- const ConsoleReporter = require('./lib/console_reporter'),
2
- webdriverModule = require('./lib/webdriver'),
3
- Server = require('./lib/server'),
4
- Runner = require('./lib/runner'),
5
- ModuleLoader = require('./lib/moduleLoader');
1
+ const ConsoleReporter = require('./lib/console_reporter');
2
+ const webdriverModule = require('./lib/webdriver');
3
+ const Server = require('./lib/server');
4
+ const Runner = require('./lib/runner');
5
+ const ModuleLoader = require('./lib/moduleLoader');
6
6
 
7
7
  async function createReporters(options, deps) {
8
8
  const result = [];
@@ -84,16 +84,10 @@ module.exports = {
84
84
  const useRemote = options.browser && options.browser.useRemoteSeleniumGrid;
85
85
  let portRequest;
86
86
 
87
- if (useSauce || useRemote) {
88
- if (options.port) {
89
- throw new Error(
90
- "Can't specify a port when browser.useSauce or browser.useRemoteSeleniumGrid is true"
91
- );
92
- }
93
-
94
- portRequest = 5555;
95
- } else if (options.port) {
87
+ if (options.port) {
96
88
  portRequest = options.port;
89
+ } else if (useSauce || useRemote) {
90
+ portRequest = 5555;
97
91
  } else {
98
92
  portRequest = 0;
99
93
  }
@@ -104,7 +98,7 @@ module.exports = {
104
98
  const webdriver = buildWebdriver(options.browser);
105
99
 
106
100
  try {
107
- const host = `http://localhost:${server.port()}`;
101
+ const host = `${server.scheme()}://${server.hostname()}:${server.port()}`;
108
102
  const runner = new RunnerClass({ webdriver, reporters, host });
109
103
 
110
104
  console.log('Running tests in the browser...');
package/lib/command.js CHANGED
@@ -11,6 +11,17 @@ const UsageError = require('./usage_error');
11
11
  const commonOptions = [
12
12
  { name: 'config', type: 'string', description: 'path to the config file' },
13
13
  { name: 'port', type: 'number', description: 'port to run the server on' },
14
+ {
15
+ name: 'tlsCert',
16
+ type: 'string',
17
+ description: 'Path to TLS cert file for https',
18
+ },
19
+ {
20
+ name: 'tlsKey',
21
+ type: 'string',
22
+ description: 'Path to TLS key file for https',
23
+ },
24
+ { name: 'hostname', type: 'string', description: 'hostname to listen on' },
14
25
  ];
15
26
 
16
27
  const subCommands = [
@@ -80,6 +91,11 @@ const subCommands = [
80
91
  type: 'string',
81
92
  description: 'which local browser to launch',
82
93
  },
94
+ {
95
+ name: 'hideDisabled',
96
+ type: 'bool',
97
+ description: 'hide disabled tests',
98
+ },
83
99
  ]),
84
100
  },
85
101
  ];
@@ -171,20 +171,20 @@ function ConsoleReporter() {
171
171
  this.specDone = function(result) {
172
172
  specCount++;
173
173
 
174
- if (result.status == 'pending') {
174
+ if (result.status === 'pending') {
175
175
  pendingSpecs.push(result);
176
176
  executableSpecCount++;
177
177
  print(colored('yellow', '*'));
178
178
  return;
179
179
  }
180
180
 
181
- if (result.status == 'passed') {
181
+ if (result.status === 'passed') {
182
182
  executableSpecCount++;
183
183
  print(colored('green', '.'));
184
184
  return;
185
185
  }
186
186
 
187
- if (result.status == 'failed') {
187
+ if (result.status === 'failed') {
188
188
  failureCount++;
189
189
  failedSpecs.push(result);
190
190
  executableSpecCount++;
@@ -210,7 +210,7 @@ function ConsoleReporter() {
210
210
  }
211
211
 
212
212
  function plural(str, count) {
213
- return count == 1 ? str : str + 's';
213
+ return count === 1 ? str : str + 's';
214
214
  }
215
215
 
216
216
  function repeat(thing, times) {
package/lib/runner.js CHANGED
@@ -76,6 +76,7 @@ function urlParams(runOptions) {
76
76
  random: runOptions.random,
77
77
  seed: runOptions.seed,
78
78
  spec: runOptions.filter,
79
+ hideDisabled: runOptions.hideDisabled,
79
80
  })
80
81
  )
81
82
  );
@@ -100,9 +101,34 @@ class Runner {
100
101
 
101
102
  async run(runOptions) {
102
103
  runOptions = runOptions || {};
103
- await this._options.webdriver.get(
104
- this._options.host + urlParams(runOptions)
105
- );
104
+
105
+ try {
106
+ await this._options.webdriver.get(
107
+ this._options.host + urlParams(runOptions)
108
+ );
109
+ } catch (error) {
110
+ // Looking for Chrome's "WebDriverError: ... net::ERR_SSL_PROTOCOL_ERROR"
111
+ // or Firefox's "WebDriverError: ... about:neterror?e=nssFailure2"
112
+ if (error.name === 'WebDriverError') {
113
+ if (
114
+ error.message.includes('ERR_SSL_PROTOCOL_ERROR') ||
115
+ error.message.includes('about:neterror?e=nssFailure2')
116
+ ) {
117
+ // Show a friendlier error.
118
+ throw new Error(
119
+ 'The browser tried to speak HTTPS to an HTTP server. This ' +
120
+ "probably means that the configured hostname is on the browser's " +
121
+ 'HSTS preload list. Try a different hostname or configure a TLS ' +
122
+ 'certificate. See ' +
123
+ '<https://github.com/jasmine/jasmine-browser-runner?tab=readme-ov-file#hostname-support> ' +
124
+ 'for more information.'
125
+ );
126
+ }
127
+ }
128
+
129
+ // Rethrow the original error.
130
+ throw error;
131
+ }
106
132
 
107
133
  return await runTillEnd(this._options.webdriver, this._options.reporters);
108
134
  }
package/lib/server.js CHANGED
@@ -1,8 +1,10 @@
1
- const defaultExpress = require('express'),
2
- glob = require('glob'),
3
- ejs = require('ejs'),
4
- path = require('path'),
5
- fs = require('fs');
1
+ const defaultExpress = require('express');
2
+ const ejs = require('ejs');
3
+ const fs = require('fs');
4
+ const glob = require('glob');
5
+ const http = require('http');
6
+ const https = require('https');
7
+ const path = require('path');
6
8
 
7
9
  /**
8
10
  * @class Server
@@ -57,11 +59,12 @@ class Server {
57
59
  }
58
60
 
59
61
  getUrls(baseDir, globs, urlRoot) {
60
- return findFiles(path.join(this.projectBaseDir, baseDir), globs || []).map(
61
- function(p) {
62
- return isUrl(p) ? p : unWindows(path.join(urlRoot, p));
63
- }
64
- );
62
+ return findFiles(
63
+ path.resolve(this.projectBaseDir, baseDir),
64
+ globs || []
65
+ ).map(function(p) {
66
+ return isUrl(p) ? p : unWindows(path.join(urlRoot, p));
67
+ });
65
68
  }
66
69
 
67
70
  getSupportFiles() {
@@ -148,11 +151,15 @@ class Server {
148
151
  );
149
152
  app.use(
150
153
  '/__spec__',
151
- this.express.static(path.join(this.projectBaseDir, this.options.specDir))
154
+ this.express.static(
155
+ path.resolve(this.projectBaseDir, this.options.specDir)
156
+ )
152
157
  );
153
158
  app.use(
154
159
  '/__src__',
155
- this.express.static(path.join(this.projectBaseDir, this.options.srcDir))
160
+ this.express.static(
161
+ path.resolve(this.projectBaseDir, this.options.srcDir)
162
+ )
156
163
  );
157
164
 
158
165
  if (this.options.middleware) {
@@ -165,7 +172,10 @@ class Server {
165
172
 
166
173
  if (this.options.importMap) {
167
174
  const dir = this.options.importMap.moduleRootDir
168
- ? path.join(this.projectBaseDir, this.options.importMap.moduleRootDir)
175
+ ? path.resolve(
176
+ this.projectBaseDir,
177
+ this.options.importMap.moduleRootDir
178
+ )
169
179
  : this.projectBaseDir;
170
180
  app.use('/__moduleRoot__', this.express.static(dir));
171
181
  }
@@ -210,12 +220,30 @@ class Server {
210
220
  });
211
221
 
212
222
  const port = findPort(serverOptions.port, this.options.port);
223
+ const tlsCert = serverOptions.tlsCert || this.options.tlsCert;
224
+ const tlsKey = serverOptions.tlsKey || this.options.tlsKey;
225
+ const hostname = serverOptions.hostname || this.options.hostname;
226
+
227
+ // NOTE: Before hostname support, jasmine-browser-runner would listen on
228
+ // all IPs (no hostname) and point browsers to "localhost". We preserve
229
+ // backward compatibility here by using different defaults for these two
230
+ // things.
231
+ const listenOptions = {
232
+ port,
233
+ host: hostname || '',
234
+ };
235
+ this._httpHostname = hostname || 'localhost';
236
+
213
237
  return new Promise(resolve => {
214
- this._httpServer = app.listen(port, () => {
238
+ const callback = () => {
215
239
  const runningPort = this._httpServer.address().port;
216
- console.log(
217
- `Jasmine server is running here: http://localhost:${runningPort}`
218
- );
240
+ const url =
241
+ this._httpServerScheme +
242
+ '://' +
243
+ this._httpHostname +
244
+ ':' +
245
+ runningPort;
246
+ console.log(`Jasmine server is running here: ${url}`);
219
247
  console.log(
220
248
  `Jasmine tests are here: ${path.resolve(
221
249
  self.options.specDir
@@ -225,7 +253,23 @@ class Server {
225
253
  `Source files are here: ${path.resolve(self.options.srcDir)}`
226
254
  );
227
255
  resolve();
228
- });
256
+ };
257
+
258
+ if (tlsKey && tlsCert) {
259
+ const httpsOptions = {
260
+ key: fs.readFileSync(tlsKey),
261
+ cert: fs.readFileSync(tlsCert),
262
+ };
263
+ this._httpServer = https
264
+ .createServer(httpsOptions, app)
265
+ .listen(listenOptions, callback);
266
+ this._httpServerScheme = 'https';
267
+ } else {
268
+ this._httpServer = http
269
+ .createServer(app)
270
+ .listen(listenOptions, callback);
271
+ this._httpServerScheme = 'http';
272
+ }
229
273
  });
230
274
  }
231
275
 
@@ -257,6 +301,28 @@ class Server {
257
301
 
258
302
  return this._httpServer.address().port;
259
303
  }
304
+
305
+ /**
306
+ * Gets the URL scheme that the server is listening on. The server must be
307
+ * started before this method is called.
308
+ * @function
309
+ * @name Server#scheme
310
+ * @return {string} The URL scheme ('http' or 'https')
311
+ */
312
+ scheme() {
313
+ return this._httpServerScheme;
314
+ }
315
+
316
+ /**
317
+ * Gets the hostname that the server is listening on. The server must be
318
+ * started before this method is called.
319
+ * @function
320
+ * @name Server#hostname
321
+ * @return {string} The hostname (localhost if not specified)
322
+ */
323
+ hostname() {
324
+ return this._httpHostname;
325
+ }
260
326
  }
261
327
 
262
328
  function findPort(serverPort, optionsPort) {
package/lib/types.js CHANGED
@@ -39,6 +39,26 @@
39
39
  * @name ServerCtorOptions#port
40
40
  * @type number | undefined
41
41
  */
42
+ /**
43
+ * The path to a TLS key. Activates HTTPS mode. If specified, tlsCert must also
44
+ * be specified.
45
+ * @name ServerCtorOptions#tlsKey
46
+ * @type string
47
+ */
48
+ /**
49
+ * The path to a TLS cert. Activates HTTPS mode. If specified, tlsKey must also
50
+ * be specified.
51
+ * @name ServerCtorOptions#tlsCert
52
+ * @type string
53
+ */
54
+ /**
55
+ * The hostname to use. This influences both the URL given to browsers and the
56
+ * addresses on which the socket listens. If blank, for backward
57
+ * compatibility, the browsers will be pointed to localhost, but the listening
58
+ * socket will listen on all IPs.
59
+ * @name ServerCtorOptions#hostname
60
+ * @type string
61
+ */
42
62
  /**
43
63
  * The root directory of the project.
44
64
  * @name ServerCtorOptions#projectBaseDir
@@ -271,6 +291,26 @@
271
291
  * @name ServerStartOptions#port
272
292
  * @type number | undefined
273
293
  */
294
+ /**
295
+ * The path to a TLS key. Activates HTTPS mode. If specified, tlsCert must also
296
+ * be specified.
297
+ * @name ServerStartOptions#tlsKey
298
+ * @type string
299
+ */
300
+ /**
301
+ * The path to a TLS cert. Activates HTTPS mode. If specified, tlsKey must also
302
+ * be specified.
303
+ * @name ServerStartOptions#tlsCert
304
+ * @type string
305
+ */
306
+ /**
307
+ * The hostname to use. This influences both the URL given to browsers and the
308
+ * addresses on which the socket listens. If blank, for backward
309
+ * compatibility, the browsers will be pointed to localhost, but the listening
310
+ * socket will listen on all IPs.
311
+ * @name ServerStartOptions#hostname
312
+ * @type string
313
+ */
274
314
 
275
315
  /**
276
316
  * Describes an import map.
package/lib/webdriver.js CHANGED
@@ -60,6 +60,12 @@ function buildWebdriver(browserInfo, webdriverBuilder) {
60
60
  }
61
61
  } else if (useSauce) {
62
62
  // handle legacy `sauce` object
63
+ console.warn(
64
+ 'Deprecation warning: Direct support for Saucelabs is deprecated and ' +
65
+ 'will be removed in a future release. Please use Saucelabs via the ' +
66
+ 'remote Selenium grid feature. See the jasmine-browser-runner README ' +
67
+ 'for details.'
68
+ );
63
69
  const sauce = browserInfo.sauce;
64
70
  if (sauce) {
65
71
  url = `http://${sauce.username}:${sauce.accessKey}@ondemand.saucelabs.com/wd/hub`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jasmine-browser-runner",
3
- "version": "2.3.0",
3
+ "version": "2.4.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",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "devDependencies": {
49
49
  "ejs-lint": "^2.0.0",
50
- "eslint": "^8.38.0",
50
+ "eslint": "^8.50.0",
51
51
  "eslint-plugin-jasmine": "^4.1.3",
52
52
  "jasmine": "^5.0.0",
53
53
  "jasmine-core": "^5.0.0",
@@ -72,7 +72,7 @@
72
72
  "expectAsync": "readonly"
73
73
  },
74
74
  "parserOptions": {
75
- "ecmaVersion": 11
75
+ "ecmaVersion": 2022
76
76
  },
77
77
  "plugins": [
78
78
  "jasmine"
@@ -102,6 +102,7 @@
102
102
  "functions": "never"
103
103
  }
104
104
  ],
105
+ "eqeqeq": "error",
105
106
  "func-call-spacing": [
106
107
  "error",
107
108
  "never"