jasmine-browser-runner 2.0.0 → 2.2.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/README.md +65 -1
- package/index.js +5 -2
- package/lib/server.js +24 -9
- package/lib/types.js +60 -0
- package/lib/webdriver.js +39 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -163,7 +163,71 @@ To run the specs:
|
|
|
163
163
|
2. Run `npx jasmine-browser-runner`.
|
|
164
164
|
3. Visit <http://localhost:8888>.
|
|
165
165
|
|
|
166
|
-
## Saucelabs
|
|
166
|
+
## Remote Grid support (Saucelabs, BrowserStack, etc.)
|
|
167
|
+
|
|
168
|
+
jasmine-browser-runner can run your Jasmine specs on a remote grid
|
|
169
|
+
provider like [Saucelabs](https://saucelabs.com/),
|
|
170
|
+
[BrowserStack](https://browserstack.com) or your own Selenium Grid.
|
|
171
|
+
To use a remote grid hub, set the `browser` object
|
|
172
|
+
in your config file as follows:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
// jasmine-browser.json
|
|
176
|
+
{
|
|
177
|
+
// ...
|
|
178
|
+
// BrowserStack
|
|
179
|
+
"browser": {
|
|
180
|
+
"name": "safari",
|
|
181
|
+
"useRemoteSeleniumGrid": true,
|
|
182
|
+
"remoteSeleniumGrid": {
|
|
183
|
+
"url": "https://hub-cloud.browserstack.com/wd/hub",
|
|
184
|
+
"bstack:options": {
|
|
185
|
+
"browserVersion": "16",
|
|
186
|
+
"os": "OS X",
|
|
187
|
+
"osVersion": "Monterey",
|
|
188
|
+
"local": "true",
|
|
189
|
+
"localIdentifier": "tunnel ID",
|
|
190
|
+
"debug": "true",
|
|
191
|
+
"userName": "your BrowserStack username",
|
|
192
|
+
"accessKey": "your BrowserStack access key"
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
```json
|
|
199
|
+
// jasmine-browser.json
|
|
200
|
+
{
|
|
201
|
+
// ...
|
|
202
|
+
// Saucelabs
|
|
203
|
+
"browser": {
|
|
204
|
+
"name": "safari",
|
|
205
|
+
"useRemoteSeleniumGrid": true,
|
|
206
|
+
"remoteSeleniumGrid": {
|
|
207
|
+
"url": "https://ondemand.saucelabs.com/wd/hub",
|
|
208
|
+
"platformName": "macOS 12",
|
|
209
|
+
"sauce:options": {
|
|
210
|
+
"tunnel-identifier": "tunnel ID",
|
|
211
|
+
"userName": "your Saucelabs username",
|
|
212
|
+
"accessKey": "your Saucelabs access key"
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
When using a remote grid provider, all properties of the `browser` object are
|
|
220
|
+
optional except for `name` which will be passed as the `browserName` capability,
|
|
221
|
+
and `useRemoteSeleniumGrid` which must be set to a value of `true`. if a
|
|
222
|
+
`remoteSeleniumGrid` object is included, any values it contains, with the
|
|
223
|
+
exception of the `url` will be used as `capabilties` sent to the grid hub url.
|
|
224
|
+
if no value is specified for the `url` then a default of
|
|
225
|
+
`http://localhost:4445/wd/hub` is used.
|
|
226
|
+
|
|
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
|
|
167
231
|
|
|
168
232
|
jasmine-browser-runner can run your Jasmine specs on [Saucelabs](https://saucelabs.com/).
|
|
169
233
|
To use Saucelabs, set `browser.name`, `browser.useSauce`, and `browser.sauce`
|
package/index.js
CHANGED
|
@@ -81,11 +81,14 @@ module.exports = {
|
|
|
81
81
|
|
|
82
82
|
const reporters = await createReporters(options, deps);
|
|
83
83
|
const useSauce = options.browser && options.browser.useSauce;
|
|
84
|
+
const useRemote = options.browser && options.browser.useRemoteSeleniumGrid;
|
|
84
85
|
let portRequest;
|
|
85
86
|
|
|
86
|
-
if (useSauce) {
|
|
87
|
+
if (useSauce || useRemote) {
|
|
87
88
|
if (options.port) {
|
|
88
|
-
throw new Error(
|
|
89
|
+
throw new Error(
|
|
90
|
+
"Can't specify a port when browser.useSauce or browser.useRemoteSeleniumGrid is true"
|
|
91
|
+
);
|
|
89
92
|
}
|
|
90
93
|
|
|
91
94
|
portRequest = 5555;
|
package/lib/server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const defaultExpress = require('express'),
|
|
2
2
|
glob = require('glob'),
|
|
3
3
|
ejs = require('ejs'),
|
|
4
4
|
path = require('path'),
|
|
@@ -15,6 +15,7 @@ class Server {
|
|
|
15
15
|
*/
|
|
16
16
|
constructor(options) {
|
|
17
17
|
this.options = { ...options };
|
|
18
|
+
this.express = this.options.express || defaultExpress;
|
|
18
19
|
this.useHtmlReporter =
|
|
19
20
|
options.useHtmlReporter === undefined ? true : options.useHtmlReporter;
|
|
20
21
|
this.projectBaseDir = options.projectBaseDir || path.resolve();
|
|
@@ -133,26 +134,40 @@ class Server {
|
|
|
133
134
|
*/
|
|
134
135
|
start(serverOptions) {
|
|
135
136
|
serverOptions = serverOptions || {};
|
|
136
|
-
const app = express();
|
|
137
|
+
const app = this.express();
|
|
137
138
|
|
|
138
|
-
app.use('/__jasmine__', express.static(this.jasmineCore.files.path));
|
|
139
|
-
app.use('/__boot__', express.static(this.jasmineCore.files.bootDir));
|
|
140
|
-
app.use(
|
|
141
|
-
|
|
139
|
+
app.use('/__jasmine__', this.express.static(this.jasmineCore.files.path));
|
|
140
|
+
app.use('/__boot__', this.express.static(this.jasmineCore.files.bootDir));
|
|
141
|
+
app.use(
|
|
142
|
+
'/__images__',
|
|
143
|
+
this.express.static(this.jasmineCore.files.imagesDir)
|
|
144
|
+
);
|
|
145
|
+
app.use(
|
|
146
|
+
'/__support__',
|
|
147
|
+
this.express.static(path.join(__dirname, 'support'))
|
|
148
|
+
);
|
|
142
149
|
app.use(
|
|
143
150
|
'/__spec__',
|
|
144
|
-
express.static(path.join(this.projectBaseDir, this.options.specDir))
|
|
151
|
+
this.express.static(path.join(this.projectBaseDir, this.options.specDir))
|
|
145
152
|
);
|
|
146
153
|
app.use(
|
|
147
154
|
'/__src__',
|
|
148
|
-
express.static(path.join(this.projectBaseDir, this.options.srcDir))
|
|
155
|
+
this.express.static(path.join(this.projectBaseDir, this.options.srcDir))
|
|
149
156
|
);
|
|
150
157
|
|
|
158
|
+
if (this.options.middleware) {
|
|
159
|
+
for (const [path, middleware] of Object.entries(
|
|
160
|
+
this.options.middleware
|
|
161
|
+
)) {
|
|
162
|
+
app.use(path, middleware);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
151
166
|
if (this.options.importMap) {
|
|
152
167
|
const dir = this.options.importMap.moduleRootDir
|
|
153
168
|
? path.join(this.projectBaseDir, this.options.importMap.moduleRootDir)
|
|
154
169
|
: this.projectBaseDir;
|
|
155
|
-
app.use('/__moduleRoot__', express.static(dir));
|
|
170
|
+
app.use('/__moduleRoot__', this.express.static(dir));
|
|
156
171
|
}
|
|
157
172
|
|
|
158
173
|
const indexTemplate = ejs.compile(
|
package/lib/types.js
CHANGED
|
@@ -132,6 +132,29 @@
|
|
|
132
132
|
* @type boolean | undefined
|
|
133
133
|
* @default false
|
|
134
134
|
*/
|
|
135
|
+
/**
|
|
136
|
+
* <p>An optional map from paths to Express application middleware to mount on
|
|
137
|
+
* those paths. This can be used to serve static files, proxy requests to
|
|
138
|
+
* another server, etc.
|
|
139
|
+
* <p>Note: Requests made by jasmine-browser-runner (e.g. /, /__jasmine__/*,
|
|
140
|
+
* /__spec__/*, etc) are considered private APIs for semver purposes. If you
|
|
141
|
+
* configure middleware that modifies these requests and responses, there is a
|
|
142
|
+
* possibility that future jasmine-browser-runner releases, including minor and
|
|
143
|
+
* patch releases, may be incompatible with that middleware.
|
|
144
|
+
* @example
|
|
145
|
+
* // jasmine-browser.js
|
|
146
|
+
* const express = require('express');
|
|
147
|
+
*
|
|
148
|
+
* module.exports = {
|
|
149
|
+
* // ...
|
|
150
|
+
* middleware: {
|
|
151
|
+
* '/assets': express.static('./path/to/assets')
|
|
152
|
+
* }
|
|
153
|
+
* }
|
|
154
|
+
* @name Configuration#middleware
|
|
155
|
+
* @type object | undefined
|
|
156
|
+
* @default undefined
|
|
157
|
+
*/
|
|
135
158
|
|
|
136
159
|
/**
|
|
137
160
|
* Describes a web browser.
|
|
@@ -143,6 +166,12 @@
|
|
|
143
166
|
* @name BrowserInfo#useSauce
|
|
144
167
|
* @type boolean | undefined
|
|
145
168
|
*/
|
|
169
|
+
/**
|
|
170
|
+
* Whether to run the specs on a remote Selenium grid.
|
|
171
|
+
* Defaults to false.
|
|
172
|
+
* @name BrowserInfo#useRemoteSeleniumGrid
|
|
173
|
+
* @type boolean | undefined
|
|
174
|
+
*/
|
|
146
175
|
/**
|
|
147
176
|
* The browser name. Valid values include "firefox", "headlessFirefox",
|
|
148
177
|
* "safari", "MicrosoftEdge", "chrome", and "headlessChrome".
|
|
@@ -154,6 +183,11 @@
|
|
|
154
183
|
* @name BrowserInfo#sauce
|
|
155
184
|
* @type SauceConfig | undefined
|
|
156
185
|
*/
|
|
186
|
+
/**
|
|
187
|
+
* Configuration for running specs on a remote Selenium grid
|
|
188
|
+
* @name BrowserInfo#remoteSeleniumGrid
|
|
189
|
+
* @type RemoteSeleniumGridConfig | undefined
|
|
190
|
+
*/
|
|
157
191
|
|
|
158
192
|
/**
|
|
159
193
|
* Configuration for running specs on {@link https://saucelabs.com/|Saucelabs}
|
|
@@ -201,6 +235,32 @@
|
|
|
201
235
|
* @type Array.<string>
|
|
202
236
|
*/
|
|
203
237
|
|
|
238
|
+
/**
|
|
239
|
+
* Configuration for running specs on a remote Selenium grid
|
|
240
|
+
* Any additional properties, such as "sauce:options" or "bstack:options", will
|
|
241
|
+
* be included in the capabilities object passed through to Selenium Webdriver.
|
|
242
|
+
* @interface RemoteSeleniumGridConfig
|
|
243
|
+
* @example
|
|
244
|
+
* {
|
|
245
|
+
* "url": "https://hub-cloud.browserstack.com/wd/hub",
|
|
246
|
+
* "bstack:options": {
|
|
247
|
+
* "browserVersion": "16",
|
|
248
|
+
* "os": "OS X",
|
|
249
|
+
* "osVersion": "Monterey",
|
|
250
|
+
* "local": "true",
|
|
251
|
+
* "localIdentifier": "tunnel ID",
|
|
252
|
+
* "debug": "true",
|
|
253
|
+
* "userName": "your BrowserStack username",
|
|
254
|
+
* "accessKey": "your BrowserStack access key"
|
|
255
|
+
* }
|
|
256
|
+
* }
|
|
257
|
+
*/
|
|
258
|
+
/**
|
|
259
|
+
* URL of the remote Selenium grid
|
|
260
|
+
* @name RemoteSeleniumGridConfig#url
|
|
261
|
+
* @type string
|
|
262
|
+
*/
|
|
263
|
+
|
|
204
264
|
/**
|
|
205
265
|
* Options passed to {@link Server#start}
|
|
206
266
|
* @interface
|
package/lib/webdriver.js
CHANGED
|
@@ -4,6 +4,8 @@ function buildWebdriver(browserInfo, webdriverBuilder) {
|
|
|
4
4
|
|
|
5
5
|
webdriverBuilder = webdriverBuilder || new webdriver.Builder();
|
|
6
6
|
const useSauce = typeof browserInfo === 'object' && browserInfo.useSauce;
|
|
7
|
+
const useRemote =
|
|
8
|
+
typeof browserInfo === 'object' && browserInfo.useRemoteSeleniumGrid;
|
|
7
9
|
let browserName;
|
|
8
10
|
|
|
9
11
|
if (typeof browserInfo === 'string') {
|
|
@@ -14,7 +16,7 @@ function buildWebdriver(browserInfo, webdriverBuilder) {
|
|
|
14
16
|
|
|
15
17
|
browserName = browserName || 'firefox';
|
|
16
18
|
|
|
17
|
-
if (!useSauce) {
|
|
19
|
+
if (!(useRemote || useSauce)) {
|
|
18
20
|
if (browserName === 'headlessChrome') {
|
|
19
21
|
const caps = webdriver.Capabilities.chrome();
|
|
20
22
|
caps.set('goog:chromeOptions', {
|
|
@@ -44,26 +46,46 @@ function buildWebdriver(browserInfo, webdriverBuilder) {
|
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
let url;
|
|
50
|
+
let capabilities;
|
|
51
|
+
if (useRemote) {
|
|
52
|
+
const remote = browserInfo.remoteSeleniumGrid;
|
|
53
|
+
if (remote) {
|
|
54
|
+
url = remote.url;
|
|
55
|
+
capabilities = {
|
|
56
|
+
...remote,
|
|
57
|
+
[Capability.BROWSER_NAME]: browserName,
|
|
58
|
+
};
|
|
59
|
+
delete capabilities.url;
|
|
60
|
+
}
|
|
61
|
+
} else if (useSauce) {
|
|
62
|
+
// handle legacy `sauce` object
|
|
63
|
+
const sauce = browserInfo.sauce;
|
|
64
|
+
if (sauce) {
|
|
65
|
+
url = `http://${sauce.username}:${sauce.accessKey}@ondemand.saucelabs.com/wd/hub`;
|
|
66
|
+
capabilities = {
|
|
67
|
+
[Capability.BROWSER_NAME]: browserName,
|
|
68
|
+
build: sauce.build,
|
|
69
|
+
tags: sauce.tags,
|
|
70
|
+
};
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
capabilities[Capability.PLATFORM_NAME] = sauce.os;
|
|
73
|
+
capabilities[Capability.BROWSER_VERSION] = sauce.browserVersion;
|
|
74
|
+
capabilities['sauce:options'] = {
|
|
75
|
+
'tunnel-identifier': sauce.tunnelIdentifier,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!capabilities) {
|
|
81
|
+
capabilities = {
|
|
82
|
+
[Capability.BROWSER_NAME]: browserName,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
59
85
|
|
|
60
86
|
return webdriverBuilder
|
|
61
87
|
.withCapabilities(capabilities)
|
|
62
|
-
.usingServer(
|
|
63
|
-
browserInfo.useSauce
|
|
64
|
-
? `http://${sauce.username}:${sauce.accessKey}@ondemand.saucelabs.com/wd/hub`
|
|
65
|
-
: 'http://@localhost:4445/wd/hub'
|
|
66
|
-
)
|
|
88
|
+
.usingServer(url || 'http://localhost:4445/wd/hub')
|
|
67
89
|
.build();
|
|
68
90
|
}
|
|
69
91
|
|