jasmine-browser-runner 2.3.0 → 2.5.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
@@ -1,6 +1,7 @@
1
1
  jasmine-browser-runner runs your Jasmine specs in a browser. It's suitable for
2
2
  interactive use with normal browsers as well as running specs in CI builds
3
- using either headless Chrome or Saucelabs.
3
+ using either headless browsers or with a remote Selenuium grid provider
4
+ such as Saucelabs.
4
5
 
5
6
  # Getting started
6
7
 
@@ -19,12 +20,22 @@ npx jasmine-browser-runner init
19
20
  If you intend to use ES modules, add `--esm` to the `jasmine-browser-runner init`
20
21
  command.
21
22
 
22
- Then, customize `spec/support/jasmine-browser.json` to suit your needs. You can
23
+ Then, customize `spec/support/jasmine-browser.mjs` to suit your needs. You can
23
24
  change the spec files, helpers, and source files that are loaded, specify the
24
25
  [Jasmine env's configuration](https://jasmine.github.io/api/edge/Configuration.html),
25
26
  and more.
26
27
 
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.
28
+ In addition to `spec/support/jasmine-browser.mjs`, jasmine-browser-runner also
29
+ supports other config file paths:
30
+
31
+ * `spec/support/jasmine-browser.js`
32
+ * `spec/support/jasmine-browser.json` (generated by previous versions of the
33
+ `init` subcommand)
34
+ * Any other JavaScript or JSON file, if you use the `--config` option. This
35
+ file can be a JSON file or a javascript file whose default export is a config
36
+ object.
37
+
38
+ More information about the configuration can be found at the runner [documentation website](https://jasmine.github.io/api/browser-runner/edge/Configuration.html).
28
39
 
29
40
  To start the server so that you can run the specs interactively (particularly
30
41
  useful for debugging):
@@ -40,10 +51,10 @@ npx jasmine-browser-runner runSpecs
40
51
  ```
41
52
 
42
53
  To use a browser other than Firefox, add a `browser` field to
43
- `jasmine-browser.json`:
54
+ `jasmine-browser.mjs`:
44
55
 
45
56
  ```javascript
46
- {
57
+ export default {
47
58
  // ...
48
59
  "browser": "chrome"
49
60
  }
@@ -52,6 +63,82 @@ To use a browser other than Firefox, add a `browser` field to
52
63
  Its value can be `"firefox"`, `"headlessFirefox"`, `"safari"`,
53
64
  `"MicrosoftEdge"`, `"chrome"`, or `"headlessChrome"`.
54
65
 
66
+ ## TLS support
67
+
68
+ To serve tests over HTTPS instead of HTTP, supply a path to a TLS cert and key
69
+ in PEM format in `jasmine-browser.mjs`:
70
+
71
+ ```javascript
72
+ export default {
73
+ // ...
74
+ "tlsKey": "/path/to/tlsKey.pem",
75
+ "tlsCert": "/path/to/tlsCert.pem",
76
+ // ...
77
+ }
78
+ ```
79
+
80
+ These can also be specified on the command line with `--tlsKey` and `--tlsCert`.
81
+
82
+ Note that if you are using a self-signed or otherwise invalid certificate, the
83
+ browser will not allow the connection by default. Additional browser configs
84
+ or command line options may be necessary to use an invalid TLS certificate.
85
+
86
+ ## Controlling which network interfaces are listened to
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.
91
+
92
+ ```javascript
93
+ export default {
94
+ // ...
95
+ "listenAddress": "localhost",
96
+ // ...
97
+ }
98
+ ```
99
+
100
+ ## Hostname support
101
+
102
+ If you need to access your tests via a specific hostname, you can do that by
103
+ setting the `hostname` configuration property:
104
+
105
+ ```javascript
106
+ export default {
107
+ // ...
108
+ "hostname": "mymachine.mynetwork",
109
+ // ...
110
+ }
111
+ ```
112
+
113
+ This can also be specified on the command line with `--hostname`.
114
+
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
+ There are a few important caveats when doing this:
121
+
122
+ 1. This name must either be an IP or a name that can really be resolved on your
123
+ system. Otherwise, you will get `ENOTFOUND` errors.
124
+ 2. This name must correspond to an IP assigned to one of the network interfaces
125
+ on your system. Otherwise, you will get `EADDRNOTAVAIL` errors.
126
+ 3. If this name matches the [HSTS preload list](https://hstspreload.org/),
127
+ browsers will force the connection to HTTPS. If you are not using TLS, you
128
+ will get an error that says `The browser tried to speak HTTPS to an HTTP
129
+ server. Misconfiguration is likely.` You may be surprised by the names on
130
+ that preload list, which include such favorite local network hostnames as:
131
+ - dev
132
+ - foo
133
+ - app
134
+ - nexus
135
+ - windows
136
+ - office
137
+ - dad
138
+ You can see a full list in [Chromium source](https://raw.githubusercontent.com/chromium/chromium/main/net/http/transport_security_state_static.json)
139
+ or query your hostname at the [HSTS preload site](https://hstspreload.org/).
140
+
141
+
55
142
  ## ES module support
56
143
 
57
144
  If a source, spec, or helper file's name ends in `.mjs`, it will be loaded as
@@ -73,7 +160,7 @@ If you have specs or helper files that use top-level await, set the
73
160
  are also supported:
74
161
 
75
162
  ```javascript
76
- {
163
+ export default {
77
164
  // ...
78
165
  "importMap": {
79
166
  "moduleRootDir": "node_modules",
@@ -93,11 +180,11 @@ whether you use the Asset Pipeline or Webpacker.
93
180
 
94
181
  ### Webpacker
95
182
 
96
- 1. Run `yarn add --dev jasmine-browser-runner`.
183
+ 1. Run `yarn add --dev jasmine-browser-runner jasmine-core`.
97
184
  2. Run `npx jasmine-browser-runner init`.
98
- 3. Edit `spec/support/jasmine-browser.json` as follows:
185
+ 3. Edit `spec/support/jasmine-browser.mjs` as follows:
99
186
  ```
100
- {
187
+ export default {
101
188
  "srcDir": ".",
102
189
  "srcFiles": [],
103
190
  "specDir": "public/packs/js",
@@ -137,9 +224,9 @@ To run the specs:
137
224
  the Rails application.
138
225
  2. Run `yarn add --dev jasmine-browser-runner`.
139
226
  3. Run `npx jasmine-browser-runner init`.
140
- 5. Edit `spec/support/jasmine-browser.json` as follows:
227
+ 5. Edit `spec/support/jasmine-browser.mjs` as follows:
141
228
  ```
142
- {
229
+ export default {
143
230
  "srcDir": "public/assets",
144
231
  "srcFiles": [
145
232
  "application-*.js"
@@ -171,9 +258,9 @@ provider like [Saucelabs](https://saucelabs.com/),
171
258
  To use a remote grid hub, set the `browser` object
172
259
  in your config file as follows:
173
260
 
174
- ```json
175
- // jasmine-browser.json
176
- {
261
+ ```javascript
262
+ // jasmine-browser.mjs
263
+ export default {
177
264
  // ...
178
265
  // BrowserStack
179
266
  "browser": {
@@ -195,9 +282,9 @@ in your config file as follows:
195
282
  }
196
283
  }
197
284
  ```
198
- ```json
199
- // jasmine-browser.json
200
- {
285
+ ```javascript
286
+ // jasmine-browser.mjs
287
+ export default {
201
288
  // ...
202
289
  // Saucelabs
203
290
  "browser": {
@@ -224,49 +311,33 @@ exception of the `url` will be used as `capabilties` sent to the grid hub url.
224
311
  if no value is specified for the `url` then a default of
225
312
  `http://localhost:4445/wd/hub` is used.
226
313
 
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.`
314
+ It's common for remote grids to support only a limited set of ports. Check your
315
+ remote grid's documentation to make sure that the port you're using is
316
+ supported. When using a remote grid, `jasmine-browser-runner` will run on port
317
+ 5555 unless you use the `--port` command line option or specify a port in the
318
+ second parameter to`startServer`.
229
319
 
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:
320
+ ## Want more control?
235
321
 
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
- ```
322
+ ```javascript
323
+ // ESM
324
+ import path from 'path';
325
+ import jasmineBrowser from 'jasmine-browser-runner';
326
+ import config from './spec/support/jasmine-browser.mjs';
253
327
 
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.
328
+ config.projectBaseDir = path.resolve('some/path');
329
+ jasmineBrowser.startServer(config, { port: 4321 });
259
330
 
260
- ## Want more control?
261
331
 
262
- ```javascript
332
+ // CommonJS
263
333
  const path = require('path');
264
334
  const jasmineBrowser = require('jasmine-browser-runner');
265
335
 
266
- const config = require(path.resolve('spec/support/jasmine-browser.json'));
267
- config.projectBaseDir = path.resolve('some/path');
268
-
269
- jasmineBrowser.startServer(config, { port: 4321 });
336
+ import('./spec/support/jasmine-browser.mjs')
337
+ .then(function({default: config}) {
338
+ config.projectBaseDir = path.resolve('some/path');
339
+ jasmineBrowser.startServer(config, { port: 4321 });
340
+ });
270
341
  ```
271
342
 
272
343
  ## Supported environments
@@ -277,9 +348,9 @@ Firefox, and Microsoft Edge) as well as Node.
277
348
  | Environment | Supported versions |
278
349
  |-------------------|------------------------|
279
350
  | Node | 18, 20 |
280
- | Safari | 15-16 |
351
+ | Safari | 15-17 |
281
352
  | Chrome | Evergreen |
282
- | Firefox | Evergreen, 102 |
353
+ | Firefox | Evergreen, 102, 115 |
283
354
  | Edge | Evergreen |
284
355
 
285
356
  For evergreen browsers, each version of jasmine-browser-runner is tested against
@@ -290,3 +361,6 @@ they aren't actively supported.
290
361
 
291
362
  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
363
 
364
+ Copyright (c) 2019 Pivotal Labs<br>
365
+ Copyright (c) 2020-2024 The Jasmine developers<br>
366
+ 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 = [];
@@ -80,20 +80,18 @@ module.exports = {
80
80
  const server = new ServerClass(options);
81
81
 
82
82
  const reporters = await createReporters(options, deps);
83
- const useSauce = options.browser && options.browser.useSauce;
83
+ const useLegacySauce = options.browser && options.browser.useSauce;
84
84
  const useRemote = options.browser && options.browser.useRemoteSeleniumGrid;
85
+ const useSauceCompletionReporting =
86
+ useLegacySauce ||
87
+ (useRemote &&
88
+ options.browser.remoteSeleniumGrid?.url?.includes('saucelabs.com'));
85
89
  let portRequest;
86
90
 
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) {
91
+ if (options.port) {
96
92
  portRequest = options.port;
93
+ } else if (useLegacySauce || useRemote) {
94
+ portRequest = 5555;
97
95
  } else {
98
96
  portRequest = 0;
99
97
  }
@@ -104,7 +102,7 @@ module.exports = {
104
102
  const webdriver = buildWebdriver(options.browser);
105
103
 
106
104
  try {
107
- const host = `http://localhost:${server.port()}`;
105
+ const host = `${server.scheme()}://${server.hostname()}:${server.port()}`;
108
106
  const runner = new RunnerClass({ webdriver, reporters, host });
109
107
 
110
108
  console.log('Running tests in the browser...');
@@ -128,7 +126,7 @@ module.exports = {
128
126
 
129
127
  return details;
130
128
  } finally {
131
- if (useSauce) {
129
+ if (useSauceCompletionReporting) {
132
130
  await webdriver.executeScript(
133
131
  `sauce:job-result=${process.exitCode === 0}`
134
132
  );
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
  ];
@@ -149,7 +165,7 @@ class Command {
149
165
  }
150
166
 
151
167
  init(options) {
152
- const dest = 'spec/support/jasmine-browser.json';
168
+ const dest = 'spec/support/jasmine-browser.mjs';
153
169
 
154
170
  if (fs.existsSync(dest)) {
155
171
  this._logger.log(`${dest} already exists.`);
package/lib/config.js CHANGED
@@ -20,10 +20,12 @@ async function loadConfig(baseDir, cliOptions) {
20
20
 
21
21
  const candidates = (specifiedConfigFile
22
22
  ? [specifiedConfigFile]
23
- : ['spec/support/jasmine-browser.js', 'spec/support/jasmine-browser.json']
24
- )
25
- .filter(name => !!name)
26
- .map(name => path.resolve(baseDir, name));
23
+ : [
24
+ 'spec/support/jasmine-browser.mjs',
25
+ 'spec/support/jasmine-browser.js',
26
+ 'spec/support/jasmine-browser.json',
27
+ ]
28
+ ).map(name => path.resolve(baseDir, name));
27
29
 
28
30
  const fullPath = candidates.find(p => fs.existsSync(p));
29
31
 
@@ -57,18 +59,15 @@ function validateConfig(config) {
57
59
  }
58
60
 
59
61
  function defaultConfig() {
60
- return fs.readFileSync(require.resolve('./examples/default_config.json'), {
62
+ return fs.readFileSync(require.resolve('./examples/default_config.mjs'), {
61
63
  encoding: 'utf8',
62
64
  });
63
65
  }
64
66
 
65
67
  function defaultEsmConfig() {
66
- return fs.readFileSync(
67
- require.resolve('./examples/default_esm_config.json'),
68
- {
69
- encoding: 'utf8',
70
- }
71
- );
68
+ return fs.readFileSync(require.resolve('./examples/default_esm_config.mjs'), {
69
+ encoding: 'utf8',
70
+ });
72
71
  }
73
72
 
74
73
  function validateImportMap(importMap) {
@@ -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) {
@@ -0,0 +1,30 @@
1
+ export default {
2
+ srcDir: "src",
3
+ srcFiles: [
4
+ "**/*.js"
5
+ ],
6
+ specDir: "spec",
7
+ specFiles: [
8
+ "**/*[sS]pec.js"
9
+ ],
10
+ helpers: [
11
+ "helpers/**/*.js"
12
+ ],
13
+ env: {
14
+ stopSpecOnExpectationFailure: false,
15
+ stopOnSpecFailure: false,
16
+ random: true
17
+ },
18
+
19
+ // For security, listen only to localhost. You can also specify a different
20
+ // hostname or IP address, or remove the property or set it to "*" to listen
21
+ // to all network interfaces.
22
+ listenAddress: "localhost",
23
+
24
+ // The hostname that the browser will use to connect to the server.
25
+ hostname: "localhost",
26
+
27
+ browser: {
28
+ name: "firefox"
29
+ }
30
+ };
@@ -0,0 +1,34 @@
1
+ export default {
2
+ srcDir: "src",
3
+ // srcFiles should usually be left empty when using ES modules, because you'll
4
+ // explicitly import sources from your specs.
5
+ srcFiles: [],
6
+ specDir: ".",
7
+ specFiles: [
8
+ "spec/**/*[sS]pec.?(m)js"
9
+ ],
10
+ helpers: [
11
+ "spec/helpers/**/*.?(m)js"
12
+ ],
13
+ esmFilenameExtension: ".mjs",
14
+ // Allows the use of top-level await in src/spec/helper files. This is off by
15
+ // default because it makes files load more slowly.
16
+ enableTopLevelAwait: false,
17
+ env: {
18
+ stopSpecOnExpectationFailure: false,
19
+ stopOnSpecFailure: false,
20
+ random: true
21
+ },
22
+
23
+ // For security, listen only to localhost. You can also specify a different
24
+ // hostname or IP address, or remove the property or set it to "*" to listen
25
+ // to all network interfaces.
26
+ listenAddress: "localhost",
27
+
28
+ // The hostname that the browser will use to connect to the server.
29
+ hostname: "localhost",
30
+
31
+ browser: {
32
+ name: "firefox"
33
+ }
34
+ };
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
@@ -13,8 +15,9 @@ class Server {
13
15
  * @constructor
14
16
  * @param {ServerCtorOptions} options
15
17
  */
16
- constructor(options) {
18
+ constructor(options, deps) {
17
19
  this.options = { ...options };
20
+ this._deps = deps || { http, https };
18
21
  this.express = this.options.express || defaultExpress;
19
22
  this.useHtmlReporter =
20
23
  options.useHtmlReporter === undefined ? true : options.useHtmlReporter;
@@ -57,11 +60,12 @@ class Server {
57
60
  }
58
61
 
59
62
  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
- );
63
+ return findFiles(
64
+ path.resolve(this.projectBaseDir, baseDir),
65
+ globs || []
66
+ ).map(function(p) {
67
+ return isUrl(p) ? p : unWindows(path.join(urlRoot, p));
68
+ });
65
69
  }
66
70
 
67
71
  getSupportFiles() {
@@ -148,11 +152,15 @@ class Server {
148
152
  );
149
153
  app.use(
150
154
  '/__spec__',
151
- this.express.static(path.join(this.projectBaseDir, this.options.specDir))
155
+ this.express.static(
156
+ path.resolve(this.projectBaseDir, this.options.specDir)
157
+ )
152
158
  );
153
159
  app.use(
154
160
  '/__src__',
155
- this.express.static(path.join(this.projectBaseDir, this.options.srcDir))
161
+ this.express.static(
162
+ path.resolve(this.projectBaseDir, this.options.srcDir)
163
+ )
156
164
  );
157
165
 
158
166
  if (this.options.middleware) {
@@ -165,7 +173,10 @@ class Server {
165
173
 
166
174
  if (this.options.importMap) {
167
175
  const dir = this.options.importMap.moduleRootDir
168
- ? path.join(this.projectBaseDir, this.options.importMap.moduleRootDir)
176
+ ? path.resolve(
177
+ this.projectBaseDir,
178
+ this.options.importMap.moduleRootDir
179
+ )
169
180
  : this.projectBaseDir;
170
181
  app.use('/__moduleRoot__', this.express.static(dir));
171
182
  }
@@ -210,12 +221,36 @@ class Server {
210
221
  });
211
222
 
212
223
  const port = findPort(serverOptions.port, this.options.port);
224
+ const tlsCert = serverOptions.tlsCert || this.options.tlsCert;
225
+ const tlsKey = serverOptions.tlsKey || this.options.tlsKey;
226
+ const hostname = serverOptions.hostname || this.options.hostname;
227
+ // The last two fallbacks here are necessary for backwards compatibility.
228
+ let listenAddress =
229
+ serverOptions.listenAddress ||
230
+ this.options.listenAddress ||
231
+ hostname ||
232
+ '';
233
+
234
+ if (listenAddress === '*') {
235
+ listenAddress = '';
236
+ }
237
+
238
+ const listenOptions = {
239
+ port,
240
+ host: listenAddress,
241
+ };
242
+ this._httpHostname = hostname || 'localhost';
243
+
213
244
  return new Promise(resolve => {
214
- this._httpServer = app.listen(port, () => {
245
+ const callback = () => {
215
246
  const runningPort = this._httpServer.address().port;
216
- console.log(
217
- `Jasmine server is running here: http://localhost:${runningPort}`
218
- );
247
+ const url =
248
+ this._httpServerScheme +
249
+ '://' +
250
+ this._httpHostname +
251
+ ':' +
252
+ runningPort;
253
+ console.log(`Jasmine server is running here: ${url}`);
219
254
  console.log(
220
255
  `Jasmine tests are here: ${path.resolve(
221
256
  self.options.specDir
@@ -225,7 +260,23 @@ class Server {
225
260
  `Source files are here: ${path.resolve(self.options.srcDir)}`
226
261
  );
227
262
  resolve();
228
- });
263
+ };
264
+
265
+ if (tlsKey && tlsCert) {
266
+ const httpsOptions = {
267
+ key: fs.readFileSync(tlsKey),
268
+ cert: fs.readFileSync(tlsCert),
269
+ };
270
+ this._httpServer = this._deps.https
271
+ .createServer(httpsOptions, app)
272
+ .listen(listenOptions, callback);
273
+ this._httpServerScheme = 'https';
274
+ } else {
275
+ this._httpServer = this._deps.http
276
+ .createServer(app)
277
+ .listen(listenOptions, callback);
278
+ this._httpServerScheme = 'http';
279
+ }
229
280
  });
230
281
  }
231
282
 
@@ -257,6 +308,28 @@ class Server {
257
308
 
258
309
  return this._httpServer.address().port;
259
310
  }
311
+
312
+ /**
313
+ * Gets the URL scheme that the server is listening on. The server must be
314
+ * started before this method is called.
315
+ * @function
316
+ * @name Server#scheme
317
+ * @return {string} The URL scheme ('http' or 'https')
318
+ */
319
+ scheme() {
320
+ return this._httpServerScheme;
321
+ }
322
+
323
+ /**
324
+ * Gets the hostname that the server is listening on. The server must be
325
+ * started before this method is called.
326
+ * @function
327
+ * @name Server#hostname
328
+ * @return {string} The hostname (localhost if not specified)
329
+ */
330
+ hostname() {
331
+ return this._httpHostname;
332
+ }
260
333
  }
261
334
 
262
335
  function findPort(serverPort, optionsPort) {
package/lib/types.js CHANGED
@@ -39,6 +39,37 @@
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 or IP address of the network interface to listen on. For
56
+ * security, this should be set to localhost or an equivalent unless you need
57
+ * the server to be accessible over other network interfaces. Set to "*" to
58
+ * listen on all interfaces, which may be required by some remote Selenium
59
+ * grids.
60
+ * @name ServerCtorOptions#listenAddress
61
+ * @default The value of {@link ServerCtorOptions#hostname} or {@link ServerStartOptions#hostname} if configured, otherwise "*"
62
+ * @type string | undefined
63
+ */
64
+ /**
65
+ * The hostname to use in the URL given to browsers. For backward compatibility,
66
+ * setting this property without also setting {@link ServerCtorOptions#listenAddress}
67
+ * or {@link ServerStartOptions#listenAddress} has the same effect as setting
68
+ * the listen address to the hostname.
69
+ * @name ServerCtorOptions#hostname
70
+ * @default "localhost"
71
+ * @type string | undefined
72
+ */
42
73
  /**
43
74
  * The root directory of the project.
44
75
  * @name ServerCtorOptions#projectBaseDir
@@ -271,6 +302,26 @@
271
302
  * @name ServerStartOptions#port
272
303
  * @type number | undefined
273
304
  */
305
+ /**
306
+ * The path to a TLS key. Activates HTTPS mode. If specified, tlsCert must also
307
+ * be specified.
308
+ * @name ServerStartOptions#tlsKey
309
+ * @type string
310
+ */
311
+ /**
312
+ * The path to a TLS cert. Activates HTTPS mode. If specified, tlsKey must also
313
+ * be specified.
314
+ * @name ServerStartOptions#tlsCert
315
+ * @type string
316
+ */
317
+ /**
318
+ * @see ServerCtorOptions#hostname
319
+ * @name ServerStartOptions#hostname
320
+ */
321
+ /**
322
+ * @see ServerCtorOptions#listenAddress
323
+ * @name ServerStartOptions#listenAddress
324
+ */
274
325
 
275
326
  /**
276
327
  * 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.5.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",
@@ -13,8 +13,8 @@
13
13
  "run.html.ejs",
14
14
  "bin/*.js",
15
15
  "lib/**/*.js",
16
- "lib/examples/default_config.json",
17
- "lib/examples/default_esm_config.json"
16
+ "lib/examples/default_config.mjs",
17
+ "lib/examples/default_esm_config.mjs"
18
18
  ],
19
19
  "scripts": {
20
20
  "posttest": "eslint bin/* lib spec index.js --ignore-path=.styleIgnore && prettier --check --ignore-path=.styleIgnore \"lib/**/*.js\" \"spec/**/*.js\" index.js",
@@ -38,7 +38,7 @@
38
38
  "homepage": "https://github.com/jasmine/jasmine-browser-runner#readme",
39
39
  "dependencies": {
40
40
  "ejs": "^3.1.6",
41
- "express": "^4.16.4",
41
+ "express": "^4.19.2",
42
42
  "glob": "^10.0.0",
43
43
  "selenium-webdriver": "^4.12.0"
44
44
  },
@@ -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"
@@ -1,21 +0,0 @@
1
- {
2
- "srcDir": "src",
3
- "srcFiles": [
4
- "**/*.js"
5
- ],
6
- "specDir": "spec",
7
- "specFiles": [
8
- "**/*[sS]pec.js"
9
- ],
10
- "helpers": [
11
- "helpers/**/*.js"
12
- ],
13
- "env": {
14
- "stopSpecOnExpectationFailure": false,
15
- "stopOnSpecFailure": false,
16
- "random": true
17
- },
18
- "browser": {
19
- "name": "firefox"
20
- }
21
- }
@@ -1,21 +0,0 @@
1
- {
2
- "srcDir": "src",
3
- "srcFiles": [],
4
- "specDir": ".",
5
- "specFiles": [
6
- "spec/**/*[sS]pec.?(m)js"
7
- ],
8
- "helpers": [
9
- "spec/helpers/**/*.?(m)js"
10
- ],
11
- "esmFilenameExtension": ".mjs",
12
- "enableTopLevelAwait": false,
13
- "env": {
14
- "stopSpecOnExpectationFailure": false,
15
- "stopOnSpecFailure": false,
16
- "random": true
17
- },
18
- "browser": {
19
- "name": "firefox"
20
- }
21
- }