Skip to content

Commit

Permalink
tests: add networkRecord-to-devtoolsLog mocking utility (#6171)
Browse files Browse the repository at this point in the history
  • Loading branch information
brendankenny authored and paulirish committed Oct 4, 2018
1 parent 1ce80f4 commit 9b68ff3
Show file tree
Hide file tree
Showing 15 changed files with 384 additions and 182 deletions.
Expand Up @@ -9,6 +9,8 @@ const TotalByteWeight = require('../../../audits/byte-efficiency/total-byte-weig
const assert = require('assert');
const URL = require('url').URL;
const options = TotalByteWeight.defaultOptions;
const Runner = require('../../../runner.js');
const networkRecordsToDevtoolsLog = require('../../network-records-to-devtools-log.js');

/* eslint-env jest */

Expand All @@ -32,10 +34,9 @@ function generateArtifacts(records) {
records = records.map(args => generateRequest(...args));
}

return {
devtoolsLogs: {defaultPass: []},
requestNetworkRecords: () => Promise.resolve(records),
};
return Object.assign(Runner.instantiateComputedArtifacts(), {
devtoolsLogs: {defaultPass: networkRecordsToDevtoolsLog(records)},
});
}

describe('Total byte weight audit', () => {
Expand Down
Expand Up @@ -7,6 +7,8 @@

const UnusedCSSAudit = require('../../../audits/byte-efficiency/unused-css-rules.js');
const assert = require('assert');
const Runner = require('../../../runner.js');
const networkRecordsToDevtoolsLog = require('../../network-records-to-devtools-log.js');

/* eslint-env jest */

Expand Down Expand Up @@ -117,33 +119,32 @@ describe('Best Practices: unused css rules audit', () => {
});

describe('#audit', () => {
const devtoolsLogs = {defaultPass: []};
const requestNetworkRecords = () => {
return Promise.resolve([
{
url: 'file://a.css',
transferSize: 10 * 1024,
resourceType: 'Stylesheet',
},
]);
};
const networkRecords = [
{
url: 'file://a.css',
transferSize: 10 * 1024,
resourceType: 'Stylesheet',
},
];

function getArtifacts({CSSUsage}) {
return Object.assign(Runner.instantiateComputedArtifacts(), {
devtoolsLogs: {defaultPass: networkRecordsToDevtoolsLog(networkRecords)},
URL: {finalUrl: ''},
CSSUsage,
});
}

it('ignores missing stylesheets', () => {
return UnusedCSSAudit.audit_({
devtoolsLogs,
requestNetworkRecords,
URL: {finalUrl: ''},
return UnusedCSSAudit.audit_(getArtifacts({
CSSUsage: {rules: [{styleSheetId: 'a', used: false}], stylesheets: []},
}).then(result => {
})).then(result => {
assert.equal(result.items.length, 0);
});
});

it('ignores stylesheets that are 100% used', () => {
return UnusedCSSAudit.audit_({
devtoolsLogs,
requestNetworkRecords,
URL: {finalUrl: ''},
return UnusedCSSAudit.audit_(getArtifacts({
CSSUsage: {rules: [
{styleSheetId: 'a', used: true},
{styleSheetId: 'a', used: true},
Expand All @@ -158,16 +159,13 @@ describe('Best Practices: unused css rules audit', () => {
content: '.my.favorite.selector { rule: content; }',
},
]},
}).then(result => {
})).then(result => {
assert.equal(result.items.length, 0);
});
});

it('fails when lots of rules are unused', () => {
return UnusedCSSAudit.audit_({
devtoolsLogs,
requestNetworkRecords,
URL: {finalUrl: ''},
return UnusedCSSAudit.audit_(getArtifacts({
CSSUsage: {rules: [
{styleSheetId: 'a', used: true, startOffset: 0, endOffset: 11}, // 44 * 1 / 4
{styleSheetId: 'b', used: true, startOffset: 0, endOffset: 6000}, // 4000 * 3 / 2
Expand All @@ -185,7 +183,7 @@ describe('Best Practices: unused css rules audit', () => {
content: `${generate('123', 450)}`, // will be filtered out
},
]},
}).then(result => {
})).then(result => {
assert.equal(result.items.length, 2);
assert.equal(result.items[0].totalBytes, 10 * 1024);
assert.equal(result.items[1].totalBytes, 6000);
Expand All @@ -195,10 +193,7 @@ describe('Best Practices: unused css rules audit', () => {
});

it('does not include empty or small sheets', () => {
return UnusedCSSAudit.audit_({
devtoolsLogs,
requestNetworkRecords,
URL: {finalUrl: ''},
return UnusedCSSAudit.audit_(getArtifacts({
CSSUsage: {rules: [
{styleSheetId: 'a', used: true, startOffset: 0, endOffset: 8000}, // 4000 * 3 / 2
{styleSheetId: 'b', used: true, startOffset: 0, endOffset: 500}, // 500 * 3 / 3
Expand All @@ -224,7 +219,7 @@ describe('Best Practices: unused css rules audit', () => {
content: ' ',
},
]},
}).then(result => {
})).then(result => {
assert.equal(result.items.length, 1);
assert.equal(Math.floor(result.items[0].wastedPercent), 33);
});
Expand Down
Expand Up @@ -9,6 +9,8 @@ const CacheHeadersAudit = require('../../../audits/byte-efficiency/uses-long-cac
const assert = require('assert');
const NetworkRequest = require('../../../lib/network-request');
const options = CacheHeadersAudit.defaultOptions;
const Runner = require('../../../runner.js');
const networkRecordsToDevtoolsLog = require('../../network-records-to-devtools-log.js');

/* eslint-env jest */

Expand All @@ -28,20 +30,20 @@ function networkRecord(options = {}) {
}

describe('Cache headers audit', () => {
let artifacts;
let networkRecords;
function getArtifacts(networkRecords) {
const devtoolLogs = networkRecordsToDevtoolsLog(networkRecords);

beforeEach(() => {
artifacts = {
devtoolsLogs: {},
requestNetworkRecords: () => Promise.resolve(networkRecords),
return Object.assign(Runner.instantiateComputedArtifacts(), {
devtoolsLogs: {
[CacheHeadersAudit.DEFAULT_PASS]: devtoolLogs,
},
requestNetworkThroughput: () => Promise.resolve(1000),
};
});
});
}

it('detects missing cache headers', () => {
networkRecords = [networkRecord()];
return CacheHeadersAudit.audit(artifacts, {options}).then(result => {
const networkRecords = [networkRecord()];
return CacheHeadersAudit.audit(getArtifacts(networkRecords), {options}).then(result => {
const items = result.extendedInfo.value.results;
assert.equal(items.length, 1);
assert.equal(items[0].cacheLifetimeMs, 0);
Expand All @@ -51,14 +53,14 @@ describe('Cache headers audit', () => {
});

it('detects low value max-age headers', () => {
networkRecords = [
const networkRecords = [
networkRecord({headers: {'cache-control': 'max-age=3600'}}), // an hour
networkRecord({headers: {'cache-control': 'max-age=3600'}, transferSize: 100000}), // an hour
networkRecord({headers: {'cache-control': 'max-age=86400'}}), // a day
networkRecord({headers: {'cache-control': 'max-age=31536000'}}), // a year
];

return CacheHeadersAudit.audit(artifacts, {options}).then(result => {
return CacheHeadersAudit.audit(getArtifacts(networkRecords), {options}).then(result => {
const items = result.details.items;
assert.equal(items.length, 3);
assert.equal(items[0].cacheLifetimeMs, 3600 * 1000);
Expand All @@ -76,14 +78,14 @@ describe('Cache headers audit', () => {
const expiresIn = seconds => new Date(Date.now() + seconds * 1000).toGMTString();
const closeEnough = (actual, exp) => assert.ok(Math.abs(actual - exp) <= 1, 'invalid expires');

networkRecords = [
const networkRecords = [
networkRecord({headers: {expires: expiresIn(86400 * 365)}}), // a year
networkRecord({headers: {expires: expiresIn(86400 * 90)}}), // 3 months
networkRecord({headers: {expires: expiresIn(86400)}}), // a day
networkRecord({headers: {expires: expiresIn(3600)}}), // an hour
];

return CacheHeadersAudit.audit(artifacts, {options}).then(result => {
return CacheHeadersAudit.audit(getArtifacts(networkRecords), {options}).then(result => {
const items = result.extendedInfo.value.results;
assert.equal(items.length, 3);
closeEnough(items[0].cacheLifetimeMs, 3600 * 1000);
Expand All @@ -98,7 +100,7 @@ describe('Cache headers audit', () => {
it('respects expires/cache-control priority', () => {
const expiresIn = seconds => new Date(Date.now() + seconds * 1000).toGMTString();

networkRecords = [
const networkRecords = [
networkRecord({headers: {
'cache-control': 'must-revalidate,max-age=3600',
'expires': expiresIn(86400),
Expand All @@ -109,7 +111,7 @@ describe('Cache headers audit', () => {
}}),
];

return CacheHeadersAudit.audit(artifacts, {options}).then(result => {
return CacheHeadersAudit.audit(getArtifacts(networkRecords), {options}).then(result => {
const items = result.extendedInfo.value.results;
assert.equal(items.length, 2);
assert.ok(Math.abs(items[0].cacheLifetimeMs - 3600 * 1000) <= 1, 'invalid expires parsing');
Expand All @@ -120,7 +122,7 @@ describe('Cache headers audit', () => {
});

it('respects multiple cache-control headers', () => {
networkRecords = [
const networkRecords = [
networkRecord({headers: {
'cache-control': 'max-age=31536000, public',
'Cache-control': 'no-transform',
Expand All @@ -132,49 +134,49 @@ describe('Cache headers audit', () => {
}}),
];

return CacheHeadersAudit.audit(artifacts, {options}).then(result => {
return CacheHeadersAudit.audit(getArtifacts(networkRecords), {options}).then(result => {
const items = result.extendedInfo.value.results;
assert.equal(items.length, 1);
});
});

it('catches records with Etags', () => {
networkRecords = [
const networkRecords = [
networkRecord({headers: {etag: 'md5hashhere'}}),
networkRecord({headers: {'etag': 'md5hashhere', 'cache-control': 'max-age=60'}}),
];

return CacheHeadersAudit.audit(artifacts, {options}).then(result => {
return CacheHeadersAudit.audit(getArtifacts(networkRecords), {options}).then(result => {
const items = result.extendedInfo.value.results;
assert.equal(items.length, 2);
});
});

it('ignores explicit no-cache policies', () => {
networkRecords = [
const networkRecords = [
networkRecord({headers: {expires: '-1'}}),
networkRecord({headers: {'cache-control': 'no-store'}}),
networkRecord({headers: {'cache-control': 'no-cache'}}),
networkRecord({headers: {'cache-control': 'max-age=0'}}),
networkRecord({headers: {pragma: 'no-cache'}}),
];

return CacheHeadersAudit.audit(artifacts, {options}).then(result => {
return CacheHeadersAudit.audit(getArtifacts(networkRecords), {options}).then(result => {
const items = result.extendedInfo.value.results;
assert.equal(result.score, 1);
assert.equal(items.length, 0);
});
});

it('ignores potentially uncacheable records', () => {
networkRecords = [
const networkRecords = [
networkRecord({statusCode: 500}),
networkRecord({url: 'https://example.com/dynamic.js?userId=crazy', transferSize: 10}),
networkRecord({url: 'data:image/jpeg;base64,what'}),
networkRecord({resourceType: NetworkRequest.TYPES.XHR}),
];

return CacheHeadersAudit.audit(artifacts, {options}).then(result => {
return CacheHeadersAudit.audit(getArtifacts(networkRecords), {options}).then(result => {
assert.equal(result.score, 1);
const items = result.extendedInfo.value.results;
assert.equal(items.length, 1);
Expand Down
8 changes: 3 additions & 5 deletions lighthouse-core/test/audits/critical-request-chains-test.js
Expand Up @@ -7,6 +7,7 @@

/* eslint-env jest */

const Runner = require('../../runner.js');
const CriticalRequestChains = require('../../audits/critical-request-chains.js');
const assert = require('assert');

Expand Down Expand Up @@ -70,17 +71,14 @@ const PASSING_REQUEST_CHAIN_2 = {
const EMPTY_REQUEST_CHAIN = {};

const mockArtifacts = (mockChain) => {
return {
return Object.assign(Runner.instantiateComputedArtifacts(), {
devtoolsLogs: {
[CriticalRequestChains.DEFAULT_PASS]: [],
},
requestNetworkRecords: () => {
return Promise.resolve([]);
},
requestCriticalRequestChains: function() {
return Promise.resolve(mockChain);
},
};
});
};

describe('Performance: critical-request-chains audit', () => {
Expand Down
36 changes: 21 additions & 15 deletions lighthouse-core/test/audits/dobetterweb/uses-http2-test.js
Expand Up @@ -5,21 +5,31 @@
*/
'use strict';

const Runner = require('../../../runner.js');
const UsesHTTP2Audit = require('../../../audits/dobetterweb/uses-http2.js');
const assert = require('assert');
const networkRecordsToDevtoolsLog = require('../../network-records-to-devtools-log.js');

const URL = 'https://webtide.com/http2-push-demo/';
const networkRecords = require('../../fixtures/networkRecords-mix.json');

/* eslint-env jest */

describe('Resources are fetched over http/2', () => {
function getArtifacts(networkRecords, finalUrl) {
// networkRecords-mix.json is an old network request format, so don't verify round-trip.
const devtoolsLog = networkRecordsToDevtoolsLog(networkRecords, {skipVerification: true});

return Object.assign(Runner.instantiateComputedArtifacts(), {
URL: {finalUrl},
devtoolsLogs: {[UsesHTTP2Audit.DEFAULT_PASS]: devtoolsLog},
});
}

it('fails when some resources were requested via http/1.x', () => {
return UsesHTTP2Audit.audit({
URL: {finalUrl: URL},
devtoolsLogs: {[UsesHTTP2Audit.DEFAULT_PASS]: []},
requestNetworkRecords: () => Promise.resolve(networkRecords),
}).then(auditResult => {
return UsesHTTP2Audit.audit(
getArtifacts(networkRecords, URL)
).then(auditResult => {
assert.equal(auditResult.rawValue, false);
assert.ok(auditResult.displayValue.match('3 requests not'));
assert.equal(auditResult.details.items.length, 3);
Expand All @@ -32,11 +42,9 @@ describe('Resources are fetched over http/2', () => {

it('displayValue is correct when only one resource fails', () => {
const entryWithHTTP1 = networkRecords.slice(1, 2);
return UsesHTTP2Audit.audit({
URL: {finalUrl: URL},
devtoolsLogs: {[UsesHTTP2Audit.DEFAULT_PASS]: []},
requestNetworkRecords: () => Promise.resolve(entryWithHTTP1),
}).then(auditResult => {
return UsesHTTP2Audit.audit(
getArtifacts(entryWithHTTP1, URL)
).then(auditResult => {
assert.ok(auditResult.displayValue.match('1 request not'));
});
});
Expand All @@ -47,11 +55,9 @@ describe('Resources are fetched over http/2', () => {
record.protocol = 'h2';
});

return UsesHTTP2Audit.audit({
URL: {finalUrl: URL},
devtoolsLogs: {[UsesHTTP2Audit.DEFAULT_PASS]: []},
requestNetworkRecords: () => Promise.resolve(h2Records),
}).then(auditResult => {
return UsesHTTP2Audit.audit(
getArtifacts(h2Records, URL)
).then(auditResult => {
assert.equal(auditResult.rawValue, true);
assert.ok(auditResult.displayValue === '');
});
Expand Down

0 comments on commit 9b68ff3

Please sign in to comment.