Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: save artifacts and assets in output path #1601

Merged
merged 8 commits into from
Feb 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ last-run-results.html
*.trace.json
*.screenshots.html
*.report.html
*.artifacts.log

closure-error.log

Expand Down
51 changes: 32 additions & 19 deletions lighthouse-cli/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,33 @@ function handleError(err: LighthouseError) {
}
}

function saveResults(results: Results,
artifacts: Object,
flags: {output: any, outputPath: string, saveArtifacts: boolean, saveAssets: boolean}) {
let promise = Promise.resolve(results);
const cwd = process.cwd();
// Use the output path as the prefix for all generated files.
// If no output path is set, generate a file prefix using the URL and date.
const configuredPath = !flags.outputPath || flags.outputPath === 'stdout' ?
assetSaver.getFilenamePrefix(results) :
flags.outputPath.replace(/\.\w{2,4}$/, '');
const resolvedPath = path.resolve(cwd, configuredPath);

if (flags.saveArtifacts) {
assetSaver.saveArtifacts(artifacts, resolvedPath);
}

if (flags.saveAssets) {
promise = promise.then(_ => assetSaver.saveAssets(artifacts, results.audits, resolvedPath));
}

if (flags.output === Printer.OutputMode[Printer.OutputMode.pretty]) {
promise = promise.then(_ => Printer.write(results, 'html', `${resolvedPath}.report.html`));
}

return promise.then(_ => Printer.write(results, flags.output, flags.outputPath));
}

