WebKit Bugzilla
Attachment 370989 Details for
Bug 196710
: Web Inspector: Audit: there should be a default test for WebInspectorAudit.Resources functionality
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-196710-20190530160643.patch (text/plain), 56.59 KB, created by
Devin Rousso
on 2019-05-30 16:06:44 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Devin Rousso
Created:
2019-05-30 16:06:44 PDT
Size:
56.59 KB
patch
obsolete
>diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index c89b5047dc7360522708d7f5eea5b1786a4d6185..c8654a69025fcd171c1352b53379676377170edf 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,14 @@ >+2019-05-30 Devin Rousso <drousso@apple.com> >+ >+ Web Inspector: Audit: there should be a default test for WebInspectorAudit.Resources functionality >+ https://bugs.webkit.org/show_bug.cgi?id=196710 >+ <rdar://problem/49712348> >+ >+ Reviewed by Joseph Pecoraro. >+ >+ * inspector/protocol/Audit.json: >+ Increment Audit version. >+ > 2019-05-29 Don Olmstead <don.olmstead@sony.com> > > Remove ENABLE definitions from WebKit config files >diff --git a/Source/WebInspectorUI/ChangeLog b/Source/WebInspectorUI/ChangeLog >index da0997eba7504d228e0b1a20ae57385001f6fe2a..f176cf6d8db264e6ed98d4cd7f9cd96443e7fe77 100644 >--- a/Source/WebInspectorUI/ChangeLog >+++ b/Source/WebInspectorUI/ChangeLog >@@ -1,3 +1,72 @@ >+2019-05-30 Devin Rousso <drousso@apple.com> >+ >+ Web Inspector: Audit: there should be a default test for WebInspectorAudit.Resources functionality >+ https://bugs.webkit.org/show_bug.cgi?id=196710 >+ <rdar://problem/49712348> >+ >+ Reviewed by Joseph Pecoraro. >+ >+ Previously, there was no way to return data from Audit that wasn't a DOM node, a DOM >+ attribute (which wasn't "shown" anywhere, as it would highlight that attribute on any >+ returned DOM nodes), or an error string. >+ >+ In order for Audits to be more flexible with the types of things they test, there needs to >+ be a way for other types of data to be sent back and displayed in the Audit tab. >+ >+ This patch makes it so that an Audit result can now contain other keys/values that are all >+ expected to be JSON serializable. It will take all of the non-"special" (e.g. "domNodes" or >+ "errors") keys/values and display them as object trees. This way, any JSON serializable data >+ can be sent with the result and be displayed in the Audit tab. >+ >+ * UserInterface/Models/AuditTestBase.js: >+ (WI.AuditTestBase.prototype.async setup): >+ (WI.AuditTestBase.prototype.clearResult): >+ * UserInterface/Models/AuditTestGroup.js: >+ (WI.AuditTestCase.prototype.clearResult): >+ (WI.AuditTestCase.prototype._updateResult): >+ * UserInterface/Models/AuditTestCase.js: >+ (WI.AuditTestCase.prototype.async run): >+ (WI.AuditTestCase.prototype.async run.async parseResponse): >+ (WI.AuditTestCase.prototype.async run.async parseResponse.checkResultProperty): >+ (WI.AuditTestCase.prototype.async run.async parseResponse.checkResultProperty.addErrorForValueType): >+ (WI.AuditTestCase.prototype.async run.async parseResponse.async resultArrayForEach): >+ (WI.AuditTestCase.prototype.async run.async parseResponse.inspectedPage_stringify): Added. >+ Rename `ResultCleared` to `ResultChanged` so that it can (semantically) be used whenever a >+ new result is set in addition to when an existing one is cleared. This is needed so that >+ `AuditTestCaseContentView` will refresh each time the result changes, instead of only in the >+ first `layout()` after the last result was cleared. >+ >+ * UserInterface/Models/AuditTestCaseResult.js: >+ (WI.AuditTestCaseResult.async fromPayload): >+ (WI.AuditTestCaseResult.prototype.toJSON): >+ Drive-by: fix the check for optional `data` values to still warn if the value is `null`. >+ >+ * UserInterface/Controllers/AuditManager.js: >+ (WI.AuditManager.prototype._addDefaultTests): >+ Add a default test "Demo Audit > Result Data > data-custom" as an example how to write an >+ Audit that returns custom data, as well as how that custom data is shown in the Audit tab. >+ >+ * UserInterface/Views/AuditTestContentView.js: >+ (WI.AuditTestContentView.prototype.shown): >+ (WI.AuditTestContentView.prototype.handleResultChanged): Added. >+ * UserInterface/Views/AuditTestCaseContentView.js: >+ (WI.AuditTestCaseContentView): >+ (WI.AuditTestCaseContentView.prototype.layout): >+ (WI.AuditTestCaseContentView.prototype.handleResultChanged): Added. >+ Preserve the UI for each section across `layout()`s, so that expand/collapse states aren't >+ reset each time the user changes the selected Audit. >+ >+ * UserInterface/Views/AuditTestCaseContentView.css: >+ (.content-view.audit-test-case > section table > tr > td + td): Added. >+ Drive-by: ensure that the "index" table column is never larger than it needs to be. >+ >+ * UserInterface/Views/AuditTreeElement.js: >+ (WI.AuditTreeElement.prototype.onattach): >+ (WI.AuditTreeElement.prototype._handleTestResultChanged): Added. >+ (WI.AuditTreeElement.prototype._handleTestResultCleared): Deleted. >+ >+ * Localizations/en.lproj/localizedStrings.js: >+ > 2019-05-28 Devin Rousso <drousso@apple.com> > > Web Inspector: Timelines: spacing around pie chart is different between CPU and Memory >diff --git a/Source/JavaScriptCore/inspector/protocol/Audit.json b/Source/JavaScriptCore/inspector/protocol/Audit.json >index 589d8cc8769191439ec38b6202b8bf7a3285840b..e41e2b8d617c593bd557b489049dcc47bba20935 100644 >--- a/Source/JavaScriptCore/inspector/protocol/Audit.json >+++ b/Source/JavaScriptCore/inspector/protocol/Audit.json >@@ -1,7 +1,7 @@ > { > "domain": "Audit", > "description": "", >- "version": 2, >+ "version": 3, > "commands": [ > { > "name": "setup", >diff --git a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js >index 61a63306128069174fed0e8ce40ea154c8a1878b..9a3c9ea4c4a35e98c0aa6440aee3fc91aae16296 100644 >--- a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js >+++ b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js >@@ -1081,8 +1081,9 @@ localizedStrings["These tests serve as a demonstration of the functionality and > localizedStrings["This action causes no visual change"] = "This action causes no visual change"; > localizedStrings["This action moves the path outside the visible area"] = "This action moves the path outside the visible area"; > localizedStrings["This audit is not supported"] = "This audit is not supported"; >+localizedStrings["This is an example of how custom result data is shown."] = "This is an example of how custom result data is shown."; > localizedStrings["This is an example of how errors are shown. The error was thrown manually, but execution errors will appear in the same way."] = "This is an example of how errors are shown. The error was thrown manually, but execution errors will appear in the same way."; >-localizedStrings["This is an example of how result DOM nodes are shown. It will pass with all elements with an id attribute."] = "This is an example of how result DOM nodes are shown. It will pass with all elements with an id attribute."; >+localizedStrings["This is an example of how result DOM attributes are highlighted on any returned DOM nodes. It will pass with all elements with an id attribute."] = "This is an example of how result DOM attributes are highlighted on any returned DOM nodes. It will pass with all elements with an id attribute."; > localizedStrings["This is an example of how result DOM nodes are shown. It will pass with the <body> element."] = "This is an example of how result DOM nodes are shown. It will pass with the <body> element."; > localizedStrings["This is what the result of a failing test with no data looks like."] = "This is what the result of a failing test with no data looks like."; > localizedStrings["This is what the result of a passing test with no data looks like."] = "This is what the result of a passing test with no data looks like."; >@@ -1204,6 +1205,7 @@ localizedStrings["\u0022%s\u0022 has a non-number \u0022%s\u0022 value"] = "\u00 > localizedStrings["\u0022%s\u0022 has a non-object \u0022%s\u0022 value"] = "\u0022%s\u0022 has a non-object \u0022%s\u0022 value"; > localizedStrings["\u0022%s\u0022 has a non-string \u0022%s\u0022 value"] = "\u0022%s\u0022 has a non-string \u0022%s\u0022 value"; > localizedStrings["\u0022%s\u0022 has an invalid \u0022%s\u0022 value"] = "\u0022%s\u0022 has an invalid \u0022%s\u0022 value"; >+localizedStrings["\u0022%s\u0022 is not JSON serializable"] = "\u0022%s\u0022 is not JSON serializable"; > localizedStrings["\u0022%s\u0022 is not valid for %s"] = "\u0022%s\u0022 is not valid for %s"; > localizedStrings["\u0022%s\u0022 is too new to run in this Web Inspector"] = "\u0022%s\u0022 is too new to run in this Web Inspector"; > localizedStrings["\u0022%s\u0022 is too new to run on this inspected page"] = "\u0022%s\u0022 is too new to run on this inspected page"; >diff --git a/Source/WebInspectorUI/UserInterface/Base/Utilities.js b/Source/WebInspectorUI/UserInterface/Base/Utilities.js >index 2bd4fad60cf21647580b7cf079edd53ac36a6e21..7240b571f1231ee0158aaffb7e4380fa40b0fe83 100644 >--- a/Source/WebInspectorUI/UserInterface/Base/Utilities.js >+++ b/Source/WebInspectorUI/UserInterface/Base/Utilities.js >@@ -80,6 +80,19 @@ Object.defineProperty(Object, "shallowEqual", > } > }); > >+Object.defineProperty(Object, "filter", >+{ >+ value(object, callback) >+ { >+ let filtered = {}; >+ for (let key in object) { >+ if (callback(key, object[key])) >+ filtered[key] = object[key]; >+ } >+ return filtered; >+ } >+}); >+ > Object.defineProperty(Object.prototype, "valueForCaseInsensitiveKey", > { > value(key) >diff --git a/Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js b/Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js >index b5d223face5429c26500bb30d3a2df258a6ef6f6..10467afa558fe7424de37df42b74db4f9f1bf08f 100644 >--- a/Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js >+++ b/Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js >@@ -891,8 +891,9 @@ WI.AuditManager = class AuditManager extends WI.Object > ], {description: WI.UIString("These are all of the different test result levels.")}), > new WI.AuditTestGroup(WI.UIString("Result Data"), [ > new WI.AuditTestCase(`data-domNodes`, `function() { return {domNodes: [document.body], level: "pass"}; }`, {description: WI.UIString("This is an example of how result DOM nodes are shown. It will pass with the <body> element.")}), >- new WI.AuditTestCase(`data-domAttributes`, `function() { return {domNodes: Array.from(document.querySelectorAll("[id]")), domAttributes: ["id"], level: "pass"}; }`, {description: WI.UIString("This is an example of how result DOM nodes are shown. It will pass with all elements with an id attribute.")}), >+ new WI.AuditTestCase(`data-domAttributes`, `function() { return {domNodes: Array.from(document.querySelectorAll("[id]")), domAttributes: ["id"], level: "pass"}; }`, {description: WI.UIString("This is an example of how result DOM attributes are highlighted on any returned DOM nodes. It will pass with all elements with an id attribute.")}), > new WI.AuditTestCase(`data-errors`, `function() { throw Error("this error was thrown from inside the audit test code."); }`, {description: WI.UIString("This is an example of how errors are shown. The error was thrown manually, but execution errors will appear in the same way.")}), >+ new WI.AuditTestCase(`data-custom`, `function() { return {level: "pass", a: 1, b: [2], c: {key: 3}}; }`, {description: WI.UIString("This is an example of how custom result data is shown.")}), > ], {description: WI.UIString("These are all of the different types of data that can be returned with the test result.")}), > ], {description: WI.UIString("These tests serve as a demonstration of the functionality and structure of audits.")}), > new WI.AuditTestGroup(WI.UIString("Accessibility"), [ >diff --git a/Source/WebInspectorUI/UserInterface/Models/AuditTestBase.js b/Source/WebInspectorUI/UserInterface/Models/AuditTestBase.js >index 2ad51105149a20897fe3019d6f7b0662089500f1..d830f48231445fbdd6f902f252fcd6a99af47c6e 100644 >--- a/Source/WebInspectorUI/UserInterface/Models/AuditTestBase.js >+++ b/Source/WebInspectorUI/UserInterface/Models/AuditTestBase.js >@@ -116,7 +116,7 @@ WI.AuditTestBase = class AuditTestBase extends WI.Object > } else { > agentCommandFunction = RuntimeAgent.evaluate; > agentCommandArguments.expression = `(function() { "use strict"; return eval(\`(${this._setup.replace(/`/g, "\\`")})\`)(); })()`; >- agentCommandArguments.objectGroup = "audit"; >+ agentCommandArguments.objectGroup = AuditTestBase.ObjectGroup; > agentCommandArguments.doNotPauseOnExceptionsAndMuteConsole = true; > } > >@@ -191,8 +191,8 @@ WI.AuditTestBase = class AuditTestBase extends WI.Object > > this._result = null; > >- if (!options.suppressResultClearedEvent) >- this.dispatchEventToListeners(WI.AuditTestBase.Event.ResultCleared); >+ if (!options.suppressResultChangedEvent) >+ this.dispatchEventToListeners(WI.AuditTestBase.Event.ResultChanged); > > return true; > } >@@ -228,13 +228,15 @@ WI.AuditTestBase = class AuditTestBase extends WI.Object > }; > > // Keep this in sync with Inspector::Protocol::Audit::VERSION. >-WI.AuditTestBase.Version = 2; >+WI.AuditTestBase.Version = 3; >+ >+WI.AuditTestBase.ObjectGroup = "audit"; > > WI.AuditTestBase.Event = { > Completed: "audit-test-base-completed", > DisabledChanged: "audit-test-base-disabled-changed", > Progress: "audit-test-base-progress", >- ResultCleared: "audit-test-base-result-cleared", >+ ResultChanged: "audit-test-base-result-changed", > Scheduled: "audit-test-base-scheduled", > Stopping: "audit-test-base-stopping", > }; >diff --git a/Source/WebInspectorUI/UserInterface/Models/AuditTestCase.js b/Source/WebInspectorUI/UserInterface/Models/AuditTestCase.js >index 61b0883af1bcd9cfeb0f0300387b87959b501d1e..85a5312970188cff43a4de5473d7b59b4b6770cb 100644 >--- a/Source/WebInspectorUI/UserInterface/Models/AuditTestCase.js >+++ b/Source/WebInspectorUI/UserInterface/Models/AuditTestCase.js >@@ -126,125 +126,173 @@ WI.AuditTestCase = class AuditTestCase extends WI.AuditTestBase > > async function parseResponse(response) { > let remoteObject = WI.RemoteObject.fromPayload(response.result, WI.mainTarget); >- if (response.wasThrown || (remoteObject.type === "object" && remoteObject.subtype === "error")) >+ if (response.wasThrown || (remoteObject.type === "object" && remoteObject.subtype === "error")) { > addError(remoteObject.description); >- else if (remoteObject.type === "boolean") >+ return; >+ } >+ >+ if (remoteObject.type === "boolean") { > setLevel(remoteObject.value ? WI.AuditTestCaseResult.Level.Pass : WI.AuditTestCaseResult.Level.Fail); >- else if (remoteObject.type === "string") >+ return; >+ } >+ >+ if (remoteObject.type === "string") { > setLevel(remoteObject.value.trim().toLowerCase()); >- else if (remoteObject.type === "object" && !remoteObject.subtype) { >- const options = { >- ownProperties: true, >- }; >+ return; >+ } > >- let properties = await new Promise((resolve, reject) => remoteObject.getPropertyDescriptorsAsObject(resolve, options)); >- >- function checkResultProperty(key, type, subtype) { >- if (!(key in properties)) >- return null; >- >- let property = properties[key].value; >- if (!property) >- return null; >- >- function addErrorForValueType(valueType) { >- let value = null; >- if (valueType === "object" || valueType === "array") >- value = WI.UIString("\u0022%s\u0022 must be an %s"); >- else >- value = WI.UIString("\u0022%s\u0022 must be a %s"); >- addError(value.format(key, valueType)); >- } >- >- if (property.subtype !== subtype) { >- addErrorForValueType(subtype); >- return null; >- } >- >- if (property.type !== type) { >- addErrorForValueType(type); >- return null; >- } >- >- if (type === "boolean" || type === "string") >- return property.value; >- >- return property; >- } >- >- async function resultArrayForEach(key, callback) { >- let array = checkResultProperty(key, "object", "array"); >- if (!array) >- return; >- >- // `getPropertyDescriptorsAsObject` returns an object, meaning that if we >- // want to iterate over `array` by index, we have to count. >- let asObject = await new Promise((resolve, reject) => array.getPropertyDescriptorsAsObject(resolve, options)); >- for (let i = 0; i < array.size; ++i) { >- if (i in asObject) >- await callback(asObject[i]); >- } >- } >- >- let levelString = checkResultProperty("level", "string"); >- if (levelString) >- setLevel(levelString.trim().toLowerCase()); >- >- if (checkResultProperty("pass", "boolean")) >- setLevel(WI.AuditTestCaseResult.Level.Pass); >- if (checkResultProperty("warn", "boolean")) >- setLevel(WI.AuditTestCaseResult.Level.Warn); >- if (checkResultProperty("fail", "boolean")) >- setLevel(WI.AuditTestCaseResult.Level.Fail); >- if (checkResultProperty("error", "boolean")) >- setLevel(WI.AuditTestCaseResult.Level.Error); >- if (checkResultProperty("unsupported", "boolean")) >- setLevel(WI.AuditTestCaseResult.Level.Unsupported); >- >- await resultArrayForEach("domNodes", async (item) => { >- if (!item || !item.value || item.value.type !== "object" || item.value.subtype !== "node") { >- addError(WI.UIString("All items in \u0022%s\u0022 must be valid DOM nodes").format(WI.unlocalizedString("domNodes"))); >- return; >- } >- >- let domNodeId = await new Promise((resolve, reject) => item.value.pushNodeToFrontend(resolve)); >- let domNode = WI.domManager.nodeForId(domNodeId); >- if (!domNode) >- return; >- >- if (!data.domNodes) >- data.domNodes = []; >- data.domNodes.push(WI.cssPath(domNode, {full: true})); >- >- if (!resolvedDOMNodes) >- resolvedDOMNodes = []; >- resolvedDOMNodes.push(domNode); >- }); >- >- await resultArrayForEach("domAttributes", (item) => { >- if (!item || !item.value || item.value.type !== "string" || !item.value.value.length) { >- addError(WI.UIString("All items in \u0022%s\u0022 must be non-empty strings").format(WI.unlocalizedString("domAttributes"))); >- return; >- } >- >- if (!data.domAttributes) >- data.domAttributes = []; >- data.domAttributes.push(item.value.value); >- }); >- >- await resultArrayForEach("errors", (item) => { >- if (!item || !item.value || item.value.type !== "object" || item.value.subtype !== "error") { >- addError(WI.UIString("All items in \u0022%s\u0022 must be error objects").format(WI.unlocalizedString("errors"))); >- return; >- } >- >- addError(item.value.description); >- }); >- >- if (window.InspectorTest && properties.__test) >- data.__test = properties.__test.value; >- } else >+ if (remoteObject.type !== "object" || remoteObject.subtype) { > addError(WI.UIString("Return value is not an object, string, or boolean")); >+ return; >+ } >+ >+ const options = { >+ ownProperties: true, >+ }; >+ >+ function checkResultProperty(key, value, type, subtype) { >+ function addErrorForValueType(valueType) { >+ let errorString = null; >+ if (valueType === "object" || valueType === "array") >+ errorString = WI.UIString("\u0022%s\u0022 must be an %s"); >+ else >+ errorString = WI.UIString("\u0022%s\u0022 must be a %s"); >+ addError(errorString.format(key, valueType)); >+ } >+ >+ if (value.subtype !== subtype) { >+ addErrorForValueType(subtype); >+ return null; >+ } >+ >+ if (value.type !== type) { >+ addErrorForValueType(type); >+ return null; >+ } >+ >+ if (type === "boolean" || type === "string") >+ return value.value; >+ >+ return value; >+ } >+ >+ async function resultArrayForEach(key, value, callback) { >+ let array = checkResultProperty(key, value, "object", "array"); >+ if (!array) >+ return; >+ >+ // `getPropertyDescriptorsAsObject` returns an object, meaning that if we >+ // want to iterate over `array` by index, we have to count. >+ let asObject = await new Promise((resolve, reject) => array.getPropertyDescriptorsAsObject(resolve, options)); >+ for (let i = 0; i < array.size; ++i) { >+ if (i in asObject) >+ await callback(asObject[i]); >+ } >+ } >+ >+ let properties = await new Promise((resolve, reject) => remoteObject.getPropertyDescriptors(resolve, options)); >+ for (let property of properties) { >+ let key = property.name; >+ if (key === "__proto__") >+ continue; >+ >+ let value = property.value; >+ >+ switch (key) { >+ case "level": { >+ let levelString = checkResultProperty(key, value, "string"); >+ if (levelString) >+ setLevel(levelString.trim().toLowerCase()); >+ break; >+ } >+ >+ case "pass": >+ if (checkResultProperty(key, value, "boolean")) >+ setLevel(WI.AuditTestCaseResult.Level.Pass); >+ break; >+ >+ case "warn": >+ if (checkResultProperty(key, value, "boolean")) >+ setLevel(WI.AuditTestCaseResult.Level.Warn); >+ break; >+ >+ case "fail": >+ if (checkResultProperty(key, value, "boolean")) >+ setLevel(WI.AuditTestCaseResult.Level.Fail); >+ break; >+ >+ case "error": >+ if (checkResultProperty(key, value, "boolean")) >+ setLevel(WI.AuditTestCaseResult.Level.Error); >+ break; >+ >+ case "unsupported": >+ if (checkResultProperty(key, value, "boolean")) >+ setLevel(WI.AuditTestCaseResult.Level.Unsupported); >+ break; >+ >+ case "domNodes": >+ await resultArrayForEach(key, value, async (item) => { >+ if (!item || !item.value || item.value.type !== "object" || item.value.subtype !== "node") { >+ addError(WI.UIString("All items in \u0022%s\u0022 must be valid DOM nodes").format(WI.unlocalizedString("domNodes"))); >+ return; >+ } >+ >+ let domNodeId = await new Promise((resolve, reject) => item.value.pushNodeToFrontend(resolve)); >+ let domNode = WI.domManager.nodeForId(domNodeId); >+ if (!domNode) >+ return; >+ >+ if (!data.domNodes) >+ data.domNodes = []; >+ data.domNodes.push(WI.cssPath(domNode, {full: true})); >+ >+ if (!resolvedDOMNodes) >+ resolvedDOMNodes = []; >+ resolvedDOMNodes.push(domNode); >+ }); >+ break; >+ >+ case "domAttributes": >+ await resultArrayForEach(key, value, (item) => { >+ if (!item || !item.value || item.value.type !== "string" || !item.value.value.length) { >+ addError(WI.UIString("All items in \u0022%s\u0022 must be non-empty strings").format(WI.unlocalizedString("domAttributes"))); >+ return; >+ } >+ >+ if (!data.domAttributes) >+ data.domAttributes = []; >+ data.domAttributes.push(item.value.value); >+ }); >+ break; >+ >+ case "errors": >+ await resultArrayForEach(key, value, (item) => { >+ if (!item || !item.value || item.value.type !== "object" || item.value.subtype !== "error") { >+ addError(WI.UIString("All items in \u0022%s\u0022 must be error objects").format(WI.unlocalizedString("errors"))); >+ return; >+ } >+ >+ addError(item.value.description); >+ }); >+ break; >+ >+ default: >+ if (value.objectId) { >+ try { >+ function inspectedPage_stringify() { >+ return JSON.stringify(this); >+ } >+ let stringifiedValue = await value.callFunction(inspectedPage_stringify); >+ data[key] = JSON.parse(stringifiedValue.value); >+ } catch { >+ addError(WI.UIString("\u0022%s\u0022 is not JSON serializable").format(key)); >+ } >+ } else >+ data[key] = value.value; >+ break; >+ } >+ } > } > > let agentCommandFunction = null; >@@ -255,7 +303,7 @@ WI.AuditTestCase = class AuditTestCase extends WI.AuditTestBase > } else { > agentCommandFunction = RuntimeAgent.evaluate; > agentCommandArguments.expression = `(function() { "use strict"; return eval(\`(${this._test.replace(/`/g, "\\`")})\`)(); })()`; >- agentCommandArguments.objectGroup = "audit"; >+ agentCommandArguments.objectGroup = WI.AuditTestCase.ObjectGroup; > agentCommandArguments.doNotPauseOnExceptionsAndMuteConsole = true; > } > >@@ -295,6 +343,8 @@ WI.AuditTestCase = class AuditTestCase extends WI.AuditTestBase > if (resolvedDOMNodes) > options.resolvedDOMNodes = resolvedDOMNodes; > this._result = new WI.AuditTestCaseResult(this.name, level, options); >+ >+ this.dispatchEventToListeners(WI.AuditTestBase.Event.ResultChanged); > } > }; > >diff --git a/Source/WebInspectorUI/UserInterface/Models/AuditTestCaseResult.js b/Source/WebInspectorUI/UserInterface/Models/AuditTestCaseResult.js >index 47e7b0b32ff2063879eb88267d9f3adefaf995ef..d35571e54acb041781384a579e7f8f910bc43bdf 100644 >--- a/Source/WebInspectorUI/UserInterface/Models/AuditTestCaseResult.js >+++ b/Source/WebInspectorUI/UserInterface/Models/AuditTestCaseResult.js >@@ -67,7 +67,7 @@ WI.AuditTestCaseResult = class AuditTestCaseResult extends WI.AuditTestResultBas > payload.data = {}; > } else { > function checkArray(key) { >- if (!payload.data[key]) >+ if (!(key in payload.data)) > return; > > if (!Array.isArray(payload.data[key])) { >@@ -132,24 +132,27 @@ WI.AuditTestCaseResult = class AuditTestCaseResult extends WI.AuditTestResultBas > > if (!isEmptyObject(payload.data)) { > options.data = {}; >- if (payload.data.domNodes && payload.data.domNodes.length) { >- if (window.DOMAgent && (!payload.metadata.url || payload.metadata.url === WI.networkManager.mainFrame.url)) { >- let documentNode = await new Promise((resolve) => WI.domManager.requestDocument(resolve)); >- options.resolvedDOMNodes = await Promise.all(payload.data.domNodes.map(async (domNodeString) => { >- let nodeId = 0; >- try { >- nodeId = await WI.domManager.querySelector(documentNode, domNodeString); >- } catch { } >- return WI.domManager.nodeForId(nodeId) || null; >- })); >+ for (let key in payload.data) { >+ if (key === "domNodes" || key === "domAttributes" || key === "errors") { >+ if (!payload.data[key].length) >+ continue; > } > >- options.data.domNodes = payload.data.domNodes; >+ if (key === "domNodes") { >+ if (window.DOMAgent && (!payload.metadata.url || payload.metadata.url === WI.networkManager.mainFrame.url)) { >+ let documentNode = await new Promise((resolve) => WI.domManager.requestDocument(resolve)); >+ options.resolvedDOMNodes = await Promise.all(payload.data.domNodes.map(async (domNodeString) => { >+ let nodeId = 0; >+ try { >+ nodeId = await WI.domManager.querySelector(documentNode, domNodeString); >+ } catch { } >+ return WI.domManager.nodeForId(nodeId) || null; >+ })); >+ } >+ } >+ >+ options.data[key] = payload.data[key]; > } >- if (payload.data.domAttributes && payload.data.domAttributes.length) >- options.data.domAttributes = payload.data.domAttributes; >- if (payload.data.errors && payload.data.errors.length) >- options.data.errors = payload.data.errors; > } > > if (!isEmptyObject(payload.metadata)) { >@@ -210,13 +213,14 @@ WI.AuditTestCaseResult = class AuditTestCaseResult extends WI.AuditTestResultBas > json.level = this._level; > > let data = {}; >- if (this._data.domNodes && this._data.domNodes.length) { >- data.domNodes = this._data.domNodes; >- if (this._data.domAttributes && this._data.domAttributes.length) >- data.domAttributes = this._data.domAttributes; >+ for (let key in this._data) { >+ if (key === "domNodes" || key === "domAttributes" || key === "errors") { >+ if (!this._data[key].length) >+ continue; >+ } >+ >+ data[key] = this._data[key]; > } >- if (this._data.errors && this._data.errors.length) >- data.errors = this._data.errors; > if (!isEmptyObject(data)) > json.data = data; > >diff --git a/Source/WebInspectorUI/UserInterface/Models/AuditTestGroup.js b/Source/WebInspectorUI/UserInterface/Models/AuditTestGroup.js >index c7f2af80d0ad311cb7b90d2b95da0b80ebe93427..14c7834ef8d18c44ba4cea979a75c53d95d9180b 100644 >--- a/Source/WebInspectorUI/UserInterface/Models/AuditTestGroup.js >+++ b/Source/WebInspectorUI/UserInterface/Models/AuditTestGroup.js >@@ -169,7 +169,7 @@ WI.AuditTestGroup = class AuditTestGroup extends WI.AuditTestBase > > return super.clearResult({ > ...options, >- suppressResultClearedEvent: !cleared, >+ suppressResultChangedEvent: !cleared, > }); > } > >@@ -210,6 +210,8 @@ WI.AuditTestGroup = class AuditTestGroup extends WI.AuditTestBase > this._result = new WI.AuditTestGroupResult(this.name, results, { > description: this.description, > }); >+ >+ this.dispatchEventToListeners(WI.AuditTestBase.Event.ResultChanged); > } > > _handleTestCompleted(event) >diff --git a/Source/WebInspectorUI/UserInterface/Views/AuditTestCaseContentView.css b/Source/WebInspectorUI/UserInterface/Views/AuditTestCaseContentView.css >index a2101aff881f4ea5d7d62cda63b4b9ffa9af0716..ccad54c61e30b846b709778bce6bf29abcad60de 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/AuditTestCaseContentView.css >+++ b/Source/WebInspectorUI/UserInterface/Views/AuditTestCaseContentView.css >@@ -127,6 +127,10 @@ > top: -1px; > } > >+.content-view.audit-test-case > section table > tr > td + td { >+ width: 100%; >+} >+ > .content-view.audit-test-case > section .CodeMirror { > width: 100%; > height: auto; >diff --git a/Source/WebInspectorUI/UserInterface/Views/AuditTestCaseContentView.js b/Source/WebInspectorUI/UserInterface/Views/AuditTestCaseContentView.js >index d0d5df0706360ab0ef558d564a4498e6fe1f69c6..950e364dcaa07c347bd6bd6acbd7a94e59f594f0 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/AuditTestCaseContentView.js >+++ b/Source/WebInspectorUI/UserInterface/Views/AuditTestCaseContentView.js >@@ -32,6 +32,10 @@ WI.AuditTestCaseContentView = class AuditTestCaseContentView extends WI.AuditTes > super(representedObject); > > this.element.classList.add("audit-test-case"); >+ >+ this._resultDataGeneralContainer = null; >+ this._resultDataDOMNodesContainer = null; >+ this._resultDataErrorsContainer = null; > } > > // Protected >@@ -133,14 +137,52 @@ WI.AuditTestCaseContentView = class AuditTestCaseContentView extends WI.AuditTes > > let resultData = result.data; > >- if (resultData.domNodes && resultData.domNodes.length) { >- let domNodesContainer = this.contentView.element.appendChild(document.createElement("div")); >- domNodesContainer.classList.add("dom-nodes"); >+ if (!this._resultDataGeneralContainer) { >+ let nonSpecialData = Object.filter(resultData, (key) => key !== "domNodes" && key !== "errors"); >+ if (!isEmptyObject(nonSpecialData)) { >+ this._resultDataGeneralContainer = document.createElement("div"); > >- let domNodeText = domNodesContainer.appendChild(document.createElement("h1")); >+ let expression = "(" + JSON.stringify(nonSpecialData) + ")"; >+ const options = { >+ objectGroup: WI.AuditTestBase.ObjectGroup, >+ doNotPauseOnExceptionsAndMuteConsole: true, >+ }; >+ WI.runtimeManager.evaluateInInspectedWindow(expression, options, (nonSpecialDataRemoteObject, wasThrown) => { >+ console.assert(!wasThrown); >+ if (!nonSpecialDataRemoteObject) >+ return; >+ >+ if (!this.representedObject.result || this.representedObject.result.data !== resultData) >+ return; >+ >+ const propertyPath = null; >+ const forceExpanding = true; >+ let element = WI.FormattedValue.createObjectTreeOrFormattedValueForRemoteObject(nonSpecialDataRemoteObject, propertyPath, forceExpanding); >+ >+ let objectTree = element.__objectTree; >+ if (objectTree) { >+ objectTree.showOnlyProperties(); >+ objectTree.expand(); >+ } >+ >+ this._resultDataGeneralContainer.appendChild(element); >+ >+ this.hidePlaceholder(); >+ }); >+ } >+ } >+ >+ if (this._resultDataGeneralContainer) >+ this.contentView.element.appendChild(this._resultDataGeneralContainer); >+ >+ if (!this._resultDataDOMNodesContainer && resultData.domNodes && resultData.domNodes.length) { >+ this._resultDataDOMNodesContainer = document.createElement("div"); >+ this._resultDataDOMNodesContainer.classList.add("dom-nodes"); >+ >+ let domNodeText = this._resultDataDOMNodesContainer.appendChild(document.createElement("h1")); > domNodeText.textContent = WI.UIString("DOM Nodes:"); > >- let tableContainer = domNodesContainer.appendChild(document.createElement("table")); >+ let tableContainer = this._resultDataDOMNodesContainer.appendChild(document.createElement("table")); > > resultData.domNodes.forEach((domNode, index) => { > domNode = result.resolvedDOMNodes[index] || domNode; >@@ -204,14 +246,17 @@ WI.AuditTestCaseContentView = class AuditTestCaseContentView extends WI.AuditTes > }); > } > >- if (resultData.errors && resultData.errors.length) { >- let errorContainer = this.contentView.element.appendChild(document.createElement("div")); >- errorContainer.classList.add("errors"); >+ if (this._resultDataDOMNodesContainer) >+ this.contentView.element.appendChild(this._resultDataDOMNodesContainer); > >- let errorText = errorContainer.appendChild(document.createElement("h1")); >+ if (!this._resultDataErrorsContainer && resultData.errors && resultData.errors.length) { >+ this._resultDataErrorsContainer = document.createElement("div"); >+ this._resultDataErrorsContainer.classList.add("errors"); >+ >+ let errorText = this._resultDataErrorsContainer.appendChild(document.createElement("h1")); > errorText.textContent = WI.UIString("Errors:"); > >- let tableContainer = errorContainer.appendChild(document.createElement("table")); >+ let tableContainer = this._resultDataErrorsContainer.appendChild(document.createElement("table")); > > resultData.errors.forEach((error, index) => { > let rowElement = tableContainer.appendChild(document.createElement("tr")); >@@ -227,10 +272,22 @@ WI.AuditTestCaseContentView = class AuditTestCaseContentView extends WI.AuditTes > }); > } > >+ if (this._resultDataErrorsContainer) >+ this.contentView.element.appendChild(this._resultDataErrorsContainer); >+ > if (!this.contentView.element.children.length) > this.showNoResultDataPlaceholder(); > } > >+ handleResultChanged(event) >+ { >+ super.handleResultChanged(event); >+ >+ this._resultDataGeneralContainer = null; >+ this._resultDataDOMNodesContainer = null; >+ this._resultDataErrorsContainer = null; >+ } >+ > showRunningPlaceholder() > { > if (!this.placeholderElement || !this.placeholderElement.__placeholderRunning) { >diff --git a/Source/WebInspectorUI/UserInterface/Views/AuditTestContentView.js b/Source/WebInspectorUI/UserInterface/Views/AuditTestContentView.js >index cb38605fffbb90c1e7d2f8d9552ab0b52773ca61..0597e10c29522131a694bea24665cb1648a3e40d 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/AuditTestContentView.js >+++ b/Source/WebInspectorUI/UserInterface/Views/AuditTestContentView.js >@@ -102,7 +102,7 @@ WI.AuditTestContentView = class AuditTestContentView extends WI.ContentView > if (this.representedObject instanceof WI.AuditTestBase) { > this.representedObject.addEventListener(WI.AuditTestBase.Event.Completed, this._handleTestChanged, this); > this.representedObject.addEventListener(WI.AuditTestBase.Event.Progress, this._handleTestChanged, this); >- this.representedObject.addEventListener(WI.AuditTestBase.Event.ResultCleared, this._handleTestChanged, this); >+ this.representedObject.addEventListener(WI.AuditTestBase.Event.ResultChanged, this.handleResultChanged, this); > this.representedObject.addEventListener(WI.AuditTestBase.Event.Scheduled, this._handleTestChanged, this); > this.representedObject.addEventListener(WI.AuditTestBase.Event.Stopping, this._handleTestChanged, this); > } >@@ -116,6 +116,13 @@ WI.AuditTestContentView = class AuditTestContentView extends WI.ContentView > super.hidden(); > } > >+ handleResultChanged(event) >+ { >+ // Overridden by sub-classes. >+ >+ this.needsLayout(); >+ } >+ > get placeholderElement() > { > return this._placeholderElement; >diff --git a/Source/WebInspectorUI/UserInterface/Views/AuditTreeElement.js b/Source/WebInspectorUI/UserInterface/Views/AuditTreeElement.js >index 1b9228a356e8194634510c0fbea46bdfca353aa3..6502d4233d4d60505c6979bdb50b6e68518c19b4 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/AuditTreeElement.js >+++ b/Source/WebInspectorUI/UserInterface/Views/AuditTreeElement.js >@@ -62,7 +62,7 @@ WI.AuditTreeElement = class AuditTreeElement extends WI.GeneralTreeElement > > if (this.representedObject instanceof WI.AuditTestBase) { > this.representedObject.addEventListener(WI.AuditTestBase.Event.DisabledChanged, this._handleTestDisabledChanged, this); >- this.representedObject.addEventListener(WI.AuditTestBase.Event.ResultCleared, this._handleTestResultCleared, this); >+ this.representedObject.addEventListener(WI.AuditTestBase.Event.ResultChanged, this._handleTestResultChanged, this); > > if (this.representedObject instanceof WI.AuditTestCase) > this.representedObject.addEventListener(WI.AuditTestBase.Event.Scheduled, this._handleTestCaseScheduled, this); >@@ -281,7 +281,7 @@ WI.AuditTreeElement = class AuditTreeElement extends WI.GeneralTreeElement > this._updateTestGroupDisabled(); > } > >- _handleTestResultCleared(event) >+ _handleTestResultChanged(event) > { > this._updateStatus(); > } >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index e7da17fe14345c7ede4d8eeed9ccca2b6687c6c6..72e456279901cc70f255035612a47ed0d95b4eb0 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,19 @@ >+2019-05-30 Devin Rousso <drousso@apple.com> >+ >+ Web Inspector: Audit: there should be a default test for WebInspectorAudit.Resources functionality >+ https://bugs.webkit.org/show_bug.cgi?id=196710 >+ <rdar://problem/49712348> >+ >+ Reviewed by Joseph Pecoraro. >+ >+ * inspector/audit/manager-start-setup.html: >+ * inspector/audit/manager-start-setup-expected.txt: >+ * inspector/model/auditTestCase-expected.txt: >+ * inspector/model/auditTestCaseResult-expected.txt: >+ * inspector/model/auditTestGroup-expected.txt: >+ * inspector/unit-tests/object-utilities.html: >+ * inspector/unit-tests/object-utilities-expected.txt: >+ > 2019-05-29 Said Abou-Hallawa <sabouhallawa@apple.com> > > REGRESSION (r244182) [Mac WK2] Layout Test imported/w3c/web-platform-tests/visual-viewport/viewport-resize-event-on-load-overflowing-page.html is a flaky failure >diff --git a/LayoutTests/inspector/audit/manager-start-setup-expected.txt b/LayoutTests/inspector/audit/manager-start-setup-expected.txt >index 0e72cbd9cb0f09628d7bb18c9f327660590cccff..0df6dfd72adf97fc4a2e11b980f1e02240a8a26a 100644 >--- a/LayoutTests/inspector/audit/manager-start-setup-expected.txt >+++ b/LayoutTests/inspector/audit/manager-start-setup-expected.txt >@@ -3,18 +3,18 @@ Tests for the AuditManager.prototype.start functionality. > > == Running test suite: AuditManager.prototype.start > -- Running test case: AuditManager.prototype.start.SyncSetup >-PASS: The setup function should have set __test to 42. >+PASS: The setup function should have set test to 42. > > -- Running test case: AuditManager.prototype.start.AsyncSetup >-PASS: The setup function should have set __test to 42. >+PASS: The setup function should have set test to 42. > > -- Running test case: AuditManager.prototype.start.SubLevelSetup >-PASS: The setup function should have set __test to undefined. >+PASS: The setup function should have set test to undefined. > > -- Running test case: AuditManager.prototype.start.OverriddenSetup >-PASS: The setup function should have set __test to B. >+PASS: The setup function should have set test to B. > > -- Running test case: AuditManager.prototype.start.MultipleTopLevel >-PASS: The setup function should have set __test to A. >-PASS: The setup function should have set __test to B. >+PASS: The setup function should have set test to A. >+PASS: The setup function should have set test to B. > >diff --git a/LayoutTests/inspector/audit/manager-start-setup.html b/LayoutTests/inspector/audit/manager-start-setup.html >index 88c22d13e29df9385596c5d28bb99863ee123fc4..4daf823baf51272fbad9ee52a4819c547ca3c80d 100644 >--- a/LayoutTests/inspector/audit/manager-start-setup.html >+++ b/LayoutTests/inspector/audit/manager-start-setup.html >@@ -7,11 +7,11 @@ > function test() > { > const auditTestString = (function() { >- return { >- level: "pass", >- __test: WebInspectorAudit.__test, >- }; >- }).toString(); >+ return { >+ level: "pass", >+ test: WebInspectorAudit.test, >+ }; >+}).toString(); > > async function wrapTest(audit, expected, {getResultCallback} = {}) { > WI.auditManager._addTest(audit); >@@ -22,7 +22,10 @@ function test() > > InspectorTest.assert(result.didPass, "The test should pass."); > InspectorTest.assert(!result.data.errors, "There should be no errors."); >- InspectorTest.expectEqual(result.data.__test.value, expected, `The setup function should have set __test to ${expected}.`); >+ if (result.data.errors) >+ InspectorTest.json(result.data.errors); >+ >+ InspectorTest.expectEqual(result.data.test, expected, `The setup function should have set test to ${expected}.`); > > WI.auditManager.removeTest(audit); > } >@@ -34,7 +37,7 @@ function test() > description: "Check that the setup of an audit is actually run.", > async test() { > const setup = (function() { >- WebInspectorAudit.__test = 42; >+ WebInspectorAudit.test = 42; > }).toString(); > > let audit = new WI.AuditTestCase("AuditManager.prototype.start.SyncSetup", auditTestString, {setup}); >@@ -52,7 +55,7 @@ function test() > setTimeout(resolve, 10); > }); > >- WebInspectorAudit.__test = 42; >+ WebInspectorAudit.test = 42; > }).toString(); > > let audit = new WI.AuditTestCase("AuditManager.prototype.start.AsyncSetup", auditTestString, {setup}); >@@ -66,7 +69,7 @@ function test() > description: "Check that the setup of a non-top-level audit is not run.", > async test() { > const setup = (function() { >- WebInspectorAudit.__test = 42; >+ WebInspectorAudit.test = 42; > }).toString(); > > let audit = new WI.AuditTestGroup("AuditManager.prototype.start.SubLevelSetup.Group", [ >@@ -86,15 +89,15 @@ function test() > description: "Check that only the setup of top-level audits is run.", > async test() { > const setupA = (function() { >- if (!WebInspectorAudit.__test) >- WebInspectorAudit.__test = ""; >- WebInspectorAudit.__test += "A"; >+ if (!WebInspectorAudit.test) >+ WebInspectorAudit.test = ""; >+ WebInspectorAudit.test += "A"; > }).toString(); > > const setupB = (function() { >- if (!WebInspectorAudit.__test) >- WebInspectorAudit.__test = ""; >- WebInspectorAudit.__test += "B"; >+ if (!WebInspectorAudit.test) >+ WebInspectorAudit.test = ""; >+ WebInspectorAudit.test += "B"; > }).toString(); > > let audit = new WI.AuditTestGroup("AuditManager.prototype.start.OverriddenLevelSetup.Group", [ >@@ -114,9 +117,9 @@ function test() > description: "Test that a new WebInspectorAudit object is created for each setup call.", > async test() { > const setupA = (function() { >- if (!WebInspectorAudit.__test) >- WebInspectorAudit.__test = ""; >- WebInspectorAudit.__test += "A"; >+ if (!WebInspectorAudit.test) >+ WebInspectorAudit.test = ""; >+ WebInspectorAudit.test += "A"; > }).toString(); > > let auditA = new WI.AuditTestCase("AuditManager.prototype.start.MultipleTopLevel.A", auditTestString, {setup: setupA}); >@@ -124,9 +127,9 @@ function test() > await wrapTest(auditA, "A"); > > const setupB = (function() { >- if (!WebInspectorAudit.__test) >- WebInspectorAudit.__test = ""; >- WebInspectorAudit.__test += "B"; >+ if (!WebInspectorAudit.test) >+ WebInspectorAudit.test = ""; >+ WebInspectorAudit.test += "B"; > }).toString(); > > let auditB = new WI.AuditTestCase("AuditManager.prototype.start.MultipleTopLevel.B", auditTestString, {setup: setupB}); >diff --git a/LayoutTests/inspector/model/auditTestCase-expected.txt b/LayoutTests/inspector/model/auditTestCase-expected.txt >index 984ad20e601bc541bd9588a603eec61794369768..77f7494b315f1d53f0e1129f82527b2727a705f6 100644 >--- a/LayoutTests/inspector/model/auditTestCase-expected.txt >+++ b/LayoutTests/inspector/model/auditTestCase-expected.txt >@@ -28,7 +28,7 @@ WARN: Audit Warning: "validWithInvalidOptionals test name" is too new to run in > { > "type": "test-case", > "name": "validWithInvalidOptionals test name", >- "supports": 3, >+ "supports": 4, > "test": "validWithInvalidOptionals test function" > } > >@@ -37,7 +37,7 @@ WARN: Audit Warning: "validWithInvalidOptionals test name" is too new to run in > "type": "test-case", > "name": "validWithValidOptionals test name", > "description": "validWithValidOptionals test description", >- "supports": 1, >+ "supports": 2, > "setup": "validWithValidOptionals test setup", > "test": "validWithValidOptionals test function" > } >diff --git a/LayoutTests/inspector/model/auditTestCaseResult-expected.txt b/LayoutTests/inspector/model/auditTestCaseResult-expected.txt >index 4e33ae2346ec41ad2fa7999929f5a8dc26f53349..94d045e3a59113c65b1bef7f11c17d7ab3570b0a 100644 >--- a/LayoutTests/inspector/model/auditTestCaseResult-expected.txt >+++ b/LayoutTests/inspector/model/auditTestCaseResult-expected.txt >@@ -32,6 +32,9 @@ WARN: Audit Warning: "validWithInvalidOptionals test result name" has a non-stri > } > > -- Running test case: AuditTestCaseResult.fromPayload.validWithInvalidSubOptionals >+WARN: Audit Warning: "validWithInvalidSubOptionals test result name" has a non-array "data.domNodes" value >+WARN: Audit Warning: "validWithInvalidSubOptionals test result name" has a non-array "data.domAttributes" value >+WARN: Audit Warning: "validWithInvalidSubOptionals test result name" has a non-array "data.errors" value > WARN: Audit Warning: "validWithInvalidSubOptionals test result name" has a non-object "metadata.startTimestamp" value > WARN: Audit Warning: "validWithInvalidSubOptionals test result name" has a non-object "metadata.asyncTimestamp" value > WARN: Audit Warning: "validWithInvalidSubOptionals test result name" has a non-object "metadata.endTimestamp" value >diff --git a/LayoutTests/inspector/model/auditTestGroup-expected.txt b/LayoutTests/inspector/model/auditTestGroup-expected.txt >index bebb554d2826e150570a98bf20b826508c70191f..141a4c04fa146c0354fc7bddaaa2a96a4bd7b097 100644 >--- a/LayoutTests/inspector/model/auditTestGroup-expected.txt >+++ b/LayoutTests/inspector/model/auditTestGroup-expected.txt >@@ -43,12 +43,12 @@ WARN: Audit Warning: "validWithInvalidOptionals group name" is too new to run in > { > "type": "test-group", > "name": "validWithInvalidOptionals group name", >- "supports": 3, >+ "supports": 4, > "tests": [ > { > "type": "test-case", > "name": "validWithInvalidOptionals test name", >- "supports": 4, >+ "supports": 5, > "test": "validWithInvalidOptionals test function" > } > ] >@@ -59,14 +59,14 @@ WARN: Audit Warning: "validWithInvalidOptionals group name" is too new to run in > "type": "test-group", > "name": "validWithValidOptionals group name", > "description": "validWithValidOptionals group description", >- "supports": 1, >+ "supports": 2, > "setup": "validWithValidOptionals group setup", > "tests": [ > { > "type": "test-case", > "name": "validWithValidOptionals test name", > "description": "validWithValidOptionals test description", >- "supports": 0, >+ "supports": 1, > "setup": "validWithValidOptionals test setup", > "test": "validWithValidOptionals test function" > } >@@ -78,21 +78,21 @@ WARN: Audit Warning: "validWithInvalidOptionals group name" is too new to run in > "type": "test-group", > "name": "validNested group name", > "description": "validNested group description", >- "supports": 1, >+ "supports": 2, > "setup": "validNested group setup", > "tests": [ > { > "type": "test-group", > "name": "validNested nested group name", > "description": "validNested nested group description", >- "supports": 0, >+ "supports": 1, > "setup": "validNested nested group setup", > "tests": [ > { > "type": "test-case", > "name": "validNested nested test name", > "description": "validNested nested test description", >- "supports": -1, >+ "supports": 0, > "setup": "validNested nested test setup", > "test": "validNested nested test function" > } >@@ -102,7 +102,7 @@ WARN: Audit Warning: "validWithInvalidOptionals group name" is too new to run in > "type": "test-case", > "name": "validNested test name", > "description": "validNested test description", >- "supports": -2, >+ "supports": -1, > "setup": "validNested test setup", > "test": "validNested test function" > } >diff --git a/LayoutTests/inspector/unit-tests/object-utilities-expected.txt b/LayoutTests/inspector/unit-tests/object-utilities-expected.txt >index fc2e8cc2a707b44029353d3a93b8c60421e17356..0826af0d0db5262f2bf11d67944b2ac400eb64ad 100644 >--- a/LayoutTests/inspector/unit-tests/object-utilities-expected.txt >+++ b/LayoutTests/inspector/unit-tests/object-utilities-expected.txt >@@ -22,3 +22,13 @@ PASS: shallowEqual of objects with equal constructors should be true. > PASS: shallowEqual of objects with different constructors should be false. > PASS: shallowEqual of objects with different constructors should be false. > >+-- Running test case: Object.filter >+PASS: filter should remove all entries where the key isn't in ["a","b","c"]. >+PASS: filter should remove all entries where the key isn't in ["a"]. >+PASS: filter should remove all entries where the key isn't in ["b"]. >+PASS: filter should remove all entries where the key isn't in ["c"]. >+PASS: filter should remove all entries where the value isn't in [1,2,3]. >+PASS: filter should remove all entries where the value isn't in [1]. >+PASS: filter should remove all entries where the value isn't in [2]. >+PASS: filter should remove all entries where the value isn't in [3]. >+ >diff --git a/LayoutTests/inspector/unit-tests/object-utilities.html b/LayoutTests/inspector/unit-tests/object-utilities.html >index 3f1c4ed5dabd3b90efe812fbe8f570fcc34fd55c..186f41ffb249b57206a4301506ab46ebaa97dd18 100644 >--- a/LayoutTests/inspector/unit-tests/object-utilities.html >+++ b/LayoutTests/inspector/unit-tests/object-utilities.html >@@ -51,6 +51,29 @@ function test() > } > }); > >+ suite.addTestCase({ >+ name: "Object.filter", >+ test() { >+ const object = {a: 1, b: 2}; >+ >+ function checkKey(keys, expected) { >+ InspectorTest.expectShallowEqual(Object.filter(object, (key, value) => keys.includes(key)), expected, `filter should remove all entries where the key isn't in ${JSON.stringify(keys)}.`); >+ } >+ checkKey(["a", "b", "c"], {a: 1, b: 2}); >+ checkKey(["a"], {a: 1}); >+ checkKey(["b"], {b: 2}); >+ checkKey(["c"], {}); >+ >+ function checkValue(values, expected) { >+ InspectorTest.expectShallowEqual(Object.filter(object, (key, value) => values.includes(value)), expected, `filter should remove all entries where the value isn't in ${JSON.stringify(values)}.`); >+ } >+ checkValue([1, 2, 3], {a: 1, b: 2}); >+ checkValue([1], {a: 1}); >+ checkValue([2], {b: 2}); >+ checkValue([3], {}); >+ } >+ }); >+ > suite.runTestCasesAndFinish(); > } > </script>
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 196710
:
370671
|
370673
|
370674
|
370679
|
370680
|
370683
|
370688
|
370699
|
370702
|
370703
|
370704
|
370707
|
370948
| 370989