-
Notifications
You must be signed in to change notification settings - Fork 9.4k
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
cli(output): Add ability to export results to CSV #4912
Conversation
Co-authored-by: Michiel van Spaendonck <mvanspaendonck@users.noreply.github.com>
Co-authored-by: Michiel van Spaendonck <mvanspaendonck@users.noreply.github.com> Co-authored-by: Kanav Anand <anandkanav92@users.noreply.github.com> Co-authored-by: Ernst Mulders <ernstmul@users.noreply.github.com>
These changes were made because the CSV generator requires type information in order to be 'correct'. It is very likely that parts of the codebase need to be annotated with these typings to increase type correctness. Co-authored-by: Michiel van Spaendonck <mvanspaendonck@users.noreply.github.com> Co-authored-by: Kanav Anand <anandkanav92@users.noreply.github.com> Co-authored-by: Ernst Mulders <ernstmul@users.noreply.github.com>
Co-authored-by: Michiel van Spaendonck <mvanspaendonck@users.noreply.github.com> Co-authored-by: Kanav Anand <anandkanav92@users.noreply.github.com> Co-authored-by: Ernst Mulders <ernstmul@users.noreply.github.com>
Co-authored-by: Michiel van Spaendonck <mvanspaendonck@users.noreply.github.com> Co-authored-by: Kanav Anand <anandkanav92@users.noreply.github.com> Co-authored-by: Ernst Mulders <ernstmul@users.noreply.github.com>
Co-authored-by: Michiel van Spaendonck <mvanspaendonck@users.noreply.github.com> Co-authored-by: Kanav Anand <anandkanav92@users.noreply.github.com> Co-authored-by: Ernst Mulders <ernstmul@users.noreply.github.com>
So there's good news and bad news. 👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there. 😕 The bad news is that it appears that one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request. Note to project maintainer: This is a terminal state, meaning the |
@mvanspaendonck, @anandkanav92, @ernstmul Y'all need to give Google the Good Ol' "I hereby give up my rights"! |
The cla should be signed by me already, but this is still aall fine with me. |
Fine by me 🤷🏼♂️ |
Fine by me as well! |
lighthouse-cli/printer.js
Outdated
|
||
// Possible TODO: tightly couple headers and row values | ||
// This would make it easier to include new / other row values | ||
const header = ['category', 'name', 'description', 'type', 'score']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we're going to be changing 'description' to 'title' soon, so we might as well use 'title' in the csv now so we don't break you in a week or so. :)
@@ -35,6 +36,22 @@ describe('Printer', () => { | |||
assert.ok(/<html lang="en"/gim.test(htmlOutput)); | |||
}); | |||
|
|||
it('creates CSV for results', () => { | |||
const mode = Printer.OutputMode.csv; | |||
const path = './.test-file.csv'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'./.test-file.csv' => './.results-as-csv.csv'
return Printer | ||
.write(sampleResults, mode, path) | ||
.then(_ => csv(path, headers).catch(err => assert.fail(err))) | ||
.then(_ => fs.unlinkSync(path)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think we want this unlinkSync in an after
block:
after(() => {
fs.unlinkSync(path)
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doing this means you don't need that catch()
anymore:
.then(_ => csv(path, headers));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I understand that would require creating another describe(...)
. Is this what you had in mind?
describe('CSV report generation', () => {
const mode = Printer.OutputMode.csv;
const path = './.results-as-csv.csv';
const headers = {
category: '',
name: '',
title: '',
type: '',
score: 42,
};
before(() => {
return Printer.write(sampleResults, mode, path);
});
it('generates valid CSV output', () => {
return csv(path, headers);
});
after(() => {
fs.unlinkSync(path);
});
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah true. i was hoping after
could be scoped to that test. whoops.
okay in that case, nevermind. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this working? assert.fail
just re-throws the error and we never enter the unlinkSync
block when there's a failure, no?
Maybe I'm just confused :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the contribution folks! yay teamwork! 🥇
lighthouse-cli/printer.js
Outdated
|
||
// Map every audit to its corresponding category | ||
// which can be queried when needed | ||
const map = {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: let's give this a more descriptive name auditIdToCategoryNameMap
?
lighthouse-cli/printer.js
Outdated
* - the score type that is used for the audit | ||
* - the score value of the audit | ||
* | ||
* @param {!LH.Results} results |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: we don't need the !
anymore
lighthouse-cli/printer.js
Outdated
// The document describes how to deal with escaping commas and quotes etc. | ||
const CRLF = '\r\n'; | ||
const separator = ','; | ||
/** @param {!string} value @returns {string} */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same !
package.json
Outdated
@@ -92,6 +92,7 @@ | |||
"chrome-devtools-frontend": "1.0.422034", | |||
"chrome-launcher": "^0.10.2", | |||
"configstore": "^3.1.1", | |||
"csv-validator": "^0.0.3", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's move this to devDependencies
Co-authored-by: Michiel van Spaendonck <mvanspaendonck@users.noreply.github.com> Co-authored-by: Kanav Anand <anandkanav92@users.noreply.github.com> Co-authored-by: Ernst Mulders <ernstmul@users.noreply.github.com>
wfm. over to patrick. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a few more small things but we're looking good!
return Printer | ||
.write(sampleResults, mode, path) | ||
.then(_ => csv(path, headers).catch(err => assert.fail(err))) | ||
.then(_ => fs.unlinkSync(path)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this working? assert.fail
just re-throws the error and we never enter the unlinkSync
block when there's a failure, no?
Maybe I'm just confused :)
typings/externs.d.ts
Outdated
@@ -88,6 +88,19 @@ declare global { | |||
artifacts?: Object; | |||
initialUrl: string; | |||
fetchedAt: string; | |||
reportCategories: [ReportCategory]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we want ReportCategory[]
, this is a type for an array of length one containing a ReportCategory :)
typings/externs.d.ts
Outdated
export interface ReportCategory { | ||
name: string; | ||
description: string; | ||
audits: [ReportAudit]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
@@ -10,6 +10,7 @@ const Printer = require('../../printer.js'); | |||
const assert = require('assert'); | |||
const fs = require('fs'); | |||
const sampleResults = require('../../../lighthouse-core/test/results/sample_v2.json'); | |||
const csv = require('csv-validator'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe const csvValidator
so it's obvious what it's up to down in the test function?
}; | ||
return Printer | ||
.write(sampleResults, mode, path) | ||
.then(_ => csv(path, headers).catch(err => assert.fail(err))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since csv(path, headers)
resolves with a json form of the CSV file, maybe also assert a row or two of the results, so it's not just checking their types?
lighthouse-cli/printer.js
Outdated
// Possible TODO: tightly couple headers and row values | ||
// This would make it easier to include new / other row values | ||
const header = ['category', 'name', 'title', 'type', 'score']; | ||
const table = Object |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like this could be turned into a single (nested) loop to skip making auditCategories
and make this a bit simpler? Correct me if I'm wrong though :) Something like
const table = results.reportCategories.map(category => {
return category.audits.map(catAudit => {
const audit = results.audits[catAudit.id];
return [category.name, audit.name, audit.description, audit.scoreDisplayMode, audit.score]
.map(value => value.toString())
.map(escape);
});
})
then just need to smoosh before outputting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These arrays are so small it really comes down to style, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@patrickhulce Maybe this makes it more clear what is going on and I have verified that this works correctly:
it('creates CSV for results', () => {
const mode = Printer.OutputMode.csv;
const path = './.results-as-csv.csv';
const headers = {
category: '',
name: '',
title: '',
type: '',
score: 42,
};
return Printer
.write(sampleResults, mode, path)
.then(async () => {
try {
await csvValidator(path, headers);
} catch (err) {
assert.fail('CSV parser error:\n' + err.join('\n'));
} finally {
fs.unlinkSync(path);
}
});
});
ah yeah async/await cleans this up a lot! let's do it 👍 :) |
Co-authored-by: Michiel van Spaendonck <mvanspaendonck@users.noreply.github.com> Co-authored-by: Kanav Anand <anandkanav92@users.noreply.github.com> Co-authored-by: Ernst Mulders <ernstmul@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could probably async up the rest of the test too ;) but this LGTM!!
thanks again folks, nice work!! 👏 💯
off to you @brendankenny
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
appveyor errors should be fixed by #4933, so good to go. Thanks for the PR! |
These changes implement the ability to export results to an CSV file.
These changes address the idea proposed in #4325 and #4481.
Each row of the CSV file contains the information of one audit. Currently the following properties are exported:
These properties were selected arbitrarily and perhaps there are more / better ones to export. The implementation relies on type information about the result object that is passed to the
Printer
inlighthouse-cli
. This information was lacking so it has been added in order to satisfy the typechecker. However, the added type information should probably be expanded further and used in other places to have better type safety in the complete code base.