diff --git a/.gitignore b/.gitignore index 3091757..e6e0f68 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ node_modules -coverage \ No newline at end of file +coverage +*.trace.json +*.devtoolslog.json +*.report.json \ No newline at end of file diff --git a/lib/cli-options.js b/lib/cli-options.js index 7019c34..56670ef 100644 --- a/lib/cli-options.js +++ b/lib/cli-options.js @@ -48,7 +48,15 @@ const options = { type: 'boolean', describe: 'Output as JSON Lines', group: 'Output:', - default: false + default: false, + alias: 'output.jsonl', + }, + saveAssets: { + type: 'boolean', + describe: 'Save Lighthouse reports', + group: 'Output:', + default: false, + alias: 'output.saveAssets', }, local: { type: 'boolean', @@ -81,9 +89,9 @@ function parseArgs({argv}) { .parse(); const url = positionalArgAt(args, 0); - const {runs, warmupRuns, metrics, jsonl, lighthouse} = args; + const {runs, warmupRuns, metrics, output, lighthouse} = args; - return {url, runs, warmupRuns, metrics, jsonl, lighthouse}; + return {url, runs, warmupRuns, metrics, output, lighthouse}; } module.exports = {parseArgs, check}; \ No newline at end of file diff --git a/lib/main.js b/lib/main.js index 1653acb..c0c5019 100755 --- a/lib/main.js +++ b/lib/main.js @@ -1,17 +1,23 @@ const {Counter} = require('./counter'); const {Runner} = require('./runner'); +const {saveAssets} = require('./save-assets'); const {resultMapper} = require('./map-result'); const {outputFormatter} = require('./format-output'); function main({ runs, warmupRuns, getLighthouseResult, url, metrics, - jsonl, + output }) { const counter = new Counter(runs, warmupRuns); const getResult = () => - getLighthouseResult(url).then(resultMapper(metrics)); - const format = outputFormatter(jsonl); + getLighthouseResult(url).then(async ({result, artifacts}) => { + if (output.saveAssets) { + await saveAssets(result, artifacts); + } + return resultMapper(metrics)(result); + }); + const format = outputFormatter(output.jsonl); return new Runner(counter, getResult, format); } diff --git a/lib/run-lighthouse.js b/lib/run-lighthouse.js index af07f02..f131ed8 100644 --- a/lib/run-lighthouse.js +++ b/lib/run-lighthouse.js @@ -22,11 +22,11 @@ async function runLighthouse({modulePath, cpuSlowDown}, url) { const chrome = await launch({chromeFlags: ['--headless']}); const options = {port: chrome.port, throttling}; - const {lhr: result} = await lighthouse(url, options, config); + const {lhr: result, artifacts} = await lighthouse(url, options, config); await chrome.kill(); - return result; + return {result, artifacts}; } module.exports = {runLighthouse, config}; diff --git a/lib/run-pagespeed.js b/lib/run-pagespeed.js index da524a1..af29bcd 100644 --- a/lib/run-pagespeed.js +++ b/lib/run-pagespeed.js @@ -8,7 +8,7 @@ async function runPagespeed(urlString) { const query = new URLSearchParams({url: url.toString(), strategy: 'mobile'}); const apiUrl = `${baseUrl}/?${query.toString()}`; const {payload} = await wreck.get(apiUrl, {json: true}); - return payload.lighthouseResult; + return {result: payload.lighthouseResult}; } module.exports = {runPagespeed}; \ No newline at end of file diff --git a/lib/save-assets.js b/lib/save-assets.js new file mode 100644 index 0000000..8e89e29 --- /dev/null +++ b/lib/save-assets.js @@ -0,0 +1,17 @@ +const {resolve} = require('path'); +const {writeFileSync} = require('fs'); +const {getFilenamePrefix} = require('lighthouse/lighthouse-core/lib/file-namer'); +const assetSaver = require('lighthouse/lighthouse-core/lib/asset-saver'); + +async function saveAssets(result, artifacts) { + const resolvedPath = resolve(process.cwd(), getFilenamePrefix(result)); + const report = JSON.stringify(result, null, 2); + + writeFileSync(resolvedPath + '-0.report.json', report, 'utf-8'); + + if (artifacts) { + await assetSaver.saveAssets(artifacts, result.audits, resolvedPath); + } +} + +module.exports = {saveAssets}; \ No newline at end of file diff --git a/package.json b/package.json index d6b0b5e..6687b97 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "scripts": { "pretest": "eslint .", "test": "jest", - "coverage": "cat ./coverage/lcov.info | coveralls" + "coverage": "cat ./coverage/lcov.info | coveralls", + "clean": "rm -f *.trace.json *.devtoolslog.json *.report.json" }, "main": "index.js", "author": "Csaba Palfi", diff --git a/tests/__snapshots__/cli-options.test.js.snap b/tests/__snapshots__/cli-options.test.js.snap index 17de61a..715bc7c 100644 --- a/tests/__snapshots__/cli-options.test.js.snap +++ b/tests/__snapshots__/cli-options.test.js.snap @@ -2,7 +2,6 @@ exports[`cli-options parseArgs returns defaults based on argv 1`] = ` Object { - "jsonl": false, "lighthouse": Object { "cpu-slow-down": 4, "cpuSlowDown": 4, @@ -16,6 +15,11 @@ Object { "user-timing-marks": Object {}, "userTimingMarks": Object {}, }, + "output": Object { + "jsonl": false, + "save-assets": false, + "saveAssets": false, + }, "runs": 9, "url": "https://www.google.com", "warmupRuns": 0, diff --git a/tests/__snapshots__/save-assets.test.js.snap b/tests/__snapshots__/save-assets.test.js.snap new file mode 100644 index 0000000..1fc43b9 --- /dev/null +++ b/tests/__snapshots__/save-assets.test.js.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`save-assets saves report and artifacts 1`] = ` +Array [ + "resolvedPath-0.report.json", + "{ + \\"audits\\": {} +}", + "utf-8", +] +`; + +exports[`save-assets saves report only if no artifacts 1`] = ` +Array [ + "resolvedPath-0.report.json", + "{ + \\"audits\\": {} +}", + "utf-8", +] +`; diff --git a/tests/main.test.js b/tests/main.test.js index 09a9920..5c413f5 100644 --- a/tests/main.test.js +++ b/tests/main.test.js @@ -7,7 +7,7 @@ describe('main', () => { getLighthouseResult: jest.fn(), url: 'https://www.google.com', metrics: {userTimingMarks: {}}, - jsonl: false + output: {jsonl: false, reports: false}, }; it('returns runner', () => { @@ -15,13 +15,13 @@ describe('main', () => { }); it('runner.getResult calls getLighthouseResult then mapResult', async () => { - const lighthouseResult = { + const result = { categories: {performance: {score: 1}}, audits: {} }; const {getLighthouseResult} = options; - getLighthouseResult.mockResolvedValue(lighthouseResult); + getLighthouseResult.mockResolvedValue({result}); const runner = main(options); const mappedResult = await runner.getResult(); diff --git a/tests/run-lighthouse.test.js b/tests/run-lighthouse.test.js index 3646ab1..2704a63 100644 --- a/tests/run-lighthouse.test.js +++ b/tests/run-lighthouse.test.js @@ -9,13 +9,19 @@ describe('run-lighthouse', () => { it('runLightHouse launches chrome and runs Lighthouse', async () => { const url = 'https://www.google.com'; const lighthouseResult = {}; + const lighthouseArtifacts = {}; const options = {modulePath: 'lighthouse', cpuSlowDown: 4}; const chrome = {port: 1234, kill: jest.fn()}; launch.mockResolvedValue(chrome); - lighthouse.mockResolvedValue({lhr: lighthouseResult}); + lighthouse.mockResolvedValue({ + lhr: lighthouseResult, + artifacts: lighthouseArtifacts + }); - await expect(runLighthouse(options, url)) - .resolves.toBe(lighthouseResult); + const {result, artifacts} = await runLighthouse(options, url); + + expect(result).toBe(lighthouseResult); + expect(artifacts).toBe(lighthouseArtifacts); expect(launch).toHaveBeenCalledTimes(1); expect(launch.mock.calls[0]).toMatchSnapshot(); diff --git a/tests/run-pagespeed.test.js b/tests/run-pagespeed.test.js index ce56a2e..c075014 100644 --- a/tests/run-pagespeed.test.js +++ b/tests/run-pagespeed.test.js @@ -7,12 +7,12 @@ describe('run-pagespeed', () => { it('runPageSpeed calls API and returns Lighthouse result', async () => { jest.spyOn(Date, 'now').mockImplementation(() => 1479427200000); - wreck.get = jest.fn() .mockResolvedValue({payload: {lighthouseResult}}); - expect(await runPagespeed('https://www.google.com')) - .toBe(lighthouseResult); + const {result} = await runPagespeed('https://www.google.com'); + + expect(result).toBe(lighthouseResult); expect(wreck.get).toHaveBeenCalledTimes(1) expect(wreck.get.mock.calls[0]).toMatchSnapshot(); }); diff --git a/tests/save-assets.test.js b/tests/save-assets.test.js new file mode 100644 index 0000000..11f9327 --- /dev/null +++ b/tests/save-assets.test.js @@ -0,0 +1,59 @@ +const {resolve} = require('path'); +const {writeFileSync} = require('fs'); +const {getFilenamePrefix} = require('lighthouse/lighthouse-core/lib/file-namer'); +const assetSaver = require('lighthouse/lighthouse-core/lib/asset-saver'); +const {saveAssets} = require('../lib/save-assets'); + +jest.mock('path'); +jest.mock('fs'); +jest.mock('lighthouse/lighthouse-core/lib/file-namer'); +jest.mock('lighthouse/lighthouse-core/lib/asset-saver'); + + +describe('save-assets', () => { + const audits = {}; + const result = {audits}; + const fileNamePrefix = 'prefix'; + const resolvedPath = 'resolvedPath'; + + afterEach(() => jest.resetAllMocks()); + + function expectToSaveReport() { + expect(getFilenamePrefix).toHaveBeenCalledTimes(1); + expect(getFilenamePrefix.mock.calls[0][0]).toBe(result); + + expect(resolve).toHaveBeenCalledTimes(1); + expect(resolve.mock.calls[0]) + .toEqual([process.cwd(), fileNamePrefix]); + + expect(writeFileSync).toHaveBeenCalledTimes(1); + expect(writeFileSync.mock.calls[0]).toMatchSnapshot(); + } + + it('saves report only if no artifacts', async () => { + getFilenamePrefix.mockReturnValue(fileNamePrefix); + resolve.mockReturnValue(resolvedPath); + + await saveAssets(result); + + expectToSaveReport(); + }); + + it('saves report and artifacts', async () => { + const artifacts = {}; + getFilenamePrefix.mockReturnValue(fileNamePrefix); + resolve.mockReturnValue(resolvedPath); + + await saveAssets(result, artifacts); + + expectToSaveReport(); + + expect(assetSaver.saveAssets).toHaveBeenCalledTimes(1); + expect(assetSaver.saveAssets.mock.calls[0][0]) + .toBe(artifacts); + expect(assetSaver.saveAssets.mock.calls[0][1]) + .toBe(audits); + expect(assetSaver.saveAssets.mock.calls[0][2]) + .toBe(resolvedPath); + }); +}); \ No newline at end of file