Skip to content
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(base-artifacts): add FormFactorIsMobile base artifact #7280

Merged
merged 10 commits into from
Mar 5, 2019
13 changes: 4 additions & 9 deletions lighthouse-core/audits/content-width.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,21 @@ class ContentWidth extends Audit {
description: 'If the width of your app\'s content doesn\'t match the width ' +
'of the viewport, your app might not be optimized for mobile screens. ' +
'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/content-sized-correctly-for-viewport).',
requiredArtifacts: ['ViewportDimensions', 'HostUserAgent'],
requiredArtifacts: ['ViewportDimensions', 'TestedAsMobileDevice'],
};
}

/**
* @param {LH.Artifacts} artifacts
* @param {LH.Audit.Context} context
* @return {LH.Audit.Product}
*/
static audit(artifacts, context) {
const userAgent = artifacts.HostUserAgent;
static audit(artifacts) {
const IsMobile = artifacts.TestedAsMobileDevice;
const viewportWidth = artifacts.ViewportDimensions.innerWidth;
const windowWidth = artifacts.ViewportDimensions.outerWidth;
const widthsMatch = viewportWidth === windowWidth;

const isMobileHost = userAgent.includes('Android') || userAgent.includes('Mobile');
const isMobile = context.settings.emulatedFormFactor === 'mobile' ||
(context.settings.emulatedFormFactor !== 'desktop' && isMobileHost);

if (isMobile) {
if (IsMobile) {
return {
rawValue: widthsMatch,
explanation: this.createExplanation(widthsMatch, artifacts.ViewportDimensions),
Expand Down
11 changes: 10 additions & 1 deletion lighthouse-core/gather/gather-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -395,10 +395,19 @@ class GatherRunner {
* @return {Promise<LH.BaseArtifacts>}
*/
static async getBaseArtifacts(options) {
const hostUserAgent = (await options.driver.getBrowserVersion()).userAgent;

const {emulatedFormFactor} = options.settings;
// Whether Lighthouse was run on a mobile device (i.e. not on a desktop machine).
const IsMobileHost = hostUserAgent.includes('Android') || hostUserAgent.includes('Mobile');
const TestedAsMobileDevice = emulatedFormFactor === 'mobile' ||
(emulatedFormFactor !== 'desktop' && IsMobileHost);

return {
fetchTime: (new Date()).toJSON(),
LighthouseRunWarnings: [],
HostUserAgent: (await options.driver.getBrowserVersion()).userAgent,
TestedAsMobileDevice,
HostUserAgent: hostUserAgent,
NetworkUserAgent: '', // updated later
BenchmarkIndex: 0, // updated later
WebAppManifest: null, // updated later
Expand Down
23 changes: 5 additions & 18 deletions lighthouse-core/test/audits/content-width-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,12 @@ const assert = require('assert');
describe('Mobile-friendly: content-width audit', () => {
it('fails when scroll width differs from viewport width', () => {
const result = Audit.audit({
HostUserAgent: 'Desktop',
TestedAsMobileDevice: true,
ViewportDimensions: {
innerWidth: 100,
outerWidth: 300,
},
}, {settings: {emulatedFormFactor: 'mobile'}});

assert.equal(result.rawValue, false);
assert.ok(result.explanation);
});

it('fails when host user agent is a phone', () => {
const result = Audit.audit({
HostUserAgent: 'Mobile Android',
ViewportDimensions: {
innerWidth: 100,
outerWidth: 300,
},
}, {settings: {emulatedFormFactor: 'none'}});
});

assert.equal(result.rawValue, false);
assert.ok(result.explanation);
Expand All @@ -47,13 +34,13 @@ describe('Mobile-friendly: content-width audit', () => {
}, {settings: {emulatedFormFactor: 'mobile'}}).rawValue, true);
});

it('not applicable when device emulation is turned off', () => {
it('not applicable when run on desktop', () => {
return assert.equal(Audit.audit({
HostUserAgent: 'Mobile Android Chrome',
TestedAsMobileDevice: false,
ViewportDimensions: {
innerWidth: 300,
outerWidth: 450,
},
}, {settings: {emulatedFormFactor: 'desktop'}}).notApplicable, true);
}).notApplicable, true);
});
});
150 changes: 81 additions & 69 deletions lighthouse-core/test/gather/fake-driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,79 @@
*/
'use strict';

function makeFakeDriver({protocolGetVersionResponse}) {
mattzeunert marked this conversation as resolved.
Show resolved Hide resolved
return {
getBrowserVersion() {
return Promise.resolve(Object.assign({}, protocolGetVersionResponse, {milestone: 71}));
},
getBenchmarkIndex() {
return Promise.resolve(125.2);
},
getAppManifest() {
return Promise.resolve(null);
},
connect() {
return Promise.resolve();
},
disconnect() {
return Promise.resolve();
},
gotoURL() {
return Promise.resolve('https://www.reddit.com/r/nba');
},
beginEmulation() {
return Promise.resolve();
},
setThrottling() {
return Promise.resolve();
},
dismissJavaScriptDialogs() {
return Promise.resolve();
},
assertNoSameOriginServiceWorkerClients() {
return Promise.resolve();
},
reloadForCleanStateIfNeeded() {
return Promise.resolve();
},
enableRuntimeEvents() {
return Promise.resolve();
},
evaluateScriptOnLoad() {
return Promise.resolve();
},
cleanBrowserCaches() {},
clearDataForOrigin() {},
cacheNatives() {
return Promise.resolve();
},
evaluateAsync() {
return Promise.resolve({});
},
registerPerformanceObserver() {
return Promise.resolve();
},
beginTrace() {
return Promise.resolve();
},
endTrace() {
return Promise.resolve(
require('../fixtures/traces/progressive-app.json')
);
},
beginDevtoolsLog() {},
endDevtoolsLog() {
return require('../fixtures/artifacts/perflog/defaultPass.devtoolslog.json');
},
blockUrlPatterns() {
return Promise.resolve();
},
setExtraHTTPHeaders() {
return Promise.resolve();
},
};
}

// https://chromedevtools.github.io/devtools-protocol/tot/Browser#method-getVersion
const protocolGetVersionResponse = {
protocolVersion: '1.3',
Expand All @@ -14,77 +87,16 @@ const protocolGetVersionResponse = {
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3577.0 Safari/537.36',
jsVersion: '7.1.314',
};
const fakeDriver = makeFakeDriver({protocolGetVersionResponse});

const fakeDriver = {
getBrowserVersion() {
return Promise.resolve(Object.assign({}, protocolGetVersionResponse, {milestone: 71}));
},
getBenchmarkIndex() {
return Promise.resolve(125.2);
},
getAppManifest() {
return Promise.resolve(null);
},
connect() {
return Promise.resolve();
},
disconnect() {
return Promise.resolve();
},
gotoURL() {
return Promise.resolve('https://www.reddit.com/r/nba');
},
beginEmulation() {
return Promise.resolve();
},
setThrottling() {
return Promise.resolve();
},
dismissJavaScriptDialogs() {
return Promise.resolve();
},
assertNoSameOriginServiceWorkerClients() {
return Promise.resolve();
},
reloadForCleanStateIfNeeded() {
return Promise.resolve();
const fakeDriverUsingRealMobileDevice = makeFakeDriver({
protocolGetVersionResponse: {
...protocolGetVersionResponse,
// eslint-disable-next-line max-len
userAgent: 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5 Build/MRA58N) AppleWebKit/537.36(KHTML, like Gecko) Chrome/69.0.3464.0 Mobile Safari/537.36',
},
enableRuntimeEvents() {
return Promise.resolve();
},
evaluateScriptOnLoad() {
return Promise.resolve();
},
cleanBrowserCaches() {},
clearDataForOrigin() {},
cacheNatives() {
return Promise.resolve();
},
evaluateAsync() {
return Promise.resolve({});
},
registerPerformanceObserver() {
return Promise.resolve();
},
beginTrace() {
return Promise.resolve();
},
endTrace() {
return Promise.resolve(
require('../fixtures/traces/progressive-app.json')
);
},
beginDevtoolsLog() {},
endDevtoolsLog() {
return require('../fixtures/artifacts/perflog/defaultPass.devtoolslog.json');
},
blockUrlPatterns() {
return Promise.resolve();
},
setExtraHTTPHeaders() {
return Promise.resolve();
},
};
});

module.exports = fakeDriver;
module.exports.fakeDriverUsingRealMobileDevice = fakeDriverUsingRealMobileDevice;
module.exports.protocolGetVersionResponse = protocolGetVersionResponse;
45 changes: 45 additions & 0 deletions lighthouse-core/test/gather/gather-runner-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class TestGathererNoArtifact extends Gatherer {
}

const fakeDriver = require('./fake-driver');
const fakeDriverUsingRealMobileDevice = fakeDriver.fakeDriverUsingRealMobileDevice;

function getMockedEmulationDriver(emulationFn, netThrottleFn, cpuThrottleFn,
blockUrlFn, extraHeadersFn) {
Expand Down Expand Up @@ -156,6 +157,50 @@ describe('GatherRunner', function() {
});
});

describe('collects TestedAsMobileDevice as an artifact', () => {
const url = 'https://example.com';

it('works when running on desktop device without emulation', async () => {
const driver = fakeDriver;
const config = new Config({passes: [{}]});
const settings = {};
const options = {url, driver, config, settings};

const results = await GatherRunner.run(config.passes, options);
expect(results.TestedAsMobileDevice).toBe(false);
});

it('works when running on desktop device with mobile emulation', async () => {
mattzeunert marked this conversation as resolved.
Show resolved Hide resolved
const driver = fakeDriver;
const config = new Config({passes: [{}]});
const settings = {emulatedFormFactor: 'mobile'};
const options = {url, driver, config, settings};

const results = await GatherRunner.run(config.passes, options);
expect(results.TestedAsMobileDevice).toBe(true);
});

it('works when running on mobile device without emulation', async () => {
const driver = fakeDriverUsingRealMobileDevice;
const config = new Config({passes: [{}]});
const settings = {};
const options = {url, driver, config, settings};

const results = await GatherRunner.run(config.passes, options);
expect(results.TestedAsMobileDevice).toBe(true);
});

it('works when running on mobile device with desktop emulation', async () => {
const driver = fakeDriverUsingRealMobileDevice;
const config = new Config({passes: [{}]});
const settings = {emulatedFormFactor: 'desktop'};
const options = {url, driver, config, settings};

const results = await GatherRunner.run(config.passes, options);
expect(results.TestedAsMobileDevice).toBe(false);
});
});

it('sets up the driver to begin emulation when all emulation flags are undefined', () => {
const tests = {
calledDeviceEmulation: false,
Expand Down
1 change: 1 addition & 0 deletions lighthouse-core/test/results/artifacts/artifacts.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"LighthouseRunWarnings": [],
"BenchmarkIndex": 1000,
"TestedAsMobileDevice": true,
"HostUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3358.0 Safari/537.36",
"NetworkUserAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5 Build/MRA58N) AppleWebKit/537.36(KHTML, like Gecko) Chrome/66.0.3359.30 Mobile Safari/537.36",
"fetchTime": "2018-03-13T00:55:45.840Z",
Expand Down
2 changes: 2 additions & 0 deletions types/artifacts.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ declare global {
fetchTime: string;
/** A set of warnings about unexpected things encountered while loading and testing the page. */
LighthouseRunWarnings: string[];
/** Whether the page was loaded on either a real or emulated mobile device. */
TestedAsMobileDevice: boolean;
/** The user agent string of the version of Chrome used. */
HostUserAgent: string;
/** The user agent string that Lighthouse used to load the page. */
Expand Down