Skip to content

Commit 9d93d95

Browse files
authored
add option to override the default browser/puppeteer instances
closes #2
2 parents 429dc0d + addaf7d commit 9d93d95

File tree

8 files changed

+171
-18
lines changed

8 files changed

+171
-18
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@
4646
},
4747
"devDependencies": {
4848
"ava": "^1.3.1",
49+
"chromium": "^2.1.0",
4950
"create-test-server": "^2.4.0",
5051
"prettier": "^1.16.4",
52+
"puppeteer-core": "^1.13.0",
5153
"xo": "^0.24.0"
5254
},
5355
"dependencies": {

readme.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,20 +65,41 @@ String.
6565

6666
Type: `Object`
6767

68+
Default: `null`
69+
6870
#### debug
6971

70-
Type: `Boolean` Default: `false`
72+
Type: `Boolean`
73+
74+
Default: `false`
7175

7276
Set to `true` if you want a Chromium window to open as it works to get all the
7377
CSS from the page.
7478

7579
#### waitUntil
7680

77-
Type: `String` Default: `networkidle2`
81+
Type: `String`
82+
83+
Default: `networkidle2`
7884

7985
Can be any value as provided by the
8086
[Puppeteer docs](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagegotourl-options).
8187

88+
#### browserOverride
89+
90+
Type: `Object`
91+
92+
Default: `null`
93+
94+
An object consisting of the following fields:
95+
96+
- `executablePath`: the path to a Chromium binary
97+
- `puppeteer`: a Puppeteer instance
98+
- `args`: arguments to start Chromium with
99+
100+
See the `test` directory for implementation examples. This option exists
101+
primarily for use with [extract-css.now.sh](https://extract-css.now.sh)
102+
82103
## Related
83104

84105
- [extract-css lambda](https://github.com/bartveneman/extract-css) - Extract CSS

src/index.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const puppeteer = require('puppeteer')
2+
const {validateBrowserOverride} = require('./validate')
23

34
function InvalidUrlError({url, statusCode, statusText}) {
45
this.name = 'InvalidUrlError'
@@ -9,12 +10,27 @@ InvalidUrlError.prototype = Error.prototype
910

1011
module.exports = async (
1112
url,
12-
{debug = false, waitUntil = 'networkidle2'} = {}
13+
{debug = false, waitUntil = 'networkidle2', browserOverride = null} = {}
1314
) => {
15+
// Basic validation for browserOverride
16+
if (browserOverride !== null) {
17+
validateBrowserOverride(browserOverride)
18+
}
19+
20+
// Setup the minimal browser options that we need to launch
21+
const browserOptions = {
22+
headless: debug !== true,
23+
executablePath: browserOverride
24+
? browserOverride.executablePath
25+
: puppeteer.executablePath(),
26+
args: browserOverride ? browserOverride.args : []
27+
}
28+
1429
// Setup a browser instance
15-
const browser = await puppeteer.launch({
16-
headless: debug !== true
17-
})
30+
const browser = await (
31+
(browserOverride && browserOverride.puppeteer) ||
32+
puppeteer
33+
).launch(browserOptions)
1834

1935
// Create a new page and navigate to it
2036
const page = await browser.newPage()

src/validate.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
function InvalidBrowserOverrideError(message) {
2+
this.name = 'InvalidBrowserOverrideError'
3+
this.message = `BrowserOverride is not valid. ${message} https://github.com/bartveneman/extract-css-core#options`
4+
}
5+
6+
InvalidBrowserOverrideError.prototype = Error.prototype
7+
8+
exports.validateBrowserOverride = ({executablePath, args, puppeteer}) => {
9+
if (typeof executablePath !== 'string') {
10+
throw new InvalidBrowserOverrideError(
11+
`Check that executablePath is a valid string, got "${JSON.stringify(
12+
executablePath
13+
)}"`
14+
)
15+
}
16+
17+
if (!Array.isArray(args)) {
18+
throw new InvalidBrowserOverrideError('Check that args is an Array.')
19+
}
20+
21+
if (typeof puppeteer.launch !== 'function') {
22+
throw new InvalidBrowserOverrideError(
23+
'Check that puppeteer.launch is a function.'
24+
)
25+
}
26+
27+
return true
28+
}

test/index.js

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ const test = require('ava')
22
const createTestServer = require('create-test-server')
33
const {readFileSync} = require('fs')
44
const {resolve} = require('path')
5+
const chromium = require('chromium')
6+
const puppeteerCore = require('puppeteer-core')
7+
58
const extractCss = require('..')
69

710
let server
@@ -113,19 +116,22 @@ test('it rejects on an invalid url', async t => {
113116
await t.throwsAsync(extractCss('site.example'))
114117
})
115118

116-
// TODO: write coverage for dynamically inserted style tags
117-
test.skip('it finds CSS-in-JS (styled components)', async t => {
118-
const path = '/css-in-js'
119-
const cssInJsExampleHtml = readFileSync(
120-
resolve(__dirname, 'css-in-js.html'),
119+
test('it accepts a browser override for usage with other browsers', async t => {
120+
const path = '/browser-override'
121+
const kitchenSinkExample = readFileSync(
122+
resolve(__dirname, 'kitchen-sink.html'),
121123
'utf8'
122124
)
123125
server.get(path, (req, res) => {
124-
res.send(cssInJsExampleHtml)
126+
res.send(kitchenSinkExample)
127+
})
128+
const actual = await extractCss(server.url + path, {
129+
browserOverride: {
130+
executablePath: chromium.path,
131+
puppeteer: puppeteerCore,
132+
args: []
133+
}
125134
})
126135

127-
const actual = await extractCss(server.url + path)
128-
const expected = 'lots of JS generated classNames and styles'
129-
130-
t.is(actual, expected)
136+
t.snapshot(actual)
131137
})

test/snapshots/index.js.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ The actual snapshot is saved in `index.js.snap`.
44

55
Generated by [AVA](https://ava.li).
66

7-
## it accepts a custom Chrome and Puppeteer version for use on AWS
7+
## it combines server generated <link> and <style> tags with client side created <link> and <style> tags
88

99
> Snapshot 1
1010
@@ -18,7 +18,21 @@ Generated by [AVA](https://ava.li).
1818
counter-increment: 2;␊
1919
}.js-style { counter-increment: 3; }`
2020

21-
## it combines server generated <link> and <style> tags with client side created <link> and <style> tags
21+
## it finds CSS-in-JS (styled components)
22+
23+
> Snapshot 1
24+
25+
`body {␊
26+
color: teal;␊
27+
}␊
28+
body {␊
29+
color: teal;␊
30+
}␊
31+
.server-style {␊
32+
counter-increment: 2;␊
33+
}.js-style { counter-increment: 3; }`
34+
35+
## it accepts a browser override for usage with other browsers
2236

2337
> Snapshot 1
2438

test/snapshots/index.js.snap

49 Bytes
Binary file not shown.

test/validate.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
const test = require('ava')
2+
const extractCss = require('..')
3+
const {validateBrowserOverride} = require('../src/validate')
4+
5+
test('it does basic validation on browserOverride', t => {
6+
const failures = [
7+
{},
8+
{executablePath: null},
9+
{executablePath: ''},
10+
{
11+
puppeteer: null
12+
},
13+
{puppeteer: {}},
14+
{puppeteer: {launch: null}}
15+
]
16+
const successes = [
17+
{
18+
executablePath: '/path/to/chromium',
19+
puppeteer: {launch: () => {}},
20+
args: []
21+
}
22+
]
23+
24+
failures.forEach(failure => {
25+
t.throws(() => validateBrowserOverride(failure))
26+
})
27+
successes.forEach(success =>
28+
t.notThrows(() => validateBrowserOverride(success))
29+
)
30+
})
31+
32+
test('it does basic validation on the browserOverride option', async t => {
33+
await t.throwsAsync(
34+
extractCss('http://google.com', {
35+
browserOverride: {
36+
executablePath: null
37+
}
38+
}),
39+
{
40+
message:
41+
'BrowserOverride is not valid. Check that executablePath is a valid string, got "null" https://github.com/bartveneman/extract-css-core#options'
42+
}
43+
)
44+
await t.throwsAsync(
45+
extractCss('http://google.com', {
46+
browserOverride: {
47+
puppeteer: null
48+
}
49+
}),
50+
{
51+
message:
52+
'BrowserOverride is not valid. Check that executablePath is a valid string, got "undefined" https://github.com/bartveneman/extract-css-core#options'
53+
}
54+
)
55+
await t.throwsAsync(
56+
extractCss('http://google.com', {
57+
browserOverride: {
58+
args: null
59+
}
60+
}),
61+
{
62+
message:
63+
'BrowserOverride is not valid. Check that executablePath is a valid string, got "undefined" https://github.com/bartveneman/extract-css-core#options'
64+
}
65+
)
66+
})

0 commit comments

Comments
 (0)