| /* |
| * Copyright (C) 2018 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| WI.AuditTreeElement = class AuditTreeElement extends WI.GeneralTreeElement |
| { |
| constructor(representedObject) |
| { |
| let isTestCase = representedObject instanceof WI.AuditTestCase; |
| let isTestGroup = representedObject instanceof WI.AuditTestGroup; |
| let isTestCaseResult = representedObject instanceof WI.AuditTestCaseResult; |
| let isTestGroupResult = representedObject instanceof WI.AuditTestGroupResult; |
| console.assert(isTestCase || isTestGroup || isTestCaseResult || isTestGroupResult); |
| |
| let classNames = ["audit"]; |
| if (isTestCase) |
| classNames.push("test-case"); |
| else if (isTestGroup) |
| classNames.push("test-group"); |
| else if (isTestCaseResult) |
| classNames.push("test-case-result"); |
| else if (isTestGroupResult) |
| classNames.push("test-group-result"); |
| |
| let options = { |
| hasChildren: isTestGroup || isTestGroupResult, |
| }; |
| |
| const subtitle = null; |
| super(classNames, representedObject.name, subtitle, representedObject, options); |
| |
| if (isTestGroup) |
| this._expandedSetting = new WI.Setting(WI.AuditTreeElement.expandedSettingKey(this.representedObject.name), false); |
| } |
| |
| // Static |
| |
| static expandedSettingKey(name) |
| { |
| return `audit-tree-element-${name}-expanded`; |
| } |
| |
| // Protected |
| |
| onattach() |
| { |
| super.onattach(); |
| |
| if (this.representedObject instanceof WI.AuditTestBase) { |
| this.representedObject.addEventListener(WI.AuditTestBase.Event.DisabledChanged, this._handleTestDisabledChanged, 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); |
| else if (this.representedObject instanceof WI.AuditTestGroup) |
| this.representedObject.addEventListener(WI.AuditTestBase.Event.Scheduled, this._handleTestGroupScheduled, this); |
| |
| if (this.representedObject.editable) { |
| this.representedObject.addEventListener(WI.AuditTestBase.Event.NameChanged, this._handleTestNameChanged, this); |
| this.representedObject.addEventListener(WI.AuditTestBase.Event.SupportedChanged, this._handleTestSupportedChanged, this); |
| |
| if (this.representedObject instanceof WI.AuditTestGroup) |
| this.representedObject.addEventListener(WI.AuditTestGroup.Event.TestAdded, this._handleTestGroupTestAdded, 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); |
| } |
| |
| if (this.representedObject.supported && this._expandedSetting && this._expandedSetting.value) |
| this.expand(); |
| |
| this._updateStatus(); |
| } |
| |
| ondetach() |
| { |
| if (this.representedObject instanceof WI.AuditTestBase) { |
| this.representedObject.removeEventListener(WI.AuditTestBase.Event.DisabledChanged, this._handleTestDisabledChanged, this); |
| this.representedObject.removeEventListener(WI.AuditTestBase.Event.ResultChanged, this._handleTestResultChanged, this); |
| |
| if (this.representedObject instanceof WI.AuditTestCase) |
| this.representedObject.removeEventListener(WI.AuditTestBase.Event.Scheduled, this._handleTestCaseScheduled, this); |
| else if (this.representedObject instanceof WI.AuditTestGroup) |
| this.representedObject.removeEventListener(WI.AuditTestBase.Event.Scheduled, this._handleTestGroupScheduled, this); |
| |
| if (this.representedObject.editable) { |
| this.representedObject.removeEventListener(WI.AuditTestBase.Event.NameChanged, this._handleTestNameChanged, this); |
| this.representedObject.removeEventListener(WI.AuditTestBase.Event.SupportedChanged, this._handleTestSupportedChanged, this); |
| |
| if (this.representedObject instanceof WI.AuditTestGroup) |
| this.representedObject.removeEventListener(WI.AuditTestGroup.Event.TestAdded, this._handleTestGroupTestAdded, this); |
| } |
| |
| WI.auditManager.removeEventListener(WI.AuditManager.Event.EditingChanged, this._handleManagerEditingChanged, this); |
| WI.auditManager.removeEventListener(WI.AuditManager.Event.TestScheduled, this._handleAuditManagerTestScheduled, this); |
| WI.auditManager.removeEventListener(WI.AuditManager.Event.TestCompleted, this._handleAuditManagerTestCompleted, this); |
| } |
| |
| super.ondetach(); |
| } |
| |
| onpopulate() |
| { |
| super.onpopulate(); |
| |
| if (this.children.length && !this.shouldRefreshChildren) |
| return; |
| |
| this.shouldRefreshChildren = false; |
| |
| this.removeChildren(); |
| |
| if (this.representedObject instanceof WI.AuditTestGroup) { |
| for (let test of this.representedObject.tests) |
| this.appendChild(new WI.AuditTreeElement(test)); |
| } else if (this.representedObject instanceof WI.AuditTestGroupResult) { |
| for (let result of this.representedObject.results) |
| this.appendChild(new WI.AuditTreeElement(result)); |
| } |
| } |
| |
| onexpand() |
| { |
| console.assert(this.expanded); |
| |
| if (this._expandedSetting) |
| this._expandedSetting.value = this.expanded; |
| } |
| |
| oncollapse() |
| { |
| console.assert(!this.expanded); |
| |
| if (this._expandedSetting) |
| this._expandedSetting.value = this.expanded; |
| } |
| |
| ondelete() |
| { |
| if (!(this.representedObject instanceof WI.AuditTestBase)) |
| return false; |
| |
| if (!WI.auditManager.editing) |
| return false; |
| |
| this.representedObject.remove(); |
| |
| return true; |
| } |
| |
| canSelectOnMouseDown(event) |
| { |
| if (this.representedObject instanceof WI.AuditTestBase && this.representedObject.supported && this.status.contains(event.target)) |
| return false; |
| |
| return super.canSelectOnMouseDown(event); |
| } |
| |
| populateContextMenu(contextMenu, event) |
| { |
| let isTest = this.representedObject instanceof WI.AuditTestBase; |
| |
| contextMenu.appendSeparator(); |
| |
| if (WI.auditManager.editing) { |
| if (isTest) { |
| if (this.representedObject.supported) { |
| contextMenu.appendItem(this.representedObject.disabled ? WI.UIString("Enable Audit") : WI.UIString("Disable Audit"), () => { |
| this.representedObject.disabled = !this.representedObject.disabled; |
| }); |
| } |
| |
| contextMenu.appendItem(WI.UIString("Duplicate Audit"), async () => { |
| let audit = await this.representedObject.clone(); |
| WI.auditManager.addTest(audit, {save: true}); |
| }); |
| |
| if (this.representedObject.editable) { |
| contextMenu.appendItem(WI.UIString("Delete Audit"), () => { |
| this.representedObject.remove(); |
| }); |
| } |
| } |
| } else { |
| if (isTest) { |
| contextMenu.appendItem(WI.UIString("Start Audit"), () => { |
| this._start(); |
| }, WI.auditManager.runningState !== WI.AuditManager.RunningState.Inactive); |
| |
| contextMenu.appendSeparator(); |
| |
| contextMenu.appendItem(WI.UIString("Export Audit"), () => { |
| WI.auditManager.export(this.representedObject); |
| }); |
| } |
| |
| contextMenu.appendItem(WI.UIString("Export Result"), () => { |
| WI.auditManager.export(this.representedObject.result); |
| }, !this.representedObject.result); |
| |
| if (isTest && this.representedObject.editable) { |
| contextMenu.appendSeparator(); |
| |
| contextMenu.appendItem(WI.UIString("Edit Audit"), () => { |
| WI.auditManager.editing = true; |
| WI.showRepresentedObject(this.representedObject); |
| }); |
| } |
| } |
| |
| contextMenu.appendSeparator(); |
| |
| super.populateContextMenu(contextMenu, event); |
| } |
| |
| // Private |
| |
| _start() |
| { |
| if (WI.auditManager.runningState !== WI.AuditManager.RunningState.Inactive) |
| return; |
| |
| WI.auditManager.start([this.representedObject]); |
| } |
| |
| _updateStatus() |
| { |
| if (this.representedObject instanceof WI.AuditTestBase && !this.representedObject.supported) { |
| this.status = document.createElement("img"); |
| this.status.title = WI.UIString("This audit is not supported"); |
| this.addClassName("unsupported"); |
| return; |
| } |
| |
| 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"); |
| return; |
| } |
| |
| let className = ""; |
| |
| let result = this.representedObject.result; |
| if (result) { |
| if (result.didError) |
| className = WI.AuditTestCaseResult.Level.Error; |
| else if (result.didFail) |
| className = WI.AuditTestCaseResult.Level.Fail; |
| else if (result.didWarn) |
| className = WI.AuditTestCaseResult.Level.Warn; |
| else if (result.didPass) |
| className = WI.AuditTestCaseResult.Level.Pass; |
| else if (result.unsupported) |
| className = WI.AuditTestCaseResult.Level.Unsupported; |
| } |
| |
| this.status = document.createElement("img"); |
| |
| if (this.representedObject instanceof WI.AuditTestBase) { |
| this.status.title = WI.UIString("Start"); |
| this.status.addEventListener("click", this._handleStatusClick.bind(this)); |
| |
| if (!className) |
| className = "show-on-hover"; |
| } |
| |
| this.status.classList.add(className); |
| |
| this.removeClassName("editing-audits"); |
| } |
| |
| _showRunningSpinner() |
| { |
| if (this.representedObject.runningState === WI.AuditManager.RunningState.Inactive) { |
| this._updateStatus(); |
| return; |
| } |
| |
| if (!this.status || !this.status.__spinner) { |
| let spinner = new WI.IndeterminateProgressSpinner; |
| this.status = spinner.element; |
| this.status.__spinner = true; |
| } |
| } |
| |
| _showRunningProgress(progress) |
| { |
| if (!this.representedObject.runningState === WI.AuditManager.RunningState.Inactive) { |
| this._updateStatus(); |
| return; |
| } |
| |
| if (!this.status || !this.status.__progress) { |
| this.status = document.createElement("progress"); |
| this.status.__progress = true; |
| } |
| |
| this.status.value = progress || 0; |
| } |
| |
| _updateTestGroupDisabled() |
| { |
| this.status.checked = !this.representedObject.disabled; |
| |
| if (this.representedObject instanceof WI.AuditTestGroup) { |
| let firstSupportedTest = this.representedObject.tests.find((test) => test.supported); |
| this.status.indeterminate = this.representedObject.tests.some((test) => test.supported && test.disabled !== firstSupportedTest.disabled); |
| } |
| } |
| |
| _handleTestCaseCompleted(event) |
| { |
| this.representedObject.removeEventListener(WI.AuditTestBase.Event.Completed, this._handleTestCaseCompleted, this); |
| |
| this._updateStatus(); |
| } |
| |
| _handleTestDisabledChanged(event) |
| { |
| if (this.status instanceof HTMLInputElement && this.status.type === "checkbox") |
| this._updateTestGroupDisabled(); |
| } |
| |
| _handleTestResultChanged(event) |
| { |
| this._updateStatus(); |
| } |
| |
| _handleTestCaseScheduled(event) |
| { |
| this.representedObject.addEventListener(WI.AuditTestBase.Event.Completed, this._handleTestCaseCompleted, this); |
| |
| this._showRunningSpinner(); |
| } |
| |
| _handleTestGroupCompleted(event) |
| { |
| this.representedObject.removeEventListener(WI.AuditTestBase.Event.Completed, this._handleTestGroupCompleted, this); |
| this.representedObject.removeEventListener(WI.AuditTestBase.Event.Progress, this._handleTestGroupProgress, this); |
| |
| this._updateStatus(); |
| } |
| |
| _handleTestGroupProgress(event) |
| { |
| let {index, count} = event.data; |
| this._showRunningProgress((index + 1) / count); |
| } |
| |
| _handleTestGroupScheduled(event) |
| { |
| this.representedObject.addEventListener(WI.AuditTestBase.Event.Completed, this._handleTestGroupCompleted, this); |
| this.representedObject.addEventListener(WI.AuditTestBase.Event.Progress, this._handleTestGroupProgress, this); |
| |
| this._showRunningProgress(); |
| } |
| |
| _handleTestNameChanged(event) |
| { |
| this.mainTitle = this.representedObject.name; |
| |
| if (this.representedObject instanceof WI.AuditTestGroup) |
| this._expandedSetting = new WI.Setting(WI.AuditTreeElement.expandedSettingKey(this.representedObject.name), !!WI.Setting.migrateValue(WI.AuditTreeElement.expandedSettingKey(event.data.oldName))); |
| } |
| |
| _handleTestSupportedChanged(event) |
| { |
| this._updateStatus(); |
| } |
| |
| _handleTestGroupTestAdded(event) |
| { |
| let {test} = event.data; |
| |
| this.appendChild(new WI.AuditTreeElement(test)); |
| } |
| |
| _handleManagerEditingChanged(event) |
| { |
| this._updateStatus(); |
| } |
| |
| _handleAuditManagerTestScheduled(event) |
| { |
| this.addClassName("manager-active"); |
| } |
| |
| _handleAuditManagerTestCompleted(event) |
| { |
| this.removeClassName("manager-active"); |
| } |
| |
| _handleStatusClick(event) |
| { |
| this._start(); |
| } |
| }; |