function runLighthouse(url: string,
flags: {port: number, skipAutolaunch: boolean, selectChrome: boolean, output: any,
outputPath: string, interactive: boolean, saveArtifacts: boolean, saveAssets: boolean},
Expand All @@ -260,30 +287,16 @@ function runLighthouse(url: string,
.then(chrome => chromeLauncher = chrome)
.then(() => lighthouse(url, flags, config))
.then((results: Results) => {
// delete artifacts from result so reports won't include artifacts.
// remove artifacts from result so reports won't include artifacts.
const artifacts = results.artifacts;
results.artifacts = undefined;

if (flags.saveArtifacts) {
assetSaver.saveArtifacts(artifacts);
}
if (flags.saveAssets) {
return assetSaver.saveAssets(artifacts, results).then(() => results);
}
return results;
})
.then((results: Results) => Printer.write(results, flags.output, flags.outputPath))
.then((results: Results) => {
if (flags.output === Printer.OutputMode[Printer.OutputMode.pretty]) {
const filename = `${assetSaver.getFilenamePrefix(results)}.report.html`;
return Printer.write(results, 'html', filename);
}
return results;
})
.then((results: Results) => {
let promise = saveResults(results, artifacts, flags);
if (flags.interactive) {
return performanceXServer.hostExperiment({url, flags, config}, results);
promise = promise.then(() => performanceXServer.hostExperiment({url, flags, config}, results));
}

return promise;
})
.then(() => chromeLauncher.kill())
.catch(err => {
Expand Down
46 changes: 24 additions & 22 deletions lighthouse-core/lib/asset-saver.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,12 @@ function getFilenamePrefix(results) {
/**
* Generate basic HTML page of screenshot filmstrip
* @param {!Array<{timestamp: number, datauri: string}>} screenshots
* @param {!Results} results
* @return {!string}
*/
function screenshotDump(screenshots, results) {
function screenshotDump(screenshots) {
return `
<!doctype html>
<title>screenshots ${getFilenamePrefix(results)}</title>
<title>screenshots</title>
<style>
html {
overflow-x: scroll;
Expand Down Expand Up @@ -89,26 +88,27 @@ img {
}

/**
* Save entire artifacts object to a single stringified file
* Save entire artifacts object to a single stringified file located at
* pathWithBasename + .artifacts.log
* @param {!Artifacts} artifacts
* @param {!string} artifactsFilename
* @param {string} pathWithBasename
*/
// Set to ignore because testing it would imply testing fs, which isn't strictly necessary.
/* istanbul ignore next */
function saveArtifacts(artifacts, artifactsFilename) {
artifactsFilename = artifactsFilename || 'artifacts.log';
function saveArtifacts(artifacts, pathWithBasename) {
const fullPath = `${pathWithBasename}.artifacts.log`;
// The networkRecords artifacts have circular references
fs.writeFileSync(artifactsFilename, stringifySafe(artifacts));
log.log('artifacts file saved to disk', artifactsFilename);
fs.writeFileSync(fullPath, stringifySafe(artifacts));
log.log('artifacts file saved to disk', fullPath);
}

/**
* Filter traces and extract screenshots to prepare for saving.
* @param {!Artifacts} artifacts
* @param {!Results} results
* @param {!Audits} audits
* @return {!Promise<!Array<{traceData: !Object, html: string}>>}
*/
function prepareAssets(artifacts, results) {
function prepareAssets(artifacts, audits) {
const passNames = Object.keys(artifacts.traces);
const assets = [];

Expand All @@ -118,10 +118,10 @@ function prepareAssets(artifacts, results) {
return chain.then(_ => artifacts.requestScreenshots(trace))
.then(screenshots => {
const traceData = Object.assign({}, trace);
const html = screenshotDump(screenshots, results);
const html = screenshotDump(screenshots);

if (results && results.audits) {
const evts = new Metrics(traceData.traceEvents, results.audits).generateFakeEvents();
if (audits) {
const evts = new Metrics(traceData.traceEvents, audits).generateFakeEvents();
traceData.traceEvents.push(...evts);
}
assets.push({
Expand All @@ -136,19 +136,21 @@ function prepareAssets(artifacts, results) {
/**
* Writes trace(s) and associated screenshot(s) to disk.
* @param {!Artifacts} artifacts
* @param {!Results} results
* @param {!Audits} audits
* @param {string} pathWithBasename
* @return {!Promise}
*/
function saveAssets(artifacts, results) {
return prepareAssets(artifacts, results).then(assets => {
function saveAssets(artifacts, audits, pathWithBasename) {
return prepareAssets(artifacts, audits).then(assets => {
assets.forEach((data, index) => {
const filenamePrefix = getFilenamePrefix(results);
const traceData = data.traceData;
fs.writeFileSync(`${filenamePrefix}-${index}.trace.json`, JSON.stringify(traceData, null, 2));
log.log('trace file saved to disk', filenamePrefix);
const traceFilename = `${pathWithBasename}-${index}.trace.json`;
fs.writeFileSync(traceFilename, JSON.stringify(traceData, null, 2));
log.log('trace file saved to disk', traceFilename);

fs.writeFileSync(`${filenamePrefix}-${index}.screenshots.html`, data.html);
log.log('screenshots saved to disk', filenamePrefix);
const screenshotsFilename = `${pathWithBasename}-${index}.screenshots.html`;
fs.writeFileSync(screenshotsFilename, data.html);
log.log('screenshots saved to disk', screenshotsFilename);
});
});
}
Expand Down
21 changes: 5 additions & 16 deletions lighthouse-core/test/lib/asset-saver-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@ describe('asset-saver helper', () => {
});

it('generates HTML', () => {
const options = {
url: 'https://testexample.com',
generatedTime: '2016-05-31T23:34:30.547Z'
};
const artifacts = {
traces: {
[Audit.DEFAULT_PASS]: {
Expand All @@ -58,19 +54,12 @@ describe('asset-saver helper', () => {
},
requestScreenshots: () => Promise.resolve([]),
};
return assetSaver.prepareAssets(artifacts, options).then(assets => {
return assetSaver.prepareAssets(artifacts).then(assets => {
assert.ok(/<!doctype/gim.test(assets[0].html));
});
});

describe('saves files', function() {
const options = {
url: 'https://testexample.com/',
generatedTime: '2016-05-31T23:34:30.547Z',
flags: {
saveAssets: true
}
};
const artifacts = {
traces: {
[Audit.DEFAULT_PASS]: {
Expand All @@ -80,17 +69,17 @@ describe('asset-saver helper', () => {
requestScreenshots: () => Promise.resolve(screenshotFilmstrip)
};

assetSaver.saveAssets(artifacts, options);
assetSaver.saveAssets(artifacts, dbwResults.audits, process.cwd() + '/the_file');

it('trace file saved to disk with data', () => {
const traceFilename = assetSaver.getFilenamePrefix(options) + '-0.trace.json';
const traceFilename = 'the_file-0.trace.json';
const traceFileContents = fs.readFileSync(traceFilename, 'utf8');
assert.ok(traceFileContents.length > 3000000);
fs.unlinkSync(traceFilename);
});

it('screenshots file saved to disk with data', () => {
const ssFilename = assetSaver.getFilenamePrefix(options) + '-0.screenshots.html';
const ssFilename = 'the_file-0.screenshots.html';
const ssFileContents = fs.readFileSync(ssFilename, 'utf8');
assert.ok(/<!doctype/gim.test(ssFileContents));
assert.ok(ssFileContents.includes('{"timestamp":674089419.919'));
Expand All @@ -108,7 +97,7 @@ describe('asset-saver helper', () => {
requestScreenshots: () => Promise.resolve([]),
};
const beforeCount = countEvents(dbwTrace);
return assetSaver.prepareAssets(mockArtifacts, dbwResults).then(preparedAssets => {
return assetSaver.prepareAssets(mockArtifacts, dbwResults.audits).then(preparedAssets => {
const afterCount = countEvents(preparedAssets[0].traceData);
const metricsSansNavStart = Metrics.metricsDefinitions.length - 1;
assert.equal(afterCount, beforeCount + (2 * metricsSansNavStart), 'unexpected event count');
Expand Down
17 changes: 17 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,23 @@ Options:
installations are found [boolean]
```

### Output Path Examples

`--output-path=~/mydir/foo.out --save-assets` generates
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe this actually works, though, because we don't do ~ expansion

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

depends on your shell I suppose, should I change to an absolute path?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

depends on your shell I suppose

ah, yeah, I have to do --output-path ~/mydir/foo.out for it to expand

* `~/mydir/foo.out`
* `~/mydir/foo.report.html`
* `~/mydir/foo-0.trace.json`
* `~/mydir/foo-0.screenshots.html`

`--output-path=./report.json --output json --save-artifacts` generates
* `./report.json`
* `./report.artifacts.log`

`--save-artifacts` prints a pretty report to `stdout` **and** generates
* `./<HOST>_<DATE>.report.html`
* `./<HOST>_<DATE>.artifacts.log`


## Lighthouse w/ mobile devices

Lighthouse can run against a real mobile device. You can follow the [Remote Debugging on Android (Legacy Workflow)](https://developer.chrome.com/devtools/docs/remote-debugging-legacy) up through step 3.3, but the TL;DR is install & run adb, enable USB debugging, then port forward 9222 from the device to the machine with Lighthouse.
Expand Down