core: merge `api.js` into `index.js`, new report generator api (#14531)
This commit is contained in:
Родитель
1b843de669
Коммит
e5dfcb28d5
|
@ -10,8 +10,7 @@ import {Buffer} from 'buffer';
|
|||
|
||||
import log from 'lighthouse-logger';
|
||||
|
||||
import lighthouse, {legacyNavigation} from '../../core/index.js';
|
||||
import {navigation, startTimespan, snapshot} from '../../core/api.js';
|
||||
import lighthouse, {legacyNavigation, navigation, startTimespan, snapshot} from '../../core/index.js';
|
||||
import {RawConnection} from '../../core/legacy/gather/connections/raw.js';
|
||||
import {lookupLocale} from '../../core/lib/i18n/i18n.js';
|
||||
import {registerLocaleData, getCanonicalLocales} from '../../shared/localization/format.js';
|
||||
|
|
80
core/api.js
80
core/api.js
|
@ -1,80 +0,0 @@
|
|||
/**
|
||||
* @license Copyright 2020 The Lighthouse Authors. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import {UserFlow, auditGatherSteps} from './user-flow.js';
|
||||
import {snapshotGather} from './gather/snapshot-runner.js';
|
||||
import {startTimespanGather} from './gather/timespan-runner.js';
|
||||
import {navigationGather} from './gather/navigation-runner.js';
|
||||
import {ReportGenerator} from '../report/generator/report-generator.js';
|
||||
import {Runner} from './runner.js';
|
||||
|
||||
/**
|
||||
* @param {LH.Puppeteer.Page} page
|
||||
* @param {LH.UserFlow.Options} [options]
|
||||
*/
|
||||
async function startFlow(page, options) {
|
||||
return new UserFlow(page, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LH.Puppeteer.Page|undefined} page
|
||||
* @param {LH.NavigationRequestor|undefined} requestor
|
||||
* @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
|
||||
* @return {Promise<LH.RunnerResult|undefined>}
|
||||
*/
|
||||
async function navigation(page, requestor, options) {
|
||||
const gatherResult = await navigationGather(page, requestor, options);
|
||||
return Runner.audit(gatherResult.artifacts, gatherResult.runnerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LH.Puppeteer.Page} page
|
||||
* @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
|
||||
* @return {Promise<LH.RunnerResult|undefined>}
|
||||
*/
|
||||
async function snapshot(page, options) {
|
||||
const gatherResult = await snapshotGather(page, options);
|
||||
return Runner.audit(gatherResult.artifacts, gatherResult.runnerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LH.Puppeteer.Page} page
|
||||
* @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
|
||||
* @return {Promise<{endTimespan: () => Promise<LH.RunnerResult|undefined>}>}
|
||||
*/
|
||||
async function startTimespan(page, options) {
|
||||
const {endTimespanGather} = await startTimespanGather(page, options);
|
||||
const endTimespan = async () => {
|
||||
const gatherResult = await endTimespanGather();
|
||||
return Runner.audit(gatherResult.artifacts, gatherResult.runnerOptions);
|
||||
};
|
||||
return {endTimespan};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LH.FlowResult} flowResult
|
||||
*/
|
||||
async function generateFlowReport(flowResult) {
|
||||
return ReportGenerator.generateFlowReportHtml(flowResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LH.UserFlow.FlowArtifacts} flowArtifacts
|
||||
* @param {LH.Config.Json} [config]
|
||||
*/
|
||||
async function auditFlowArtifacts(flowArtifacts, config) {
|
||||
const {gatherSteps, name} = flowArtifacts;
|
||||
return await auditGatherSteps(gatherSteps, {name, config});
|
||||
}
|
||||
|
||||
export {
|
||||
snapshot,
|
||||
startTimespan,
|
||||
navigation,
|
||||
startFlow,
|
||||
generateFlowReport,
|
||||
auditFlowArtifacts,
|
||||
};
|
|
@ -10,9 +10,13 @@ import {Runner} from './runner.js';
|
|||
import {CriConnection} from './legacy/gather/connections/cri.js';
|
||||
import {Config} from './legacy/config/config.js';
|
||||
import UrlUtils from './lib/url-utils.js';
|
||||
import * as fraggleRock from './api.js';
|
||||
import {Driver} from './legacy/gather/driver.js';
|
||||
import {initializeConfig} from './config/config.js';
|
||||
import {UserFlow, auditGatherSteps} from './user-flow.js';
|
||||
import {ReportGenerator} from '../report/generator/report-generator.js';
|
||||
import {startTimespanGather} from './gather/timespan-runner.js';
|
||||
import {snapshotGather} from './gather/snapshot-runner.js';
|
||||
import {navigationGather} from './gather/navigation-runner.js';
|
||||
|
||||
/** @typedef {import('./legacy/gather/connections/connection.js').Connection} Connection */
|
||||
|
||||
|
@ -40,7 +44,7 @@ import {initializeConfig} from './config/config.js';
|
|||
* @return {Promise<LH.RunnerResult|undefined>}
|
||||
*/
|
||||
async function lighthouse(url, flags = {}, configJSON, page) {
|
||||
return fraggleRock.navigation(page, url, {config: configJSON, flags});
|
||||
return navigation(page, url, {config: configJSON, flags});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,6 +77,75 @@ async function legacyNavigation(url, flags = {}, configJSON, userConnection) {
|
|||
return Runner.audit(artifacts, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LH.Puppeteer.Page} page
|
||||
* @param {LH.UserFlow.Options} [options]
|
||||
*/
|
||||
async function startFlow(page, options) {
|
||||
return new UserFlow(page, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LH.Puppeteer.Page|undefined} page
|
||||
* @param {LH.NavigationRequestor|undefined} requestor
|
||||
* @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
|
||||
* @return {Promise<LH.RunnerResult|undefined>}
|
||||
*/
|
||||
async function navigation(page, requestor, options) {
|
||||
const gatherResult = await navigationGather(page, requestor, options);
|
||||
return Runner.audit(gatherResult.artifacts, gatherResult.runnerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LH.Puppeteer.Page} page
|
||||
* @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
|
||||
* @return {Promise<LH.RunnerResult|undefined>}
|
||||
*/
|
||||
async function snapshot(page, options) {
|
||||
const gatherResult = await snapshotGather(page, options);
|
||||
return Runner.audit(gatherResult.artifacts, gatherResult.runnerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LH.Puppeteer.Page} page
|
||||
* @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
|
||||
* @return {Promise<{endTimespan: () => Promise<LH.RunnerResult|undefined>}>}
|
||||
*/
|
||||
async function startTimespan(page, options) {
|
||||
const {endTimespanGather} = await startTimespanGather(page, options);
|
||||
const endTimespan = async () => {
|
||||
const gatherResult = await endTimespanGather();
|
||||
return Runner.audit(gatherResult.artifacts, gatherResult.runnerOptions);
|
||||
};
|
||||
return {endTimespan};
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {LH.Result|LH.FlowResult} R
|
||||
* @param {R} result
|
||||
* @param {[R] extends [LH.Result] ? LH.OutputMode : Exclude<LH.OutputMode, 'csv'>} [format]
|
||||
* @return {string}
|
||||
*/
|
||||
function generateReport(result, format = 'html') {
|
||||
const reportOutput = ReportGenerator.generateReport(result, format);
|
||||
if (Array.isArray(reportOutput)) {
|
||||
// In theory the output should never be an array.
|
||||
// This is mostly for type checking.
|
||||
return reportOutput[0];
|
||||
} else {
|
||||
return reportOutput;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LH.UserFlow.FlowArtifacts} flowArtifacts
|
||||
* @param {LH.Config.Json} [config]
|
||||
*/
|
||||
async function auditFlowArtifacts(flowArtifacts, config) {
|
||||
const {gatherSteps, name} = flowArtifacts;
|
||||
return await auditGatherSteps(gatherSteps, {name, config});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Lighthouse Config.
|
||||
* @param {LH.Config.Json=} configJson Configuration for the Lighthouse run. If
|
||||
|
@ -113,6 +186,12 @@ export {default as Gatherer} from './gather/base-gatherer.js';
|
|||
export {NetworkRecords} from './computed/network-records.js';
|
||||
export {
|
||||
legacyNavigation,
|
||||
startFlow,
|
||||
navigation,
|
||||
startTimespan,
|
||||
snapshot,
|
||||
generateReport,
|
||||
auditFlowArtifacts,
|
||||
generateConfig,
|
||||
generateLegacyConfig,
|
||||
getAuditList,
|
||||
|
|
|
@ -16,7 +16,7 @@ import yargs from 'yargs';
|
|||
import {getChromePath} from 'chrome-launcher';
|
||||
|
||||
import {LH_ROOT} from '../../root.js';
|
||||
import * as api from '../api.js';
|
||||
import * as api from '../index.js';
|
||||
import * as assetSaver from '../lib/asset-saver.js';
|
||||
|
||||
const ARTIFACTS_PATH =
|
||||
|
@ -153,7 +153,7 @@ async function generateFlowResult() {
|
|||
fs.writeFileSync(args.outputPath, JSON.stringify(flowResult, null, 2));
|
||||
|
||||
if (args.view) {
|
||||
const htmlReport = await api.generateFlowReport(flowResult);
|
||||
const htmlReport = await api.generateReport(flowResult);
|
||||
fs.writeFileSync(FLOW_REPORT_PATH, htmlReport);
|
||||
open(FLOW_REPORT_PATH);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import jestMock from 'jest-mock';
|
||||
|
||||
import * as lighthouse from '../../api.js';
|
||||
import * as api from '../../index.js';
|
||||
import {createTestState, getAuditsBreakdown} from './pptr-test-utils.js';
|
||||
import {LH_ROOT} from '../../../root.js';
|
||||
|
||||
|
@ -36,7 +36,7 @@ describe('Fraggle Rock API', function() {
|
|||
it('should compute accessibility results on the page as-is', async () => {
|
||||
await setupTestPage();
|
||||
|
||||
const result = await lighthouse.snapshot(state.page);
|
||||
const result = await api.snapshot(state.page);
|
||||
if (!result) throw new Error('Lighthouse failed to produce a result');
|
||||
|
||||
const {lhr, artifacts} = result;
|
||||
|
@ -63,7 +63,7 @@ describe('Fraggle Rock API', function() {
|
|||
});
|
||||
|
||||
it('should compute ConsoleMessage results across a span of time', async () => {
|
||||
const run = await lighthouse.startTimespan(state.page);
|
||||
const run = await api.startTimespan(state.page);
|
||||
|
||||
await setupTestPage();
|
||||
|
||||
|
@ -120,7 +120,7 @@ describe('Fraggle Rock API', function() {
|
|||
await page.goto(`${serverBaseUrl}/onclick.html`);
|
||||
await page.waitForSelector('button');
|
||||
|
||||
const run = await lighthouse.startTimespan(state.page);
|
||||
const run = await api.startTimespan(state.page);
|
||||
|
||||
await page.click('button');
|
||||
await page.waitForSelector('input');
|
||||
|
@ -155,7 +155,7 @@ describe('Fraggle Rock API', function() {
|
|||
it('should compute both snapshot & timespan results', async () => {
|
||||
const {page, serverBaseUrl} = state;
|
||||
const url = `${serverBaseUrl}/index.html`;
|
||||
const result = await lighthouse.navigation(page, url);
|
||||
const result = await api.navigation(page, url);
|
||||
if (!result) throw new Error('Lighthouse failed to produce a result');
|
||||
|
||||
const {lhr, artifacts} = result;
|
||||
|
@ -194,7 +194,7 @@ describe('Fraggle Rock API', function() {
|
|||
await page.click('a');
|
||||
});
|
||||
|
||||
const result = await lighthouse.navigation(page, requestor);
|
||||
const result = await api.navigation(page, requestor);
|
||||
if (!result) throw new Error('Lighthouse failed to produce a result');
|
||||
|
||||
expect(requestor).toHaveBeenCalled();
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import * as lighthouse from '../../api.js';
|
||||
import * as api from '../../index.js';
|
||||
import {createTestState} from './pptr-test-utils.js';
|
||||
import {LH_ROOT} from '../../../root.js';
|
||||
|
||||
|
@ -26,7 +26,7 @@ describe('Cross origin timespan', function() {
|
|||
it('should resolve all stylesheets', async () => {
|
||||
await state.page.goto(`${state.serverBaseUrl}/start.html`, {waitUntil: ['networkidle0']});
|
||||
|
||||
const timespan = await lighthouse.startTimespan(state.page);
|
||||
const timespan = await api.startTimespan(state.page);
|
||||
await state.page.goto(`${state.secondaryServerBaseUrl}/end.html`);
|
||||
const result = await timespan.endTimespan();
|
||||
if (!result) throw new Error('Lighthouse did not return a result');
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
/* eslint-env browser */
|
||||
|
||||
import * as lighthouse from '../../api.js';
|
||||
import * as api from '../../index.js';
|
||||
import {createTestState} from './pptr-test-utils.js';
|
||||
import {LH_ROOT} from '../../../root.js';
|
||||
|
||||
|
@ -29,7 +29,7 @@ describe('Disconnect', function() {
|
|||
const session = await state.page.target().createCDPSession();
|
||||
await session.send('Network.enable');
|
||||
|
||||
const timespan = await lighthouse.startTimespan(state.page, {
|
||||
const timespan = await api.startTimespan(state.page, {
|
||||
flags: {
|
||||
blockedUrlPatterns: ['*'],
|
||||
},
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import * as lighthouse from '../../api.js';
|
||||
import * as api from '../../index.js';
|
||||
import {createTestState, getAuditsBreakdown} from './pptr-test-utils.js';
|
||||
import {LH_ROOT} from '../../../root.js';
|
||||
|
||||
|
@ -26,7 +26,7 @@ describe('Start/End navigation', function() {
|
|||
const pageUrl = `${state.serverBaseUrl}/links-to-index.html`;
|
||||
await state.page.goto(pageUrl, {waitUntil: ['networkidle0']});
|
||||
|
||||
const flow = await lighthouse.startFlow(state.page);
|
||||
const flow = await api.startFlow(state.page);
|
||||
|
||||
await flow.startNavigation();
|
||||
await state.page.click('a');
|
||||
|
|
|
@ -146,28 +146,42 @@ class ReportGenerator {
|
|||
.join(CRLF);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LHResult|FlowResult} result
|
||||
* @return {result is FlowResult}
|
||||
*/
|
||||
static isFlowResult(result) {
|
||||
return 'steps' in result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the results output in a format based on the `mode`.
|
||||
* @param {LHResult} lhr
|
||||
* @param {LHResult|FlowResult} result
|
||||
* @param {LHResult['configSettings']['output']} outputModes
|
||||
* @return {string|string[]}
|
||||
*/
|
||||
static generateReport(lhr, outputModes) {
|
||||
static generateReport(result, outputModes) {
|
||||
const outputAsArray = Array.isArray(outputModes);
|
||||
if (typeof outputModes === 'string') outputModes = [outputModes];
|
||||
|
||||
const output = outputModes.map(outputMode => {
|
||||
// HTML report.
|
||||
if (outputMode === 'html') {
|
||||
return ReportGenerator.generateReportHtml(lhr);
|
||||
if (ReportGenerator.isFlowResult(result)) {
|
||||
return ReportGenerator.generateFlowReportHtml(result);
|
||||
}
|
||||
return ReportGenerator.generateReportHtml(result);
|
||||
}
|
||||
// CSV report.
|
||||
if (outputMode === 'csv') {
|
||||
return ReportGenerator.generateReportCSV(lhr);
|
||||
if (ReportGenerator.isFlowResult(result)) {
|
||||
throw new Error('CSV output is not support for user flows');
|
||||
}
|
||||
return ReportGenerator.generateReportCSV(result);
|
||||
}
|
||||
// JSON report.
|
||||
if (outputMode === 'json') {
|
||||
return JSON.stringify(lhr, null, 2);
|
||||
return JSON.stringify(result, null, 2);
|
||||
}
|
||||
|
||||
throw new Error('Invalid output mode: ' + outputMode);
|
||||
|
|
|
@ -16,6 +16,7 @@ import {ReportGenerator} from '../../generator/report-generator.js';
|
|||
import {readJson} from '../../../core/test/test-utils.js';
|
||||
|
||||
const sampleResults = readJson('core/test/results/sample_v2.json');
|
||||
const sampleFlowResult = readJson('core/test/fixtures/fraggle-rock/reports/sample-flow-result.json');
|
||||
|
||||
describe('ReportGenerator', () => {
|
||||
describe('#replaceStrings', () => {
|
||||
|
@ -75,12 +76,23 @@ describe('ReportGenerator', () => {
|
|||
assert.doesNotThrow(_ => JSON.parse(jsonOutput));
|
||||
});
|
||||
|
||||
it('creates JSON for flow result', () => {
|
||||
const jsonOutput = ReportGenerator.generateReport(sampleFlowResult, 'json');
|
||||
assert.doesNotThrow(_ => JSON.parse(jsonOutput));
|
||||
});
|
||||
|
||||
it('creates HTML for results', () => {
|
||||
const htmlOutput = ReportGenerator.generateReport(sampleResults, 'html');
|
||||
assert.ok(/<!doctype/gim.test(htmlOutput));
|
||||
assert.ok(/<html lang="en"/gim.test(htmlOutput));
|
||||
});
|
||||
|
||||
it('creates HTML for flow result', () => {
|
||||
const htmlOutput = ReportGenerator.generateReport(sampleFlowResult, 'html');
|
||||
assert.ok(/<!doctype/gim.test(htmlOutput));
|
||||
assert.ok(/<html lang="en"/gim.test(htmlOutput));
|
||||
});
|
||||
|
||||
it('creates CSV for results', async () => {
|
||||
const path = './.results-as-csv.csv';
|
||||
|
||||
|
@ -126,6 +138,12 @@ category,audit,score,displayValue,description
|
|||
expect(csvOutput).toContain('pwa');
|
||||
});
|
||||
|
||||
it('throws when creating CSV for flow result', () => {
|
||||
expect(() => {
|
||||
ReportGenerator.generateReport(sampleFlowResult, 'csv');
|
||||
}).toThrow('CSV output is not support for user flows');
|
||||
});
|
||||
|
||||
it('writes extended info', () => {
|
||||
const htmlOutput = ReportGenerator.generateReport(sampleResults, 'html');
|
||||
const outputCheck = new RegExp('dobetterweb/dbw_tester.css', 'i');
|
||||
|
|
Загрузка…
Ссылка в новой задаче