-
Notifications
You must be signed in to change notification settings - Fork 800
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
791 additions
and
359 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,7 +20,7 @@ | |
"build:node": "tsc --project tsconfig.node.json", | ||
"build": "npm run clean && npm run build:browser && npm run build:node", | ||
"clean": "rm -rf lib/ && rm -rf dist/", | ||
"test": "jest tests" | ||
"test": "NODE_ENV=test NODE_PATH=./node_modules jest --forceExit --config=tests/config/jest.config.js" | ||
}, | ||
"main": "./lib/back-end.js", | ||
"browser": "./dist/bundle.js", | ||
|
@@ -40,14 +40,16 @@ | |
"eslint": "8.22.0", | ||
"express": "4.18.1", | ||
"https-browserify": "1.0.0", | ||
"jest-environment-puppeteer": "6.1.1", | ||
"jest": "29.7.0", | ||
"jest-environment-puppeteer": "10.0.1", | ||
"node-fetch": "3.2.10", | ||
"nodemon": "3.1.2", | ||
"os-browserify": "0.3.0", | ||
"path-browserify": "1.0.1", | ||
"playwright-core": "1.33.0", | ||
"prettier": "npm:[email protected]", | ||
"puppeteer": "16.2.0", | ||
"puppeteer": "22.13.1", | ||
"puppeteer-core": "22.13.1", | ||
"rollup": "2.78.1", | ||
"rollup-plugin-polyfill-node": "0.10.2", | ||
"rollup-plugin-terser": "7.0.2", | ||
|
@@ -73,6 +75,14 @@ | |
"clean-css": "5.3.1", | ||
"css-tree": "2.3.1" | ||
}, | ||
"peerDependencies": { | ||
"puppeteer-core": "22.13.1" | ||
}, | ||
"peerDependenciesMeta": { | ||
"puppeteer-core": { | ||
"optional": true | ||
} | ||
}, | ||
"overrides": { | ||
"eslint-plugin-jsdoc": "46.8.2" | ||
} | ||
|
12 changes: 12 additions & 0 deletions
12
projects/js-packages/critical-css-gen/tests/babel.config.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"presets": [ | ||
[ | ||
"@babel/preset-env", | ||
{ | ||
"targets": { | ||
"node": "current" | ||
} | ||
} | ||
] | ||
] | ||
} |
3 changes: 3 additions & 0 deletions
3
projects/js-packages/critical-css-gen/tests/config/jest-setup.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
jest.setTimeout( 60000 ); | ||
jest.spyOn( global.console, 'log' ).mockImplementation( () => jest.fn() ); | ||
jest.spyOn( global.console, 'debug' ).mockImplementation( () => jest.fn() ); |
11 changes: 11 additions & 0 deletions
11
projects/js-packages/critical-css-gen/tests/config/jest.config.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export default { | ||
rootDir: '../', | ||
testEnvironment: 'jest-environment-node', | ||
testMatch: [ '**/?(*.)+(spec|test).js' ], | ||
setupFilesAfterEnv: [ './config/jest-setup.js' ], | ||
collectCoverageFrom: [ '../lib/*.js' ], | ||
globalSetup: 'jest-environment-puppeteer/setup', | ||
globalTeardown: 'jest-environment-puppeteer/teardown', | ||
testPathIgnorePatterns: [ '/node_modules/', 'config/jest-setup.js', 'lib/*' ], | ||
moduleDirectories: [ 'lib', 'node_modules' ], | ||
}; |
4 changes: 4 additions & 0 deletions
4
projects/js-packages/critical-css-gen/tests/data/page-a/complex-rules.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/* This rule is included using complex media="" rules, so should be appropriately wrapped */ | ||
div.complex_media_rules { | ||
color: purple; | ||
} |
29 changes: 29 additions & 0 deletions
29
projects/js-packages/critical-css-gen/tests/data/page-a/index.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<html> | ||
<head> | ||
<title>Test page</title> | ||
<link rel="stylesheet" href="style.css" /> | ||
<link rel="stylesheet" href="print.css" media="print" /> | ||
<link rel="stylesheet" href="min-width.css" media="(min-width: 50px)" /> | ||
<link | ||
rel="stylesheet" | ||
type="text/css" | ||
media="only screen and (max-device-width: 480px) and (orientation: landscape)" | ||
href="complex-rules.css" | ||
/> | ||
" | ||
<meta name="testing-page" content="testing-page" /> | ||
</head> | ||
|
||
<body> | ||
<div class="top"></div> | ||
<div class="four_eighty"></div> | ||
<div class="eight_hundred"></div> | ||
<div class="sir_not_appearing_in_this_film"></div> | ||
<div class="min_width_screen"></div> | ||
<div class="complex_media_rules"></div> | ||
|
||
<script> | ||
document.writeln(["script", "created", "content"].join("-")); | ||
</script> | ||
</body> | ||
</html> |
6 changes: 6 additions & 0 deletions
6
projects/js-packages/critical-css-gen/tests/data/page-a/min-width.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
@media screen { | ||
/* This rule should appear inside two media rules; min-width and screen */ | ||
div.min_width_screen { | ||
color: pink; | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
projects/js-packages/critical-css-gen/tests/data/page-a/print.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/* These rules are included in a media="print" link, and so should not be | ||
included in the end result, ever. */ | ||
|
||
div.sir_not_appearing_in_this_film { | ||
color: red; | ||
} |
54 changes: 54 additions & 0 deletions
54
projects/js-packages/critical-css-gen/tests/data/page-a/style.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
body { | ||
padding: 0px; | ||
margin: 0px; | ||
} | ||
|
||
div.top { | ||
position: absolute; | ||
top: 0px; | ||
} | ||
|
||
div.four_eighty { | ||
position: absolute; | ||
top: 480px; | ||
} | ||
|
||
div.six_hundred { | ||
position: absolute; | ||
top: 600px; | ||
} | ||
|
||
div.seven_sixty_eight { | ||
position: absolute; | ||
top: 768px; | ||
} | ||
|
||
@media print { | ||
div.top::before { | ||
content: "print"; | ||
} | ||
} | ||
|
||
@media not screen { | ||
div.top::before { | ||
content: "not screen"; | ||
} | ||
} | ||
|
||
@media print { | ||
div.top::before { | ||
content: "print"; | ||
} | ||
} | ||
|
||
@media screen { | ||
div.top::before { | ||
content: "screen"; | ||
} | ||
} | ||
|
||
@media all { | ||
div.top::before { | ||
content: "all"; | ||
} | ||
} |
208 changes: 208 additions & 0 deletions
208
projects/js-packages/critical-css-gen/tests/unit/browser-interface-iframe.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
/* global CriticalCSSGenerator */ | ||
const puppeteer = require( 'puppeteer' ); | ||
|
||
const path = require( 'path' ); | ||
const TestServer = require( '../lib/test-server' ); | ||
const { dataDirectory } = require( '../lib/data-directory' ); | ||
|
||
let testServer = null; | ||
let browser; | ||
|
||
describe( 'Iframe interface', () => { | ||
// Start test server to serve wrapped content. | ||
beforeAll( async () => { | ||
testServer = new TestServer( { | ||
'page-a': path.join( dataDirectory, 'page-a' ), | ||
} ); | ||
await testServer.start(); | ||
|
||
browser = await puppeteer.launch(); | ||
} ); | ||
|
||
// Kill test server. | ||
afterAll( async () => { | ||
if ( testServer ) { | ||
await testServer.stop(); | ||
testServer = null; | ||
} | ||
if ( browser ) { | ||
await browser.close(); | ||
} | ||
} ); | ||
|
||
it( 'Successfully generates via iframes', async () => { | ||
const page = await browser.newPage(); | ||
page.on( 'console', msg => process.stderr.write( msg.text() + '\n\n' ) ); | ||
await page.goto( testServer.getUrl() ); | ||
|
||
const innerUrl = path.join( testServer.getUrl(), 'page-a' ); | ||
|
||
const [ css, warnings ] = await page.evaluate( url => { | ||
return CriticalCSSGenerator.generateCriticalCSS( { | ||
urls: [ url ], | ||
viewports: [ { width: 640, height: 480 } ], | ||
browserInterface: new CriticalCSSGenerator.BrowserInterfaceIframe( { | ||
verifyPage: ( url, innerWindow, innerDocument ) => { | ||
return !! innerDocument.querySelector( 'meta[name="testing-page"]' ); | ||
}, | ||
} ), | ||
} ); | ||
}, innerUrl ); | ||
|
||
expect( warnings ).toHaveLength( 0 ); | ||
expect( css ).toContain( 'div.top' ); | ||
|
||
await page.close(); | ||
} ); | ||
|
||
it( 'Allows scripts if not explicitly turned off', async () => { | ||
const page = await browser.newPage(); | ||
await page.goto( testServer.getUrl() ); | ||
|
||
const innerUrl = path.join( testServer.getUrl(), 'page-a' ); | ||
|
||
// Will throw an error if the inner page does not contain | ||
// 'script-created-content'; a string appended to page-a by a script. | ||
await page.evaluate( async url => { | ||
const iframeInterface = new CriticalCSSGenerator.BrowserInterfaceIframe( { | ||
verifyPage: ( url, innerWindow, innerDocument ) => { | ||
return innerDocument.documentElement.innerHTML.includes( 'script-created-content' ); | ||
}, | ||
} ); | ||
|
||
await iframeInterface.loadPage( url ); | ||
}, innerUrl ); | ||
|
||
await page.close(); | ||
} ); | ||
|
||
it( 'Blocks scripts if turned off', async () => { | ||
const page = await browser.newPage(); | ||
await page.goto( testServer.getUrl() ); | ||
|
||
const innerUrl = path.join( testServer.getUrl(), 'page-a' ); | ||
|
||
// Will throw an error if the inner page contains | ||
// 'script-created-content'; a string appended to page-a by a script. | ||
await page.evaluate( async url => { | ||
const iframeInterface = new CriticalCSSGenerator.BrowserInterfaceIframe( { | ||
verifyPage: ( url, innerWindow, innerDocument ) => { | ||
return ! innerDocument.documentElement.innerHTML.includes( 'script-created-content' ); | ||
}, | ||
allowScripts: false, | ||
} ); | ||
|
||
await iframeInterface.loadPage( url ); | ||
}, innerUrl ); | ||
|
||
await page.close(); | ||
} ); | ||
|
||
it( 'Can successfully generate using an iframe with JavaScript off', async () => { | ||
const page = await browser.newPage(); | ||
await page.goto( testServer.getUrl() ); | ||
|
||
const innerUrl = path.join( testServer.getUrl(), 'page-a' ); | ||
|
||
const [ css, warnings ] = await page.evaluate( url => { | ||
return CriticalCSSGenerator.generateCriticalCSS( { | ||
urls: [ url ], | ||
viewports: [ { width: 640, height: 480 } ], | ||
browserInterface: new CriticalCSSGenerator.BrowserInterfaceIframe( { | ||
verifyPage: ( url, innerWindow, innerDocument ) => { | ||
return !! innerDocument.querySelector( 'meta[name="testing-page"]' ); | ||
}, | ||
allowScripts: false, | ||
} ), | ||
} ); | ||
}, innerUrl ); | ||
|
||
expect( warnings ).toHaveLength( 0 ); | ||
expect( css ).toContain( 'div.top' ); | ||
|
||
await page.close(); | ||
} ); | ||
|
||
it( 'Throws an error if a successRatio is not met', async () => { | ||
const page = await browser.newPage(); | ||
await page.goto( testServer.getUrl() ); | ||
|
||
const innerUrl = path.join( testServer.getUrl(), 'page-a' ); | ||
|
||
await expect( async () => { | ||
await page.evaluate( () => { | ||
return CriticalCSSGenerator.generateCriticalCSS( { | ||
urls: [ 'about:blank', 'about:blank' ], | ||
viewports: [ { width: 640, height: 480 } ], | ||
browserInterface: new CriticalCSSGenerator.BrowserInterfaceIframe( { | ||
verifyPage: () => false, | ||
} ), | ||
successRatio: 0.5, | ||
} ); | ||
} ); | ||
} ).rejects.toThrow( /Insufficient pages loaded/ ); | ||
|
||
await page.close(); | ||
} ); | ||
|
||
it( 'Does not throw an error if successRatio is met', async () => { | ||
const page = await browser.newPage(); | ||
await page.goto( testServer.getUrl() ); | ||
|
||
const innerUrl = path.join( testServer.getUrl(), 'page-a' ); | ||
|
||
const [ css, warnings ] = await page.evaluate( url => { | ||
return CriticalCSSGenerator.generateCriticalCSS( { | ||
urls: [ 'about:blank', url ], | ||
viewports: [ { width: 640, height: 480 } ], | ||
browserInterface: new CriticalCSSGenerator.BrowserInterfaceIframe( { | ||
verifyPage: ( url, innerWindow, innerDocument ) => { | ||
return !! innerDocument.querySelector( 'meta[name="testing-page"]' ); | ||
}, | ||
} ), | ||
successRatio: 0.5, | ||
} ); | ||
}, innerUrl ); | ||
|
||
expect( warnings ).toHaveLength( 0 ); | ||
expect( css ).toContain( 'div.top' ); | ||
|
||
await page.close(); | ||
} ); | ||
|
||
it( 'Does not load more pages than the maxPages specifies', async () => { | ||
const page = await browser.newPage(); | ||
await page.goto( testServer.getUrl() ); | ||
|
||
const pageA = path.join( testServer.getUrl(), 'page-a' ); | ||
const pageB = path.join( testServer.getUrl(), 'page-b' ); | ||
|
||
const [ css, warnings, pages ] = await page.evaluate( | ||
async ( pageA, pageB ) => { | ||
let pagesVerified = []; | ||
const result = await CriticalCSSGenerator.generateCriticalCSS( { | ||
urls: [ 'about:blank', pageA, pageB, 'about:blank' ], | ||
viewports: [ { width: 640, height: 480 } ], | ||
browserInterface: new CriticalCSSGenerator.BrowserInterfaceIframe( { | ||
verifyPage: ( url, innerWindow, innerDocument ) => { | ||
pagesVerified.push( url ); | ||
return !! innerDocument.querySelector( 'meta[name="testing-page"]' ); | ||
}, | ||
} ), | ||
successRatio: 0.25, | ||
maxPages: 1, | ||
} ); | ||
|
||
return [ ...result, pagesVerified ]; | ||
}, | ||
pageA, | ||
pageB | ||
); | ||
|
||
expect( pages ).not.toContain( pageB ); | ||
expect( warnings ).toHaveLength( 0 ); | ||
expect( css ).toContain( 'div.top' ); | ||
|
||
await page.close(); | ||
} ); | ||
} ); |
Oops, something went wrong.