https://bugs.webkit.org/show_bug.cgi?id=192210
<rdar://problem/
46423583>
Reviewed by Joseph Pecoraro.
Source/WebInspectorUI:
* UserInterface/Controllers/AuditManager.js:
(WI.AuditManager.prototype.get editing): Added.
(WI.AuditManager.prototype.set editing): Added.
(WI.AuditManager.prototype.stop):
(WI.AuditManager.prototype.addDefaultTestsIfNeeded):
Since default audits aren't stored, keep a list of disabled default tests in a `WI.Setting`.
* UserInterface/Models/AuditTestBase.js:
(WI.AuditTestBase):
(WI.AuditTestBase.prototype.get disabled): Added.
(WI.AuditTestBase.prototype.set disabled): Added.
(WI.AuditTestBase.prototype.async start):
(WI.AuditTestBase.prototype.stop):
(WI.AuditTestBase.toJSON):
* UserInterface/Models/AuditTestCase.js:
(WI.AuditTestCase):
(WI.AuditTestCase.async fromPayload):
(WI.AuditTestCase.prototype.toJSON):
* UserInterface/Models/AuditTestGroup.js:
(WI.AuditTestGroup):
(WI.AuditTestGroup.async fromPayload):
(WI.AuditTestGroup.prototype.get disabled): Added.
(WI.AuditTestGroup.prototype.set disabled): Added.
(WI.AuditTestGroup.prototype.toJSON):
(WI.AuditTestGroup.prototype.async run):
(WI.AuditTestGroup.prototype._handleTestDisabledChanged): Added.
(WI.AuditTestGroup.prototype._handleTestProgress):
Propagate `disabled` changes to all sub-tests, unless the change was caused by one of the
sub-tests, in which case we are now in an intermediate state.
* UserInterface/Views/AuditNavigationSidebarPanel.js:
(WI.AuditNavigationSidebarPanel):
(WI.AuditNavigationSidebarPanel.prototype.showDefaultContentView):
(WI.AuditNavigationSidebarPanel.prototype.initialLayout):
(WI.AuditNavigationSidebarPanel.prototype.hasCustomFilters): Added.
(WI.AuditNavigationSidebarPanel.prototype.matchTreeElementAgainstCustomFilters): Added.
(WI.AuditNavigationSidebarPanel.prototype._addTest):
(WI.AuditNavigationSidebarPanel.prototype._addResult):
(WI.AuditNavigationSidebarPanel.prototype._updateStartStopButtonNavigationItemState):
(WI.AuditNavigationSidebarPanel.prototype._updateEditButtonNavigationItemState): Added.
(WI.AuditNavigationSidebarPanel.prototype._handleAuditManagerEditingChanged): Added.
(WI.AuditNavigationSidebarPanel.prototype._handleAuditTestRemoved):
(WI.AuditNavigationSidebarPanel.prototype._handleAuditTestScheduled):
(WI.AuditNavigationSidebarPanel.prototype._treeSelectionDidChange):
(WI.AuditNavigationSidebarPanel.prototype._handleEditButtonNavigationItemClicked): Added.
* UserInterface/Views/AuditNavigationSidebarPanel.css:
(.sidebar > .panel.navigation.audit > .content):
(.sidebar > .panel.navigation.audit > .content > .tree-outline): Added.
(.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled):active): Added.
(.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled).activated): Added.
(.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled).activated:active): Added.
(.sidebar > .panel.navigation.audit > .content .edit-audits.disabled): Added.
(.finish-editing-audits-placeholder.message-text-view .navigation-item-help .navigation-bar): Added.
Leverage custom filters to ensure that disabled audits arent shown when not editing and that
result tree elements aren't shown while editing.
* UserInterface/Views/AuditTestGroupContentView.js:
(WI.AuditTestGroupContentView.prototype.shown):
* UserInterface/Views/AuditTreeElement.js:
(WI.AuditTreeElement.prototype.onattach):
(WI.AuditTreeElement.prototype.canSelectOnMouseDown): Added.
(WI.AuditTreeElement.prototype._updateTestGroupDisabled): Added.
(WI.AuditTreeElement.prototype._handleTestDisabledChanged): Added.
(WI.AuditTreeElement.prototype._handleManagerEditingChanged): Added.
* UserInterface/Views/AuditTreeElement.css:
(.tree-outline .item.audit > .status:not(:hover) > img.show-on-hover, .tree-outline .item.audit.test-group.expanded:not(.editing-audits) > .status:not(:hover)): Added.
(.tree-outline .item.audit.manager-active > .status > img.show-on-hover, .tree-outline .item.audit.test-group.expanded:not(.editing-audits) > .status:hover > :not(img), .tree-outline .item.audit.test-group-result.expanded > .status): Added.
(.tree-outline .item.audit > .status:not(:hover) > img.show-on-hover, .tree-outline .item.audit.test-group.expanded > .status:not(:hover)): Deleted.
(.tree-outline .item.audit.manager-active > .status > img.show-on-hover, .tree-outline .item.audit.test-group.expanded > .status:hover > :not(img), .tree-outline .item.audit.test-group-result.expanded > .status): Deleted.
Prevent selection and running when editing.
* UserInterface/Views/TreeOutline.css:
(.tree-outline .children.expanded:not([hidden])): Added.
(.tree-outline .children.expanded): Deleted.
* UserInterface/Base/ObjectStore.js:
(WI.ObjectStore._open):
Batch operations together to help avoid multiple simultaneous `indexedDB.open` calls. This
should also help preserve the order of operations, as once the database is open, operations
are executed in the order they were enqueued.
(WI.ObjectStore.prototype.async.addObject):
Pass a unique `Symbol` to the `toJSON` call on the given object so that the object can save
additional values that wouldn't normally be saved. This doesn't conflict with normal usage
of `toJSON` (e.g. `JSON.stringify`) because that case also passes in a value:
- `undefined`, if it was called directly on the object
- the key for this object in the containing object
- the index of this object in the containing array
In any case, the value can never equal the unique `Symbol`, so it's guaranteed that the code
will only run for `WI.ObjectStore` operations.
(WI.ObjectStore.prototype.async.clear): Added.
* Localizations/en.lproj/localizedStrings.js:
LayoutTests:
* inspector/unit-tests/objectStore/clear.html: Added.
* inspector/unit-tests/objectStore/clear-expected.txt: Added.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@239858
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2019-01-10 Devin Rousso <drousso@apple.com>
+
+ Web Inspector: Audit: allow audits to be enabled/disabled
+ https://bugs.webkit.org/show_bug.cgi?id=192210
+ <rdar://problem/46423583>
+
+ Reviewed by Joseph Pecoraro.
+
+ * inspector/unit-tests/objectStore/clear.html: Added.
+ * inspector/unit-tests/objectStore/clear-expected.txt: Added.
+
2019-01-10 Justin Fan <justin_fan@apple.com>
[WebGPU] WebGPUBindGroup and device::createBindGroup prototype
--- /dev/null
+Tests WI.ObjectStore.prototype.clear.
+
+
+== Running test suite: WI.ObjectStore.prototype.clear
+-- Running test case: WI.ObjectStore.prototype.clear.Empty
+[]
+[]
+
+-- Running test case: WI.ObjectStore.prototype.clear.NotEmpty
+[true,1,"foo",["bar"],{"a":1}]
+[]
+
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../../http/tests/inspector/resources/inspector-test.js"></script>
+<script src="resources/objectStore-utilities.js"></script>
+<script>
+function test()
+{
+ let suite = InspectorTest.ObjectStore.createSuite("WI.ObjectStore.prototype.clear");
+
+ function testClear(name, {objects}) {
+ InspectorTest.ObjectStore.wrapTest(name, async function() {
+ let objectStore = InspectorTest.ObjectStore.createObjectStore({autoIncrement: true});
+
+ for (let object of objects)
+ await objectStore.add(object);
+
+ await InspectorTest.ObjectStore.logValues();
+
+ await objectStore.clear();
+ });
+ }
+
+ testClear("WI.ObjectStore.prototype.clear.Empty", {
+ objects: [],
+ });
+
+ testClear("WI.ObjectStore.prototype.clear.NotEmpty", {
+ objects: [true, 1, "foo", ["bar"], {a: 1}],
+ });
+
+ suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+ <p>Tests WI.ObjectStore.prototype.clear.</p>
+</body>
+</html>
+2019-01-10 Devin Rousso <drousso@apple.com>
+
+ Web Inspector: Audit: allow audits to be enabled/disabled
+ https://bugs.webkit.org/show_bug.cgi?id=192210
+ <rdar://problem/46423583>
+
+ Reviewed by Joseph Pecoraro.
+
+ * UserInterface/Controllers/AuditManager.js:
+ (WI.AuditManager.prototype.get editing): Added.
+ (WI.AuditManager.prototype.set editing): Added.
+ (WI.AuditManager.prototype.stop):
+ (WI.AuditManager.prototype.addDefaultTestsIfNeeded):
+ Since default audits aren't stored, keep a list of disabled default tests in a `WI.Setting`.
+
+ * UserInterface/Models/AuditTestBase.js:
+ (WI.AuditTestBase):
+ (WI.AuditTestBase.prototype.get disabled): Added.
+ (WI.AuditTestBase.prototype.set disabled): Added.
+ (WI.AuditTestBase.prototype.async start):
+ (WI.AuditTestBase.prototype.stop):
+ (WI.AuditTestBase.toJSON):
+
+ * UserInterface/Models/AuditTestCase.js:
+ (WI.AuditTestCase):
+ (WI.AuditTestCase.async fromPayload):
+ (WI.AuditTestCase.prototype.toJSON):
+
+ * UserInterface/Models/AuditTestGroup.js:
+ (WI.AuditTestGroup):
+ (WI.AuditTestGroup.async fromPayload):
+ (WI.AuditTestGroup.prototype.get disabled): Added.
+ (WI.AuditTestGroup.prototype.set disabled): Added.
+ (WI.AuditTestGroup.prototype.toJSON):
+ (WI.AuditTestGroup.prototype.async run):
+ (WI.AuditTestGroup.prototype._handleTestDisabledChanged): Added.
+ (WI.AuditTestGroup.prototype._handleTestProgress):
+ Propagate `disabled` changes to all sub-tests, unless the change was caused by one of the
+ sub-tests, in which case we are now in an intermediate state.
+
+ * UserInterface/Views/AuditNavigationSidebarPanel.js:
+ (WI.AuditNavigationSidebarPanel):
+ (WI.AuditNavigationSidebarPanel.prototype.showDefaultContentView):
+ (WI.AuditNavigationSidebarPanel.prototype.initialLayout):
+ (WI.AuditNavigationSidebarPanel.prototype.hasCustomFilters): Added.
+ (WI.AuditNavigationSidebarPanel.prototype.matchTreeElementAgainstCustomFilters): Added.
+ (WI.AuditNavigationSidebarPanel.prototype._addTest):
+ (WI.AuditNavigationSidebarPanel.prototype._addResult):
+ (WI.AuditNavigationSidebarPanel.prototype._updateStartStopButtonNavigationItemState):
+ (WI.AuditNavigationSidebarPanel.prototype._updateEditButtonNavigationItemState): Added.
+ (WI.AuditNavigationSidebarPanel.prototype._handleAuditManagerEditingChanged): Added.
+ (WI.AuditNavigationSidebarPanel.prototype._handleAuditTestRemoved):
+ (WI.AuditNavigationSidebarPanel.prototype._handleAuditTestScheduled):
+ (WI.AuditNavigationSidebarPanel.prototype._treeSelectionDidChange):
+ (WI.AuditNavigationSidebarPanel.prototype._handleEditButtonNavigationItemClicked): Added.
+ * UserInterface/Views/AuditNavigationSidebarPanel.css:
+ (.sidebar > .panel.navigation.audit > .content):
+ (.sidebar > .panel.navigation.audit > .content > .tree-outline): Added.
+ (.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled):active): Added.
+ (.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled).activated): Added.
+ (.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled).activated:active): Added.
+ (.sidebar > .panel.navigation.audit > .content .edit-audits.disabled): Added.
+ (.finish-editing-audits-placeholder.message-text-view .navigation-item-help .navigation-bar): Added.
+ Leverage custom filters to ensure that disabled audits arent shown when not editing and that
+ result tree elements aren't shown while editing.
+
+ * UserInterface/Views/AuditTestGroupContentView.js:
+ (WI.AuditTestGroupContentView.prototype.shown):
+
+ * UserInterface/Views/AuditTreeElement.js:
+ (WI.AuditTreeElement.prototype.onattach):
+ (WI.AuditTreeElement.prototype.canSelectOnMouseDown): Added.
+ (WI.AuditTreeElement.prototype._updateTestGroupDisabled): Added.
+ (WI.AuditTreeElement.prototype._handleTestDisabledChanged): Added.
+ (WI.AuditTreeElement.prototype._handleManagerEditingChanged): Added.
+ * UserInterface/Views/AuditTreeElement.css:
+ (.tree-outline .item.audit > .status:not(:hover) > img.show-on-hover, .tree-outline .item.audit.test-group.expanded:not(.editing-audits) > .status:not(:hover)): Added.
+ (.tree-outline .item.audit.manager-active > .status > img.show-on-hover, .tree-outline .item.audit.test-group.expanded:not(.editing-audits) > .status:hover > :not(img), .tree-outline .item.audit.test-group-result.expanded > .status): Added.
+ (.tree-outline .item.audit > .status:not(:hover) > img.show-on-hover, .tree-outline .item.audit.test-group.expanded > .status:not(:hover)): Deleted.
+ (.tree-outline .item.audit.manager-active > .status > img.show-on-hover, .tree-outline .item.audit.test-group.expanded > .status:hover > :not(img), .tree-outline .item.audit.test-group-result.expanded > .status): Deleted.
+ Prevent selection and running when editing.
+
+ * UserInterface/Views/TreeOutline.css:
+ (.tree-outline .children.expanded:not([hidden])): Added.
+ (.tree-outline .children.expanded): Deleted.
+
+ * UserInterface/Base/ObjectStore.js:
+ (WI.ObjectStore._open):
+ Batch operations together to help avoid multiple simultaneous `indexedDB.open` calls. This
+ should also help preserve the order of operations, as once the database is open, operations
+ are executed in the order they were enqueued.
+
+ (WI.ObjectStore.prototype.async.addObject):
+ Pass a unique `Symbol` to the `toJSON` call on the given object so that the object can save
+ additional values that wouldn't normally be saved. This doesn't conflict with normal usage
+ of `toJSON` (e.g. `JSON.stringify`) because that case also passes in a value:
+ - `undefined`, if it was called directly on the object
+ - the key for this object in the containing object
+ - the index of this object in the containing array
+ In any case, the value can never equal the unique `Symbol`, so it's guaranteed that the code
+ will only run for `WI.ObjectStore` operations.
+
+ (WI.ObjectStore.prototype.async.clear): Added.
+
+ * Localizations/en.lproj/localizedStrings.js:
+
2019-01-09 Devin Rousso <drousso@apple.com>
Web Inspector: Protocol Logging: log messages as objects if inspector^2 is open
localizedStrings["Edit \u201Cspring\u201D function"] = "Edit \u201Cspring\u201D function";
localizedStrings["Edit configuration"] = "Edit configuration";
localizedStrings["Edit custom gradient"] = "Edit custom gradient";
+localizedStrings["Editing audits"] = "Editing audits";
localizedStrings["Element"] = "Element";
localizedStrings["Element clips compositing descendants"] = "Element clips compositing descendants";
localizedStrings["Element has CSS blending applied and composited descendants"] = "Element has CSS blending applied and composited descendants";
localizedStrings["Press %s to import a test or result file"] = "Press %s to import a test or result file";
localizedStrings["Press %s to load a recording from file."] = "Press %s to load a recording from file.";
localizedStrings["Press %s to start running the audit"] = "Press %s to start running the audit";
+localizedStrings["Press %s to stop editing"] = "Press %s to stop editing";
localizedStrings["Pressed"] = "Pressed";
localizedStrings["Pretty print"] = "Pretty print";
localizedStrings["Preview"] = "Preview";
return;
}
+ if (Array.isArray(WI.ObjectStore._databaseCallbacks)) {
+ WI.ObjectStore._databaseCallbacks.push(callback);
+ return;
+ }
+
+ WI.ObjectStore._databaseCallbacks = [callback];
+
const version = 1; // Increment this for every edit to `WI.objectStores`.
let databaseRequest = indexedDB.open(WI.ObjectStore._databaseName, version);
WI.ObjectStore._database = null;
});
- callback(WI.ObjectStore._database);
+ for (let databaseCallback of WI.ObjectStore._databaseCallbacks)
+ databaseCallback(WI.ObjectStore._database);
+
+ WI.ObjectStore._databaseCallbacks = null;
});
}
return undefined;
console.assert(typeof object.toJSON === "function", "ObjectStore cannot store an object without JSON serialization", object.constructor.name);
- let result = await this.add(object.toJSON(), ...args);
+ let result = await this.add(object.toJSON(WI.ObjectStore.toJSONSymbol), ...args);
this.associateObject(object, args[0], result);
return result;
}
return this.delete(this._resolveKeyPath(object).value, ...args);
}
+ async clear(...args)
+ {
+ if (!WI.ObjectStore.supported())
+ return undefined;
+
+ return this._operation("readwrite", (objectStore) => objectStore.clear(...args));
+ }
+
// Private
_resolveKeyPath(object, keyPath)
};
WI.ObjectStore._database = null;
+WI.ObjectStore._databaseCallbacks = null;
+
+WI.ObjectStore.toJSONSymbol = Symbol("ObjectStore-toJSON");
// Be sure to update the `version` above when making changes.
WI.objectStores = {
this._runningState = WI.AuditManager.RunningState.Inactive;
this._runningTests = [];
+ this._disabledDefaultTestsSetting = new WI.Setting("audit-disabled-default-tests", []);
+
WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._handleFrameMainResourceDidChange, this);
}
get results() { return this._results; }
get runningState() { return this._runningState; }
+ get editing()
+ {
+ return this._runningState === WI.AuditManager.RunningState.Disabled;
+ }
+
+ set editing(editing)
+ {
+ console.assert(this._runningState === WI.AuditManager.RunningState.Disabled || this._runningState === WI.AuditManager.RunningState.Inactive);
+ if (this._runningState !== WI.AuditManager.RunningState.Disabled && this._runningState !== WI.AuditManager.RunningState.Inactive)
+ return;
+
+ let runningState = editing ? WI.AuditManager.RunningState.Disabled : WI.AuditManager.RunningState.Inactive;
+ console.assert(runningState !== this._runningState);
+ if (runningState === this._runningState)
+ return;
+
+ this._runningState = runningState;
+
+ this.dispatchEventToListeners(WI.AuditManager.Event.EditingChanged);
+
+ if (!this.editing) {
+ WI.objectStores.audits.clear();
+
+ let disabledDefaultTests = [];
+ let saveDisabledDefaultTest = (test) => {
+ if (test.disabled)
+ disabledDefaultTests.push(test.name);
+
+ if (test instanceof WI.AuditTestGroup) {
+ for (let child of test.tests)
+ saveDisabledDefaultTest(child);
+ }
+ };
+
+ for (let test of this._tests) {
+ if (test.__default)
+ saveDisabledDefaultTest(test);
+ else
+ WI.objectStores.audits.addObject(test);
+ }
+
+ this._disabledDefaultTestsSetting.value = disabledDefaultTests;
+ }
+ }
+
async start(tests)
{
console.assert(this._runningState === WI.AuditManager.RunningState.Inactive);
if (this._runningState !== WI.AuditManager.RunningState.Active)
return;
+ this._runningState = WI.AuditManager.RunningState.Stopping;
+
for (let test of this._runningTests)
test.stop();
-
- this._runningState = WI.AuditManager.RunningState.Stopping;
}
async processJSON({json, error})
], {description: WI.UIString("Tests for ways to improve accessibility.")}),
];
+ let checkDisabledDefaultTest = (test) => {
+ if (this._disabledDefaultTestsSetting.value.includes(test.name))
+ test.disabled = true;
+
+ if (test instanceof WI.AuditTestGroup) {
+ for (let child of test.tests)
+ checkDisabledDefaultTest(child);
+ }
+ };
+
for (let test of defaultTests) {
+ checkDisabledDefaultTest(test);
+
test.__default = true;
this._addTest(test);
}
};
WI.AuditManager.RunningState = {
+ Disabled: "disabled",
Inactive: "inactive",
Active: "active",
Stopping: "stopping",
};
WI.AuditManager.Event = {
+ EditingChanged: "audit-manager-editing-changed",
TestAdded: "audit-manager-test-added",
TestCompleted: "audit-manager-test-completed",
TestRemoved: "audit-manager-test-removed",
WI.AuditTestBase = class AuditTestBase extends WI.Object
{
- constructor(name, {description} = {})
+ constructor(name, {description, disabled} = {})
{
console.assert(typeof name === "string");
console.assert(!description || typeof description === "string");
+ console.assert(disabled === undefined || typeof disabled === "boolean");
super();
+ // This class should not be instantiated directly. Create a concrete subclass instead.
+ console.assert(this.constructor !== WI.AuditTestBase && this instanceof WI.AuditTestBase);
+
this._name = name;
this._description = description || null;
- this._runningState = WI.AuditManager.RunningState.Inactive;
+ this._runningState = disabled ? WI.AuditManager.RunningState.Disabled : WI.AuditManager.RunningState.Inactive;
this._result = null;
}
get runningState() { return this._runningState; }
get result() { return this._result; }
+ get disabled()
+ {
+ return this._runningState === WI.AuditManager.RunningState.Disabled;
+ }
+
+ set disabled(disabled)
+ {
+ console.assert(this._runningState === WI.AuditManager.RunningState.Disabled || this._runningState === WI.AuditManager.RunningState.Inactive);
+ if (this._runningState !== WI.AuditManager.RunningState.Disabled && this._runningState !== WI.AuditManager.RunningState.Inactive)
+ return;
+
+ let runningState = disabled ? WI.AuditManager.RunningState.Disabled : WI.AuditManager.RunningState.Inactive;
+ if (runningState === this._runningState)
+ return;
+
+ this._runningState = runningState;
+
+ this.dispatchEventToListeners(WI.AuditTestBase.Event.DisabledChanged);
+ }
+
async start()
{
// Called from WI.AuditManager.
+ if (this.disabled)
+ return;
+
console.assert(WI.auditManager.runningState === WI.AuditManager.RunningState.Active);
console.assert(this._runningState === WI.AuditManager.RunningState.Inactive);
{
// Called from WI.AuditManager.
- console.assert(this._runningState !== WI.AuditManager.RunningState.Inactive);
+ if (this.disabled)
+ return;
+
+ console.assert(WI.auditManager.runningState === WI.AuditManager.RunningState.Stopping);
if (this._runningState !== WI.AuditManager.RunningState.Active)
return;
cookie["audit-" + this.constructor.TypeIdentifier + "-name"] = this._name;
}
- toJSON()
+ toJSON(key)
{
let json = {
type: this.constructor.TypeIdentifier,
};
if (this._description)
json.description = this._description;
+ if (key === WI.ObjectStore.toJSONSymbol)
+ json.disabled = this.disabled;
return json;
}
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",
Scheduled: "audit-test-base-scheduled",
WI.AuditTestCase = class AuditTestCase extends WI.AuditTestBase
{
- constructor(name, test, {description} = {})
+ constructor(name, test, options = {})
{
console.assert(typeof test === "string");
- super(name, {description});
+ super(name, options);
this._test = test;
}
if (typeof payload !== "object" || payload === null)
return null;
- let {type, name, test, description} = payload;
+ let {type, name, test, description, disabled} = payload;
if (type !== WI.AuditTestCase.TypeIdentifier)
return null;
let options = {};
if (typeof description === "string")
options.description = description;
+ if (typeof disabled === "boolean")
+ options.disabled = disabled;
return new WI.AuditTestCase(name, test, options);
}
get test() { return this._test; }
- toJSON()
+ toJSON(key)
{
- let json = super.toJSON();
+ let json = super.toJSON(key);
json.test = this._test;
return json;
}
WI.AuditTestGroup = class AuditTestGroup extends WI.AuditTestBase
{
- constructor(name, tests, {description} = {})
+ constructor(name, tests, options = {})
{
console.assert(Array.isArray(tests));
- super(name, {description});
+ // Set disabled once `_tests` is set so that it propagates.
+ let disabled = options.disabled;
+ options.disabled = false;
+
+ super(name, options);
this._tests = tests;
+ this._preventDisabledPropagation = false;
+
+ if (disabled)
+ this.disabled = disabled;
for (let test of this._tests) {
test.addEventListener(WI.AuditTestBase.Event.Completed, this._handleTestCompleted, this);
+ test.addEventListener(WI.AuditTestBase.Event.DisabledChanged, this._handleTestDisabledChanged, this);
test.addEventListener(WI.AuditTestBase.Event.Progress, this._handleTestProgress, this);
}
}
if (typeof payload !== "object" || payload === null)
return null;
- let {type, name, tests, description} = payload;
+ let {type, name, tests, description, disabled} = payload;
if (type !== WI.AuditTestGroup.TypeIdentifier)
return null;
let options = {};
if (typeof description === "string")
options.description = description;
+ if (typeof disabled === "boolean")
+ options.disabled = disabled;
return new WI.AuditTestGroup(name, tests, options);
}
get tests() { return this._tests; }
+ get disabled()
+ {
+ return super.disabled;
+ }
+
+ set disabled(disabled)
+ {
+ if (!this._preventDisabledPropagation) {
+ for (let test of this._tests)
+ test.disabled = disabled;
+ }
+
+ super.disabled = disabled;
+ }
+
stop()
{
// Called from WI.AuditManager.
});
}
- toJSON()
+ toJSON(key)
{
- let json = super.toJSON();
- json.tests = this._tests.map((testCase) => testCase.toJSON());
+ let json = super.toJSON(key);
+ json.tests = this._tests.map((testCase) => testCase.toJSON(key));
return json;
}
let count = this._tests.length;
for (let index = 0; index < count && this._runningState === WI.AuditManager.RunningState.Active; ++index) {
let test = this._tests[index];
+ if (test.disabled)
+ continue;
await test.start();
this.dispatchEventToListeners(WI.AuditTestBase.Event.Completed);
}
+ _handleTestDisabledChanged(event)
+ {
+ let enabledTestCount = this._tests.filter((test) => !test.disabled).length;
+ if (event.target.disabled && !enabledTestCount)
+ this.disabled = true;
+ else if (!event.target.disabled && enabledTestCount === 1) {
+ this._preventDisabledPropagation = true;
+ this.disabled = false;
+ this._preventDisabledPropagation = false;
+ } else {
+ // Don't change `disabled`, as we're currently in an "indeterminate" state.
+ this.dispatchEventToListeners(WI.AuditTestBase.Event.DisabledChanged);
+ }
+ }
+
_handleTestProgress(event)
{
if (this._runningState !== WI.AuditManager.RunningState.Active)
let walk = (tests) => {
let count = 0;
for (let test of tests) {
+ if (test.disabled)
+ continue;
+
if (test instanceof WI.AuditTestCase)
++count;
else if (test instanceof WI.AuditTestGroup)
};
this.dispatchEventToListeners(WI.AuditTestBase.Event.Progress, {
- index: event.data.index + walk(this.tests.slice(0, this.tests.indexOf(event.target))),
- count: walk(this.tests),
+ index: event.data.index + walk(this._tests.slice(0, this._tests.indexOf(event.target))),
+ count: walk(this._tests),
});
}
};
*/
.sidebar > .panel.navigation.audit > .content {
+ display: flex;
+ flex-direction: column;
top: var(--navigation-bar-height);
}
+.sidebar > .panel.navigation.audit > .content > .tree-outline {
+ flex-grow: 1;
+}
+
+.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled):active {
+ color: var(--glyph-color-pressed);
+}
+
+.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled).activated {
+ color: var(--glyph-color-active);
+}
+
+.sidebar > .panel.navigation.audit > .content .edit-audits:not(.disabled).activated:active {
+ color: var(--glyph-color-active-pressed);
+}
+
+.sidebar > .panel.navigation.audit > .content .edit-audits.disabled {
+ color: var(--glyph-color-disabled);
+}
+
.sidebar > .panel.navigation.audit.has-results:not(.has-tests) > .content > .message-text-view {
position: initial;
border-bottom: 1px solid var(--border-color);
.sidebar > .panel.navigation.audit.has-results:not(.has-tests) > .content > .message-text-view > button {
margin: 8px 0 7px;
}
+
+.finish-editing-audits-placeholder.message-text-view .navigation-item-help .navigation-bar {
+ padding: 0;
+ vertical-align: 0.5px;
+}
{
let contentView = new WI.ContentView;
- let contentPlaceholder = WI.createMessageTextView(WI.UIString("No audit selected"));
- contentView.element.appendChild(contentPlaceholder);
-
- let importNavigationItem = new WI.ButtonNavigationItem("import-audit", WI.UIString("Import"), "Images/Import.svg", 15, 15);
- importNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
- importNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this);
-
- let importHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to import a test or result file"), importNavigationItem);
- contentPlaceholder.appendChild(importHelpElement);
+ if (WI.auditManager.editing) {
+ let contentPlaceholder = WI.createMessageTextView(WI.UIString("Editing audits"));
+ contentPlaceholder.classList.add("finish-editing-audits-placeholder");
+ contentView.element.appendChild(contentPlaceholder);
+
+ let finishEditingNavigationItem = new WI.ButtonNavigationItem("finish-editing-audits", WI.UIString("Done"));
+ finishEditingNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, (event) => {
+ WI.auditManager.editing = false;
+ });
+
+ let importHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to stop editing"), finishEditingNavigationItem);
+ contentPlaceholder.appendChild(importHelpElement);
+ } else {
+ let contentPlaceholder = WI.createMessageTextView(WI.UIString("No audit selected"));
+ contentView.element.appendChild(contentPlaceholder);
+
+ let importNavigationItem = new WI.ButtonNavigationItem("import-audit", WI.UIString("Import"), "Images/Import.svg", 15, 15);
+ importNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
+ importNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this);
+
+ let importHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to import a test or result file"), importNavigationItem);
+ contentPlaceholder.appendChild(importHelpElement);
+ }
this.contentBrowser.showContentView(contentView);
}
this.contentTreeOutline.allowsRepeatSelection = false;
- let navigationBar = new WI.NavigationBar;
+ let controlsNavigationBar = new WI.NavigationBar;
this._startStopButtonNavigationItem = new WI.ToggleButtonNavigationItem("audit-start-stop", WI.UIString("Start"), WI.UIString("Stop"), "Images/AuditStart.svg", "Images/AuditStop.svg", 13, 13);
this._startStopButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
this._updateStartStopButtonNavigationItemState();
this._startStopButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleStartStopButtonNavigationItemClicked, this);
- navigationBar.addNavigationItem(this._startStopButtonNavigationItem);
+ controlsNavigationBar.addNavigationItem(this._startStopButtonNavigationItem);
- navigationBar.addNavigationItem(new WI.DividerNavigationItem);
+ controlsNavigationBar.addNavigationItem(new WI.DividerNavigationItem);
let importButtonNavigationItem = new WI.ButtonNavigationItem("audit-import", WI.UIString("Import"), "Images/Import.svg", 15, 15);
importButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText;
importButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
importButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this);
- navigationBar.addNavigationItem(importButtonNavigationItem);
+ controlsNavigationBar.addNavigationItem(importButtonNavigationItem);
+
+ this.addSubview(controlsNavigationBar);
+
+ let editNavigationbar = new WI.NavigationBar;
+
+ this._editButtonNavigationItem = new WI.ActivateButtonNavigationItem("edit-audits", WI.UIString("Edit"), WI.UIString("Done"));
+ this._editButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleEditButtonNavigationItemClicked, this);
+ editNavigationbar.addNavigationItem(this._editButtonNavigationItem);
- this.addSubview(navigationBar);
+ this.contentView.addSubview(editNavigationbar);
for (let test of WI.auditManager.tests)
this._addTest(test);
this._addResult(result, i);
});
+ WI.auditManager.addEventListener(WI.AuditManager.Event.EditingChanged, this._handleAuditManagerEditingChanged, this);
WI.auditManager.addEventListener(WI.AuditManager.Event.TestAdded, this._handleAuditTestAdded, this);
WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditTestCompleted, this);
WI.auditManager.addEventListener(WI.AuditManager.Event.TestRemoved, this._handleAuditTestRemoved, this);
this._updateNoAuditsPlaceholder();
}
+ hasCustomFilters()
+ {
+ return true;
+ }
+
+ matchTreeElementAgainstCustomFilters(treeElement, flags)
+ {
+ if (WI.auditManager.editing) {
+ if (treeElement.representedObject instanceof WI.AuditTestResultBase || treeElement.hasAncestor(this._resultsFolderTreeElement) || treeElement === this._resultsFolderTreeElement)
+ return false;
+ } else {
+ if (treeElement.representedObject instanceof WI.AuditTestBase && treeElement.representedObject.disabled)
+ return false;
+ }
+
+ return super.matchTreeElementAgainstCustomFilters(treeElement, flags);
+ }
+
// Private
_addTest(test)
{
this.element.classList.add("has-tests");
- this._updateStartStopButtonNavigationItemState();
-
let treeElement = new WI.AuditTreeElement(test);
if (this._resultsFolderTreeElement) {
} else
this.contentTreeOutline.appendChild(treeElement);
+ this._updateStartStopButtonNavigationItemState();
+ this._updateEditButtonNavigationItemState();
+
this.hideEmptyContentPlaceholder();
}
{
this.element.classList.add("has-results");
- this._updateStartStopButtonNavigationItemState();
-
if (!this._resultsFolderTreeElement) {
this._resultsFolderTreeElement = new WI.FolderTreeElement(WI.UIString("Results"));
this.contentTreeOutline.appendChild(this._resultsFolderTreeElement);
}
this._resultsFolderTreeElement.appendChild(resultFolderTreeElement);
+ console.assert(this._resultsFolderTreeElement.children.length === WI.auditManager.results.length);
+
for (let resultItem of result)
resultFolderTreeElement.appendChild(new WI.AuditTreeElement(resultItem));
+
+ this._updateStartStopButtonNavigationItemState();
+ this._updateEditButtonNavigationItemState();
}
_updateStartStopButtonNavigationItemState()
{
- this._startStopButtonNavigationItem.toggled = WI.auditManager.runningState !== WI.AuditManager.RunningState.Inactive;
- this._startStopButtonNavigationItem.enabled = WI.auditManager.tests.length && WI.auditManager.runningState !== WI.AuditManager.RunningState.Stopping;
+ this._startStopButtonNavigationItem.toggled = WI.auditManager.runningState === WI.AuditManager.RunningState.Active || WI.auditManager.runningState === WI.AuditManager.RunningState.Stopping;
+ this._startStopButtonNavigationItem.enabled = WI.auditManager.tests.length && (WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive || WI.auditManager.runningState === WI.AuditManager.RunningState.Active);
+ }
+
+ _updateEditButtonNavigationItemState()
+ {
+ this._editButtonNavigationItem.label = WI.auditManager.editing ? this._editButtonNavigationItem.activatedToolTip : this._editButtonNavigationItem.defaultToolTip;
+ this._editButtonNavigationItem.activated = WI.auditManager.editing;
+ this._editButtonNavigationItem.enabled = WI.auditManager.tests.length && (WI.auditManager.editing || WI.auditManager.runningState === WI.AuditManager.RunningState.Inactive);
}
_updateNoAuditsPlaceholder()
// be styled such that only the button is visible.
this.contentView.element.insertBefore(contentPlaceholder, this.contentView.element.firstChild);
}
+
+ this._updateEditButtonNavigationItemState();
+ }
+
+ _handleAuditManagerEditingChanged(event)
+ {
+ if (WI.auditManager.editing) {
+ console.assert(!this._selectedTreeElementBeforeEditing);
+ this._selectedTreeElementBeforeEditing = this.contentTreeOutline.selectedTreeElement;
+ if (this._selectedTreeElementBeforeEditing)
+ this._selectedTreeElementBeforeEditing.deselect();
+ } else if (this._selectedTreeElementBeforeEditing) {
+ if (!(this._selectedTreeElementBeforeEditing.representedObject instanceof WI.AuditTestBase) || !this._selectedTreeElementBeforeEditing.representedObject.disabled)
+ this._selectedTreeElementBeforeEditing.select();
+ this._selectedTreeElementBeforeEditing = null;
+ }
+
+ if (!this.contentTreeOutline.selectedTreeElement)
+ this.showDefaultContentView();
+
+ this._updateStartStopButtonNavigationItemState();
+ this._updateEditButtonNavigationItemState();
+
+ this.updateFilter();
}
_handleAuditTestAdded(event)
_handleAuditTestScheduled(event)
{
this._updateStartStopButtonNavigationItemState();
+ this._updateEditButtonNavigationItemState();
}
_treeSelectionDidChange(event)
return;
}
+ console.assert(!WI.auditManager.editing);
+ if (WI.auditManager.editing)
+ return;
+
let representedObject = treeElement.representedObject;
if (representedObject instanceof WI.AuditTestCase || representedObject instanceof WI.AuditTestGroup
|| representedObject instanceof WI.AuditTestCaseResult || representedObject instanceof WI.AuditTestGroupResult) {
{
WI.FileUtilities.importJSON((result) => WI.auditManager.processJSON(result));
}
+
+ _handleEditButtonNavigationItemClicked(event)
+ {
+ WI.auditManager.editing = !WI.auditManager.editing;
+ }
};
}
for (let subobject of this._subobjects()) {
+ if (subobject instanceof WI.AuditTestBase && subobject.disabled)
+ continue;
+
let view = WI.ContentView.contentViewForRepresentedObject(subobject);
this.contentView.addSubview(view);
view.shown();
}
.tree-outline .item.audit > .status:not(:hover) > img.show-on-hover,
-.tree-outline .item.audit.test-group.expanded > .status:not(:hover) {
+.tree-outline .item.audit.test-group.expanded:not(.editing-audits) > .status:not(:hover) {
opacity: 0;
}
.tree-outline .item.audit.manager-active > .status > img.show-on-hover,
-.tree-outline .item.audit.test-group.expanded > .status:hover > :not(img),
+.tree-outline .item.audit.test-group.expanded:not(.editing-audits) > .status:hover > :not(img),
.tree-outline .item.audit.test-group-result.expanded > .status {
display: none;
}
super.onattach();
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);
if (this.representedObject instanceof WI.AuditTestCase)
else if (this.representedObject instanceof WI.AuditTestGroup)
this.representedObject.addEventListener(WI.AuditTestBase.Event.Scheduled, this._handleTestGroupScheduled, this);
+ WI.auditManager.addEventListener(WI.AuditManager.Event.EditingChanged, this._handleManagerEditingChanged, this);
WI.auditManager.addEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditManagerTestScheduled, this);
WI.auditManager.addEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditManagerTestCompleted, this);
}
super.populateContextMenu(contextMenu, event);
}
+ canSelectOnMouseDown(event)
+ {
+ return !WI.auditManager.editing;
+ }
+
// Private
_start()
this.status.value = progress || 0;
}
+ _updateTestGroupDisabled()
+ {
+ this.status.checked = !this.representedObject.disabled;
+
+ if (this.representedObject instanceof WI.AuditTestGroup)
+ this.status.indeterminate = this.representedObject.tests.some((test) => test.disabled !== this.representedObject.tests[0].disabled);
+ }
+
_handleTestCaseCompleted(event)
{
this.representedObject.removeEventListener(WI.AuditTestBase.Event.Completed, this._handleTestCaseCompleted, this);
this._updateLevel();
}
+ _handleTestDisabledChanged(event)
+ {
+ if (this.status instanceof HTMLInputElement && this.status.type === "checkbox")
+ this._updateTestGroupDisabled();
+ }
+
_handleTestResultCleared(event)
{
this._updateLevel();
this._showRunningProgress();
}
+ _handleManagerEditingChanged(event)
+ {
+ if (WI.auditManager.editing) {
+ this.status = document.createElement("input");
+ this.status.type = "checkbox";
+ this._updateTestGroupDisabled();
+ this.status.addEventListener("change", () => {
+ this.representedObject.disabled = !this.representedObject.disabled;
+ });
+
+ this.addClassName("editing-audits");
+ } else {
+ this.removeClassName("editing-audits");
+
+ this._updateLevel();
+ }
+ }
+
_handleAuditManagerTestScheduled(event)
{
this.addClassName("manager-active");
display: none;
}
-.tree-outline .children.expanded {
+.tree-outline .children.expanded:not([hidden]) {
display: block;
}