/* * 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(); if (WI.FileUtilities.canSave(WI.FileUtilities.SaveMode.FileVariants)) { contextMenu.appendItem(WI.UIString("Export"), () => { WI.auditManager.export(WI.FileUtilities.SaveMode.FileVariants, this.representedObject); }); } else if (WI.FileUtilities.canSave(WI.FileUtilities.SaveMode.SingleFile)) { if (isTest) { contextMenu.appendItem(WI.FileUtilities.SaveMode.SingleFile, WI.UIString("Export Audit"), () => { WI.auditManager.export(this.representedObject); }); } contextMenu.appendItem(WI.FileUtilities.SaveMode.SingleFile, 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(); } };