diff --git a/lighthouse-cli/test/fixtures/dobetterweb/dbw_tester.html b/lighthouse-cli/test/fixtures/dobetterweb/dbw_tester.html
index 0ab54e3eb094..3e98cbe370f4 100644
--- a/lighthouse-cli/test/fixtures/dobetterweb/dbw_tester.html
+++ b/lighthouse-cli/test/fixtures/dobetterweb/dbw_tester.html
@@ -56,6 +56,9 @@
+
+
+
diff --git a/lighthouse-cli/test/smokehouse/dbw-config.js b/lighthouse-cli/test/smokehouse/dbw-config.js
index c4f87d8356db..468dfe34494a 100644
--- a/lighthouse-cli/test/smokehouse/dbw-config.js
+++ b/lighthouse-cli/test/smokehouse/dbw-config.js
@@ -22,4 +22,8 @@ module.exports = {
'apple-touch-icon', // pull in apple touch icon to test `LinkElements`
],
},
+ audits: [
+ // Test the `ignoredPatterns` audit option.
+ {path: 'errors-in-console', options: {ignoredPatterns: ['An ignored error']}},
+ ],
};
diff --git a/lighthouse-cli/test/smokehouse/dobetterweb/dbw-expectations.js b/lighthouse-cli/test/smokehouse/dobetterweb/dbw-expectations.js
index 20a5f71d6bc7..8a9d0121317b 100644
--- a/lighthouse-cli/test/smokehouse/dobetterweb/dbw-expectations.js
+++ b/lighthouse-cli/test/smokehouse/dobetterweb/dbw-expectations.js
@@ -184,16 +184,21 @@ const expectations = [
score: 0,
details: {
items: [
- {
- source: 'network',
- description: 'Failed to load resource: the server responded with a status of 404 (Not Found)',
- url: 'http://localhost:10200/dobetterweb/unknown404.css?delay=200',
- },
{
source: 'other',
description: 'Application Cache Error event: Manifest fetch failed (404) http://localhost:10200/dobetterweb/clock.appcache',
url: 'http://localhost:10200/dobetterweb/dbw_tester.html',
},
+ {
+ source: 'Runtime.exception',
+ description: /^Error: A distinctive error\s+at http:\/\/localhost:10200\/dobetterweb\/dbw_tester.html:\d+:\d+$/,
+ url: 'http://localhost:10200/dobetterweb/dbw_tester.html',
+ },
+ {
+ source: 'network',
+ description: 'Failed to load resource: the server responded with a status of 404 (Not Found)',
+ url: 'http://localhost:10200/dobetterweb/unknown404.css?delay=200',
+ },
{
source: 'network',
description: 'Failed to load resource: the server responded with a status of 404 (Not Found)',
@@ -209,11 +214,6 @@ const expectations = [
description: 'Failed to load resource: the server responded with a status of 404 (Not Found)',
url: 'http://localhost:10200/dobetterweb/unknown404.css?delay=200',
},
- {
- source: 'Runtime.exception',
- description: /^Error: A distinctive error\s+at http:\/\/localhost:10200\/dobetterweb\/dbw_tester.html:\d+:\d+$/,
- url: 'http://localhost:10200/dobetterweb/dbw_tester.html',
- },
],
},
},
diff --git a/lighthouse-core/audits/errors-in-console.js b/lighthouse-core/audits/errors-in-console.js
index 05cff456b8c7..341204bfff79 100644
--- a/lighthouse-core/audits/errors-in-console.js
+++ b/lighthouse-core/audits/errors-in-console.js
@@ -28,6 +28,8 @@ const UIStrings = {
const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
+/** @typedef {{ignoredPatterns?: Array}} AuditOptions */
+
class ErrorLogs extends Audit {
/**
* @return {LH.Audit.Meta}
@@ -42,11 +44,41 @@ class ErrorLogs extends Audit {
};
}
+ /** @return {AuditOptions} */
+ static defaultOptions() {
+ return {};
+ }
+
+
+ /**
+ * @template {{description: string | undefined}} T
+ * @param {Array} items
+ * @param {AuditOptions} options
+ * @return {Array}
+ */
+ static filterAccordingToOptions(items, options) {
+ const {ignoredPatterns} = options;
+ if (!ignoredPatterns) return items;
+
+ return items.filter(({description}) => {
+ if (!description) return true;
+ for (const pattern of ignoredPatterns) {
+ if (pattern instanceof RegExp && pattern.test(description)) return false;
+ if (typeof pattern === 'string' && description.includes(pattern)) return false;
+ }
+
+ return true;
+ });
+ }
+
/**
* @param {LH.Artifacts} artifacts
+ * @param {LH.Audit.Context} context
* @return {LH.Audit.Product}
*/
- static audit(artifacts) {
+ static audit(artifacts, context) {
+ const auditOptions = /** @type {AuditOptions} */ (context.options);
+
const consoleEntries = artifacts.ConsoleMessages;
const runtimeExceptions = artifacts.RuntimeExceptions;
/** @type {Array<{source: string, description: string|undefined, url: string|undefined}>} */
@@ -73,7 +105,10 @@ class ErrorLogs extends Audit {
};
});
- const tableRows = consoleRows.concat(runtimeExRows);
+ const tableRows = ErrorLogs.filterAccordingToOptions(
+ consoleRows.concat(runtimeExRows),
+ auditOptions
+ ).sort((a, b) => (a.description || '').localeCompare(b.description || ''));
/** @type {LH.Audit.Details.Table['headings']} */
const headings = [
diff --git a/lighthouse-core/test/audits/errors-in-console-test.js b/lighthouse-core/test/audits/errors-in-console-test.js
index 482d52a0d0ad..c9480cb1da5a 100644
--- a/lighthouse-core/test/audits/errors-in-console-test.js
+++ b/lighthouse-core/test/audits/errors-in-console-test.js
@@ -15,7 +15,7 @@ describe('Console error logs audit', () => {
const auditResult = ErrorLogsAudit.audit({
ConsoleMessages: [],
RuntimeExceptions: [],
- });
+ }, {options: {}});
assert.equal(auditResult.numericValue, 0);
assert.equal(auditResult.score, 1);
assert.ok(!auditResult.displayValue, 0);
@@ -34,7 +34,7 @@ describe('Console error logs audit', () => {
},
],
RuntimeExceptions: [],
- });
+ }, {options: {}});
assert.equal(auditResult.numericValue, 0);
assert.equal(auditResult.score, 1);
assert.equal(auditResult.details.items.length, 0);
@@ -79,7 +79,7 @@ describe('Console error logs audit', () => {
'executionContextId': 3,
},
}],
- });
+ }, {options: {}});
assert.equal(auditResult.numericValue, 3);
assert.equal(auditResult.score, 0);
@@ -87,13 +87,13 @@ describe('Console error logs audit', () => {
assert.equal(auditResult.details.items[0].url, 'http://www.example.com/favicon.ico');
assert.equal(auditResult.details.items[0].description,
'The server responded with a status of 404 (Not Found)');
- assert.equal(auditResult.details.items[1].url, 'http://www.example.com/wsconnect.ws');
- assert.equal(auditResult.details.items[1].description,
- 'WebSocket connection failed: Unexpected response code: 500');
- assert.equal(auditResult.details.items[2].url,
+ assert.equal(auditResult.details.items[1].url,
'http://example.com/fancybox.js');
- assert.equal(auditResult.details.items[2].description,
+ assert.equal(auditResult.details.items[1].description,
'TypeError: Cannot read property \'msie\' of undefined');
+ assert.equal(auditResult.details.items[2].url, 'http://www.example.com/wsconnect.ws');
+ assert.equal(auditResult.details.items[2].description,
+ 'WebSocket connection failed: Unexpected response code: 500');
});
it('handle the case when some logs fields are undefined', () => {
@@ -106,7 +106,7 @@ describe('Console error logs audit', () => {
},
],
RuntimeExceptions: [],
- });
+ }, {options: {}});
assert.equal(auditResult.numericValue, 1);
assert.equal(auditResult.score, 0);
assert.equal(auditResult.details.items.length, 1);
@@ -137,7 +137,7 @@ describe('Console error logs audit', () => {
'executionContextId': 3,
},
}],
- });
+ }, {options: {}});
assert.equal(auditResult.numericValue, 1);
assert.equal(auditResult.score, 0);
assert.equal(auditResult.details.items.length, 1);
@@ -145,4 +145,121 @@ describe('Console error logs audit', () => {
assert.strictEqual(auditResult.details.items[0].description,
'TypeError: Cannot read property \'msie\' of undefined');
});
+
+ describe('options', () => {
+ it('does nothing with an empty pattern', () => {
+ const options = {ignoredPatterns: ''};
+ const result = ErrorLogsAudit.audit({
+ ConsoleMessages: [
+ {
+ entry: {
+ level: 'error',
+ source: 'network',
+ text: 'This is a simple error msg',
+ },
+ },
+ ],
+ RuntimeExceptions: [],
+ }, {options});
+
+ expect(result.score).toBe(0);
+ expect(result.details.items).toHaveLength(1);
+ });
+
+ it('does nothing with an empty description', () => {
+ const options = {ignoredPatterns: 'pattern'};
+ const result = ErrorLogsAudit.audit({
+ ConsoleMessages: [
+ {
+ entry: {
+ level: 'error',
+ },
+ },
+ ],
+ RuntimeExceptions: [],
+ }, {options});
+
+ expect(result.score).toBe(0);
+ expect(result.details.items).toHaveLength(1);
+ });
+
+ it('does nothing with an empty description', () => {
+ const options = {ignoredPatterns: 'pattern'};
+ const result = ErrorLogsAudit.audit({
+ ConsoleMessages: [
+ {
+ entry: {
+ level: 'error',
+ },
+ },
+ ],
+ RuntimeExceptions: [],
+ }, {options});
+
+ expect(result.score).toBe(0);
+ expect(result.details.items).toHaveLength(1);
+ });
+
+ it('filters console messages as a string', () => {
+ const options = {ignoredPatterns: ['simple']};
+ const result = ErrorLogsAudit.audit({
+ ConsoleMessages: [
+ {
+ entry: {
+ level: 'error',
+ source: 'network',
+ text: 'This is a simple error msg',
+ },
+ },
+ ],
+ RuntimeExceptions: [],
+ }, {options});
+
+ expect(result.score).toBe(1);
+ expect(result.details.items).toHaveLength(0);
+ });
+
+ it('filters console messages as a regex', () => {
+ const options = {ignoredPatterns: [/simple.*msg/]};
+ const result = ErrorLogsAudit.audit({
+ ConsoleMessages: [
+ {
+ entry: {
+ level: 'error',
+ source: 'network',
+ text: 'This is a simple error msg',
+ },
+ },
+ ],
+ RuntimeExceptions: [],
+ }, {options});
+
+ expect(result.score).toBe(1);
+ expect(result.details.items).toHaveLength(0);
+ });
+
+ it('filters exceptions with both regex and strings', () => {
+ const options = {ignoredPatterns: [/s.mple/i, 'really']};
+ const result = ErrorLogsAudit.audit({
+ ConsoleMessages: [],
+ RuntimeExceptions: [
+ {
+ exceptionDetails: {
+ url: 'http://example.com/url.js',
+ text: 'Simple Error: You messed up',
+ },
+ },
+ {
+ exceptionDetails: {
+ url: 'http://example.com/url.js',
+ text: 'Bad Error: You really messed up',
+ },
+ },
+ ],
+ }, {options});
+
+ expect(result.score).toBe(1);
+ expect(result.details.items).toHaveLength(0);
+ });
+ });
});
diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json
index fe2621605fac..20f6dc01d8d1 100644
--- a/lighthouse-core/test/results/sample_v2.json
+++ b/lighthouse-core/test/results/sample_v2.json
@@ -236,6 +236,11 @@
"description": "Application Cache Error event: Manifest fetch failed (404) http://localhost:10200/dobetterweb/clock.appcache",
"url": "http://localhost:10200/dobetterweb/dbw_tester.html"
},
+ {
+ "source": "Runtime.exception",
+ "description": "Error: An error\n at http://localhost:10200/dobetterweb/dbw_tester.html:42:38",
+ "url": "http://localhost:10200/dobetterweb/dbw_tester.html"
+ },
{
"source": "network",
"description": "Failed to load resource: the server responded with a status of 404 (Not Found)",
@@ -250,11 +255,6 @@
"source": "network",
"description": "Failed to load resource: the server responded with a status of 404 (Not Found)",
"url": "http://localhost:10200/dobetterweb/unknown404.css?delay=200"
- },
- {
- "source": "Runtime.exception",
- "description": "Error: An error\n at http://localhost:10200/dobetterweb/dbw_tester.html:42:38",
- "url": "http://localhost:10200/dobetterweb/dbw_tester.html"
}
]
}
diff --git a/proto/sample_v2_round_trip.json b/proto/sample_v2_round_trip.json
index 0df7875e302a..588fc80074ec 100644
--- a/proto/sample_v2_round_trip.json
+++ b/proto/sample_v2_round_trip.json
@@ -600,6 +600,11 @@
"source": "other",
"url": "http://localhost:10200/dobetterweb/dbw_tester.html"
},
+ {
+ "description": "Error: An error\n at http://localhost:10200/dobetterweb/dbw_tester.html:42:38",
+ "source": "Runtime.exception",
+ "url": "http://localhost:10200/dobetterweb/dbw_tester.html"
+ },
{
"description": "Failed to load resource: the server responded with a status of 404 (Not Found)",
"source": "network",
@@ -614,11 +619,6 @@
"description": "Failed to load resource: the server responded with a status of 404 (Not Found)",
"source": "network",
"url": "http://localhost:10200/dobetterweb/unknown404.css?delay=200"
- },
- {
- "description": "Error: An error\n at http://localhost:10200/dobetterweb/dbw_tester.html:42:38",
- "source": "Runtime.exception",
- "url": "http://localhost:10200/dobetterweb/dbw_tester.html"
}
],
"type": "table"