Skip to content

Commit 9b075d4

Browse files
committed
feat: add static webserver command
1 parent 7533ed4 commit 9b075d4

File tree

15 files changed

+589
-29
lines changed

15 files changed

+589
-29
lines changed

README.md

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@ All browser/provider specific dependencies are peer dependencies and are dynamic
3030

3131
## Features
3232

33-
- retrieve html as ASTElements (using [`vue-template-compiler`](https://www.npmjs.com/package/vue-template-compiler))
33+
- Retrieve html as ASTElements (using [`vue-template-compiler`](https://www.npmjs.com/package/vue-template-compiler))
3434
- Very easy to write page function to run in the browser
3535
- just remember to only use language features the loaded page already has polyfills for
3636
- syntax is automatically transpiled when browser version is specified
3737
- e.g. arrow functions will be transpiled to normal functions when you specify 'safari 5.1'
38-
- Supports BrowserStack-Local to easily tests local code
38+
- Supports BrowserStack-Local to easily tests local html files
39+
- Serve your local html files with a simple webserver
3940
- Automatically starts Xvfb for non-headless support (on supported platforms)
4041
- set `xvfb: false` if you want to specify DISPLAY manually
4142

@@ -88,21 +89,28 @@ also check [our e2e tests](./test/e2e/basic.test.js) for more information
8889
```js
8990
import { createBrowser, commands: { Xvfb, BrowserStackLocal } } from 'tib'
9091

92+
const browserString = 'windows 10/chrome 71/browserstack/local/1920x1080'
93+
// const browserString = 'puppeteer/core/staticserver'
94+
9195
describe('my e2e test', () => {
9296
let myBrowser
9397

9498
beforeAll(async () => {
95-
myBrowser = await createBrowser('windows 10/chrome 71/browserstack/local/1920x1080', {
99+
myBrowser = await createBrowser(browserString, {
96100
// if true or undefined then Xvfb is automatically started before
97101
// the browser and the displayNum=99 added to the process.env
98102
xvfb: false,
103+
folder: process.cwd(),
104+
staticServer: {
105+
host: 'localhost', // or set process.env.HOST
106+
port: 3000 // or set process.env.PORT
107+
},
99108
// only used for BrowserStackLocal browsers
100109
BrowserStackLocal: {
101110
start: true, // default, if false then call 'const pid = await BrowserStackLocal.start()'
102111
stop: true, // default, if false then call 'await BrowserStackLocal.stop(pid)'
103112
user: process.env.BROWSERSTACK_USER,
104-
key: process.env.BROWSERSTACK_KEY,
105-
folder: process.cwd()
113+
key: process.env.BROWSERSTACK_KEY
106114
},
107115
extendPage(page) {
108116
return {
@@ -136,7 +144,7 @@ describe('my e2e test', () => {
136144

137145
test('router', async () => {
138146
// note: this method is only available for browserstack/local browsers
139-
const url = myBrowser.getLocalFolderUrl()
147+
const url = myBrowser.getUrl()
140148

141149
const page = await myBrowser.page(url)
142150

@@ -229,9 +237,8 @@ If you use Jest for testing, you might also need to exclude `tib` from the [`tra
229237

230238
## TODO
231239
- validation
232-
- local ie/edge/safari
240+
- local ie/edge
233241
- more platforms, which ones?
234242
- SauceLabs (unable to test as I have no key)
235243
- screenshotting
236244
- increase coverage
237-
- ?

docs/API.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
88
## Configuration
99

10+
### `folder`
11+
12+
The local folder with static html files you wish to test. This folder is used both by browserstack-local and the static webserver.
13+
If you dont use either of those, the files will be loaded through the `file://` protocol.
14+
1015
### `xvfb`
1116
_boolean_ (default: `true`)
1217

@@ -26,6 +31,18 @@ This function is called after a page is opened and receives the `webpage` instan
2631

2732
It should return or resolve to an object which is used in the [page proxy](#pageproxy)
2833

34+
### `staticServer`
35+
_object_
36+
37+
The configuration for the included static webserver.
38+
39+
- `host` (_string_, default `localhost`)<br/>
40+
The hostname / ip to run the static server on. Optionally you can also set the `HOST` env variable.
41+
- `port` (_integer_, default `3000`)<br/>
42+
The port the static server should run on. Optionally you can also set the `PORT` env variable.
43+
- `folder` (_string_, optional)<br/>
44+
The path used as the webroot for the webserver. If not provided the default `folder` browser config option is used
45+
2946
### `BrowserStackLocal`
3047
_object_
3148

@@ -37,14 +54,14 @@ The object can list the following properties:
3754
Whether to start the browserstack-local daemon on `browser.start`
3855
- `stop` (_boolean_, default `true`)<br/>
3956
Whether to stop the browserstack-local daemon on `browser.close`
40-
- `folder` (_string_)<br/>
41-
The path to the folder which should be accessible through the browserstack-local tunnel
4257
- `user` (_string_)<br/>
4358
Your browserstack user name<br/>
4459
> for security reasons it is recommended to use the env var `BROWSERSTACK_USER` instead
4560
- `key` (_string_)<br/>
4661
Your browserstack key<br/>
4762
> for security reasons it is recommended to use the env var `BROWSERSTACK_KEY` instead
63+
- `folder` (_string_, optional)<br/>
64+
The path to the folder which should be accessible through the browserstack-local tunnel. If not provided the default `folder` browser config option is used
4865

4966
## Browser methods
5067

@@ -76,6 +93,13 @@ _resolves_ `void`
7693

7794
Closes the browser and all commands that were started
7895

96+
### `getUrl`
97+
_arguments_
98+
- path (type: `string`)
99+
_returns_ `url`
100+
101+
Returns the full url for a path. Depending your browser config it returns the url to/for browserstack-local, static server or just local files.
102+
79103
### `page`
80104
_arguments_
81105
- url (type: `string`)

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"eslint-plugin-promise": "^4.2.1",
6363
"eslint-plugin-standard": "^4.0.0",
6464
"eslint-plugin-vue": "^5.2.3",
65+
"express": "^4.17.1",
6566
"geckodriver": "^1.16.2",
6667
"glob": "^7.1.4",
6768
"jest": "^24.8.0",
@@ -75,15 +76,18 @@
7576
"rollup-plugin-json": "^4.0.0",
7677
"rollup-plugin-node-resolve": "^5.2.0",
7778
"selenium-webdriver": "^4.0.0-alpha.1",
79+
"serve-static": "^1.14.1",
7880
"standard-version": "^6.0.1",
7981
"yarn": "^1.17.3"
8082
},
8183
"peerDependencies": {
8284
"browserstack-local": "^1.4.0",
8385
"chromedriver": "^75.1.0",
86+
"express": "^4.17.1",
8487
"geckodriver": "^1.16.2",
8588
"puppeteer": "^1.18.1",
8689
"puppeteer-core": "^1.18.1",
87-
"selenium-webdriver": "^4.0.0-alpha.1"
90+
"selenium-webdriver": "^4.0.0-alpha.1",
91+
"serve-static": "^1.14.1"
8892
}
8993
}

src/browser.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import path from 'path'
12
import Hookable from 'hable'
23
import onExit from 'signal-exit'
34
import BrowserError from './utils/error'
4-
import Xvfb from './utils/commands/xvfb'
5+
import { Xvfb, StaticServer } from './utils/commands'
56
import { browsers } from './browsers'
67
import {
78
abstractGuard,
@@ -42,6 +43,11 @@ export default class Browser extends Hookable {
4243
continue
4344
}
4445

46+
if (key === 'staticserver') {
47+
this.config.staticServer = true
48+
continue
49+
}
50+
4551
if (key === 'xvfb') {
4652
if (this.config.browserConfig[key] === 'false' || this.config.browserConfig.headless) {
4753
continue
@@ -67,9 +73,8 @@ export default class Browser extends Hookable {
6773
this.config.xvfb = Xvfb.isSupported()
6874
}
6975

70-
if (this.config.xvfb) {
71-
Xvfb.load(this)
72-
}
76+
Xvfb.load(this)
77+
StaticServer.load(this)
7378

7479
if (isMockedFunction(setTimeout, 'setTimeout')) {
7580
// eslint-disable-next-line no-console
@@ -128,6 +133,15 @@ The browser probably won't ever start with globally mocked timers. Will try to a
128133
await this.callHook('dependencies:loaded')
129134
}
130135

136+
getUrl(urlPath) {
137+
if (this.config.staticServer) {
138+
const { host, port } = this.config.staticServer
139+
return `http://${host}:${port}${urlPath}`
140+
}
141+
142+
return `file://${path.join(this.config.folder, urlPath)}`
143+
}
144+
131145
getCapabilities(capabilities) {
132146
if (!capabilities) {
133147
return this.capabilities

src/browserstack/local.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export default class BrowserStackLocalBrowser extends BrowserStackBrowser {
1010
start: true,
1111
stop: true,
1212
key: this.getConfigProperty('key'),
13+
folder: this.config.folder,
1314
...this.config.BrowserStackLocal
1415
}
1516

@@ -39,4 +40,8 @@ export default class BrowserStackLocalBrowser extends BrowserStackBrowser {
3940
getLocalFolderUrl(path = '/') {
4041
return `http://${this.getConfigProperty('user')}.browserstack.com${path}`
4142
}
43+
44+
getUrl(path) {
45+
return this.getLocalFolderUrl(path)
46+
}
4247
}

src/utils/commands/browserstack-local.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default class BrowserStackLocal {
1818
}
1919

2020
static async start(config = {}) {
21+
// TODO: support webserver
2122
if (!config.folder) {
2223
config.folder = path.resolve(process.cwd())
2324
}

src/utils/commands/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import Xvfb from './xvfb'
22
import BrowserStackLocal from './browserstack-local'
3+
import StaticServer from './static-server'
34

45
export {
56
Xvfb,
6-
BrowserStackLocal
7+
BrowserStackLocal,
8+
StaticServer
79
}

src/utils/commands/static-server.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { loadDependency } from '..'
2+
3+
export default class StaticServer {
4+
static async loadDependencies() {
5+
if (StaticServer.express) {
6+
return
7+
}
8+
9+
StaticServer.express = await loadDependency('express')
10+
StaticServer.serveStatic = await loadDependency('serve-static')
11+
}
12+
13+
static load(browser) {
14+
if (!browser.config.staticServer) {
15+
return
16+
}
17+
18+
if (browser.config.staticServer === true) {
19+
const { folder } = browser.config
20+
browser.config.staticServer = { folder }
21+
}
22+
23+
if (!browser.config.staticServer.folder) {
24+
return
25+
}
26+
27+
browser.hook('start:before', () => StaticServer.start(browser.config.staticServer))
28+
browser.hook('close:after', StaticServer.stop)
29+
}
30+
31+
static async start(config) {
32+
await StaticServer.loadDependencies()
33+
34+
const app = StaticServer.express()
35+
36+
app.use(StaticServer.serveStatic(config.folder))
37+
38+
let { host, port } = config
39+
host = process.env.HOST || host || 'localhost'
40+
port = process.env.PORT || port || 3000
41+
42+
StaticServer.server = app.listen(port, host)
43+
44+
// eslint-disable-next-line no-console
45+
console.info(`tib: Static server started on http://${host}:${port}`)
46+
47+
config.host = host
48+
config.port = port
49+
}
50+
51+
static stop() {
52+
if (StaticServer.server) {
53+
return new Promise((resolve) => {
54+
StaticServer.server.close(() => {
55+
StaticServer.server = undefined
56+
resolve()
57+
})
58+
})
59+
}
60+
}
61+
}

src/utils/commands/xvfb.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ const supportedPlatforms = [
1515

1616
export default class Xvfb {
1717
static load(browser) {
18+
if (!browser.config.xvfb) {
19+
return
20+
}
21+
1822
Xvfb.isSupported(true)
1923

2024
const browserConfig = browser.config.browserConfig

src/utils/constants.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ export const browsers = [
1212

1313
export const browserOptions = [
1414
'headless',
15-
'xvfb'
15+
'xvfb',
16+
'staticserver'
1617
]
1718

1819
export const drivers = [

0 commit comments

Comments
 (0)