From ca1a32a6584220abaa3779926a12a6cf683fb06c Mon Sep 17 00:00:00 2001 From: Patrick Hulce Date: Mon, 19 Jul 2021 20:57:50 -0500 Subject: [PATCH 1/2] cli(fr): add --fraggle-rock flag --- lighthouse-cli/cli-flags.js | 5 +++ lighthouse-cli/run.js | 23 ++++++++++++- lighthouse-core/fraggle-rock/config/config.js | 2 +- .../fraggle-rock/gather/navigation-runner.js | 6 ++-- .../fraggle-rock/gather/snapshot-runner.js | 5 +-- .../fraggle-rock/gather/timespan-runner.js | 5 +-- .../gather/navigation-runner-test.js | 34 +++++++++++++++++-- .../gather/snapshot-runner-test.js | 18 ++++++++++ .../gather/timespan-runner-test.js | 18 ++++++++++ types/config.d.ts | 10 ++++++ types/externs.d.ts | 2 ++ 11 files changed, 117 insertions(+), 11 deletions(-) diff --git a/lighthouse-cli/cli-flags.js b/lighthouse-cli/cli-flags.js index 49756c9fd9c7..aab061061cd0 100644 --- a/lighthouse-cli/cli-flags.js +++ b/lighthouse-cli/cli-flags.js @@ -108,6 +108,11 @@ function getFlags(manualArgv, options = {}) { default: false, describe: 'Print the normalized config for the given config and options, then exit.', }, + 'fraggle-rock': { + type: 'boolean', + default: false, + describe: '[EXPERIMENTAL] Use the new Fraggle Rock navigation runner to gather results.', + }, 'additional-trace-categories': { type: 'string', describe: 'Additional categories to capture with the trace (comma-delimited).', diff --git a/lighthouse-cli/run.js b/lighthouse-cli/run.js index a0898ec09d72..0c780e610fc1 100644 --- a/lighthouse-cli/run.js +++ b/lighthouse-cli/run.js @@ -195,6 +195,25 @@ async function potentiallyKillChrome(launchedChrome) { }); } +/** + * @param {string} url + * @param {LH.CliFlags} flags + * @param {LH.Config.Json|undefined} config + * @param {ChromeLauncher.LaunchedChrome} launchedChrome + * @return {Promise} + */ +async function runLighthouseWithFraggleRock(url, flags, config, launchedChrome) { + const fraggleRock = require('../lighthouse-core/fraggle-rock/api.js'); + const puppeteer = require('puppeteer'); + // @ts-expect-error - FIXME: lighthouse-logger is greedy and takes over `debug` for all packages + require('debug').enable('-puppeteer:*'); + const browser = await puppeteer.connect({browserURL: `http://localhost:${launchedChrome.port}`}); + const page = await browser.newPage(); + flags.channel = 'fraggle-rock-cli'; + const configContext = {configPath: flags.configPath, settingsOverrides: flags}; + return fraggleRock.navigation({url, page, config, configContext}); +} + /** * @param {string} url * @param {LH.CliFlags} flags @@ -223,7 +242,9 @@ async function runLighthouse(url, flags, config) { flags.port = launchedChrome.port; } - const runnerResult = await lighthouse(url, flags, config); + const runnerResult = flags.fraggleRock && launchedChrome ? + await runLighthouseWithFraggleRock(url, flags, config, launchedChrome) : + await lighthouse(url, flags, config); // If in gatherMode only, there will be no runnerResult. if (runnerResult) { diff --git a/lighthouse-core/fraggle-rock/config/config.js b/lighthouse-core/fraggle-rock/config/config.js index db07e6a28891..2d56c0cc2352 100644 --- a/lighthouse-core/fraggle-rock/config/config.js +++ b/lighthouse-core/fraggle-rock/config/config.js @@ -163,7 +163,7 @@ function resolveNavigationsToDefns(navigations, artifactDefns) { /** * @param {LH.Config.Json|undefined} configJSON - * @param {{gatherMode: LH.Gatherer.GatherMode, configPath?: string, settingsOverrides?: LH.SharedFlagsSettings}} context + * @param {Omit & {gatherMode: LH.Gatherer.GatherMode}} context * @return {{config: LH.Config.FRConfig, warnings: string[]}} */ function initializeConfig(configJSON, context) { diff --git a/lighthouse-core/fraggle-rock/gather/navigation-runner.js b/lighthouse-core/fraggle-rock/gather/navigation-runner.js index 9aa04b312a9f..e7da3d17a66c 100644 --- a/lighthouse-core/fraggle-rock/gather/navigation-runner.js +++ b/lighthouse-core/fraggle-rock/gather/navigation-runner.js @@ -239,12 +239,12 @@ async function _cleanup({requestedUrl, driver, config}) { } /** - * @param {{url: string, page: import('puppeteer').Page, config?: LH.Config.Json}} options + * @param {{url: string, page: import('puppeteer').Page, config?: LH.Config.Json, configContext?: LH.Config.FRContext}} options * @return {Promise} */ async function navigation(options) { - const {url: requestedUrl, page} = options; - const {config} = initializeConfig(options.config, {gatherMode: 'navigation'}); + const {url: requestedUrl, page, configContext = {}} = options; + const {config} = initializeConfig(options.config, {...configContext, gatherMode: 'navigation'}); const computedCache = new Map(); return Runner.run( diff --git a/lighthouse-core/fraggle-rock/gather/snapshot-runner.js b/lighthouse-core/fraggle-rock/gather/snapshot-runner.js index be0b7f139ea3..35c3916aeeec 100644 --- a/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +++ b/lighthouse-core/fraggle-rock/gather/snapshot-runner.js @@ -15,9 +15,10 @@ const { const {initializeConfig} = require('../config/config.js'); const {getBaseArtifacts, finalizeArtifacts} = require('./base-artifacts.js'); -/** @param {{page: import('puppeteer').Page, config?: LH.Config.Json}} options */ +/** @param {{page: import('puppeteer').Page, config?: LH.Config.Json, configContext?: LH.Config.FRContext}} options */ async function snapshot(options) { - const {config} = initializeConfig(options.config, {gatherMode: 'snapshot'}); + const {configContext = {}} = options; + const {config} = initializeConfig(options.config, {...configContext, gatherMode: 'snapshot'}); const driver = new Driver(options.page); await driver.connect(); diff --git a/lighthouse-core/fraggle-rock/gather/timespan-runner.js b/lighthouse-core/fraggle-rock/gather/timespan-runner.js index 9fbb68276023..2983aabf2c63 100644 --- a/lighthouse-core/fraggle-rock/gather/timespan-runner.js +++ b/lighthouse-core/fraggle-rock/gather/timespan-runner.js @@ -16,11 +16,12 @@ const {initializeConfig} = require('../config/config.js'); const {getBaseArtifacts, finalizeArtifacts} = require('./base-artifacts.js'); /** - * @param {{page: import('puppeteer').Page, config?: LH.Config.Json}} options + * @param {{page: import('puppeteer').Page, config?: LH.Config.Json, configContext?: LH.Config.FRContext}} options * @return {Promise<{endTimespan(): Promise}>} */ async function startTimespan(options) { - const {config} = initializeConfig(options.config, {gatherMode: 'timespan'}); + const {configContext = {}} = options; + const {config} = initializeConfig(options.config, {...configContext, gatherMode: 'timespan'}); const driver = new Driver(options.page); await driver.connect(); diff --git a/lighthouse-core/test/fraggle-rock/gather/navigation-runner-test.js b/lighthouse-core/test/fraggle-rock/gather/navigation-runner-test.js index 4a4ef5f54faa..5cb204f34ad1 100644 --- a/lighthouse-core/test/fraggle-rock/gather/navigation-runner-test.js +++ b/lighthouse-core/test/fraggle-rock/gather/navigation-runner-test.js @@ -7,15 +7,21 @@ /* eslint-env jest */ -const {createMockDriver, mockDriverSubmodules} = require('./mock-driver.js'); +const {createMockDriver, mockDriverSubmodules, mockRunnerModule} = require('./mock-driver.js'); const mocks = mockDriverSubmodules(); -const runner = require('../../../fraggle-rock/gather/navigation-runner.js'); const {initializeConfig} = require('../../../fraggle-rock/config/config.js'); const {defaultNavigationConfig} = require('../../../config/constants.js'); const LighthouseError = require('../../../lib/lh-error.js'); const DevtoolsLogGatherer = require('../../../gather/gatherers/devtools-log.js'); const toDevtoolsLog = require('../../network-records-to-devtools-log.js'); +// Establish the mocks before we require our file under test. +let mockRunnerRun = jest.fn(); + +jest.mock('../../../runner.js', () => mockRunnerModule(() => mockRunnerRun)); + +const runner = require('../../../fraggle-rock/gather/navigation-runner.js'); + /** @typedef {{meta: LH.Gatherer.GathererMeta<'Accessibility'>, getArtifact: jest.Mock, startInstrumentation:jest.Mock, stopInstrumentation: jest.Mock, startSensitiveInstrumentation:jest.Mock, stopSensitiveInstrumentation: jest.Mock}} MockGatherer */ describe('NavigationRunner', () => { @@ -79,6 +85,7 @@ describe('NavigationRunner', () => { beforeEach(() => { requestedUrl = 'http://example.com'; + mockRunnerRun = jest.fn(); config = initializeConfig(undefined, {gatherMode: 'navigation'}).config; navigation = createNavigation().navigation; computedCache = new Map(); @@ -381,4 +388,27 @@ describe('NavigationRunner', () => { expect(mocks.storageMock.clearDataForOrigin).not.toHaveBeenCalled(); }); }); + + describe('navigation', () => { + it('should initialize config', async () => { + const settingsOverrides = { + formFactor: /** @type {'desktop'} */ ('desktop'), + maxWaitForLoad: 1234, + screenEmulation: {mobile: false}, + }; + + const configContext = {settingsOverrides}; + await runner.navigation({ + url: 'http://example.com', + page: mockDriver._page.asPage(), + configContext, + }); + + expect(mockRunnerRun.mock.calls[0][1]).toMatchObject({ + config: { + settings: settingsOverrides, + }, + }); + }); + }); }); diff --git a/lighthouse-core/test/fraggle-rock/gather/snapshot-runner-test.js b/lighthouse-core/test/fraggle-rock/gather/snapshot-runner-test.js index 8c9f7afa0899..aedba45df9c6 100644 --- a/lighthouse-core/test/fraggle-rock/gather/snapshot-runner-test.js +++ b/lighthouse-core/test/fraggle-rock/gather/snapshot-runner-test.js @@ -89,6 +89,24 @@ describe('Snapshot Runner', () => { expect(gathererB.getArtifact).toHaveBeenCalled(); }); + + it('should use configContext', async () => { + const settingsOverrides = { + formFactor: /** @type {'desktop'} */ ('desktop'), + maxWaitForLoad: 1234, + screenEmulation: {mobile: false}, + }; + + const configContext = {settingsOverrides}; + await snapshot({page, config, configContext}); + + expect(mockRunnerRun.mock.calls[0][1]).toMatchObject({ + config: { + settings: settingsOverrides, + }, + }); + }); + it('should not invoke instrumentation methods', async () => { await snapshot({page, config}); await mockRunnerRun.mock.calls[0][0](); diff --git a/lighthouse-core/test/fraggle-rock/gather/timespan-runner-test.js b/lighthouse-core/test/fraggle-rock/gather/timespan-runner-test.js index 8706dd94de3a..3728bf8d72c5 100644 --- a/lighthouse-core/test/fraggle-rock/gather/timespan-runner-test.js +++ b/lighthouse-core/test/fraggle-rock/gather/timespan-runner-test.js @@ -98,6 +98,24 @@ describe('Timespan Runner', () => { }); }); + it('should use configContext', async () => { + const settingsOverrides = { + formFactor: /** @type {'desktop'} */ ('desktop'), + maxWaitForLoad: 1234, + screenEmulation: {mobile: false}, + }; + + const configContext = {settingsOverrides}; + const timespan = await startTimespan({page, config, configContext}); + await timespan.endTimespan(); + + expect(mockRunnerRun.mock.calls[0][1]).toMatchObject({ + config: { + settings: settingsOverrides, + }, + }); + }); + it('should invoke stop instrumentation', async () => { const timespan = await startTimespan({page, config}); await timespan.endTimespan(); diff --git a/types/config.d.ts b/types/config.d.ts index d5038979c947..98bcfa764c98 100644 --- a/types/config.d.ts +++ b/types/config.d.ts @@ -56,6 +56,16 @@ declare global { groups: Record | null; } + /** + * Additional information about the context in which a Fraggle Rock config should be interpreted. + * This information is typically set by the CLI or other channel integrations. + */ + export interface FRContext { + gatherMode?: LH.Gatherer.GatherMode; + configPath?: string; + settingsOverrides?: LH.SharedFlagsSettings; + } + interface SharedPassNavigationJson { /** * Controls the behavior when the navigation fails to complete (due to server error, no FCP, etc). diff --git a/types/externs.d.ts b/types/externs.d.ts index 86c13994ffba..bb195a16351a 100644 --- a/types/externs.d.ts +++ b/types/externs.d.ts @@ -257,6 +257,8 @@ declare global { quiet: boolean; /** A flag to print the normalized config for the given config and options, then exit. */ printConfig: boolean; + /** Use the new Fraggle Rock navigation runner to gather CLI results. */ + fraggleRock: boolean; /** Path to the file where precomputed lantern data should be read from. */ precomputedLanternDataPath?: string; /** Path to the file where precomputed lantern data should be written to. */ From fdf95240897ec936c45a35dcfdbd2293b12253eb Mon Sep 17 00:00:00 2001 From: Patrick Hulce Date: Tue, 20 Jul 2021 08:29:33 -0500 Subject: [PATCH 2/2] add flag to test --- lighthouse-cli/test/cli/bin-test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lighthouse-cli/test/cli/bin-test.js b/lighthouse-cli/test/cli/bin-test.js index 219c460832a1..4df8ac052618 100644 --- a/lighthouse-cli/test/cli/bin-test.js +++ b/lighthouse-cli/test/cli/bin-test.js @@ -52,6 +52,7 @@ beforeEach(() => { view: false, verbose: false, quiet: false, + fraggleRock: false, port: 0, hostname: '', // Command modes