diff --git a/packages/utils/src/assertions.js b/packages/utils/src/assertions.js index 26fe0bb13..e8bc70e3f 100644 --- a/packages/utils/src/assertions.js +++ b/packages/utils/src/assertions.js @@ -287,7 +287,7 @@ function resolveAssertionOptionsAndLhrs(baseOptions, unfilteredLhrs) { optionsToUse = _.merge(_.cloneDeep(presetData), optionsToUse); } - const {assertions = {}, matchingUrlPattern: urlPattern} = optionsToUse; + const {assertions = {}, matchingUrlPattern: urlPattern, aggregationMethod} = optionsToUse; const lhrs = urlPattern ? unfilteredLhrs.filter(lhr => doesLHRMatchPattern(urlPattern, lhr)) : unfilteredLhrs; @@ -312,6 +312,7 @@ function resolveAssertionOptionsAndLhrs(baseOptions, unfilteredLhrs) { assertions, auditsToAssert, medianLhrs, + aggregationMethod, lhrs: lhrs, url: (lhrs[0] && lhrs[0].finalUrl) || '', }; @@ -323,10 +324,14 @@ function resolveAssertionOptionsAndLhrs(baseOptions, unfilteredLhrs) { * @return {AssertionResult[]} */ function getAllFilteredAssertionResults(baseOptions, unfilteredLhrs) { - const {assertions, auditsToAssert, medianLhrs, lhrs, url} = resolveAssertionOptionsAndLhrs( - baseOptions, - unfilteredLhrs - ); + const { + assertions, + auditsToAssert, + medianLhrs, + lhrs, + url, + aggregationMethod, + } = resolveAssertionOptionsAndLhrs(baseOptions, unfilteredLhrs); // If we don't have any data, just return early. if (!lhrs.length) return []; @@ -338,14 +343,14 @@ function getAllFilteredAssertionResults(baseOptions, unfilteredLhrs) { const [level, assertionOptions] = normalizeAssertion(assertions[assertionKey]); if (level === 'off') continue; - const lhrsToUseForAudit = - assertionOptions.aggregationMethod === 'median-run' ? medianLhrs : lhrs; + const options = {aggregationMethod, ...assertionOptions}; + const lhrsToUseForAudit = options.aggregationMethod === 'median-run' ? medianLhrs : lhrs; const auditResults = lhrsToUseForAudit.map(lhr => lhr.audits[auditId]); const assertionResults = getAssertionResultsForAudit( auditId, auditProperty, auditResults, - assertionOptions + options ); for (const result of assertionResults) { diff --git a/packages/utils/test/assertions.test.js b/packages/utils/test/assertions.test.js index f6ca43d80..e3635ff7e 100644 --- a/packages/utils/test/assertions.test.js +++ b/packages/utils/test/assertions.test.js @@ -164,6 +164,16 @@ describe('getAllAssertionResults', () => { }); describe('aggregationMethod', () => { + it('should default to aggregationMethod optimistic', () => { + const assertions = { + 'first-contentful-paint': ['warn', {minScore: 1}], + 'network-requests': ['warn', {maxLength: 1}], + }; + + const results = getAllAssertionResults({assertions}, lhrs); + expect(results).toMatchObject([{actual: 0.8}, {actual: 2}]); + }); + it('should use aggregationMethod optimistic', () => { const assertions = { 'first-contentful-paint': ['warn', {aggregationMethod: 'optimistic', minScore: 1}], @@ -243,6 +253,26 @@ describe('getAllAssertionResults', () => { ]); }); + it('should use file-wide default when set', () => { + const assertions = { + 'first-contentful-paint': ['warn', {minScore: 1}], + 'network-requests': ['warn', {maxLength: 1}], + }; + + const results = getAllAssertionResults({assertions, aggregationMethod: 'pessimistic'}, lhrs); + expect(results).toMatchObject([{actual: 0.6}, {actual: 4}]); + }); + + it('should override file-wide default when set', () => { + const assertions = { + 'first-contentful-paint': ['warn', {minScore: 1, aggregationMethod: 'pessimistic'}], + 'network-requests': ['warn', {maxLength: 1}], + }; + + const results = getAllAssertionResults({assertions, aggregationMethod: 'median'}, lhrs); + expect(results).toMatchObject([{actual: 0.6}, {actual: 3}]); + }); + it('should handle partial failure with mode optimistic', () => { const assertions = { 'first-contentful-paint': ['warn', {aggregationMethod: 'optimistic'}], diff --git a/types/assert.d.ts b/types/assert.d.ts index b2b5ea877..ec043ae35 100644 --- a/types/assert.d.ts +++ b/types/assert.d.ts @@ -28,6 +28,7 @@ declare global { export interface BaseOptions { matchingUrlPattern?: string; + aggregationMethod?: AssertionAggregationMethod; preset?: 'lighthouse:all' | 'lighthouse:recommended'; assertions?: Assertions; }