Skip to content

Commit 184c92c

Browse files
authored
core(fr): convert optimized-images gatherer (#12491)
1 parent 1624e30 commit 184c92c

File tree

4 files changed

+63
-42
lines changed

4 files changed

+63
-42
lines changed

lighthouse-core/fraggle-rock/config/default-config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const artifacts = {
3232
MainDocumentContent: '',
3333
MetaElements: '',
3434
NetworkUserAgent: '',
35+
OptimizedImages: '',
3536
PasswordInputsWithPreventedPaste: '',
3637
RobotsTxt: '',
3738
SourceMaps: '',
@@ -76,6 +77,7 @@ const defaultConfig = {
7677
{id: artifacts.MainDocumentContent, gatherer: 'main-document-content'},
7778
{id: artifacts.MetaElements, gatherer: 'meta-elements'},
7879
{id: artifacts.NetworkUserAgent, gatherer: 'network-user-agent'},
80+
{id: artifacts.OptimizedImages, gatherer: 'dobetterweb/optimized-images'},
7981
{id: artifacts.PasswordInputsWithPreventedPaste, gatherer: 'dobetterweb/password-inputs-with-prevented-paste'},
8082
{id: artifacts.RobotsTxt, gatherer: 'seo/robots-txt'},
8183
{id: artifacts.SourceMaps, gatherer: 'source-maps'},
@@ -118,6 +120,7 @@ const defaultConfig = {
118120
artifacts.MainDocumentContent,
119121
artifacts.MetaElements,
120122
artifacts.NetworkUserAgent,
123+
artifacts.OptimizedImages,
121124
artifacts.PasswordInputsWithPreventedPaste,
122125
artifacts.RobotsTxt,
123126
artifacts.SourceMaps,

lighthouse-core/gather/gatherers/dobetterweb/optimized-images.js

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@
1111
'use strict';
1212

1313
const log = require('lighthouse-logger');
14-
const Gatherer = require('../gatherer.js');
14+
const FRGatherer = require('../../../fraggle-rock/gather/base-gatherer.js');
1515
const URL = require('../../../lib/url-shim.js');
1616
const NetworkRequest = require('../../../lib/network-request.js');
1717
const Sentry = require('../../../lib/sentry.js');
18-
const Driver = require('../../driver.js'); // eslint-disable-line no-unused-vars
18+
const NetworkRecords = require('../../../computed/network-records.js');
19+
const DevtoolsLog = require('../devtools-log.js');
1920

2021
// Image encoding can be slow and we don't want to spend forever on it.
2122
// Cap our encoding to 5 seconds, anything after that will be estimated.
@@ -32,7 +33,13 @@ const IMAGE_REGEX = /^image\/((x|ms|x-ms)-)?(png|bmp|jpeg)$/;
3233

3334
/** @typedef {{requestId: string, url: string, mimeType: string, resourceSize: number}} SimplifiedNetworkRecord */
3435

35-
class OptimizedImages extends Gatherer {
36+
class OptimizedImages extends FRGatherer {
37+
/** @type {LH.Gatherer.GathererMeta<'DevtoolsLog'>} */
38+
meta = {
39+
supportedModes: ['timespan', 'navigation'],
40+
dependencies: {DevtoolsLog: DevtoolsLog.symbol},
41+
}
42+
3643
constructor() {
3744
super();
3845
this._encodingStartAt = 0;
@@ -70,25 +77,25 @@ class OptimizedImages extends Gatherer {
7077
}
7178

7279
/**
73-
* @param {Driver} driver
80+
* @param {LH.Gatherer.FRProtocolSession} session
7481
* @param {string} requestId
7582
* @param {'jpeg'|'webp'} encoding Either webp or jpeg.
7683
* @return {Promise<LH.Crdp.Audits.GetEncodedResponseResponse>}
7784
*/
78-
_getEncodedResponse(driver, requestId, encoding) {
85+
_getEncodedResponse(session, requestId, encoding) {
7986
requestId = NetworkRequest.getRequestIdForBackend(requestId);
8087

8188
const quality = encoding === 'jpeg' ? JPEG_QUALITY : WEBP_QUALITY;
8289
const params = {requestId, encoding, quality, sizeOnly: true};
83-
return driver.sendCommand('Audits.getEncodedResponse', params);
90+
return session.sendCommand('Audits.getEncodedResponse', params);
8491
}
8592

8693
/**
87-
* @param {Driver} driver
94+
* @param {LH.Gatherer.FRProtocolSession} session
8895
* @param {SimplifiedNetworkRecord} networkRecord
8996
* @return {Promise<{originalSize: number, jpegSize?: number, webpSize?: number}>}
9097
*/
91-
async calculateImageStats(driver, networkRecord) {
98+
async calculateImageStats(session, networkRecord) {
9299
const originalSize = networkRecord.resourceSize;
93100
// Once we've hit our execution time limit or when the image is too big, don't try to re-encode it.
94101
// Images in this execution path will fallback to byte-per-pixel heuristics on the audit side.
@@ -97,8 +104,8 @@ class OptimizedImages extends Gatherer {
97104
return {originalSize, jpegSize: undefined, webpSize: undefined};
98105
}
99106

100-
const jpegData = await this._getEncodedResponse(driver, networkRecord.requestId, 'jpeg');
101-
const webpData = await this._getEncodedResponse(driver, networkRecord.requestId, 'webp');
107+
const jpegData = await this._getEncodedResponse(session, networkRecord.requestId, 'jpeg');
108+
const webpData = await this._getEncodedResponse(session, networkRecord.requestId, 'webp');
102109

103110
return {
104111
originalSize,
@@ -108,19 +115,19 @@ class OptimizedImages extends Gatherer {
108115
}
109116

110117
/**
111-
* @param {Driver} driver
118+
* @param {LH.Gatherer.FRProtocolSession} session
112119
* @param {Array<SimplifiedNetworkRecord>} imageRecords
113120
* @return {Promise<LH.Artifacts['OptimizedImages']>}
114121
*/
115-
async computeOptimizedImages(driver, imageRecords) {
122+
async computeOptimizedImages(session, imageRecords) {
116123
this._encodingStartAt = Date.now();
117124

118125
/** @type {LH.Artifacts['OptimizedImages']} */
119126
const results = [];
120127

121128
for (const record of imageRecords) {
122129
try {
123-
const stats = await this.calculateImageStats(driver, record);
130+
const stats = await this.calculateImageStats(session, record);
124131
/** @type {LH.Artifacts.OptimizedImage} */
125132
const image = {failed: false, ...stats, ...record};
126133
results.push(image);
@@ -145,26 +152,40 @@ class OptimizedImages extends Gatherer {
145152
}
146153

147154
/**
148-
* @param {LH.Gatherer.PassContext} passContext
149-
* @param {LH.Gatherer.LoadData} loadData
155+
* @param {LH.Gatherer.FRTransitionalContext} context
156+
* @param {LH.Artifacts.NetworkRequest[]} networkRecords
150157
* @return {Promise<LH.Artifacts['OptimizedImages']>}
151158
*/
152-
afterPass(passContext, loadData) {
153-
const networkRecords = loadData.networkRecords;
159+
async _getArtifact(context, networkRecords) {
154160
const imageRecords = OptimizedImages
155161
.filterImageRequests(networkRecords)
156162
.sort((a, b) => b.resourceSize - a.resourceSize);
157163

158-
return Promise.resolve()
159-
.then(_ => this.computeOptimizedImages(passContext.driver, imageRecords))
160-
.then(results => {
161-
const successfulResults = results.filter(result => !result.failed);
162-
if (results.length && !successfulResults.length) {
163-
throw new Error('All image optimizations failed');
164-
}
164+
const results = await this.computeOptimizedImages(context.driver.defaultSession, imageRecords);
165+
const successfulResults = results.filter(result => !result.failed);
166+
if (results.length && !successfulResults.length) {
167+
throw new Error('All image optimizations failed');
168+
}
169+
return results;
170+
}
165171

166-
return results;
167-
});
172+
/**
173+
* @param {LH.Gatherer.FRTransitionalContext<'DevtoolsLog'>} context
174+
* @return {Promise<LH.Artifacts['OptimizedImages']>}
175+
*/
176+
async getArtifact(context) {
177+
const devtoolsLog = context.dependencies.DevtoolsLog;
178+
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
179+
return this._getArtifact(context, networkRecords);
180+
}
181+
182+
/**
183+
* @param {LH.Gatherer.PassContext} passContext
184+
* @param {LH.Gatherer.LoadData} loadData
185+
* @return {Promise<LH.Artifacts['OptimizedImages']>}
186+
*/
187+
async afterPass(passContext, loadData) {
188+
return this._getArtifact({...passContext, dependencies: {}}, loadData.networkRecords);
168189
}
169190
}
170191

lighthouse-core/test/gather/gatherers/dobetterweb/optimized-images-test.js

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
/* eslint-env jest */
99

1010
const OptimizedImages = require('../../../../gather/gatherers/dobetterweb/optimized-images.js');
11+
const {createMockContext} = require('../../../fraggle-rock/gather/mock-driver.js');
1112

12-
let options;
13+
let context = createMockContext();
1314
let optimizedImages;
1415

1516
const traceData = {
@@ -112,19 +113,16 @@ describe('Optimized images', () => {
112113
// Reset the Gatherer before each test.
113114
beforeEach(() => {
114115
optimizedImages = new OptimizedImages();
115-
options = {
116-
url: 'http://google.com/',
117-
driver: {
118-
sendCommand: function(command, params) {
119-
const encodedSize = params.encoding === 'webp' ? 60 : 80;
120-
return Promise.resolve({encodedSize});
121-
},
122-
},
123-
};
116+
context = createMockContext();
117+
context.url = 'http://google.com';
118+
context.driver.defaultSession.sendCommand.mockImplementation((_, params) => {
119+
const encodedSize = params.encoding === 'webp' ? 60 : 80;
120+
return Promise.resolve({encodedSize});
121+
});
124122
});
125123

126124
it('returns all images, sorted with sizes', async () => {
127-
const artifact = await optimizedImages.afterPass(options, traceData);
125+
const artifact = await optimizedImages.afterPass(context, traceData);
128126
expect(artifact).toHaveLength(5);
129127
expect(artifact).toMatchObject([
130128
{
@@ -162,16 +160,16 @@ describe('Optimized images', () => {
162160

163161
it('handles partial driver failure', () => {
164162
let calls = 0;
165-
options.driver.sendCommand = () => {
163+
context.driver.defaultSession.sendCommand.mockImplementation(() => {
166164
calls++;
167165
if (calls > 2) {
168166
return Promise.reject(new Error('whoops driver failed'));
169167
} else {
170168
return Promise.resolve({encodedSize: 60});
171169
}
172-
};
170+
});
173171

174-
return optimizedImages.afterPass(options, traceData).then(artifact => {
172+
return optimizedImages.afterPass(context, traceData).then(artifact => {
175173
const failed = artifact.find(record => record.failed);
176174

177175
expect(artifact).toHaveLength(5);
@@ -194,7 +192,7 @@ describe('Optimized images', () => {
194192
],
195193
};
196194

197-
const artifact = await optimizedImages.afterPass(options, traceData);
195+
const artifact = await optimizedImages.afterPass(context, traceData);
198196
expect(artifact).toHaveLength(1);
199197
});
200198
});

types/artifacts.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ declare global {
2626
| 'InspectorIssues'
2727
| 'Manifest'
2828
| 'MixedContent'
29-
| 'OptimizedImages'
3029
| 'ResponseCompression'
3130
| 'ScriptElements'
3231
| 'ServiceWorker'

0 commit comments

Comments
 (0)