+2019-01-25 Devin Rousso <drousso@apple.com>
+
+ Web Inspector: improve invalid Audit/Recording JSON error messages
+ https://bugs.webkit.org/show_bug.cgi?id=193476
+ <rdar://problem/47303659>
+
+ Reviewed by Joseph Pecoraro.
+
+ * inspector/model/auditTestCase.html:
+ * inspector/model/auditTestCase-expected.txt:
+ * inspector/model/auditTestCaseResult-expected.txt:
+ * inspector/model/auditTestGroup.html:
+ * inspector/model/auditTestGroup-expected.txt:
+ * inspector/model/auditTestGroupResult-expected.txt:
+ * inspector/model/recording-expected.txt:
+
2019-01-24 Devin Rousso <drousso@apple.com>
Web Inspector: Audit: add supports key to test/group for compatibility
}
-- Running test case: AuditTestCase.fromPayload.validWithInvalidOptionals
+WARN: Audit Warning: "validWithInvalidOptionals test name" has a non-string "description" value
+WARN: Audit Warning: "validWithInvalidOptionals test name" is too new to run in this Web Inspector
{
"type": "test-case",
"name": "validWithInvalidOptionals test name",
+ "supports": 2,
"test": "validWithInvalidOptionals test function"
}
"type": "test-case",
"name": "validWithValidOptionals test name",
"description": "validWithValidOptionals test description",
+ "supports": 0,
"test": "validWithValidOptionals test function"
}
type: WI.AuditTestCase.TypeIdentifier,
name: "validWithInvalidOptionals test name",
description: null,
+ supports: WI.AuditTestBase.Version + 1,
test: "validWithInvalidOptionals test function",
},
},
type: WI.AuditTestCase.TypeIdentifier,
name: "validWithValidOptionals test name",
description: "validWithValidOptionals test description",
+ supports: WI.AuditTestBase.Version - 1,
test: "validWithValidOptionals test function",
},
},
}
-- Running test case: AuditTestCaseResult.fromPayload.validWithInvalidOptionals
+WARN: Audit Warning: "validWithInvalidOptionals test result name" has a non-object "data" value
+WARN: Audit Warning: "validWithInvalidOptionals test result name" has a non-object "metadata" value
+WARN: Audit Warning: "validWithInvalidOptionals test result name" has a non-string "description" value
{
"type": "test-case-result",
"name": "validWithInvalidOptionals test result name",
}
-- Running test case: AuditTestCaseResult.fromPayload.validWithInvalidSubOptionals
+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
+WARN: Audit Warning: "validWithInvalidSubOptionals test result name" has a non-object "metadata.url" value
{
"type": "test-case-result",
"name": "validWithInvalidSubOptionals test result name",
}
-- Running test case: AuditTestGroup.fromPayload.validWithInvalidOptionals
+WARN: Audit Warning: "validWithInvalidOptionals test name" has a non-string "description" value
+WARN: Audit Warning: "validWithInvalidOptionals test name" is too new to run in this Web Inspector
+WARN: Audit Warning: "validWithInvalidOptionals group name" has a non-string "description" value
+WARN: Audit Warning: "validWithInvalidOptionals group name" is too new to run in this Web Inspector
{
"type": "test-group",
"name": "validWithInvalidOptionals group name",
+ "supports": 2,
"tests": [
{
"type": "test-case",
"name": "validWithInvalidOptionals test name",
+ "supports": 3,
"test": "validWithInvalidOptionals test function"
}
]
"type": "test-group",
"name": "validWithValidOptionals group name",
"description": "validWithValidOptionals group description",
+ "supports": 0,
"tests": [
{
"type": "test-case",
"name": "validWithValidOptionals test name",
"description": "validWithValidOptionals test description",
+ "supports": -1,
"test": "validWithValidOptionals test function"
}
]
"type": "test-group",
"name": "validNested group name",
"description": "validNested group description",
+ "supports": 0,
"tests": [
{
"type": "test-group",
"name": "validNested nested group name",
"description": "validNested nested group description",
+ "supports": -1,
"tests": [
{
"type": "test-case",
"name": "validNested nested test name",
"description": "validNested nested test description",
+ "supports": -2,
"test": "validNested nested test function"
}
]
"type": "test-case",
"name": "validNested test name",
"description": "validNested test description",
+ "supports": -3,
"test": "validNested test function"
}
]
type: WI.AuditTestGroup.TypeIdentifier,
name: "validWithInvalidOptionals group name",
description: null,
+ supports: WI.AuditTestBase.Version + 1,
tests: [
{
type: WI.AuditTestCase.TypeIdentifier,
name: "validWithInvalidOptionals test name",
description: null,
+ supports: WI.AuditTestBase.Version + 2,
test: "validWithInvalidOptionals test function",
},
],
type: WI.AuditTestGroup.TypeIdentifier,
name: "validWithValidOptionals group name",
description: "validWithValidOptionals group description",
+ supports: WI.AuditTestBase.Version - 1,
tests: [
{
type: WI.AuditTestCase.TypeIdentifier,
name: "validWithValidOptionals test name",
description: "validWithValidOptionals test description",
+ supports: WI.AuditTestBase.Version - 2,
test: "validWithValidOptionals test function",
},
],
type: WI.AuditTestGroup.TypeIdentifier,
name: "validNested group name",
description: "validNested group description",
+ supports: WI.AuditTestBase.Version - 1,
tests: [
{
type: WI.AuditTestGroup.TypeIdentifier,
name: "validNested nested group name",
description: "validNested nested group description",
+ supports: WI.AuditTestBase.Version - 2,
tests: [
{
type: WI.AuditTestCase.TypeIdentifier,
name: "validNested nested test name",
description: "validNested nested test description",
+ supports: WI.AuditTestBase.Version - 3,
test: "validNested nested test function",
},
],
type: WI.AuditTestCase.TypeIdentifier,
name: "validNested test name",
description: "validNested test description",
+ supports: WI.AuditTestBase.Version - 4,
test: "validNested test function",
},
],
}
-- Running test case: AuditTestGroupResult.fromPayload.validWithInvalidOptionals
+WARN: Audit Warning: "validWithInvalidOptionals test result name" has a non-object "data" value
+WARN: Audit Warning: "validWithInvalidOptionals test result name" has a non-object "metadata" value
+WARN: Audit Warning: "validWithInvalidOptionals test result name" has a non-string "description" value
+WARN: Audit Warning: "validWithInvalidOptionals group result name" has a non-string "description" value
{
"type": "test-group-result",
"name": "validWithInvalidOptionals group result name",
null
-- Running test case: Recording.fromPayload.emptyObject
+ERROR: Recording Error: non-number version
null
-- Running test case: Recording.fromPayload.invalidTopLevelMembers
+ERROR: Recording Error: non-number version
null
-- Running test case: Recording.fromPayload.invalidSubMembers
+WARN: Recording Warning: unknown type "test"
+WARN: Recording Warning: non-object initialState.attributes
+WARN: Recording Warning: non-array initialState.states
+WARN: Recording Warning: non-array initialState.attributes
+WARN: Recording Warning: non-string initialState.content
{
"version": 1,
"type": "test",
}
-- Running test case: Recording.fromPayload.invalidFrame
+WARN: Recording Warning: unknown type "test"
+WARN: Recording Warning: non-array actions
{
"version": 1,
"type": "test",
}
-- Running test case: Recording.fromPayload.invalidAction
+WARN: Recording Warning: unknown type "test"
{
"version": 1,
"type": "test",
}
-- Running test case: Recording.fromPayload.invalidActionMembers
+WARN: Recording Warning: unknown type "test"
+WARN: Recording Warning: non-number name
+WARN: Recording Warning: non-array parameters
+WARN: Recording Warning: non-array swizzleTypes
+WARN: Recording Warning: non-number trace
{
"version": 1,
"type": "test",
{
"actions": [
[
- null,
+ -1,
[],
[],
[]
}
-- Running test case: Recording.fromPayload.valid
+WARN: Recording Warning: unknown type "test"
{
"version": 1,
"type": "test",
+2019-01-25 Devin Rousso <drousso@apple.com>
+
+ Web Inspector: improve invalid Audit/Recording JSON error messages
+ https://bugs.webkit.org/show_bug.cgi?id=193476
+ <rdar://problem/47303659>
+
+ Reviewed by Joseph Pecoraro.
+
+ * UserInterface/Models/AuditTestBase.js:
+ (WI.AuditTestBase):
+ * UserInterface/Models/AuditTestCase.js:
+ (WI.AuditTestCase.async fromPayload):
+ * UserInterface/Models/AuditTestGroup.js:
+ (WI.AuditTestGroup.async fromPayload):
+ * UserInterface/Models/AuditTestCaseResult.js:
+ (WI.AuditTestCaseResult.async fromPayload.checkArray):
+ (WI.AuditTestCaseResult.async fromPayload):
+ * UserInterface/Models/AuditTestGroupResult.js:
+ (WI.AuditTestGroupResult.async fromPayload):
+ * UserInterface/Controllers/AuditManager.js:
+ (WI.AuditManager.synthesizeWarning): Added.
+ (WI.AuditManager.synthesizeError):
+ (WI.AuditManager.prototype.async processJSON):
+
+ * UserInterface/Models/Recording.js:
+ (WI.Recording.fromPayload):
+ (WI.Recording.synthesizeWarning): Added.
+ (WI.Recording.synthesizeError):
+ * UserInterface/Models/RecordingFrame.js:
+ (WI.RecordingFrame.fromPayload):
+ * UserInterface/Models/RecordingAction.js:
+ (WI.RecordingAction.fromPayload):
+ (WI.RecordingAction.prototype.async swizzle):
+ (WI.RecordingAction.prototype.apply):
+ * UserInterface/Controllers/CanvasManager.js:
+ (WI.CanvasManager.prototype.processJSON):
+
+ * Localizations/en.lproj/localizedStrings.js:
+
2019-01-24 Devin Rousso <drousso@apple.com>
Web Inspector: Audit: add supports key to test/group for compatibility
localizedStrings["Attribute Modified"] = "Attribute Modified";
localizedStrings["Attributes"] = "Attributes";
localizedStrings["Audit"] = "Audit";
-localizedStrings["Audit error: %s"] = "Audit error: %s";
+localizedStrings["Audit Error: %s"] = "Audit Error: %s";
+localizedStrings["Audit Warning: %s"] = "Audit Warning: %s";
localizedStrings["Audit:"] = "Audit:";
localizedStrings["Audits"] = "Audits";
localizedStrings["Author Stylesheet"] = "Author Stylesheet";
localizedStrings["Record first %s frames"] = "Record first %s frames";
localizedStrings["Recording"] = "Recording";
localizedStrings["Recording %d"] = "Recording %d";
+localizedStrings["Recording Error: %s"] = "Recording Error: %s";
localizedStrings["Recording Timeline Data"] = "Recording Timeline Data";
-localizedStrings["Recording error: %s"] = "Recording error: %s";
+localizedStrings["Recording Warning: %s"] = "Recording Warning: %s";
localizedStrings["Recordings"] = "Recordings";
localizedStrings["Redirect Response"] = "Redirect Response";
localizedStrings["Redirects"] = "Redirects";
localizedStrings["XPath"] = "XPath";
localizedStrings["Yes"] = "Yes";
localizedStrings["Zoom:"] = "Zoom:";
-localizedStrings["\u0022%s\u0022 is invalid."] = "\u0022%s\u0022 is invalid.";
+localizedStrings["\u0022%s\u0022 has a non-array \u0022%s\u0022 value"] = "\u0022%s\u0022 has a non-array \u0022%s\u0022 value";
+localizedStrings["\u0022%s\u0022 has a non-number \u0022%s\u0022 value"] = "\u0022%s\u0022 has a non-number \u0022%s\u0022 value";
+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 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";
localizedStrings["\u0022%s\u0022 must be a %s"] = "\u0022%s\u0022 must be a %s";
localizedStrings["\u0022%s\u0022 must be an %s"] = "\u0022%s\u0022 must be an %s";
-localizedStrings["\u0022%s\u0022 threw an error."] = "\u0022%s\u0022 threw an error.";
+localizedStrings["\u0022%s\u0022 threw an error"] = "\u0022%s\u0022 threw an error";
localizedStrings["\u201C%s\u201D Event Fired"] = "\u201C%s\u201D Event Fired";
localizedStrings["\u201C%s\u201D Profile Recorded"] = "\u201C%s\u201D Profile Recorded";
localizedStrings["computed"] = "computed";
localizedStrings["default"] = "default";
localizedStrings["for changes to take effect"] = "for changes to take effect";
-localizedStrings["invalid JSON."] = "invalid JSON.";
+localizedStrings["invalid JSON"] = "invalid JSON";
localizedStrings["key"] = "key";
localizedStrings["line "] = "line ";
+localizedStrings["non-array %s"] = "non-array %s";
+localizedStrings["non-integer %s"] = "non-integer %s";
+localizedStrings["non-number %s"] = "non-number %s";
+localizedStrings["non-object %s"] = "non-object %s";
+localizedStrings["non-string %s"] = "non-string %s";
localizedStrings["originally %s"] = "originally %s";
localizedStrings["popup"] = "popup";
localizedStrings["popup, toggle"] = "popup, toggle";
localizedStrings["time before stopping"] = "time before stopping";
localizedStrings["times before stopping"] = "times before stopping";
localizedStrings["toggle"] = "toggle";
-localizedStrings["unsupported version."] = "unsupported version.";
+localizedStrings["unknown %s \u0022%s\u0022"] = "unknown %s \u0022%s\u0022";
+localizedStrings["unsupported %s"] = "unsupported %s";
localizedStrings["value"] = "value";
WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._handleFrameMainResourceDidChange, this);
}
+ // Static
+
+ static synthesizeWarning(message)
+ {
+ message = WI.UIString("Audit Warning: %s").format(message);
+
+ if (window.InspectorTest) {
+ console.warn(message);
+ return;
+ }
+
+ let consoleMessage = new WI.ConsoleMessage(WI.mainTarget, WI.ConsoleMessage.MessageSource.Other, WI.ConsoleMessage.MessageLevel.Warning, message);
+ consoleMessage.shouldRevealConsole = true;
+
+ WI.consoleLogViewController.appendConsoleMessage(consoleMessage);
+ }
+
static synthesizeError(message)
{
- let consoleMessage = new WI.ConsoleMessage(WI.mainTarget, WI.ConsoleMessage.MessageSource.Other, WI.ConsoleMessage.MessageLevel.Error, WI.UIString("Audit error: %s").format(message));
+ message = WI.UIString("Audit Error: %s").format(message);
+
+ if (window.InspectorTest) {
+ console.error(message);
+ return;
+ }
+
+ let consoleMessage = new WI.ConsoleMessage(WI.mainTarget, WI.ConsoleMessage.MessageSource.Other, WI.ConsoleMessage.MessageLevel.Error, message);
consoleMessage.shouldRevealConsole = true;
WI.consoleLogViewController.appendConsoleMessage(consoleMessage);
return;
}
- let object = await WI.AuditTestGroup.fromPayload(json) || await WI.AuditTestCase.fromPayload(json);
- if (!object) {
- object = await WI.AuditTestGroupResult.fromPayload(json) || await WI.AuditTestCaseResult.fromPayload(json);
- if (!object) {
- WI.AuditManager.synthesizeError(WI.UIString("invalid JSON."));
- return;
- }
+ if (typeof json !== "object" || json === null) {
+ WI.AuditManager.synthesizeError(WI.UIString("invalid JSON"));
+ return;
}
+ if (json.type !== WI.AuditTestCase.TypeIdentifier || json.type !== WI.AuditTestGroup.TypeIdentifier
+ || json.type !== WI.AuditTestCaseResult.TypeIdentifier || json.type !== WI.AuditTestGroupResult.TypeIdentifier) {
+ WI.AuditManager.synthesizeError(WI.UIString("unknown %s \u0022%s\u0022").format(WI.unlocalizedString("type"), json.type));
+ return;
+ }
+
+ let object = await WI.AuditTestGroup.fromPayload(json) || await WI.AuditTestCase.fromPayload(json) || await WI.AuditTestGroupResult.fromPayload(json) || await WI.AuditTestCaseResult.fromPayload(json);
+ if (!object)
+ return;
+
if (object instanceof WI.AuditTestBase) {
this._addTest(object);
WI.objectStores.audits.addObject(object);
return;
}
- let recording = WI.Recording.fromPayload(json);
- if (!recording) {
- WI.Recording.synthesizeError(WI.UIString("unsupported version."));
+ if (typeof json !== "object" || json === null) {
+ WI.Recording.synthesizeError(WI.UIString("invalid JSON"));
return;
}
+ let recording = WI.Recording.fromPayload(json);
+ if (!recording)
+ return;
+
let extensionStart = filename.lastIndexOf(".");
if (extensionStart !== -1)
filename = filename.substring(0, extensionStart);
this._supported = true;
if (typeof this._supports === "number") {
- if (this._supports > WI.AuditTestBase.Version)
+ if (this._supports > WI.AuditTestBase.Version) {
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 is too new to run in this Web Inspector").format(this.name));
this._supported = false;
- else if (InspectorBackend.domains.Audit && this._supports > InspectorBackend.domains.Audit.VERSION)
+ } else if (InspectorBackend.domains.Audit && this._supports > InspectorBackend.domains.Audit.VERSION) {
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 is too new to run on this inspected page").format(this.name));
this._supported = false;
+ }
}
if (!this.supported)
if (typeof payload !== "object" || payload === null)
return null;
- let {type, name, test, description, supports, disabled} = payload;
-
- if (type !== WI.AuditTestCase.TypeIdentifier)
+ if (payload.type !== WI.AuditTestCase.TypeIdentifier)
return null;
- if (typeof name !== "string")
+ if (typeof payload.name !== "string") {
+ WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("name")));
return null;
+ }
- if (typeof test !== "string")
+ if (typeof payload.test !== "string") {
+ WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("test")));
return null;
+ }
let options = {};
- if (typeof description === "string")
- options.description = description;
- if (typeof supports === "number")
- options.supports = supports;
- if (typeof disabled === "boolean")
- options.disabled = disabled;
-
- return new WI.AuditTestCase(name, test, options);
+
+ if (typeof payload.description === "string")
+ options.description = payload.description;
+ else if ("description" in payload)
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("description")));
+
+ if (typeof payload.supports === "number")
+ options.supports = payload.supports;
+ else if ("supports" in payload)
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-number \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("supports")));
+
+ if (typeof payload.disabled === "boolean")
+ options.disabled = payload.disabled;
+
+ return new WI.AuditTestCase(payload.name, payload.test, options);
}
// Public
if (typeof payload !== "object" || payload === null)
return null;
- let {type, name, description, level, data, metadata} = payload;
-
- if (type !== WI.AuditTestCaseResult.TypeIdentifier)
+ if (payload.type !== WI.AuditTestCaseResult.TypeIdentifier)
return null;
- if (typeof name !== "string")
+ if (typeof payload.name !== "string") {
+ WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("name")));
return null;
+ }
- if (!Object.values(WI.AuditTestCaseResult.Level).includes(level))
+ if (!Object.values(WI.AuditTestCaseResult.Level).includes(payload.level)) {
+ WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has an invalid \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("level")));
return null;
+ }
- if (typeof data !== "object" || data === null)
- data = {};
- else {
+ if (typeof payload.data !== "object" || payload.data === null) {
+ if ("data" in payload)
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("data")));
+ payload.data = {};
+ } else {
function checkArray(key) {
- if (!data[key])
+ if (!payload.data[key])
return;
- if (!Array.isArray(data[key]))
- data[key] = [];
+ if (!Array.isArray(payload.data[key])) {
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-array \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("data.%s").format(key)));
+ payload.data[key] = [];
+ }
- data[key] = data[key].filter((item) => typeof item === "string");
+ payload.data[key] = payload.data[key].filter((item) => typeof item === "string");
}
checkArray("domNodes");
checkArray("domAttributes");
checkArray("errors");
}
- if (typeof metadata !== "object" || metadata === null)
- metadata = {};
- else {
- metadata.startTimestamp = typeof metadata.startTimestamp === "string" ? new Date(metadata.startTimestamp) : null;
- metadata.asyncTimestamp = typeof metadata.asyncTimestamp === "string" ? new Date(metadata.asyncTimestamp) : null;
- metadata.endTimestamp = typeof metadata.endTimestamp === "string" ? new Date(metadata.endTimestamp) : null;
- metadata.url = typeof metadata.url === "string" ? metadata.url : null;
+ if (typeof payload.metadata !== "object" || payload.metadata === null) {
+ if ("metadata" in payload)
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata")));
+
+ payload.metadata = {};
+ } else {
+ if (typeof payload.metadata.startTimestamp === "string")
+ payload.metadata.startTimestamp = new Date(payload.metadata.startTimestamp);
+ else {
+ if ("startTimestamp" in payload.metadata)
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata.startTimestamp")));
+
+ payload.metadata.startTimestamp = null;
+ }
+
+ if (typeof payload.metadata.asyncTimestamp === "string")
+ payload.metadata.asyncTimestamp = new Date(payload.metadata.asyncTimestamp);
+ else {
+ if ("asyncTimestamp" in payload.metadata)
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata.asyncTimestamp")));
+
+ payload.metadata.asyncTimestamp = null;
+ }
+
+ if (typeof payload.metadata.endTimestamp === "string")
+ payload.metadata.endTimestamp = new Date(payload.metadata.endTimestamp);
+ else {
+ if ("endTimestamp" in payload.metadata)
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata.endTimestamp")));
+
+ payload.metadata.endTimestamp = null;
+ }
+
+ if (typeof payload.metadata.url !== "string") {
+ if ("url" in payload.metadata)
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-object \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("metadata.url")));
+
+ payload.metadata.url = null;
+ }
}
let options = {};
- if (typeof description === "string")
- options.description = description;
- if (!isEmptyObject(data)) {
+
+ if (typeof payload.description === "string")
+ options.description = payload.description;
+ else if ("description" in payload)
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("description")));
+
+ if (!isEmptyObject(payload.data)) {
options.data = {};
- if (data.domNodes && data.domNodes.length) {
- if (window.DOMAgent && (!metadata.url || metadata.url === WI.networkManager.mainFrame.url)) {
+ 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(data.domNodes.map(async (domNodeString) => {
+ options.resolvedDOMNodes = await Promise.all(payload.data.domNodes.map(async (domNodeString) => {
let nodeId = 0;
try {
nodeId = await WI.domManager.querySelector(documentNode, domNodeString);
}));
}
- options.data.domNodes = data.domNodes;
+ options.data.domNodes = payload.data.domNodes;
}
- if (data.domAttributes && data.domAttributes.length)
- options.data.domAttributes = data.domAttributes;
- if (data.errors && data.errors.length)
- options.data.errors = data.errors;
+ 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(metadata)) {
+
+ if (!isEmptyObject(payload.metadata)) {
options.metadata = {};
- if (metadata.startTimestamp && !isNaN(metadata.startTimestamp))
- options.metadata.startTimestamp = metadata.startTimestamp;
- if (metadata.asyncTimestamp && !isNaN(metadata.asyncTimestamp))
- options.metadata.asyncTimestamp = metadata.asyncTimestamp;
- if (metadata.endTimestamp && !isNaN(metadata.endTimestamp))
- options.metadata.endTimestamp = metadata.endTimestamp;
- if (metadata.url)
- options.metadata.url = metadata.url;
+ if (payload.metadata.startTimestamp && !isNaN(payload.metadata.startTimestamp))
+ options.metadata.startTimestamp = payload.metadata.startTimestamp;
+ if (payload.metadata.asyncTimestamp && !isNaN(payload.metadata.asyncTimestamp))
+ options.metadata.asyncTimestamp = payload.metadata.asyncTimestamp;
+ if (payload.metadata.endTimestamp && !isNaN(payload.metadata.endTimestamp))
+ options.metadata.endTimestamp = payload.metadata.endTimestamp;
+ if (payload.metadata.url)
+ options.metadata.url = payload.metadata.url;
}
- return new WI.AuditTestCaseResult(name, level, options);
+ return new WI.AuditTestCaseResult(payload.name, payload.level, options);
}
// Public
if (typeof payload !== "object" || payload === null)
return null;
- let {type, name, tests, description, supports, disabled} = payload;
-
- if (type !== WI.AuditTestGroup.TypeIdentifier)
+ if (payload.type !== WI.AuditTestGroup.TypeIdentifier)
return null;
- if (typeof name !== "string")
+ if (typeof payload.name !== "string") {
+ WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("name")));
return null;
+ }
- if (!Array.isArray(tests))
+ if (!Array.isArray(payload.tests)) {
+ WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-array \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("tests")));
return null;
+ }
- tests = await Promise.all(tests.map(async (test) => {
+ let tests = await Promise.all(payload.tests.map(async (test) => {
let testCase = await WI.AuditTestCase.fromPayload(test);
if (testCase)
return testCase;
return null;
let options = {};
- if (typeof description === "string")
- options.description = description;
- if (typeof supports === "number")
- options.supports = supports;
- if (typeof disabled === "boolean")
- options.disabled = disabled;
-
- return new WI.AuditTestGroup(name, tests, options);
+
+ if (typeof payload.description === "string")
+ options.description = payload.description;
+ else if ("description" in payload)
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("description")));
+
+ if (typeof payload.supports === "number")
+ options.supports = payload.supports;
+ else if ("supports" in payload)
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-number \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("supports")));
+
+ if (typeof payload.disabled === "boolean")
+ options.disabled = payload.disabled;
+
+ return new WI.AuditTestGroup(payload.name, tests, options);
}
// Public
if (typeof payload !== "object" || payload === null)
return null;
- let {type, name, description, results} = payload;
-
- if (type !== WI.AuditTestGroupResult.TypeIdentifier)
+ if (payload.type !== WI.AuditTestGroupResult.TypeIdentifier)
return null;
- if (typeof name !== "string")
+ if (typeof payload.name !== "string") {
+ WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("name")));
return null;
+ }
- if (!Array.isArray(results))
+ if (!Array.isArray(payload.results)) {
+ WI.AuditManager.synthesizeError(WI.UIString("\u0022%s\u0022 has a non-array \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("results")));
return null;
+ }
- results = await Promise.all(results.map(async (test) => {
+ let results = await Promise.all(payload.results.map(async (test) => {
let testCaseResult = await WI.AuditTestCaseResult.fromPayload(test);
if (testCaseResult)
return testCaseResult;
return null;
let options = {};
- if (typeof description === "string")
- options.description = description;
- return new WI.AuditTestGroupResult(name, results, options);
+ if (typeof payload.description === "string")
+ options.description = payload.description;
+ else if ("description" in payload)
+ WI.AuditManager.synthesizeWarning(WI.UIString("\u0022%s\u0022 has a non-string \u0022%s\u0022 value").format(payload.name, WI.unlocalizedString("description")));
+
+ return new WI.AuditTestGroupResult(payload.name, results, options);
}
// Public
if (typeof payload !== "object" || payload === null)
return null;
- if (isNaN(payload.version) || payload.version <= 0 || payload.version > WI.Recording.Version)
+ if (typeof payload.version !== "number") {
+ WI.Recording.synthesizeError(WI.UIString("non-number %s").format(WI.unlocalizedString("version")));
return null;
+ }
+
+ if (payload.version < 1 || payload.version > WI.Recording.Version) {
+ WI.Recording.synthesizeError(WI.UIString("unsupported %s").format(WI.unlocalizedString("version")));
+ return null;
+ }
+
+ if (parseInt(payload.version) !== payload.version) {
+ WI.Recording.synthesizeWarning(WI.UIString("non-integer %s").format(WI.unlocalizedString("version")));
+ payload.version = parseInt(payload.version);
+ }
let type = null;
switch (payload.type) {
type = WI.Recording.Type.CanvasWebGL;
break;
default:
+ WI.Recording.synthesizeWarning(WI.UIString("unknown %s \u0022%s\u0022").format(WI.unlocalizedString("type"), payload.type));
type = String(payload.type);
break;
}
- if (typeof payload.initialState !== "object" || payload.initialState === null)
+ if (typeof payload.initialState !== "object" || payload.initialState === null) {
+ if ("initialState" in payload)
+ WI.Recording.synthesizeWarning(WI.UIString("non-object %s").format(WI.unlocalizedString("initialState")));
+
payload.initialState = {};
- if (typeof payload.initialState.attributes !== "object" || payload.initialState.attributes === null)
+ }
+
+ if (typeof payload.initialState.attributes !== "object" || payload.initialState.attributes === null) {
+ if ("attributes" in payload.initialState)
+ WI.Recording.synthesizeWarning(WI.UIString("non-object %s").format(WI.unlocalizedString("initialState.attributes")));
+
payload.initialState.attributes = {};
+ }
+
if (!Array.isArray(payload.initialState.states) || payload.initialState.states.some((item) => typeof item !== "object" || item === null)) {
+ if ("states" in payload.initialState)
+ WI.Recording.synthesizeWarning(WI.UIString("non-array %s").format(WI.unlocalizedString("initialState.states")));
+
payload.initialState.states = [];
// COMPATIBILITY (iOS 12.0): Recording.InitialState.states did not exist yet
payload.initialState.states.push(state);
}
}
- if (!Array.isArray(payload.initialState.parameters))
+
+ if (!Array.isArray(payload.initialState.parameters)) {
+ if ("parameters" in payload.initialState)
+ WI.Recording.synthesizeWarning(WI.UIString("non-array %s").format(WI.unlocalizedString("initialState.attributes")));
+
payload.initialState.parameters = [];
- if (typeof payload.initialState.content !== "string")
+ }
+
+ if (typeof payload.initialState.content !== "string") {
+ if ("content" in payload.initialState)
+ WI.Recording.synthesizeWarning(WI.UIString("non-string %s").format(WI.unlocalizedString("initialState.content")));
+
payload.initialState.content = "";
+ }
+
+ if (!Array.isArray(payload.frames)) {
+ if ("frames" in payload)
+ WI.Recording.synthesizeWarning(WI.UIString("non-array %s").format(WI.unlocalizedString("frames")));
- if (!Array.isArray(payload.frames))
payload.frames = [];
+ }
+
+ if (!Array.isArray(payload.data)) {
+ if ("data" in payload)
+ WI.Recording.synthesizeWarning(WI.UIString("non-array %s").format(WI.unlocalizedString("data")));
- if (!Array.isArray(payload.data))
payload.data = [];
+ }
if (!frames)
frames = payload.frames.map(WI.RecordingFrame.fromPayload)
}
}
+ static synthesizeWarning(message)
+ {
+ message = WI.UIString("Recording Warning: %s").format(message);
+
+ if (window.InspectorTest) {
+ console.warn(message);
+ return;
+ }
+
+ let consoleMessage = new WI.ConsoleMessage(WI.mainTarget, WI.ConsoleMessage.MessageSource.Other, WI.ConsoleMessage.MessageLevel.Warning, message);
+ consoleMessage.shouldRevealConsole = true;
+
+ WI.consoleLogViewController.appendConsoleMessage(consoleMessage);
+ }
+
static synthesizeError(message)
{
- const target = WI.mainTarget;
- const source = WI.ConsoleMessage.MessageSource.Other;
- const level = WI.ConsoleMessage.MessageLevel.Error;
- let consoleMessage = new WI.ConsoleMessage(target, source, level, WI.UIString("Recording error: %s").format(message));
+ message = WI.UIString("Recording Error: %s").format(message);
+
+ if (window.InspectorTest) {
+ console.error(message);
+ return;
+ }
+
+ let consoleMessage = new WI.ConsoleMessage(WI.mainTarget, WI.ConsoleMessage.MessageSource.Other, WI.ConsoleMessage.MessageLevel.Error, message);
consoleMessage.shouldRevealConsole = true;
WI.consoleLogViewController.appendConsoleMessage(consoleMessage);
if (!Array.isArray(payload))
payload = [];
- if (isNaN(payload[0]))
+ if (typeof payload[0] !== "number") {
+ if (payload.length > 0)
+ WI.Recording.synthesizeWarning(WI.UIString("non-number %s").format(WI.unlocalizedString("name")));
+
payload[0] = -1;
+ }
+
+ if (!Array.isArray(payload[1])) {
+ if (payload.length > 1)
+ WI.Recording.synthesizeWarning(WI.UIString("non-array %s").format(WI.unlocalizedString("parameters")));
- if (!Array.isArray(payload[1]))
payload[1] = [];
+ }
+
+ if (!Array.isArray(payload[2])) {
+ if (payload.length > 2)
+ WI.Recording.synthesizeWarning(WI.UIString("non-array %s").format(WI.unlocalizedString("swizzleTypes")));
- if (!Array.isArray(payload[2]))
payload[2] = [];
+ }
- if (isNaN(payload[3]) || (!payload[3] && payload[3] !== 0)) {
+ if (typeof payload[3] !== "number" || isNaN(payload[3]) || (!payload[3] && payload[3] !== 0)) {
// COMPATIBILITY (iOS 12.1): "trace" was sent as an array of call frames instead of a single call stack
- if (!Array.isArray(payload[3]))
+ if (!Array.isArray(payload[3])) {
+ if (payload.length > 3)
+ WI.Recording.synthesizeWarning(WI.UIString("non-number %s").format(WI.unlocalizedString("trace")));
+
payload[3] = [];
+ }
}
- if (payload.length >= 5 && isNaN(payload[4]))
+ if (typeof payload[4] !== "number" || isNaN(payload[4])) {
+ if (payload.length > 4)
+ WI.Recording.synthesizeWarning(WI.UIString("non-number %s").format(WI.unlocalizedString("snapshot")));
+
payload[4] = -1;
+ }
return new WI.RecordingAction(...payload);
}
if (prototype && !(name in prototype)) {
this.markInvalid();
- WI.Recording.synthesizeError(WI.UIString("\u0022%s\u0022 is invalid.").format(name));
+ WI.Recording.synthesizeWarning(WI.UIString("\u0022%s\u0022 is not valid for %s").format(name, prototype.constructor.name));
}
}
}
} catch {
this.markInvalid();
- WI.Recording.synthesizeError(WI.UIString("\u0022%s\u0022 threw an error.").format(this._name));
+ WI.Recording.synthesizeWarning(WI.UIString("\u0022%s\u0022 threw an error").format(this._name));
}
}
if (typeof payload !== "object" || payload === null)
payload = {};
- if (!Array.isArray(payload.actions))
+ if (!Array.isArray(payload.actions)) {
+ if ("actions" in payload)
+ WI.Recording.synthesizeWarning(WI.UIString("non-array %s").format(WI.unlocalizedString("actions")));
+
payload.actions = [];
+ }
let actions = payload.actions.map(WI.RecordingAction.fromPayload);
return new WI.RecordingFrame(actions, {