Permalink
Browse files

Fix: Refactor debugging protocol

Refactor how the interaction with the debugging protocol is handled
while fixing some of the most recent issues found.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Fix #1398
Fix #1395
Fix #1403
Fix webhintio/rfcs#44

Close #1409
  • Loading branch information...
molant authored and alrra committed Oct 16, 2018
1 parent 6ba19e0 commit 5d8a804c0344a0a10991ff81488fcbc0b700c0da
@@ -26,9 +26,18 @@ export class CDPLauncher extends Launcher {
private chromeFlags: Array<string>;
public constructor(options: LauncherOptions) {
super(options);
const flags = options && options.flags || ['--no-default-browser-check'];
this.chromeFlags = options && options.flags || ['--no-default-browser-check'];
/* istanbul ignore next */
if (isCI) {
flags.push('--headless', '--disable-gpu');
} else if (process.env.DOCKER === 'true') { // eslint-disable-line no-process-env
flags.push('--headless');
}
super(Object.assign({}, options, { flags: Array.from(new Set(flags)) }));
this.chromeFlags = flags;
// `userDataDir` is a property in `chrome-launcher`: https://github.com/GoogleChrome/chrome-launcher#launch-options
/* istanbul ignore next */
this.userDataDir = options && typeof options.defaultProfile === 'boolean' && options.defaultProfile ?
@@ -131,13 +140,6 @@ export class CDPLauncher extends Launcher {
}
try {
/* istanbul ignore next */
if (isCI) {
this.chromeFlags.push('--headless', '--disable-gpu');
} else if (process.env.DOCKER === 'true') { // eslint-disable-line no-process-env
this.chromeFlags.push('--headless');
}
const chrome: chromeLauncher.LaunchedChrome = await chromeLauncher.launch({
chromeFlags: this.chromeFlags,
connectionPollInterval: 1000,
@@ -55,11 +55,19 @@ const runTest = async (t: GenericTestContext<Context<any>>, ConnectorConstructor
await connector.close();
};
test(`[${name}] The HTML is downloaded and the right event emitted`, async (t) => {
const serverConfig: ServerConfiguration = { '/': generateHTMLPage(`<title>Test</title>`) };
await runTest(t, ChromeConnector, serverConfig);
t.is(t.context.engine.emitAsync.withArgs('fetch::end::html').callCount, 1);
});
test(`[${name}] Favicon is present in a 'link' element with 'rel' attribute set to 'icon' `, async (t) => {
const faviconInLinkElementDir = `http://localhost:${t.context.server.port}/images/favicon-32x32.png`;
const serverConfig: ServerConfiguration = {
'/': generateHTMLPage(`<link rel="icon" type="image/png" href="/images/favicon-32x32.png" sizes="32x32">`),
'/images/favicon-favicon-32x32.png': fs.readFileSync(pathToFaviconInLinkElement)
'/images/favicon-32x32.png': fs.readFileSync(pathToFaviconInLinkElement)
};
await runTest(t, ChromeConnector, serverConfig);
@@ -84,7 +92,7 @@ test(`[${name}] Favicon is present in both the root directory and the 'link' ele
const serverConfig: ServerConfiguration = {
'/': generateHTMLPage(`<link rel="icon" type="image/png" href="/images/favicon-32x32.png" sizes="32x32">`),
'/favicon.ico': fs.readFileSync(pathToFaviconInDir),
'/images/favicon-favicon-32x32.png': fs.readFileSync(pathToFaviconInLinkElement)
'/images/favicon-32x32.png': fs.readFileSync(pathToFaviconInLinkElement)
};
await runTest(t, ChromeConnector, serverConfig);
@@ -110,8 +118,9 @@ test(`[${name}] Favicon is present in both the root directory and the 'link' ele
test(`[${name}] Favicon is not present in either the root directory or the 'link' element`, async (t) => {
const faviconInRootDir = `http://localhost:${t.context.server.port}/favicon.ico`;
const serverConfig: ServerConfiguration = { '/': generateHTMLPage() };
await runTest(t, ChromeConnector);
await runTest(t, ChromeConnector, serverConfig);
// Requests to `/favicon.ico` are always sent when favicon doesn't exist as a `link` tag in the html.
t.is(t.context.engine.emitAsync.withArgs('fetch::end::image').callCount, 1);
@@ -134,7 +134,7 @@ const events = [
element: {
getAttribute(attr: string) {
if (attr === 'href') {
return 'test://fa.il';
return '/script5.js';
}
return '';
@@ -295,7 +295,7 @@ test(`[${name}] Events`, async (t) => {
}
// List of events that only have to be called once per execution
const singles = ['fetch::error', 'scan::start', 'scan::end', 'fetch::end::html'];
const singles = ['scan::start', 'scan::end', 'fetch::end::html'];
const groupedEvents = groupBy(invokes, (invoke) => {
return invoke[0];
});
@@ -42,6 +42,7 @@ export type BrowserInfo = {
export interface ILauncher {
launch(url: string, options?: any): Promise<BrowserInfo>;
options?: any;
}
export type LauncherOptions = {
@@ -30,7 +30,7 @@ export type FetchEnd = Event & {
/** The object emitted on `fetch::error::*` */
export type FetchError = Event & {
/** The element that initiated the request. */
element: IAsyncHTMLElement;
element: IAsyncHTMLElement | null;
/** The error found. */
error: any;
/** The redirects performed for the url. */
@@ -1,9 +1,10 @@
import { HttpHeaders } from 'hint/src/lib/types';
/** Normalize all keys of an `HttpHeader` to lowercase. */
export const normalizeHeaders = (headers?: HttpHeaders | null) => {
if (headers) {
return Object.keys(headers).reduce((result, key) => {
result[key.toLowerCase()] = headers[key];
result[key.toLowerCase().trim()] = headers[key];
return result;
}, {} as HttpHeaders);
Oops, something went wrong.

0 comments on commit 5d8a804

Please sign in to comment.