-
Notifications
You must be signed in to change notification settings - Fork 626
/
end-to-end.ts
206 lines (165 loc) · 6.7 KB
/
end-to-end.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
import * as isCI from 'is-ci';
import { launch, Browser, Frame, Page, Target } from 'puppeteer-core';
import test from 'ava';
import { chromiumFinder, misc } from '@hint/utils';
import { Server } from '@hint/utils-create-server';
import { Events, Results } from '../src/shared/types';
import { readFixture } from './helpers/fixtures';
const { delay } = misc;
const executablePath = chromiumFinder.getInstallationPath();
const pathToExtension = `${__dirname}/../bundle`;
const getPageFromTarget = async (target: Target) => {
/*
* TODO: Replace this hack with something more stable.
* See https://github.com/GoogleChrome/puppeteer/issues/4247
*/
(target as any)._targetInfo.type = 'page';
return await target.page();
};
/**
* Find the Puppeteer `Page` associated with the background script
* for the webhint browser extension.
*
* Needed because Chromium has a built-in extension with a background
* script that runs even when all other extensions are disabled. The
* order in which the background scripts are returned varies randomly
* between runs, so clear identification is necessary.
*
* @param browser The Puppeteer `Browser` instance to search.
* @returns The found page for the background script.
*/
const findBackgroundScriptPage = async (browser: Browser): Promise<Page> => {
const targets = await browser.targets();
const bgTargets = targets.filter((t) => {
return t.type() === 'background_page';
});
const matches = await Promise.all(bgTargets.map(async (t) => {
const page = await t.page();
// TODO: Rename `background-script.js` to make the ID more unique.
return await page.$('script[src="background-script.js"]');
}));
const bgTarget = bgTargets.filter((t, i) => {
return matches[i];
})[0];
return await bgTarget.page();
};
/**
* Find the Puppeteer `Page` associated with the devtools panel
* for the webhint browser extension.
*
* Needed because Puppeteer doesn't expose the devtools as a page.
*
* @param browser The Puppeteer `Browser` instance to search.
* @returns The found devtools panel for the extension.
*/
const findWebhintDevtoolsPanel = async (browser: Browser): Promise<Frame> => {
const targets = await browser.targets();
const devtoolsTarget = targets.filter((t) => {
return t.type() === 'other' && t.url().startsWith('chrome-devtools://');
})[0];
const devtoolsPage = await getPageFromTarget(devtoolsTarget);
await delay(500);
/*
* Select the last tab in the devtools by navigating left using keyboard
* shortcuts. The last tab will be webhint so long as we've only loaded
* one devtools extension.
*
* Based on https://github.com/GoogleChrome/puppeteer/issues/3699#issuecomment-450526587
*/
await devtoolsPage.keyboard.down('Control');
await devtoolsPage.keyboard.press('[');
await devtoolsPage.keyboard.up('Control');
await delay(500);
const webhintTarget = (await browser.targets()).filter((target) => {
return target.url().startsWith('chrome-extension://') &&
target.url().endsWith('/panel.html');
})[0];
const webhintPanelPage = await getPageFromTarget(webhintTarget);
const webhintPanelFrame = webhintPanelPage.frames()[0];
return webhintPanelFrame;
};
test('It runs end-to-end in a page', async (t) => {
const server = await Server.create({ configuration: await readFixture('missing-lang.html') });
const url = `http://localhost:${server.port}/`;
const browser = await launch({ executablePath });
const page = (await browser.pages())[0];
await page.goto(url);
const resultsPromise = page.evaluate(() => {
return new Promise<Results>((resolve) => {
let onMessage: ((events: Events) => void) = () => { };
window.chrome = {
runtime: {
onMessage: {
addListener: (fn: () => void) => {
onMessage = fn;
},
removeListener: () => { }
},
sendMessage: (event: Events) => {
if (event.requestConfig) {
onMessage({ config: {} });
}
if (event.results) {
resolve(event.results);
}
}
}
} as any;
});
});
await page.addScriptTag({ path: `${__dirname}/../bundle/content-script/webhint.js` });
const results = await resultsPromise;
t.not(results.categories.length, 0);
t.true(results.categories.some((category) => {
return category.hints.some((hint) => {
return hint.problems.some((problem) => {
return problem.message === '<html> element must have a lang attribute';
});
});
}), 'Missing `lang` attribute was not reported');
await browser.close();
server.stop();
});
// TODO: Get this working in CI (at least for Linux).
if (!isCI) {
test.skip('It runs end-to-end as an extension', async (t) => {
const server = await Server.create({ configuration: await readFixture('missing-lang.html') });
const url = `http://localhost:${server.port}/`;
const browser = await launch({
args: [
`--disable-extensions-except=${pathToExtension}`,
`--load-extension=${pathToExtension}`
],
defaultViewport: null,
devtools: true,
executablePath,
headless: false
});
const pages = await browser.pages();
await pages[0].goto(url);
await delay(500);
const backgroundPage = await findBackgroundScriptPage(browser);
const webhintPanel = await findWebhintDevtoolsPanel(browser);
await delay(500);
await webhintPanel.click('button[type="submit"]');
const results: Results = await backgroundPage.evaluate(() => {
return new Promise<Results>((resolve) => {
chrome.runtime.onMessage.addListener((message: Events) => {
if (message.results) {
resolve(message.results);
}
});
});
});
t.not(results.categories.length, 0);
t.true(results.categories.some((category) => {
return category.hints.some((hint) => {
return hint.problems.some((problem) => {
return problem.message === '<html> element must have a lang attribute';
});
});
}), 'Missing `lang` attribute was not reported');
await browser.close();
server.stop();
});
}