-
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
core(config): refactor config cloning for fraggle rock #11759
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/** | ||
* @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. | ||
*/ | ||
'use strict'; | ||
|
||
const helpers = require('../../config/config-helpers.js'); | ||
const Gatherer = require('../../gather/gatherers/gatherer.js'); | ||
const UserTimingsAudit = require('../../audits/user-timings.js'); | ||
|
||
/* eslint-env jest */ | ||
|
||
describe('.deepClone', () => { | ||
it('should clone things deeply', () => { | ||
const input = {a: {b: {c: 1}}}; | ||
const output = helpers.deepClone(input); | ||
expect(output).not.toBe(input); | ||
expect(output).toEqual(input); | ||
output.a.b.c = 2; | ||
expect(input.a.b.c).toEqual(1); | ||
}); | ||
}); | ||
|
||
describe('.deepCloneConfigJson', () => { | ||
it('should clone a config deeply', () => { | ||
const TimingGatherer = new Gatherer(); | ||
const input = { | ||
artifacts: [{id: 'Timing', gatherer: TimingGatherer}], | ||
passes: [{passName: 'defaultPass', gatherers: []}], | ||
audits: [{path: 'user-timings'}], | ||
categories: {random: {auditRefs: [{id: 'user-timings'}]}}, | ||
}; | ||
|
||
const output = helpers.deepCloneConfigJson(input); | ||
expect(output).not.toBe(input); | ||
expect(output).toEqual(input); | ||
output.artifacts[0].id = 'NewName'; | ||
output.passes[0].passName = 'newName'; | ||
output.audits[0].path = 'new-audit'; | ||
output.categories.random.auditRefs[0].id = 'new-audit'; | ||
expect(input.artifacts[0].id).toEqual('Timing'); | ||
expect(input.passes[0].passName).toEqual('defaultPass'); | ||
expect(input.audits[0].path).toEqual('user-timings'); | ||
expect(input.categories.random.auditRefs[0].id).toEqual('user-timings'); | ||
}); | ||
|
||
it('should preserve gatherer implementations in passes', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Perhaps it's cleaner this way and it doesn't really matter, but it seems a bit unnecessary for these to be separate cases. We could just have a "preserves implementations" test that covers all three cases as they are all testing the same function and behavior. |
||
const TimingGatherer = new Gatherer(); | ||
const input = { | ||
passes: [{passName: 'defaultPass', gatherers: [TimingGatherer]}], | ||
}; | ||
|
||
const output = helpers.deepCloneConfigJson(input); | ||
expect(output.passes[0].gatherers[0]).toEqual(TimingGatherer); | ||
}); | ||
|
||
it('should preserve gatherer implementations in artifacts', () => { | ||
const TimingGatherer = new Gatherer(); | ||
const input = { | ||
artifacts: [{id: 'Timing', gatherer: TimingGatherer}], | ||
}; | ||
|
||
const output = helpers.deepCloneConfigJson(input); | ||
expect(output.artifacts[0].gatherer).toEqual(TimingGatherer); | ||
}); | ||
|
||
it('should preserve audit implementations', () => { | ||
const input = { | ||
audits: [{implementation: UserTimingsAudit}], | ||
}; | ||
|
||
const output = helpers.deepCloneConfigJson(input); | ||
expect(output.audits[0].implementation).toEqual(UserTimingsAudit); | ||
}); | ||
}); | ||
|
||
|
||
describe('.requireAudits', () => { | ||
it('should expand audit short-hand', () => { | ||
const result = helpers.requireAudits(['user-timings']); | ||
|
||
expect(result).toEqual([{path: 'user-timings', options: {}, implementation: UserTimingsAudit}]); | ||
}); | ||
|
||
it('should handle multiple audit definition styles', () => { | ||
const result = helpers.requireAudits(['user-timings', {implementation: UserTimingsAudit}]); | ||
|
||
expect(result).toMatchObject([{path: 'user-timings'}, {implementation: UserTimingsAudit}]); | ||
}); | ||
|
||
it('should merge audit options', () => { | ||
const audits = [ | ||
'user-timings', | ||
{path: 'is-on-https', options: {x: 1, y: 1}}, | ||
{path: 'is-on-https', options: {x: 2}}, | ||
]; | ||
const merged = helpers.requireAudits(audits); | ||
expect(merged).toMatchObject([ | ||
{path: 'user-timings', options: {}}, | ||
{path: 'is-on-https', options: {x: 2, y: 1}}, | ||
]); | ||
}); | ||
|
||
it('throws for invalid auditDefns', () => { | ||
expect(() => helpers.requireAudits([new Gatherer()])).toThrow(/Invalid Audit type/); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
/* eslint-disable strict */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is anyone else having this problem in vscode where eslint is marking 100% of our .d.ts files as failing because of the strictness? started happening for me once we switched to the typescript parser in eslint There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yup same. vscode's really unhappy with my .d.ts files. |
||
/** | ||
* @license Copyright 2018 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 | ||
|
@@ -16,13 +17,36 @@ declare global { | |
export interface Json { | ||
extends?: 'lighthouse:default' | string | boolean; | ||
settings?: SharedFlagsSettings; | ||
artifacts?: ArtifactJson[] | null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah that's pleasant. I faintly remember some other hack where we allowed something to be expressed in JSON instead of read from a filepath. Ah, looks like it was --extra-headers. nevermind carry on! |
||
passes?: PassJson[] | null; | ||
audits?: Config.AuditJson[] | null; | ||
categories?: Record<string, CategoryJson> | null; | ||
groups?: Record<string, Config.GroupJson> | null; | ||
plugins?: Array<string>, | ||
} | ||
|
||
/** | ||
* The normalized and fully resolved config. | ||
*/ | ||
export interface Config { | ||
settings: Settings; | ||
passes: Pass[] | null; | ||
audits: AuditDefn[] | null; | ||
categories: Record<string, Category> | null; | ||
groups: Record<string, Group> | null; | ||
} | ||
|
||
/** | ||
* The normalized and fully resolved Fraggle Rock config. | ||
*/ | ||
export interface FRConfig { | ||
settings: Settings; | ||
artifacts: ArtifactDefn[] | null; | ||
audits: AuditDefn[] | null; | ||
categories: Record<string, Category> | null; | ||
groups: Record<string, Group> | null; | ||
} | ||
|
||
export interface PassJson { | ||
passName: string; | ||
loadFailureMode?: 'fatal'|'warn'|'ignore'; | ||
|
@@ -37,6 +61,11 @@ declare global { | |
gatherers?: GathererJson[]; | ||
} | ||
|
||
export interface ArtifactJson { | ||
id: string; | ||
gatherer: GathererJson; | ||
} | ||
|
||
export type GathererJson = { | ||
path: string; | ||
options?: {}; | ||
|
@@ -86,6 +115,11 @@ declare global { | |
gatherers: GathererDefn[]; | ||
} | ||
|
||
export interface ArtifactDefn { | ||
id: string; | ||
gatherer: GathererDefn; | ||
} | ||
|
||
export interface GathererDefn { | ||
implementation?: typeof Gatherer; | ||
instance: InstanceType<typeof Gatherer>; | ||
|
@@ -125,4 +159,4 @@ declare global { | |
} | ||
|
||
// empty export to keep file a module | ||
export {} | ||
export {}; |
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.
this function used to be called
cloneArrayWithPluginSafety
(we used to call the custom audit/gatherer definition "plugin support") which became very misleading once we actually introduced plugin support that means something else. This is just more straightforward for what it was.