Skip to content

Commit

Permalink
Adds auto-requested geolocation audit (#510)
Browse files Browse the repository at this point in the history
  • Loading branch information
paullewis authored and brendankenny committed Jul 26, 2016
1 parent c14e7a3 commit 60a06c5
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 2 deletions.
54 changes: 54 additions & 0 deletions lighthouse-core/audits/geolocation-on-start.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* @license
* Copyright 2016 Google Inc. 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 Audit = require('./audit');

class GeolocationOnStart extends Audit {
/**
* @return {!AuditMeta}
*/
static get meta() {
return {
category: 'UX',
name: 'geolocation',
description: 'Page does not automatically request geolocation',
requiredArtifacts: ['GeolocationOnStart']
};
}

/**
* @param {!Artifacts} artifacts
* @return {!AuditResult}
*/
static audit(artifacts) {
if (typeof artifacts.GeolocationOnStart === 'undefined' ||
artifacts.GeolocationOnStart === -1) {
return GeolocationOnStart.generateAuditResult({
rawValue: false,
debugString: 'Unable to get geolocation values.'
});
}

return GeolocationOnStart.generateAuditResult({
rawValue: artifacts.GeolocationOnStart
});
}
}

module.exports = GeolocationOnStart;
3 changes: 3 additions & 0 deletions lighthouse-core/closure/typedefs/Artifacts.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,6 @@ Artifacts.prototype.ContentWidth;

/** @type {!Array<string>} */
Artifacts.prototype.CacheContents;

/** @type {boolean|number} */
Artifacts.prototype.GeolocationOnStart;
10 changes: 8 additions & 2 deletions lighthouse-core/config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"critical-request-chains",
"speedline",
"content-width",
"cache-contents"
"cache-contents",
"geolocation-on-start"
]
},
{
Expand Down Expand Up @@ -66,7 +67,8 @@
"label",
"tabindex",
"content-width",
"cache-start-url"
"cache-start-url",
"geolocation-on-start"
],

"aggregations": [{
Expand Down Expand Up @@ -277,6 +279,10 @@
"rawValue": true,
"weight": 1
},
"geolocation-on-start": {
"rawValue": true,
"weight": 1
},
"serviceworker-push": {
"rawValue": true,
"weight": 0,
Expand Down
6 changes: 6 additions & 0 deletions lighthouse-core/driver/drivers/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ class DriverBase {
return Promise.reject(new Error('Not implemented'));
}

evaluateScriptOnLoad(scriptSource) {
return this.sendCommand('Page.addScriptToEvaluateOnLoad', {
scriptSource
});
}

evaluateAsync(asyncExpression) {
return new Promise((resolve, reject) => {
let asyncTimeout;
Expand Down
62 changes: 62 additions & 0 deletions lighthouse-core/driver/gatherers/geolocation-on-start.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* @license
* Copyright 2016 Google Inc. 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 Gather = require('./gather');

/**
* @fileoverview Tests whether the page attempts to request geolocation on page load. This often
* represents a poor user experience, since it lacks context. As such, if the page requests
* geolocation the gatherer will intercept the call and mark a boolean flag to true. The audit that
* corresponds with this gatherer then checks for the flag.
* @author Paul Lewis
*/

/* global navigator, window, __returnResults */

/* istanbul ignore next */
function overrideGeo() {
window.__didNotCallGeo = true;
// Override the geo functions so that if they're called they're intercepted and we know about it.
navigator.geolocation.getCurrentPosition =
navigator.geolocation.watchPosition = function() {
window.__didNotCallGeo = false;
};
}

function collectGeoState() {
__returnResults(window.__didNotCallGeo);
}

class GeolocationOnStart extends Gather {

beforePass(options) {
return options.driver.evaluateScriptOnLoad(`(${overrideGeo.toString()}())`);
}

afterPass(options) {
return options.driver.evaluateAsync(`(${collectGeoState.toString()}())`)
.then(returnedValue => {
this.artifact = returnedValue;
}, _ => {
this.artifact = -1;
return;
});
}
}

module.exports = GeolocationOnStart;
43 changes: 43 additions & 0 deletions lighthouse-core/test/audits/geolocation-on-start.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright 2016 Google Inc. 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.
*/
const Audit = require('../../audits/geolocation-on-start.js');
const assert = require('assert');

/* global describe, it*/

describe('UX: geolocation audit', () => {
it('fails when no input present', () => {
return assert.equal(Audit.audit({}).rawValue, false);
});

it('fails when no input present', () => {
return assert.equal(Audit.audit({
GeolocationOnStart: -1
}).rawValue, false);
});

it('fails when geolocation has been automatically requested', () => {
return assert.equal(Audit.audit({
GeolocationOnStart: false
}).rawValue, false);
});

it('passes when geolocation has not been automatically requested', () => {
return assert.equal(Audit.audit({
GeolocationOnStart: true
}).rawValue, true);
});
});
68 changes: 68 additions & 0 deletions lighthouse-core/test/driver/gatherers/geolocation-on-start.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Copyright 2016 Google Inc. 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';

/* eslint-env mocha */

const GeolocationGatherer = require('../../../driver/gatherers/geolocation-on-start');
const assert = require('assert');
let geolocationGatherer;

describe('Geolocation gatherer', () => {
// Reset the Gatherer before each test.
beforeEach(() => {
geolocationGatherer = new GeolocationGatherer();
});

it('returns an artifact', () => {
return geolocationGatherer.beforePass({
driver: {
evaluateScriptOnLoad() {
return Promise.resolve();
}
}
}).then(_ => geolocationGatherer.afterPass({
driver: {
evaluateAsync() {
return Promise.resolve(true);
}
}
})).then(_ => {
assert.ok(typeof geolocationGatherer.artifact === 'boolean');
assert.equal(geolocationGatherer.artifact, true);
});
});

it('handles driver failure', () => {
return geolocationGatherer.beforePass({
driver: {
evaluateScriptOnLoad() {
return Promise.resolve();
}
}
}).then(_ => geolocationGatherer.afterPass({
driver: {
evaluateAsync() {
return Promise.reject('such a fail');
}
}
})).then(_ => {
assert(false);
}).catch(_ => {
assert.equal(geolocationGatherer.artifact, -1);
});
});
});

0 comments on commit 60a06c5

Please sign in to comment.