UNPKG

4.65 kBJavaScriptView Raw
1"use strict";
2/**
3 * @license
4 * Copyright (c) 2020 The Polymer Project Authors. All rights reserved.
5 * This code may only be used under the BSD style license found at
6 * http://polymer.github.io/LICENSE.txt The complete set of authors may be found
7 * at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
8 * be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
9 * Google as part of the polymer project is also subject to an additional IP
10 * rights grant found at http://polymer.github.io/PATENTS.txt
11 */
12Object.defineProperty(exports, "__esModule", { value: true });
13exports.measurementName = exports.measure = void 0;
14const util_1 = require("./util");
15/**
16 * Try to take a measurement in milliseconds from the given browser. Returns
17 * undefined if the measurement is not available (which may just mean we need to
18 * wait some more time).
19 */
20async function measure(driver, measurement, server) {
21 switch (measurement.mode) {
22 case 'callback':
23 if (server === undefined) {
24 throw new Error('Internal error: no server for spec');
25 }
26 return (await server.nextResults()).millis;
27 case 'expression':
28 return queryForExpression(driver, measurement.expression);
29 case 'performance':
30 return queryForPerformanceEntry(driver, measurement);
31 }
32 util_1.throwUnreachable(measurement, `Internal error: unknown measurement type ` +
33 JSON.stringify(measurement));
34}
35exports.measure = measure;
36/**
37 * Query the browser for the Performance Entry matching the given criteria.
38 * Returns undefined if no matching entry is found. Throws if the performance
39 * entry has an unsupported type. If there are multiple entries matching the
40 * same criteria, returns only the first one.
41 */
42async function queryForPerformanceEntry(driver, measurement) {
43 const escaped = escapeStringLiteral(measurement.entryName);
44 const script = `return window.performance.getEntriesByName(\`${escaped}\`);`;
45 const entries = await driver.executeScript(script);
46 if (entries.length === 0) {
47 return undefined;
48 }
49 if (entries.length > 1) {
50 console.log('WARNING: Found multiple performance marks/measurements with name ' +
51 `"${measurement.entryName}". This likely indicates an error. ` +
52 'Picking the first one.');
53 }
54 const entry = entries[0];
55 switch (entry.entryType) {
56 case 'measure':
57 return entry.duration;
58 case 'mark':
59 case 'paint':
60 return entry.startTime;
61 default:
62 // We may want to support other entry types, but we'll need to investigate
63 // how to interpret them, and we may need additional criteria to decide
64 // which exact numbers to report from them.
65 throw new Error(`Performance entry type not supported: ${entry.entryType}`);
66 }
67}
68/**
69 * Execute the given expression in the browser and return the result, if it is a
70 * positive number. If null or undefined, returns undefined. If some other type,
71 * throws.
72 */
73async function queryForExpression(driver, expression) {
74 const result = await driver.executeScript(`return (${expression});`);
75 if (result !== undefined && result !== null) {
76 if (typeof result !== 'number') {
77 throw new Error(`'${expression}' was type ` +
78 `${typeof result}, expected number.`);
79 }
80 if (result < 0) {
81 throw new Error(`'${expression}' was negative: ${result}`);
82 }
83 return result;
84 }
85}
86/**
87 * Escape a string such that it can be safely embedded in a JavaScript template
88 * literal (backtick string).
89 */
90function escapeStringLiteral(unescaped) {
91 return unescaped.replace(/\\/g, '\\\\')
92 .replace(/`/g, '\\`')
93 .replace(/\$/g, '\\$');
94}
95/**
96 * Return a good-enough label for the given measurement, to disambiguate cases
97 * where there are multiple measurements on the same page.
98 */
99function measurementName(measurement) {
100 if (measurement.name) {
101 return measurement.name;
102 }
103 switch (measurement.mode) {
104 case 'callback':
105 return 'callback';
106 case 'expression':
107 return measurement.expression;
108 case 'performance':
109 return measurement.entryName === 'first-contentful-paint' ?
110 'fcp' :
111 measurement.entryName;
112 }
113 util_1.throwUnreachable(measurement, `Internal error: unknown measurement type ` +
114 JSON.stringify(measurement));
115}
116exports.measurementName = measurementName;
117//# sourceMappingURL=measure.js.map
\No newline at end of file