lighthouse 12.2.0-dev.20240902 → 12.2.0-dev.20240904
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/cli/test/smokehouse/lighthouse-runners/bundle.d.ts +4 -3
- package/cli/test/smokehouse/lighthouse-runners/bundle.js +16 -9
- package/cli/test/smokehouse/lighthouse-runners/cli.d.ts +4 -3
- package/cli/test/smokehouse/lighthouse-runners/cli.js +26 -32
- package/cli/test/smokehouse/lighthouse-runners/devtools.d.ts +3 -3
- package/cli/test/smokehouse/lighthouse-runners/devtools.js +8 -5
- package/cli/test/smokehouse/smokehouse.js +6 -1
- package/core/lib/third-party-web.d.ts +7 -0
- package/core/lib/third-party-web.js +13 -1
- package/package.json +1 -1
- package/types/internal/smokehouse.d.ts +2 -1
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
* Launch Chrome and do a full Lighthouse run via the Lighthouse DevTools bundle.
|
|
3
3
|
* @param {string} url
|
|
4
4
|
* @param {LH.Config=} config
|
|
5
|
+
* @param {LocalConsole=} logger
|
|
5
6
|
* @param {Smokehouse.SmokehouseOptions['testRunnerOptions']=} testRunnerOptions
|
|
6
|
-
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts
|
|
7
|
+
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts}>}
|
|
7
8
|
*/
|
|
8
|
-
export function runLighthouse(url: string, config?: LH.Config | undefined, testRunnerOptions?: Smokehouse.SmokehouseOptions["testRunnerOptions"] | undefined): Promise<{
|
|
9
|
+
export function runLighthouse(url: string, config?: LH.Config | undefined, logger?: LocalConsole | undefined, testRunnerOptions?: Smokehouse.SmokehouseOptions["testRunnerOptions"] | undefined): Promise<{
|
|
9
10
|
lhr: LH.Result;
|
|
10
11
|
artifacts: LH.Artifacts;
|
|
11
|
-
log: string;
|
|
12
12
|
}>;
|
|
13
|
+
import { LocalConsole } from '../lib/local-console.js';
|
|
13
14
|
//# sourceMappingURL=bundle.d.ts.map
|
|
@@ -18,9 +18,11 @@ import {once} from 'events';
|
|
|
18
18
|
|
|
19
19
|
import puppeteer from 'puppeteer-core';
|
|
20
20
|
import * as ChromeLauncher from 'chrome-launcher';
|
|
21
|
+
import thirdPartyWebLib from 'third-party-web/nostats-subset.js';
|
|
21
22
|
|
|
22
23
|
import {LH_ROOT} from '../../../../shared/root.js';
|
|
23
24
|
import {loadArtifacts, saveArtifacts} from '../../../../core/lib/asset-saver.js';
|
|
25
|
+
import {LocalConsole} from '../lib/local-console.js';
|
|
24
26
|
|
|
25
27
|
// This runs only in the worker. The rest runs on the main thread.
|
|
26
28
|
if (!isMainThread && parentPort) {
|
|
@@ -73,6 +75,11 @@ async function runBundledLighthouse(url, config, testRunnerOptions) {
|
|
|
73
75
|
// @ts-expect-error - not worth giving test global an actual type.
|
|
74
76
|
const lighthouse = global.runBundledLighthouse;
|
|
75
77
|
|
|
78
|
+
/** @type {import('../../../../core/lib/third-party-web.js')['default']} */
|
|
79
|
+
// @ts-expect-error
|
|
80
|
+
const thirdPartyWeb = global.thirdPartyWeb;
|
|
81
|
+
thirdPartyWeb.provideThirdPartyWeb(thirdPartyWebLib);
|
|
82
|
+
|
|
76
83
|
// Launch and connect to Chrome.
|
|
77
84
|
const launchedChrome = await ChromeLauncher.launch({
|
|
78
85
|
chromeFlags: [
|
|
@@ -105,12 +112,13 @@ async function runBundledLighthouse(url, config, testRunnerOptions) {
|
|
|
105
112
|
* Launch Chrome and do a full Lighthouse run via the Lighthouse DevTools bundle.
|
|
106
113
|
* @param {string} url
|
|
107
114
|
* @param {LH.Config=} config
|
|
115
|
+
* @param {LocalConsole=} logger
|
|
108
116
|
* @param {Smokehouse.SmokehouseOptions['testRunnerOptions']=} testRunnerOptions
|
|
109
|
-
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts
|
|
117
|
+
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts}>}
|
|
110
118
|
*/
|
|
111
|
-
async function runLighthouse(url, config, testRunnerOptions = {}) {
|
|
112
|
-
|
|
113
|
-
|
|
119
|
+
async function runLighthouse(url, config, logger, testRunnerOptions = {}) {
|
|
120
|
+
logger = logger || new LocalConsole();
|
|
121
|
+
|
|
114
122
|
const worker = new Worker(new URL(import.meta.url), {
|
|
115
123
|
stdout: true,
|
|
116
124
|
stderr: true,
|
|
@@ -119,16 +127,16 @@ async function runLighthouse(url, config, testRunnerOptions = {}) {
|
|
|
119
127
|
worker.stdout.setEncoding('utf8');
|
|
120
128
|
worker.stderr.setEncoding('utf8');
|
|
121
129
|
worker.stdout.addListener('data', (data) => {
|
|
122
|
-
|
|
130
|
+
logger.log(`[STDOUT] ${data}`);
|
|
123
131
|
});
|
|
124
132
|
worker.stderr.addListener('data', (data) => {
|
|
125
|
-
|
|
133
|
+
logger.log(`[STDERR] ${data}`);
|
|
126
134
|
});
|
|
127
135
|
const [workerResponse] = await once(worker, 'message');
|
|
128
|
-
const log = logs.join('') + '\n';
|
|
129
136
|
|
|
130
137
|
if (workerResponse.type === 'error') {
|
|
131
|
-
|
|
138
|
+
const log = logger.getLog();
|
|
139
|
+
throw new Error(`Worker returned an error: ${workerResponse.value}\nLog:\n${log}\n`);
|
|
132
140
|
}
|
|
133
141
|
|
|
134
142
|
const result = workerResponse.value;
|
|
@@ -142,7 +150,6 @@ async function runLighthouse(url, config, testRunnerOptions = {}) {
|
|
|
142
150
|
return {
|
|
143
151
|
lhr: result.lhr,
|
|
144
152
|
artifacts,
|
|
145
|
-
log,
|
|
146
153
|
};
|
|
147
154
|
}
|
|
148
155
|
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
* Launch Chrome and do a full Lighthouse run via the Lighthouse CLI.
|
|
3
3
|
* @param {string} url
|
|
4
4
|
* @param {LH.Config=} config
|
|
5
|
+
* @param {LocalConsole=} logger
|
|
5
6
|
* @param {Smokehouse.SmokehouseOptions['testRunnerOptions']=} testRunnerOptions
|
|
6
|
-
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts
|
|
7
|
+
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts}>}
|
|
7
8
|
*/
|
|
8
|
-
export function runLighthouse(url: string, config?: LH.Config | undefined, testRunnerOptions?: Smokehouse.SmokehouseOptions["testRunnerOptions"] | undefined): Promise<{
|
|
9
|
+
export function runLighthouse(url: string, config?: LH.Config | undefined, logger?: LocalConsole | undefined, testRunnerOptions?: Smokehouse.SmokehouseOptions["testRunnerOptions"] | undefined): Promise<{
|
|
9
10
|
lhr: LH.Result;
|
|
10
11
|
artifacts: LH.Artifacts;
|
|
11
|
-
log: string;
|
|
12
12
|
}>;
|
|
13
|
+
import { LocalConsole } from '../lib/local-console.js';
|
|
13
14
|
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import {promises as fs} from 'fs';
|
|
15
|
-
import {
|
|
16
|
-
import {execFile} from 'child_process';
|
|
15
|
+
import {spawn} from 'child_process';
|
|
17
16
|
|
|
18
17
|
import log from 'lighthouse-logger';
|
|
19
18
|
|
|
@@ -22,21 +21,20 @@ import {LocalConsole} from '../lib/local-console.js';
|
|
|
22
21
|
import {ChildProcessError} from '../lib/child-process-error.js';
|
|
23
22
|
import {LH_ROOT} from '../../../../shared/root.js';
|
|
24
23
|
|
|
25
|
-
const execFileAsync = promisify(execFile);
|
|
26
|
-
|
|
27
24
|
/**
|
|
28
25
|
* Launch Chrome and do a full Lighthouse run via the Lighthouse CLI.
|
|
29
26
|
* @param {string} url
|
|
30
27
|
* @param {LH.Config=} config
|
|
28
|
+
* @param {LocalConsole=} logger
|
|
31
29
|
* @param {Smokehouse.SmokehouseOptions['testRunnerOptions']=} testRunnerOptions
|
|
32
|
-
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts
|
|
30
|
+
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts}>}
|
|
33
31
|
*/
|
|
34
|
-
async function runLighthouse(url, config, testRunnerOptions = {}) {
|
|
32
|
+
async function runLighthouse(url, config, logger, testRunnerOptions = {}) {
|
|
35
33
|
const {isDebug} = testRunnerOptions;
|
|
36
34
|
const tmpDir = `${LH_ROOT}/.tmp/smokehouse`;
|
|
37
35
|
await fs.mkdir(tmpDir, {recursive: true});
|
|
38
36
|
const tmpPath = await fs.mkdtemp(`${tmpDir}/smokehouse-`);
|
|
39
|
-
return internalRun(url, tmpPath, config, testRunnerOptions)
|
|
37
|
+
return internalRun(url, tmpPath, config, logger, testRunnerOptions)
|
|
40
38
|
// Wait for internalRun() before removing scratch directory.
|
|
41
39
|
.finally(() => !isDebug && fs.rm(tmpPath, {recursive: true, force: true}));
|
|
42
40
|
}
|
|
@@ -46,12 +44,13 @@ async function runLighthouse(url, config, testRunnerOptions = {}) {
|
|
|
46
44
|
* @param {string} url
|
|
47
45
|
* @param {string} tmpPath
|
|
48
46
|
* @param {LH.Config=} config
|
|
47
|
+
* @param {LocalConsole=} logger
|
|
49
48
|
* @param {Smokehouse.SmokehouseOptions['testRunnerOptions']=} options
|
|
50
|
-
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts
|
|
49
|
+
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts}>}
|
|
51
50
|
*/
|
|
52
|
-
async function internalRun(url, tmpPath, config, options) {
|
|
51
|
+
async function internalRun(url, tmpPath, config, logger, options) {
|
|
53
52
|
const {isDebug, headless} = options || {};
|
|
54
|
-
|
|
53
|
+
logger = logger || new LocalConsole();
|
|
55
54
|
|
|
56
55
|
const outputPath = `${tmpPath}/smokehouse.report.json`;
|
|
57
56
|
const artifactsDirectory = `${tmpPath}/artifacts/`;
|
|
@@ -78,29 +77,25 @@ async function internalRun(url, tmpPath, config, options) {
|
|
|
78
77
|
|
|
79
78
|
const command = 'node';
|
|
80
79
|
const env = {...process.env, NODE_ENV: 'test'};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
localConsole.log(`exit code ${exitCode}`);
|
|
95
|
-
localConsole.log(`STDOUT: ${execResult.stdout}`);
|
|
96
|
-
localConsole.log(`STDERR: ${execResult.stderr}`);
|
|
80
|
+
logger.log(`${log.dim}$ ${command} ${args.join(' ')} ${log.reset}`);
|
|
81
|
+
|
|
82
|
+
const cp = spawn(command, args, {env});
|
|
83
|
+
cp.stdout.on('data', data => logger.log(`[STDOUT] ${data.toString().trim()}`));
|
|
84
|
+
cp.stderr.on('data', data => logger.log(`[STDERR] ${data.toString().trim()}`));
|
|
85
|
+
/** @type {Promise<number|null>} */
|
|
86
|
+
const cpPromise = new Promise((resolve, reject) => {
|
|
87
|
+
cp.addListener('exit', resolve);
|
|
88
|
+
cp.addListener('error', reject);
|
|
89
|
+
});
|
|
90
|
+
const exitCode = await cpPromise;
|
|
91
|
+
if (exitCode) {
|
|
92
|
+
logger.log(`exit code ${exitCode}`);
|
|
97
93
|
}
|
|
98
94
|
|
|
99
95
|
try {
|
|
100
96
|
await fs.access(outputPath);
|
|
101
97
|
} catch (e) {
|
|
102
|
-
throw new ChildProcessError(`Lighthouse run failed to produce a report
|
|
103
|
-
localConsole.getLog());
|
|
98
|
+
throw new ChildProcessError(`Lighthouse run failed to produce a report.`, logger.getLog());
|
|
104
99
|
}
|
|
105
100
|
|
|
106
101
|
/** @type {LH.Result} */
|
|
@@ -109,21 +104,20 @@ async function internalRun(url, tmpPath, config, options) {
|
|
|
109
104
|
|
|
110
105
|
// Output has been established as existing, so can log for debug.
|
|
111
106
|
if (isDebug) {
|
|
112
|
-
|
|
113
|
-
|
|
107
|
+
logger.log(`LHR output available at: ${outputPath}`);
|
|
108
|
+
logger.log(`Artifacts avaiable in: ${artifactsDirectory}`);
|
|
114
109
|
}
|
|
115
110
|
|
|
116
111
|
// There should either be both an error exitCode and a lhr.runtimeError or neither.
|
|
117
112
|
if (Boolean(exitCode) !== Boolean(lhr.runtimeError)) {
|
|
118
113
|
const runtimeErrorCode = lhr.runtimeError?.code;
|
|
119
114
|
throw new ChildProcessError(`Lighthouse did not exit with an error correctly, exiting with ${exitCode} but with runtimeError '${runtimeErrorCode}'`, // eslint-disable-line max-len
|
|
120
|
-
|
|
115
|
+
logger.getLog());
|
|
121
116
|
}
|
|
122
117
|
|
|
123
118
|
return {
|
|
124
119
|
lhr,
|
|
125
120
|
artifacts,
|
|
126
|
-
log: localConsole.getLog(),
|
|
127
121
|
};
|
|
128
122
|
}
|
|
129
123
|
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
* CHROME_PATH determines which Chrome is used–otherwise the default is puppeteer's chrome binary.
|
|
6
6
|
* @param {string} url
|
|
7
7
|
* @param {LH.Config=} config
|
|
8
|
+
* @param {import('../lib/local-console.js').LocalConsole=} logger
|
|
8
9
|
* @param {Smokehouse.SmokehouseOptions['testRunnerOptions']=} testRunnerOptions
|
|
9
|
-
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts
|
|
10
|
+
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts}>}
|
|
10
11
|
*/
|
|
11
|
-
export function runLighthouse(url: string, config?: LH.Config | undefined, testRunnerOptions?: Smokehouse.SmokehouseOptions["testRunnerOptions"] | undefined): Promise<{
|
|
12
|
+
export function runLighthouse(url: string, config?: LH.Config | undefined, logger?: import("../lib/local-console.js").LocalConsole | undefined, testRunnerOptions?: Smokehouse.SmokehouseOptions["testRunnerOptions"] | undefined): Promise<{
|
|
12
13
|
lhr: LH.Result;
|
|
13
14
|
artifacts: LH.Artifacts;
|
|
14
|
-
log: string;
|
|
15
15
|
}>;
|
|
16
16
|
/**
|
|
17
17
|
* Download/pull latest DevTools, build Lighthouse for DevTools, roll to DevTools, and build DevTools.
|
|
@@ -40,21 +40,24 @@ async function setup() {
|
|
|
40
40
|
* CHROME_PATH determines which Chrome is used–otherwise the default is puppeteer's chrome binary.
|
|
41
41
|
* @param {string} url
|
|
42
42
|
* @param {LH.Config=} config
|
|
43
|
+
* @param {import('../lib/local-console.js').LocalConsole=} logger
|
|
43
44
|
* @param {Smokehouse.SmokehouseOptions['testRunnerOptions']=} testRunnerOptions
|
|
44
|
-
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts
|
|
45
|
+
* @return {Promise<{lhr: LH.Result, artifacts: LH.Artifacts}>}
|
|
45
46
|
*/
|
|
46
|
-
async function runLighthouse(url, config, testRunnerOptions) {
|
|
47
|
+
async function runLighthouse(url, config, logger, testRunnerOptions) {
|
|
47
48
|
const chromeFlags = [
|
|
48
49
|
testRunnerOptions?.headless ? '--headless=new' : '',
|
|
49
50
|
`--custom-devtools-frontend=file://${devtoolsDir}/out/LighthouseIntegration/gen/front_end`,
|
|
50
51
|
];
|
|
52
|
+
// TODO: `testUrlFromDevtools` should accept a logger, so we get some output even for time outs.
|
|
51
53
|
const {lhr, artifacts, logs} = await testUrlFromDevtools(url, {
|
|
52
54
|
config,
|
|
53
55
|
chromeFlags,
|
|
54
56
|
});
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
if (logger) {
|
|
58
|
+
logger.log(logs.join('') + '\n');
|
|
59
|
+
}
|
|
60
|
+
return {lhr, artifacts};
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
export {
|
|
@@ -161,6 +161,8 @@ async function runSmokeTest(smokeTestDefn, testOptions) {
|
|
|
161
161
|
bufferedConsole.log(` Retrying run (${i} out of ${retries} retries)…`);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
const logger = new LocalConsole();
|
|
165
|
+
|
|
164
166
|
// Run Lighthouse.
|
|
165
167
|
try {
|
|
166
168
|
// Each individual runner has internal timeouts, but we've had bugs where
|
|
@@ -170,12 +172,13 @@ async function runSmokeTest(smokeTestDefn, testOptions) {
|
|
|
170
172
|
reject(new Error('Timed out waiting for provided lighthouseRunner')), 1000 * 120);
|
|
171
173
|
});
|
|
172
174
|
const timedResult = await Promise.race([
|
|
173
|
-
lighthouseRunner(requestedUrl, config, testRunnerOptions),
|
|
175
|
+
lighthouseRunner(requestedUrl, config, logger, testRunnerOptions),
|
|
174
176
|
timeoutPromise,
|
|
175
177
|
]);
|
|
176
178
|
result = {
|
|
177
179
|
...timedResult,
|
|
178
180
|
networkRequests: takeNetworkRequestUrls ? takeNetworkRequestUrls() : undefined,
|
|
181
|
+
log: logger.getLog(),
|
|
179
182
|
};
|
|
180
183
|
|
|
181
184
|
if (!result.lhr?.audits || !result.artifacts) {
|
|
@@ -188,6 +191,8 @@ async function runSmokeTest(smokeTestDefn, testOptions) {
|
|
|
188
191
|
if (takeNetworkRequestUrls) takeNetworkRequestUrls();
|
|
189
192
|
|
|
190
193
|
logChildProcessError(bufferedConsole, e);
|
|
194
|
+
bufferedConsole.log('Timed out. log from lighthouseRunner:');
|
|
195
|
+
bufferedConsole.log(logger.getLog());
|
|
191
196
|
continue; // Retry, if possible.
|
|
192
197
|
}
|
|
193
198
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
declare namespace _default {
|
|
2
|
+
export { provideThirdPartyWeb };
|
|
2
3
|
export { getEntity };
|
|
3
4
|
export { getProduct };
|
|
4
5
|
export { isThirdParty };
|
|
@@ -7,6 +8,12 @@ declare namespace _default {
|
|
|
7
8
|
export default _default;
|
|
8
9
|
export type ThirdPartyEntity = import("third-party-web").IEntity;
|
|
9
10
|
export type ThirdPartyProduct = import("third-party-web").IProduct;
|
|
11
|
+
/**
|
|
12
|
+
* For use by DevTools.
|
|
13
|
+
*
|
|
14
|
+
* @param {typeof import('third-party-web/nostats-subset.js')} providedThirdPartyWeb
|
|
15
|
+
*/
|
|
16
|
+
declare function provideThirdPartyWeb(providedThirdPartyWeb: typeof import("third-party-web/nostats-subset.js")): void;
|
|
10
17
|
/** @typedef {import("third-party-web").IEntity} ThirdPartyEntity */
|
|
11
18
|
/** @typedef {import("third-party-web").IProduct} ThirdPartyProduct */
|
|
12
19
|
/**
|
|
@@ -4,7 +4,18 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import thirdPartyWebLib from 'third-party-web/nostats-subset.js';
|
|
8
|
+
|
|
9
|
+
let thirdPartyWeb = thirdPartyWebLib;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* For use by DevTools.
|
|
13
|
+
*
|
|
14
|
+
* @param {typeof import('third-party-web/nostats-subset.js')} providedThirdPartyWeb
|
|
15
|
+
*/
|
|
16
|
+
function provideThirdPartyWeb(providedThirdPartyWeb) {
|
|
17
|
+
thirdPartyWeb = providedThirdPartyWeb;
|
|
18
|
+
}
|
|
8
19
|
|
|
9
20
|
/** @typedef {import("third-party-web").IEntity} ThirdPartyEntity */
|
|
10
21
|
/** @typedef {import("third-party-web").IProduct} ThirdPartyProduct */
|
|
@@ -45,6 +56,7 @@ function isFirstParty(url, mainDocumentEntity) {
|
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
export default {
|
|
59
|
+
provideThirdPartyWeb,
|
|
48
60
|
getEntity,
|
|
49
61
|
getProduct,
|
|
50
62
|
isThirdParty,
|
package/package.json
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import {Artifacts} from '../artifacts.js';
|
|
8
8
|
import Config from '../config.js';
|
|
9
9
|
import LHResult from '../lhr/lhr.js';
|
|
10
|
+
import {LocalConsole} from '../../cli/test/smokehouse/lib/local-console.js';
|
|
10
11
|
|
|
11
12
|
declare global {
|
|
12
13
|
module Smokehouse {
|
|
@@ -53,7 +54,7 @@ declare global {
|
|
|
53
54
|
{expectations: Smokehouse.ExpectedRunnerResult | Array<Smokehouse.ExpectedRunnerResult>}
|
|
54
55
|
|
|
55
56
|
export type LighthouseRunner =
|
|
56
|
-
{runnerName?: string} & ((url: string, config?: Config, runnerOptions?: {isDebug?: boolean}) => Promise<{lhr: LHResult, artifacts: Artifacts
|
|
57
|
+
{runnerName?: string} & ((url: string, config?: Config, logger?: LocalConsole, runnerOptions?: {isDebug?: boolean}) => Promise<{lhr: LHResult, artifacts: Artifacts}>);
|
|
57
58
|
|
|
58
59
|
export interface SmokehouseOptions {
|
|
59
60
|
/** Options to pass to the specific Lighthouse runner. */
|