Skip to content

Commit 8e352e4

Browse files
author
Mark George
committed
wip
1 parent 91e0ed5 commit 8e352e4

22 files changed

+4465
-4704
lines changed

index.d.ts

-43
This file was deleted.

index.js

-14
This file was deleted.

index.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export { BrowserInterfacePuppeteer } from './lib/browser-interface-puppeteer';
2+
export { BrowserInterfacePlaywright } from './lib/browser-interface-playwright';
3+
export { BrowserInterfaceIframe } from './lib/browser-interface-iframe';
4+
export { BrowserInterface } from './lib/browser-interface';
5+
export { generateCriticalCSS } from './lib/generate-critical-css';
6+
7+
export * from './lib/errors';
8+
9+
export const version = '0.0.2';

jest.config.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
module.exports = {
22
testMatch: [ '**/?(*.)+(spec|test).js' ],
3+
transform: {
4+
"^.+\\.ts?$": "ts-jest"
5+
},
36
setupFilesAfterEnv: [ './tests/config/jest-setup.js' ],
4-
collectCoverageFrom: [ 'lib/**/*.js', 'index.js' ],
7+
collectCoverageFrom: [ 'lib/**/*.ts', 'index.ts' ],
58
globalSetup: 'jest-environment-puppeteer/setup',
69
globalTeardown: 'jest-environment-puppeteer/teardown',
710
testEnvironment: 'jest-environment-puppeteer',

lib/browser-interface-iframe.js lib/browser-interface-iframe.ts

+53-34
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,48 @@
1-
const BrowserInterface = require( './browser-interface' );
2-
const {
1+
import { BrowserInterface } from './browser-interface';
2+
import {
33
CrossDomainError,
44
HttpError,
55
LoadTimeoutError,
66
RedirectError,
77
UrlVerifyError,
88
UnknownError,
99
XFrameDenyError,
10-
} = require( './errors' );
10+
UrlError,
11+
} from './errors';
12+
import { Viewport, NullableViewport } from './types';
1113

1214
const defaultLoadTimeout = 60 * 1000;
1315

14-
class BrowserInterfaceIframe extends BrowserInterface {
16+
type VerifyMethod = (
17+
rawUrl: string,
18+
contentWindow: Window,
19+
contentDocument: Document
20+
) => Promise< boolean >;
21+
22+
type BrowserInterfaceIframeOptions = {
23+
requestGetParameters?: { [ key: string ]: string };
24+
loadTimeout?: number;
25+
verifyPage: VerifyMethod;
26+
allowScripts?: boolean;
27+
};
28+
29+
export class BrowserInterfaceIframe extends BrowserInterface {
30+
private requestGetParameters: { [ key: string ]: string };
31+
private loadTimeout: number;
32+
private verifyPage: VerifyMethod;
33+
34+
private currentUrl: string | null;
35+
private currentSize: NullableViewport;
36+
37+
private wrapperDiv: HTMLDivElement;
38+
private iframe: HTMLIFrameElement;
39+
1540
constructor( {
1641
requestGetParameters,
1742
loadTimeout,
1843
verifyPage,
1944
allowScripts,
20-
} = {} ) {
45+
}: BrowserInterfaceIframeOptions ) {
2146
super();
2247

2348
this.requestGetParameters = requestGetParameters || {};
@@ -27,12 +52,8 @@ class BrowserInterfaceIframe extends BrowserInterface {
2752
// Default 'allowScripts' to true if not specified.
2853
allowScripts = allowScripts !== false;
2954

30-
if ( ! verifyPage ) {
31-
throw new Error( 'You must specify a page verification callback' );
32-
}
33-
3455
this.currentUrl = null;
35-
this.currentSize = { width: undefined, height: undefined };
56+
this.currentSize = { width: null, height: null };
3657

3758
// Create a wrapper div to keep the iframe invisible.
3859
this.wrapperDiv = document.createElement( 'div' );
@@ -56,24 +77,21 @@ class BrowserInterfaceIframe extends BrowserInterface {
5677
this.wrapperDiv.append( this.iframe );
5778
}
5879

59-
cleanup() {
80+
async cleanup() {
6081
this.iframe.remove();
6182
this.wrapperDiv.remove();
6283
}
6384

64-
/**
65-
* Wrapper for window.fetch. Overload this to change CSS or HTML fetching
66-
* behaviour.
67-
*
68-
* @param {string} url URL to fetch.
69-
* @param {Object} options Fetch options.
70-
* @param {string} _role 'css' or 'html' indicating what kind of thing is being fetched.
71-
*/
72-
async fetch( url, options, _role ) {
85+
async fetch( url: string, options: RequestInit, _role: 'css' | 'html' ) {
7386
return window.fetch( url, options );
7487
}
7588

76-
async runInPage( pageUrl, viewport, method, ...args ) {
89+
async runInPage< ReturnType >(
90+
pageUrl: string,
91+
viewport: Viewport | null,
92+
method: Function,
93+
...args: any[]
94+
): Promise< ReturnType > {
7795
await this.loadPage( pageUrl );
7896

7997
if ( viewport ) {
@@ -85,7 +103,7 @@ class BrowserInterfaceIframe extends BrowserInterface {
85103
return method( { innerWindow: this.iframe.contentWindow, args } );
86104
}
87105

88-
addGetParameters( rawUrl ) {
106+
addGetParameters( rawUrl: string ): string {
89107
const urlObject = new URL( rawUrl );
90108
for ( const key of Object.keys( this.requestGetParameters ) ) {
91109
urlObject.searchParams.append(
@@ -97,7 +115,7 @@ class BrowserInterfaceIframe extends BrowserInterface {
97115
return urlObject.toString();
98116
}
99117

100-
async diagnoseUrlError( url ) {
118+
async diagnoseUrlError( url: string ): Promise< UrlError | null > {
101119
try {
102120
const response = await this.fetch(
103121
url,
@@ -127,18 +145,18 @@ class BrowserInterfaceIframe extends BrowserInterface {
127145
}
128146
}
129147

130-
sameOrigin( url ) {
148+
sameOrigin( url: string ): boolean {
131149
return new URL( url ).origin === window.location.origin;
132150
}
133151

134-
async loadPage( rawUrl ) {
152+
async loadPage( rawUrl: string ): Promise< void > {
135153
if ( rawUrl === this.currentUrl ) {
136154
return;
137155
}
138156

139157
const fullUrl = this.addGetParameters( rawUrl );
140158

141-
await new Promise( ( resolve, rawReject ) => {
159+
return new Promise( ( resolve, rawReject ) => {
142160
// Track all URL errors.
143161
const reject = ( err ) => {
144162
this.trackUrlError( rawUrl, err );
@@ -153,18 +171,21 @@ class BrowserInterfaceIframe extends BrowserInterface {
153171

154172
// Set a timeout.
155173
const timeoutId = setTimeout( () => {
156-
this.iframe.onload = undefined;
174+
this.iframe.onload = null;
157175
reject( new LoadTimeoutError( { url: fullUrl } ) );
158176
}, this.loadTimeout );
159177

160178
// Catch load event.
161179
this.iframe.onload = async () => {
162180
try {
163-
this.iframe.onload = undefined;
181+
this.iframe.onload = null;
164182
clearTimeout( timeoutId );
165183

166184
// Verify the inner document is readable.
167-
if ( ! this.iframe.contentDocument ) {
185+
if (
186+
! this.iframe.contentDocument ||
187+
! this.iframe.contentWindow
188+
) {
168189
throw (
169190
( await this.diagnoseUrlError( fullUrl ) ) ||
170191
new CrossDomainError( { url: fullUrl } )
@@ -195,7 +216,7 @@ class BrowserInterfaceIframe extends BrowserInterface {
195216
} );
196217
}
197218

198-
async resize( { width, height } ) {
219+
async resize( { width, height }: Viewport ) {
199220
if (
200221
this.currentSize.width === width &&
201222
this.currentSize.height === height
@@ -205,13 +226,11 @@ class BrowserInterfaceIframe extends BrowserInterface {
205226

206227
return new Promise( ( resolve ) => {
207228
// Set iframe size.
208-
this.iframe.width = width;
209-
this.iframe.height = height;
229+
this.iframe.width = width.toString();
230+
this.iframe.height = height.toString();
210231

211232
// Bounce to browser main loop to allow resize to complete.
212233
setTimeout( resolve, 1 );
213234
} );
214235
}
215236
}
216-
217-
module.exports = BrowserInterfaceIframe;

lib/browser-interface-playwright.js lib/browser-interface-playwright.ts

+13-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
const BrowserInterface = require( './browser-interface' );
1+
import { Viewport } from './types';
2+
import { Page } from 'playwright';
3+
import { BrowserInterface } from './browser-interface';
24

3-
class BrowserInterfacePlaywright extends BrowserInterface {
4-
constructor( pages ) {
5+
export class BrowserInterfacePlaywright extends BrowserInterface {
6+
constructor( private pages: { [ url: string ]: Page } ) {
57
super();
6-
7-
this.pages = pages;
88
}
99

10-
async runInPage( pageUrl, viewport, method, ...args ) {
10+
async runInPage< ReturnType >(
11+
pageUrl: string,
12+
viewport: Viewport | null,
13+
method: Function,
14+
...args: any[]
15+
): Promise< ReturnType > {
1116
const page = this.pages[ pageUrl ];
1217

1318
if ( ! page ) {
@@ -23,7 +28,7 @@ class BrowserInterfacePlaywright extends BrowserInterface {
2328
// The inner window in Playwright is the directly accessible main window object.
2429
// The evaluating method does not need a separate window object.
2530
// Call inner method within the Playwright context.
26-
return page.evaluate( method, { innerWindow: null, args } );
31+
return page.evaluate( method.toString(), { innerWindow: null, args } );
2732
}
2833

2934
/**
@@ -34,10 +39,8 @@ class BrowserInterfacePlaywright extends BrowserInterface {
3439
* @param {Object} options Fetch options.
3540
* @param {string} _role 'css' or 'html' indicating what kind of thing is being fetched.
3641
*/
37-
async fetch( url, options, _role ) {
42+
async fetch( url: string, options: RequestInit, _role: 'css' | 'html' ) {
3843
const nodeFetch = require( 'node-fetch' );
3944
return nodeFetch( url, options );
4045
}
4146
}
42-
43-
module.exports = BrowserInterfacePlaywright;

lib/browser-interface-puppeteer.js lib/browser-interface-puppeteer.ts

+13-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
const BrowserInterface = require( './browser-interface' );
1+
import { Viewport } from './types';
2+
import { Page } from 'puppeteer';
3+
import { BrowserInterface } from './browser-interface';
24

3-
class BrowserInterfacePuppeteer extends BrowserInterface {
4-
constructor( pages ) {
5+
export class BrowserInterfacePuppeteer extends BrowserInterface {
6+
constructor( private pages: { [ url: string ]: Page } ) {
57
super();
6-
7-
this.pages = pages;
88
}
99

10-
async runInPage( pageUrl, viewport, method, ...args ) {
10+
async runInPage< ReturnType >(
11+
pageUrl: string,
12+
viewport: Viewport | null,
13+
method: Function,
14+
...args: any[]
15+
): Promise< ReturnType > {
1116
const page = this.pages[ pageUrl ];
1217

1318
if ( ! page ) {
@@ -26,7 +31,7 @@ class BrowserInterfacePuppeteer extends BrowserInterface {
2631
// The inner window in Puppeteer is the directly accessible main window object.
2732
// The evaluating method does not need a separate window object.
2833
// Call inner method within the Puppeteer context.
29-
return page.evaluate( method, { innerWindow: null, args } );
34+
return page.evaluate( method.toString(), { innerWindow: null, args } );
3035
}
3136

3237
/**
@@ -37,10 +42,8 @@ class BrowserInterfacePuppeteer extends BrowserInterface {
3742
* @param {Object} options Fetch options.
3843
* @param {string} _role 'css' or 'html' indicating what kind of thing is being fetched.
3944
*/
40-
async fetch( url, options, _role ) {
45+
async fetch( url: string, options: RequestInit, _role: 'css' | 'html' ) {
4146
const nodeFetch = require( 'node-fetch' );
4247
return nodeFetch( url, options );
4348
}
4449
}
45-
46-
module.exports = BrowserInterfacePuppeteer;

0 commit comments

Comments
 (0)