@wdio/lighthouse-service 9.0.0-alpha.369 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/auditor.d.ts +2 -2
- package/build/auditor.d.ts.map +1 -1
- package/build/constants.d.ts +1 -1
- package/build/constants.d.ts.map +1 -1
- package/build/gatherer/trace.d.ts +0 -1
- package/build/gatherer/trace.d.ts.map +1 -1
- package/build/index.js +1094 -117
- package/build/lighthouse/cri.d.ts +0 -1
- package/build/lighthouse/cri.d.ts.map +1 -1
- package/package.json +12 -10
- package/build/auditor.js +0 -147
- package/build/commands.js +0 -212
- package/build/constants.js +0 -150
- package/build/gatherer/devtools.js +0 -12
- package/build/gatherer/pwa.js +0 -66
- package/build/gatherer/trace.js +0 -233
- package/build/handler/network.js +0 -133
- package/build/lighthouse/cri.js +0 -29
- package/build/scripts/collectMetaElements.js +0 -37
- package/build/scripts/registerPerformanceObserverInPage.js +0 -23
- package/build/types.js +0 -1
- package/build/utils.js +0 -64
- /package/{LICENSE-MIT → LICENSE} +0 -0
package/build/gatherer/trace.js
DELETED
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'node:events';
|
|
2
|
-
import NetworkRecorder from 'lighthouse/lighthouse-core/lib/network-recorder.js';
|
|
3
|
-
import NetworkMonitor from 'lighthouse/lighthouse-core/gather/driver/network-monitor.js';
|
|
4
|
-
import ProtocolSession from 'lighthouse/lighthouse-core/fraggle-rock/gather/session.js';
|
|
5
|
-
import { waitForFullyLoaded } from 'lighthouse/lighthouse-core/gather/driver/wait-for-condition.js';
|
|
6
|
-
import logger from '@wdio/logger';
|
|
7
|
-
import registerPerformanceObserverInPage from '../scripts/registerPerformanceObserverInPage.js';
|
|
8
|
-
import { FRAME_LOAD_START_TIMEOUT, TRACING_TIMEOUT, MAX_TRACE_WAIT_TIME, CLICK_TRANSITION, NETWORK_RECORDER_EVENTS } from '../constants.js';
|
|
9
|
-
import { isSupportedUrl } from '../utils.js';
|
|
10
|
-
const log = logger('@wdio/lighthouse-service:TraceGatherer');
|
|
11
|
-
export default class TraceGatherer extends EventEmitter {
|
|
12
|
-
_session;
|
|
13
|
-
_page;
|
|
14
|
-
_driver;
|
|
15
|
-
_failingFrameLoadIds = [];
|
|
16
|
-
_pageLoadDetected = false;
|
|
17
|
-
_networkListeners = {};
|
|
18
|
-
_frameId;
|
|
19
|
-
_loaderId;
|
|
20
|
-
_pageUrl;
|
|
21
|
-
_networkStatusMonitor;
|
|
22
|
-
_networkMonitor;
|
|
23
|
-
_protocolSession;
|
|
24
|
-
_trace;
|
|
25
|
-
_traceStart;
|
|
26
|
-
_clickTraceTimeout;
|
|
27
|
-
_waitConditionPromises = [];
|
|
28
|
-
constructor(_session, _page, _driver) {
|
|
29
|
-
super();
|
|
30
|
-
this._session = _session;
|
|
31
|
-
this._page = _page;
|
|
32
|
-
this._driver = _driver;
|
|
33
|
-
NETWORK_RECORDER_EVENTS.forEach((method) => {
|
|
34
|
-
this._networkListeners[method] = (params) => this._networkStatusMonitor.dispatch({ method, params });
|
|
35
|
-
});
|
|
36
|
-
this._protocolSession = new ProtocolSession(_session);
|
|
37
|
-
this._networkMonitor = new NetworkMonitor(_session);
|
|
38
|
-
}
|
|
39
|
-
async startTracing(url) {
|
|
40
|
-
/**
|
|
41
|
-
* delete old trace
|
|
42
|
-
*/
|
|
43
|
-
delete this._trace;
|
|
44
|
-
/**
|
|
45
|
-
* register listener for network status monitoring
|
|
46
|
-
*/
|
|
47
|
-
this._networkStatusMonitor = new NetworkRecorder();
|
|
48
|
-
NETWORK_RECORDER_EVENTS.forEach((method) => {
|
|
49
|
-
this._session.on(method, this._networkListeners[method]);
|
|
50
|
-
});
|
|
51
|
-
this._traceStart = Date.now();
|
|
52
|
-
log.info(`Start tracing frame with url ${url}`);
|
|
53
|
-
await this._driver.beginTrace();
|
|
54
|
-
/**
|
|
55
|
-
* if this tracing was started from a click transition
|
|
56
|
-
* then we want to discard page trace if no load detected
|
|
57
|
-
*/
|
|
58
|
-
if (url === CLICK_TRANSITION) {
|
|
59
|
-
log.info('Start checking for page load for click');
|
|
60
|
-
this._clickTraceTimeout = setTimeout(async () => {
|
|
61
|
-
log.info('No page load detected, canceling trace');
|
|
62
|
-
return this.finishTracing();
|
|
63
|
-
}, FRAME_LOAD_START_TIMEOUT);
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* register performance observer
|
|
67
|
-
*/
|
|
68
|
-
await this._page.evaluateOnNewDocument(registerPerformanceObserverInPage);
|
|
69
|
-
this._waitConditionPromises.push(waitForFullyLoaded(this._protocolSession, this._networkMonitor, { timedOut: 1 }));
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* store frame id of frames that are being traced
|
|
73
|
-
*/
|
|
74
|
-
async onFrameNavigated(msgObj) {
|
|
75
|
-
if (!this.isTracing) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* page load failed, cancel tracing
|
|
80
|
-
*/
|
|
81
|
-
if (this._failingFrameLoadIds.includes(msgObj.frame.url)) {
|
|
82
|
-
this._waitConditionPromises = [];
|
|
83
|
-
this._frameId = '"unsuccessful loaded frame"';
|
|
84
|
-
this.finishTracing();
|
|
85
|
-
this.emit('tracingError', new Error(`Page with url "${msgObj.frame.url}" failed to load`));
|
|
86
|
-
if (this._clickTraceTimeout) {
|
|
87
|
-
clearTimeout(this._clickTraceTimeout);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* ignore event if
|
|
92
|
-
*/
|
|
93
|
-
if (
|
|
94
|
-
// we already detected a frameId before
|
|
95
|
-
this._frameId ||
|
|
96
|
-
// the event was thrown for a sub frame (e.g. iframe)
|
|
97
|
-
msgObj.frame.parentId ||
|
|
98
|
-
// we don't support the url of given frame
|
|
99
|
-
!isSupportedUrl(msgObj.frame.url)) {
|
|
100
|
-
log.info(`Ignore navigated frame with url ${msgObj.frame.url}`);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
this._frameId = msgObj.frame.id;
|
|
104
|
-
this._loaderId = msgObj.frame.loaderId;
|
|
105
|
-
this._pageUrl = msgObj.frame.url;
|
|
106
|
-
log.info(`Page load detected: ${this._pageUrl}, set frameId ${this._frameId}, set loaderId ${this._loaderId}`);
|
|
107
|
-
/**
|
|
108
|
-
* clear click tracing timeout if it's still waiting
|
|
109
|
-
*
|
|
110
|
-
* the reason we have to tie this to Page.frameNavigated instead of Page.frameStartedLoading
|
|
111
|
-
* is because the latter can sometimes occur without the former, which will cause a hang
|
|
112
|
-
* e.g. with duolingo's sign-in button
|
|
113
|
-
*/
|
|
114
|
-
if (this._clickTraceTimeout && !this._pageLoadDetected) {
|
|
115
|
-
log.info('Page load detected for click, clearing click trace timeout}');
|
|
116
|
-
this._pageLoadDetected = true;
|
|
117
|
-
clearTimeout(this._clickTraceTimeout);
|
|
118
|
-
}
|
|
119
|
-
this.emit('tracingStarted', msgObj.frame.id);
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* once the page load event has fired, we can grab some performance
|
|
123
|
-
* metrics and timing
|
|
124
|
-
*/
|
|
125
|
-
async onLoadEventFired() {
|
|
126
|
-
if (!this.isTracing) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Ensure that page is fully loaded and all metrics can be calculated.
|
|
131
|
-
*/
|
|
132
|
-
const loadPromise = Promise.all(this._waitConditionPromises).then(() => async () => {
|
|
133
|
-
/**
|
|
134
|
-
* ensure that we trace at least for 5s to ensure that we can
|
|
135
|
-
* calculate "interactive"
|
|
136
|
-
*/
|
|
137
|
-
const minTraceTime = TRACING_TIMEOUT - (Date.now() - (this._traceStart || 0));
|
|
138
|
-
if (minTraceTime > 0) {
|
|
139
|
-
log.info(`page load happen to quick, waiting ${minTraceTime}ms more`);
|
|
140
|
-
await new Promise((resolve) => setTimeout(resolve, minTraceTime));
|
|
141
|
-
}
|
|
142
|
-
return this.completeTracing();
|
|
143
|
-
});
|
|
144
|
-
const cleanupFn = await Promise.race([
|
|
145
|
-
loadPromise,
|
|
146
|
-
this.waitForMaxTimeout()
|
|
147
|
-
]);
|
|
148
|
-
this._waitConditionPromises = [];
|
|
149
|
-
return cleanupFn();
|
|
150
|
-
}
|
|
151
|
-
onFrameLoadFail(request) {
|
|
152
|
-
const frame = request.frame();
|
|
153
|
-
if (frame) {
|
|
154
|
-
this._failingFrameLoadIds.push(frame.url());
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
get isTracing() {
|
|
158
|
-
return typeof this._traceStart === 'number';
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* once tracing has finished capture trace logs into memory
|
|
162
|
-
*/
|
|
163
|
-
async completeTracing() {
|
|
164
|
-
const traceDuration = Date.now() - (this._traceStart || 0);
|
|
165
|
-
log.info(`Tracing completed after ${traceDuration}ms, capturing performance data for frame ${this._frameId}`);
|
|
166
|
-
/**
|
|
167
|
-
* download all tracing data
|
|
168
|
-
* in case it fails, continue without capturing any data
|
|
169
|
-
*/
|
|
170
|
-
try {
|
|
171
|
-
const traceEvents = await this._driver.endTrace();
|
|
172
|
-
/**
|
|
173
|
-
* modify pid of renderer frame to be the same as where tracing was started
|
|
174
|
-
* possibly related to https://github.com/GoogleChrome/lighthouse/issues/6968
|
|
175
|
-
*/
|
|
176
|
-
const startedInBrowserEvt = traceEvents.traceEvents.find(e => e.name === 'TracingStartedInBrowser');
|
|
177
|
-
const mainFrame = (startedInBrowserEvt &&
|
|
178
|
-
startedInBrowserEvt.args &&
|
|
179
|
-
startedInBrowserEvt.args.data.frames?.find((frame) => !frame.parent));
|
|
180
|
-
if (mainFrame && mainFrame.processId) {
|
|
181
|
-
const threadNameEvt = traceEvents.traceEvents.find(e => e.ph === 'R' &&
|
|
182
|
-
e.cat === 'blink.user_timing' && e.name === 'navigationStart' && e.args.data.isLoadingMainFrame);
|
|
183
|
-
if (threadNameEvt) {
|
|
184
|
-
log.info(`Replace mainFrame process id ${mainFrame.processId} with actual thread process id ${threadNameEvt.pid}`);
|
|
185
|
-
mainFrame.processId = threadNameEvt.pid;
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
log.info(`Couldn't replace mainFrame process id ${mainFrame.processId} with actual thread process id`);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
this._trace = {
|
|
192
|
-
...traceEvents,
|
|
193
|
-
frameId: this._frameId,
|
|
194
|
-
loaderId: this._loaderId,
|
|
195
|
-
pageUrl: this._pageUrl,
|
|
196
|
-
traceStart: this._traceStart,
|
|
197
|
-
traceEnd: Date.now()
|
|
198
|
-
};
|
|
199
|
-
this.emit('tracingComplete', this._trace);
|
|
200
|
-
this.finishTracing();
|
|
201
|
-
}
|
|
202
|
-
catch (err) {
|
|
203
|
-
log.error(`Error capturing tracing logs: ${err.stack}`);
|
|
204
|
-
this.emit('tracingError', err);
|
|
205
|
-
return this.finishTracing();
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* clear tracing states and emit tracingFinished
|
|
210
|
-
*/
|
|
211
|
-
finishTracing() {
|
|
212
|
-
log.info(`Tracing for ${this._frameId} completed`);
|
|
213
|
-
this._pageLoadDetected = false;
|
|
214
|
-
/**
|
|
215
|
-
* clean up the listeners
|
|
216
|
-
*/
|
|
217
|
-
NETWORK_RECORDER_EVENTS.forEach((method) => this._session.off(method, this._networkListeners[method]));
|
|
218
|
-
delete this._networkStatusMonitor;
|
|
219
|
-
delete this._traceStart;
|
|
220
|
-
delete this._frameId;
|
|
221
|
-
delete this._loaderId;
|
|
222
|
-
delete this._pageUrl;
|
|
223
|
-
this._failingFrameLoadIds = [];
|
|
224
|
-
this._waitConditionPromises = [];
|
|
225
|
-
this.emit('tracingFinished');
|
|
226
|
-
}
|
|
227
|
-
waitForMaxTimeout(maxWaitForLoadedMs = MAX_TRACE_WAIT_TIME) {
|
|
228
|
-
return new Promise((resolve) => setTimeout(resolve, maxWaitForLoadedMs)).then(() => async () => {
|
|
229
|
-
log.error('Neither network nor CPU idle time could be detected within timeout, wrapping up tracing');
|
|
230
|
-
return this.completeTracing();
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
}
|
package/build/handler/network.js
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { IGNORED_URLS } from '../constants.js';
|
|
2
|
-
export default class NetworkHandler {
|
|
3
|
-
requestLog = { requests: [] };
|
|
4
|
-
requestTypes = {};
|
|
5
|
-
cachedFirstRequest;
|
|
6
|
-
constructor(session) {
|
|
7
|
-
session.on('Network.dataReceived', this.onDataReceived.bind(this));
|
|
8
|
-
session.on('Network.responseReceived', this.onNetworkResponseReceived.bind(this));
|
|
9
|
-
session.on('Network.requestWillBeSent', this.onNetworkRequestWillBeSent.bind(this));
|
|
10
|
-
session.on('Page.frameNavigated', this.onPageFrameNavigated.bind(this));
|
|
11
|
-
}
|
|
12
|
-
findRequest(params) {
|
|
13
|
-
let request = this.requestLog.requests.find((req) => req.id === params.requestId);
|
|
14
|
-
/**
|
|
15
|
-
* If no match is found, check if the corresponding request is the cached first request
|
|
16
|
-
*/
|
|
17
|
-
if (!request && this.cachedFirstRequest && this.cachedFirstRequest.id === params.requestId) {
|
|
18
|
-
request = this.cachedFirstRequest;
|
|
19
|
-
}
|
|
20
|
-
return request;
|
|
21
|
-
}
|
|
22
|
-
onDataReceived(params) {
|
|
23
|
-
const request = this.findRequest(params);
|
|
24
|
-
/**
|
|
25
|
-
* ensure that
|
|
26
|
-
* - a requestWillBeSent event was triggered before
|
|
27
|
-
* - the request type is accurate and known (sometimes this is not the case when `Network.requestWillBeSent` is triggered)
|
|
28
|
-
*/
|
|
29
|
-
if (!request || !request.type || !this.requestTypes[request.type]) {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
const type = request.type;
|
|
33
|
-
const requestType = this.requestTypes[type] || {};
|
|
34
|
-
requestType.size += params.dataLength;
|
|
35
|
-
requestType.encoded += params.encodedDataLength;
|
|
36
|
-
}
|
|
37
|
-
onNetworkResponseReceived(params) {
|
|
38
|
-
const request = this.findRequest(params);
|
|
39
|
-
/**
|
|
40
|
-
* ensure that a requestWillBeSent event was triggered before
|
|
41
|
-
*/
|
|
42
|
-
if (!request) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
request.statusCode = params.response.status;
|
|
46
|
-
request.requestHeaders = params.response.requestHeaders;
|
|
47
|
-
request.responseHeaders = params.response.headers;
|
|
48
|
-
request.timing = params.response.timing;
|
|
49
|
-
request.type = params.type;
|
|
50
|
-
}
|
|
51
|
-
onNetworkRequestWillBeSent(params) {
|
|
52
|
-
let isFirstRequestOfFrame = false;
|
|
53
|
-
if (
|
|
54
|
-
/**
|
|
55
|
-
* A new page was opened when request type is a document.
|
|
56
|
-
* The first request is sent before the Page.frameNavigated event is triggered,
|
|
57
|
-
* so this request must be cached to be able to add it to the requestLog later.
|
|
58
|
-
*/
|
|
59
|
-
params.type === 'Document' &&
|
|
60
|
-
/**
|
|
61
|
-
* ensure that only page loads triggered by non scripts (devtools only) are considered
|
|
62
|
-
* new page loads
|
|
63
|
-
*/
|
|
64
|
-
params.initiator.type === 'other' &&
|
|
65
|
-
/**
|
|
66
|
-
* ignore pages not initated by the user
|
|
67
|
-
*/
|
|
68
|
-
IGNORED_URLS.filter((url) => params.request.url.startsWith(url)).length === 0) {
|
|
69
|
-
isFirstRequestOfFrame = true;
|
|
70
|
-
/**
|
|
71
|
-
* reset the request type sizes
|
|
72
|
-
*/
|
|
73
|
-
this.requestTypes = {};
|
|
74
|
-
}
|
|
75
|
-
const log = {
|
|
76
|
-
id: params.requestId,
|
|
77
|
-
url: params.request.url,
|
|
78
|
-
method: params.request.method
|
|
79
|
-
};
|
|
80
|
-
if (params.redirectResponse) {
|
|
81
|
-
log.redirect = {
|
|
82
|
-
url: params.redirectResponse.url,
|
|
83
|
-
statusCode: params.redirectResponse.status,
|
|
84
|
-
requestHeaders: params.redirectResponse.requestHeaders,
|
|
85
|
-
responseHeaders: params.redirectResponse.headers,
|
|
86
|
-
timing: params.redirectResponse.timing
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
if (params.type) {
|
|
90
|
-
const requestType = this.requestTypes[params.type];
|
|
91
|
-
if (!requestType) {
|
|
92
|
-
this.requestTypes[params.type] = {
|
|
93
|
-
size: 0,
|
|
94
|
-
encoded: 0,
|
|
95
|
-
count: 1
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
else if (requestType) {
|
|
99
|
-
requestType.count++;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (isFirstRequestOfFrame) {
|
|
103
|
-
log.loaderId = params.loaderId;
|
|
104
|
-
this.cachedFirstRequest = log;
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
return this.requestLog.requests.push(log);
|
|
108
|
-
}
|
|
109
|
-
onPageFrameNavigated(params) {
|
|
110
|
-
/**
|
|
111
|
-
* Only create a requestLog for pages that don't have a parent frame.
|
|
112
|
-
* I.e. iframes are ignored
|
|
113
|
-
*/
|
|
114
|
-
if (!params.frame.parentId && IGNORED_URLS.filter((url) => params.frame.url.startsWith(url)).length === 0) {
|
|
115
|
-
this.requestLog = {
|
|
116
|
-
id: params.frame.loaderId,
|
|
117
|
-
url: params.frame.url,
|
|
118
|
-
requests: []
|
|
119
|
-
};
|
|
120
|
-
/**
|
|
121
|
-
* Add the first request that was cached before the actual requestLog could be created
|
|
122
|
-
*/
|
|
123
|
-
if (this.cachedFirstRequest && this.cachedFirstRequest.loaderId === params.frame.loaderId) {
|
|
124
|
-
/**
|
|
125
|
-
* Delete the loaderId of the first request so that all request data has the same structure
|
|
126
|
-
*/
|
|
127
|
-
delete this.cachedFirstRequest.loaderId;
|
|
128
|
-
this.requestLog.requests.push(this.cachedFirstRequest);
|
|
129
|
-
this.cachedFirstRequest = undefined;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
package/build/lighthouse/cri.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import CriConnection from 'lighthouse/lighthouse-core/gather/connections/cri.js';
|
|
2
|
-
const DEFAULT_HOSTNAME = 'localhost';
|
|
3
|
-
const DEFAULT_PORT = '9222';
|
|
4
|
-
/**
|
|
5
|
-
* this class got patched to enable connecting to a remote path like
|
|
6
|
-
* ws://192.168.0.39:4444/session/349a44a32846c2659c703e71403bd472/se/cdp
|
|
7
|
-
* as it requires to attach to a session before.
|
|
8
|
-
*/
|
|
9
|
-
export default class ChromeProtocolPatched extends CriConnection {
|
|
10
|
-
_sessionId;
|
|
11
|
-
/**
|
|
12
|
-
* Add constructor for typing safety
|
|
13
|
-
* @param {number=} port Optional port number. Defaults to 9222;
|
|
14
|
-
* @param {string=} hostname Optional hostname. Defaults to localhost.
|
|
15
|
-
* @constructor
|
|
16
|
-
*/
|
|
17
|
-
constructor(port = DEFAULT_PORT, hostname = DEFAULT_HOSTNAME) {
|
|
18
|
-
super(port, hostname);
|
|
19
|
-
}
|
|
20
|
-
setSessionId(sessionId) {
|
|
21
|
-
this._sessionId = sessionId;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* force every command to be send with the given session id
|
|
25
|
-
*/
|
|
26
|
-
sendCommand(method, sessionId, ...paramArgs) {
|
|
27
|
-
return super.sendCommand(method, sessionId || this._sessionId, ...paramArgs);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export default function collectMetaElements() {
|
|
2
|
-
const selector = 'head meta';
|
|
3
|
-
const realMatchesFn = window.Element.prototype.matches;
|
|
4
|
-
const metas = [];
|
|
5
|
-
const _findAllElements = (nodes) => {
|
|
6
|
-
// eslint-disable-next-line no-cond-assign
|
|
7
|
-
for (let i = 0, el; el = nodes[i]; ++i) {
|
|
8
|
-
if (!selector || realMatchesFn.call(el, selector)) {
|
|
9
|
-
metas.push(el);
|
|
10
|
-
}
|
|
11
|
-
// If the element has a shadow root, dig deeper.
|
|
12
|
-
if (el.shadowRoot) {
|
|
13
|
-
_findAllElements(el.shadowRoot.querySelectorAll('*'));
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
_findAllElements(document.querySelectorAll('*'));
|
|
18
|
-
return metas.map(meta => {
|
|
19
|
-
const getAttribute = (name) => {
|
|
20
|
-
const attr = meta.attributes.getNamedItem(name);
|
|
21
|
-
if (!attr) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
return attr.value;
|
|
25
|
-
};
|
|
26
|
-
return {
|
|
27
|
-
// @ts-ignore
|
|
28
|
-
name: meta.name.toLowerCase(),
|
|
29
|
-
// @ts-ignore
|
|
30
|
-
content: meta.content,
|
|
31
|
-
property: getAttribute('property'),
|
|
32
|
-
// @ts-ignore
|
|
33
|
-
httpEquiv: meta.httpEquiv ? meta.httpEquiv.toLowerCase() : undefined,
|
|
34
|
-
charset: getAttribute('charset'),
|
|
35
|
-
};
|
|
36
|
-
});
|
|
37
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Used by _waitForCPUIdle and executed in the context of the page, updates the ____lastLongTask
|
|
3
|
-
* property on window to the end time of the last long task.
|
|
4
|
-
*/
|
|
5
|
-
export default function registerPerformanceObserverInPage() {
|
|
6
|
-
window.____lastLongTask = window.performance.now();
|
|
7
|
-
const observer = new window.PerformanceObserver(entryList => {
|
|
8
|
-
const entries = entryList.getEntries();
|
|
9
|
-
for (const entry of entries) {
|
|
10
|
-
if (entry.entryType === 'longtask') {
|
|
11
|
-
const taskEnd = entry.startTime + entry.duration;
|
|
12
|
-
window.____lastLongTask = Math.max(window.____lastLongTask, taskEnd);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
observer.observe({ entryTypes: ['longtask'] });
|
|
17
|
-
// HACK: A PerformanceObserver will be GC'd if there are no more references to it, so attach it to
|
|
18
|
-
// window to ensure we still receive longtask notifications. See https://crbug.com/742530.
|
|
19
|
-
// For an example test of this behavior see https://gist.github.com/patrickhulce/69d8bed1807e762218994b121d06fea6.
|
|
20
|
-
// FIXME COMPAT: This hack isn't neccessary as of Chrome 62.0.3176.0
|
|
21
|
-
// https://bugs.chromium.org/p/chromium/issues/detail?id=742530#c7
|
|
22
|
-
window.____lhPerformanceObserver = observer;
|
|
23
|
-
}
|
package/build/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/build/utils.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import Driver from 'lighthouse/lighthouse-core/gather/driver.js';
|
|
2
|
-
import ChromeProtocol from './lighthouse/cri.js';
|
|
3
|
-
import { IGNORED_URLS, UNSUPPORTED_ERROR_MESSAGE } from './constants.js';
|
|
4
|
-
const CUSTOM_COMMANDS = [
|
|
5
|
-
'getMetrics',
|
|
6
|
-
'startTracing',
|
|
7
|
-
'getDiagnostics',
|
|
8
|
-
'getCoverageReport',
|
|
9
|
-
'enablePerformanceAudits',
|
|
10
|
-
'disablePerformanceAudits',
|
|
11
|
-
'getMainThreadWorkBreakdown',
|
|
12
|
-
'checkPWA'
|
|
13
|
-
];
|
|
14
|
-
export function setUnsupportedCommand(browser) {
|
|
15
|
-
for (const command of CUSTOM_COMMANDS) {
|
|
16
|
-
browser.addCommand(command, /* istanbul ignore next */ () => {
|
|
17
|
-
throw new Error(UNSUPPORTED_ERROR_MESSAGE);
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Create a sum of a specific key from a list of objects
|
|
23
|
-
* @param list list of key/value objects
|
|
24
|
-
* @param key key of value to be summed up
|
|
25
|
-
*/
|
|
26
|
-
export function sumByKey(list, key) {
|
|
27
|
-
return list.map((data) => data[key]).reduce((acc, val) => acc + val, 0);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* check if url is supported for tracing
|
|
31
|
-
* @param {string} url to check for
|
|
32
|
-
* @return {Boolean} true if url was opened by user
|
|
33
|
-
*/
|
|
34
|
-
export function isSupportedUrl(url) {
|
|
35
|
-
return IGNORED_URLS.filter((ignoredUrl) => url.startsWith(ignoredUrl)).length === 0;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Either request the page list directly from the browser or if Selenium
|
|
39
|
-
* or Selenoid is used connect to a target manually
|
|
40
|
-
*/
|
|
41
|
-
export async function getLighthouseDriver(session, target) {
|
|
42
|
-
const connection = session.connection();
|
|
43
|
-
if (!connection) {
|
|
44
|
-
throw new Error('Couldn\'t find a CDP connection');
|
|
45
|
-
}
|
|
46
|
-
const cUrl = new URL(connection.url());
|
|
47
|
-
const cdpConnection = new ChromeProtocol(cUrl.port, cUrl.hostname);
|
|
48
|
-
/**
|
|
49
|
-
* only create a new DevTools session if our WebSocket url doesn't already indicate
|
|
50
|
-
* that we are using one
|
|
51
|
-
*/
|
|
52
|
-
if (!cUrl.pathname.startsWith('/devtools/browser')) {
|
|
53
|
-
await cdpConnection._connectToSocket({
|
|
54
|
-
webSocketDebuggerUrl: connection.url(),
|
|
55
|
-
id: (await target.asPage()).mainFrame()._id
|
|
56
|
-
});
|
|
57
|
-
const { sessionId } = await cdpConnection.sendCommand('Target.attachToTarget', undefined, { targetId: (await target.asPage()).mainFrame()._id, flatten: true });
|
|
58
|
-
cdpConnection.setSessionId(sessionId);
|
|
59
|
-
return new Driver(cdpConnection);
|
|
60
|
-
}
|
|
61
|
-
const list = await cdpConnection._runJsonCommand('list');
|
|
62
|
-
await cdpConnection._connectToSocket(list[0]);
|
|
63
|
-
return new Driver(cdpConnection);
|
|
64
|
-
}
|
/package/{LICENSE-MIT → LICENSE}
RENAMED
|
File without changes
|