Changeset 237613 in webkit


Ignore:
Timestamp:
Oct 30, 2018 6:11:36 PM (5 years ago)
Author:
Devin Rousso
Message:

Web Inspector: Audit: create Audit Tab
https://bugs.webkit.org/show_bug.cgi?id=190754

Reviewed by Matt Baker.

Source/WebInspectorUI:

Create an Audit tab for running audits on the inspected page. Leverage Runtime.evaluate
for running the audit tests (arbitrary JavaScript), and use the returned value to generate
a preview UI of the results. All tests/results can be exported/imported to formatted JSON:

AuditTestCase JSON:

{

"type": "test-case",
"name": <string>,
<optional> "description": <string>,
"test": <stringified JavaScript function>,

}

AuditTestGroup JSON:

{

"type": "test-group",
"name": <string>,
<optional> "description": <string>,
"tests": [...<AuditTestCase, AuditTestGroup>],

}

AuditTestCaseResult JSON:

{

"type": "test-case-result",
"name": <string>,
<optional> "description": <string>,
"level": <"pass", "warn", "fail", "error", "unsupported">,
"data": {

"domNodes": [...<stringified CSS path>],
"domAttributes": [...<string>],
"errors": [...<string>],

},

}

AuditTestGroupResult JSON:

{

"type": "test-group-result",
"name": <string>,
<optional> "description": <string>,
"results": [...<AuditTestCaseResult, AuditTestGroupResult>],

}

More keys may be added in the future (especially for AuditTestCaseResult.data).

  • UserInterface/Controllers/AuditManager.js:

(WI.AuditManager):
(WI.AuditManager.synthesizeError): Added.
(WI.AuditManager.prototype.get tests): Added.
(WI.AuditManager.prototype.get results): Added.
(WI.AuditManager.prototype.get runningState): Added.
(WI.AuditManager.prototype.start): Added.
(WI.AuditManager.prototype.stop): Added.
(WI.AuditManager.prototype.import): Added.
(WI.AuditManager.prototype.export): Added.
(WI.AuditManager.prototype._addTest): Added.
(WI.AuditManager.prototype._addResult): Added.
(WI.AuditManager.prototype.get testSuites): Deleted.
(WI.AuditManager.prototype.get reports): Deleted.
(WI.AuditManager.prototype.async runAuditTestByRepresentedObject): Deleted.
(WI.AuditManager.prototype.reportForId): Deleted.
(WI.AuditManager.prototype.removeAllReports): Deleted.
(WI.AuditManager.prototype.async _runTestCase): Deleted.

  • UserInterface/Models/AuditTestBase.js: Added.

(WI.AuditTestBases):
(WI.AuditTestBases.prototype.get name):
(WI.AuditTestBases.prototype.get description):
(WI.AuditTestBases.prototype.get runningState):
(WI.AuditTestBases.prototype.get result):
(WI.AuditTestBases.prototype.async start):
(WI.AuditTestBases.prototype.stop):
(WI.AuditTestBases.prototype.clearResult):
(WI.AuditTestBases.prototype.saveIdentityToCookie):
(WI.AuditTestBases.prototype.toJSON):
(WI.AuditTestBases.prototype.async run):

  • UserInterface/Models/AuditTestCase.js:

(WI.AuditTestCase):
(WI.AuditTestCase.fromPayload): Added.
(WI.AuditTestCase.prototype.toJSON): Added.
(WI.AuditTestCase.prototype.async run): Added.
(WI.AuditTestCase.prototype.async run.setLevel): Added.
(WI.AuditTestCase.prototype.async run.addError): Added.
(WI.AuditTestCase.prototype.async run.checkResultProperty.addErrorForValueType): Added.
(WI.AuditTestCase.prototype.async run.checkResultProperty): Added.
(WI.AuditTestCase.prototype.async run.async resultArrayForEach): Added.
(WI.AuditTestCase.prototype.get id): Deleted.
(WI.AuditTestCase.prototype.get name): Deleted.
(WI.AuditTestCase.prototype.get suite): Deleted.
(WI.AuditTestCase.prototype.get setup): Deleted.
(WI.AuditTestCase.prototype.get tearDown): Deleted.
(WI.AuditTestCase.prototype.get errorDetails): Deleted.

  • UserInterface/Models/AuditTestGroup.js: Added.

(WI.AuditTestGroup):
(WI.AuditTestGroup.fromPayload):
(WI.AuditTestGroup.prototype.get tests):
(WI.AuditTestGroup.prototype.stop):
(WI.AuditTestGroup.prototype.clearResult):
(WI.AuditTestGroup.prototype.async run):
(WI.AuditTestGroup.prototype.toJSON):
(WI.AuditTestGroup.prototype._updateResult):
(WI.AuditTestGroup.prototype._handleTestCompleted):
(WI.AuditTestGroup.prototype._handleTestProgress):

  • UserInterface/Models/AuditTestResultBase.js: Added.

(WI.AuditTestResultBase):
(WI.AuditTestResultBase.prototype.get name):
(WI.AuditTestResultBase.prototype.get description):
(WI.AuditTestResultBase.prototype.get result):
(WI.AuditTestResultBase.prototype.get didPass):
(WI.AuditTestResultBase.prototype.get didWarn):
(WI.AuditTestResultBase.prototype.get didFail):
(WI.AuditTestResultBase.prototype.get didError):
(WI.AuditTestResultBase.prototype.get unsupported):
(WI.AuditTestResultBase.prototype.saveIdentityToCookie):
(WI.AuditTestResultBase.prototype.toJSON):

  • UserInterface/Models/AuditTestCaseResult.js: Added.

(WI.AuditTestCaseResult):
(WI.AuditTestCaseResult.fromPayload.checkArray):
(WI.AuditTestCaseResult.fromPayload):
(WI.AuditTestCaseResult.prototype.get level):
(WI.AuditTestCaseResult.prototype.get data):
(WI.AuditTestCaseResult.prototype.get didPass):
(WI.AuditTestCaseResult.prototype.get didWarn):
(WI.AuditTestCaseResult.prototype.get didFail):
(WI.AuditTestCaseResult.prototype.get didError):
(WI.AuditTestCaseResult.prototype.get unsupported):
(WI.AuditTestCaseResult.prototype.toJSON):

  • UserInterface/Models/AuditTestGroupResult.js: Added.

(WI.AuditTestGroupResult):
(WI.AuditTestGroupResult.fromPayload):
(WI.AuditTestGroupResult.prototype.get results):
(WI.AuditTestGroupResult.prototype.get levelCounts):
(WI.AuditTestGroupResult.prototype.get didPass):
(WI.AuditTestGroupResult.prototype.get didWarn):
(WI.AuditTestGroupResult.prototype.get didFail):
(WI.AuditTestGroupResult.prototype.get didError):
(WI.AuditTestGroupResult.prototype.get unsupported):
(WI.AuditTestGroupResult.prototype.toJSON):

  • UserInterface/Views/AuditTabContentView.js: Added.

(WI.AuditTabContentView):
(WI.AuditTabContentView.tabInfo):
(WI.AuditTabContentView.isTabAllowed):
(WI.AuditTabContentView.prototype.get type):
(WI.AuditTabContentView.prototype.get supportsSplitContentBrowser):
(WI.AuditTabContentView.prototype.canShowRepresentedObject):
(WI.AuditTabContentView.prototype.shown):
(WI.AuditTabContentView.prototype.hidden):
(WI.AuditTabContentView.prototype._handleSpace):

  • UserInterface/Views/AuditNavigationSidebarPanel.js: Added.

(WI.AuditNavigationSidebarPanel):
(WI.AuditNavigationSidebarPanel.prototype.showDefaultContentView):
(WI.AuditNavigationSidebarPanel.prototype.initialLayout):
(WI.AuditNavigationSidebarPanel.prototype.closed):
(WI.AuditNavigationSidebarPanel.prototype._addTest):
(WI.AuditNavigationSidebarPanel.prototype._addResult):
(WI.AuditNavigationSidebarPanel.prototype._updateStartStopButtonNavigationItemState):
(WI.AuditNavigationSidebarPanel.prototype._handleAuditTestAdded):
(WI.AuditNavigationSidebarPanel.prototype._handleAuditTestCompleted):
(WI.AuditNavigationSidebarPanel.prototype._handleAuditTestScheduled):
(WI.AuditNavigationSidebarPanel.prototype._treeSelectionDidChange):
(WI.AuditNavigationSidebarPanel.prototype._handleStartStopButtonNavigationItemClicked):
(WI.AuditNavigationSidebarPanel.prototype._handleImportButtonNavigationItemClicked):

  • UserInterface/Views/AuditNavigationSidebarPanel.css: Added.

(.sidebar > .panel.navigation.audit > .content):

  • UserInterface/Views/AuditTreeElement.js: Added.

(WI.AuditTreeElement):
(WI.AuditTreeElement.prototype.get result):
(WI.AuditTreeElement.prototype.onattach):
(WI.AuditTreeElement.prototype.ondetach):
(WI.AuditTreeElement.prototype.onpopulate):
(WI.AuditTreeElement.prototype.populateContextMenu):
(WI.AuditTreeElement.prototype._start):
(WI.AuditTreeElement.prototype._updateLevel):
(WI.AuditTreeElement.prototype._showRunningSpinner):
(WI.AuditTreeElement.prototype._showRunningProgress):
(WI.AuditTreeElement.prototype._handleTestCaseCompleted):
(WI.AuditTreeElement.prototype._handleTestResultCleared):
(WI.AuditTreeElement.prototype._handleTestCaseScheduled):
(WI.AuditTreeElement.prototype._handleTestGroupCompleted):
(WI.AuditTreeElement.prototype._handleTestGroupProgress):
(WI.AuditTreeElement.prototype._handleTestGroupScheduled):
(WI.AuditTreeElement.prototype._handleStatusClick):

  • UserInterface/Views/AuditTreeElement.css: Added.

(.tree-outline .item.audit > .status):
(.tree-outline .item.audit > .status > img):
(.tree-outline .item.audit:matches(.test-case, .test-group) > .status:hover > img):
(.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 > .status:hover > :not(img), .tree-outline .item.audit.test-group-result.expanded > .status):
(.tree-outline .item.audit > .status > img.pass):
(.tree-outline .item.audit > .status > img.warn):
(.tree-outline .item.audit > .status > img.fail):
(.tree-outline .item.audit > .status > img.error):
(.tree-outline .item.audit > .status > img.unsupported):
(.audit.test-case .icon):
(.audit.test-group .icon):
(.audit.test-case-result .icon):
(.audit.test-group-result .icon):

  • UserInterface/Views/AuditTestContentView.js: Added.

(WI.AuditTestContentView):
(WI.AuditTestContentView.prototype.get navigationItems):
(WI.AuditTestContentView.prototype.get headerView):
(WI.AuditTestContentView.prototype.get contentView):
(WI.AuditTestContentView.prototype.get supportsSave):
(WI.AuditTestContentView.prototype.get saveData):
(WI.AuditTestContentView.prototype.initialLayout):
(WI.AuditTestContentView.prototype.layout):
(WI.AuditTestContentView.prototype.shown):
(WI.AuditTestContentView.prototype.hidden):
(WI.AuditTestContentView.prototype.get placeholderElement):
(WI.AuditTestContentView.prototype.set placeholderElement):
(WI.AuditTestContentView.prototype.showRunningPlaceholder):
(WI.AuditTestContentView.prototype.showStoppingPlaceholder):
(WI.AuditTestContentView.prototype.showNoResultPlaceholder):
(WI.AuditTestContentView.prototype.showNoResultDataPlaceholder):
(WI.AuditTestContentView.prototype.showFilteredPlaceholder):
(WI.AuditTestContentView.prototype.hidePlaceholder):
(WI.AuditTestContentView.prototype.applyFilter):
(WI.AuditTestContentView.prototype.resetFilter):
(WI.AuditTestContentView.prototype._exportAudit):
(WI.AuditTestContentView.prototype._updateExportButtonNavigationItemState):
(WI.AuditTestContentView.prototype._showPlaceholder):
(WI.AuditTestContentView.prototype._handleExportButtonNavigationItemClicked):
(WI.AuditTestContentView.prototype._handleTestChanged):

  • UserInterface/Views/AuditTestContentView.css: Added.

(.content-view-container > .content-view.audit-test):
(.content-view-container > .content-view.audit-test > header):
(.content-view-container > .content-view.audit-test > header h1):
(.content-view-container > .content-view.audit-test > header p):
(.content-view.audit-test):
(.content-view.audit-test h1):
(.content-view.audit-test > header):
(.content-view.audit-test > header p):
(.content-view.audit-test .audit-test.filtered, .content-view.audit-test .audit-test .message-text-view):
(.content-view.audit-test > section):
(.content-view.audit-test > section > .message-text-view):
(.content-view.audit-test.showing-placeholder):
(.content-view.audit-test.showing-placeholder > section):
(.content-view.audit-test.showing-placeholder > section > :not(.message-text-view)):
(@media (prefers-dark-interface) .content-view.audit-test):

  • UserInterface/Views/AuditTestCaseContentView.js: Added.

(WI.AuditTestCaseContentView):
(WI.AuditTestCaseContentView.prototype.initialLayout):
(WI.AuditTestCaseContentView.prototype.layout):
(WI.AuditTestCaseContentView.prototype.showRunningPlaceholder):

  • UserInterface/Views/AuditTestCaseContentView.css: Added.

(.content-view-container > .content-view.audit-test-case > header):
(.content-view-container > .content-view.audit-test-case > section > :not(.message-text-view):first-child):
(.content-view.audit-test-case > header > h1):
(.content-view.audit-test-case > header > h1 > img):
(.content-view.audit-test-case > section > :not(.message-text-view)):
(.content-view.audit-test-case > section > :not(.message-text-view):last-child):
(.content-view.audit-test-case > section > :not(.message-text-view) + :not(.message-text-view)):
(.content-view.audit-test-case > section h1):
(.content-view.audit-test-case > section table):
(.content-view.audit-test-case > section table > tr + tr > td):
(.content-view.audit-test-case > section table > tr > td > :not(.tree-outline)):
(.content-view.audit-test-case > section table > tr > td:first-child):
(.content-view.audit-test-case > section > .dom-nodes > table > tr > td:first-child):
(.content-view.audit-test-case > section code):
(.content-view.audit-test-case > section mark):

  • UserInterface/Views/AuditTestGroupContentView.js: Added.

(WI.AuditTestGroupContentView):
(WI.AuditTestGroupContentView.prototype.initialLayout):
(WI.AuditTestGroupContentView.prototype.layout):
(WI.AuditTestGroupContentView.prototype.shown):
(WI.AuditTestGroupContentView.prototype.hidden):
(WI.AuditTestGroupContentView.prototype.applyFilter):
(WI.AuditTestGroupContentView.prototype.resetFilter):
(WI.AuditTestGroupContentView.prototype.showRunningPlaceholder):
(WI.AuditTestGroupContentView.prototype._subobjects):
(WI.AuditTestGroupContentView.prototype._updateLevelScopeBar):
(WI.AuditTestGroupContentView.prototype._handleTestGroupCompleted):
(WI.AuditTestGroupContentView.prototype._handleTestGroupProgress):
(WI.AuditTestGroupContentView.prototype._handleTestGroupScheduled):
(WI.AuditTestGroupContentView.prototype._handleLevelScopeBarSelectionChanged):

  • UserInterface/Views/AuditTestGroupContentView.css: Added.

(.content-view-container > .content-view.audit-test-group > header):
(.content-view.audit-test-group > header):
(.content-view.audit-test-group.no-matches + .audit-test-group > header):
(.content-view.audit-test-group > header, .content-view.audit-test-group:not(.filtered):last-child > header):
(.content-view.audit-test-group.contains-test-case > header):
(.content-view.audit-test-group.contains-test-case + .audit-test-group.contains-test-case):
(.content-view.audit-test-group.contains-test-case:not(.contains-test-group) > section, .content-view.audit-test-group.contains-test-case.contains-test-group > section > .audit-test-case):
(.content-view.audit-test-group > header > .information):
(.content-view.audit-test-group > header > .information > p):
(.content-view.audit-test-group > header > nav):
(.content-view.audit-test-group > header > nav:empty):
(.content-view.audit-test-group > header > nav:not(:empty):before):
(.content-view.audit-test-group > header > nav > .scope-bar > li):
(.content-view.audit-test-group > header > nav > .scope-bar > li:not(:hover, .selected)):
(.content-view.audit-test-group > header > nav > .scope-bar > li:last-child):
(.content-view.audit-test-group > header > nav > .scope-bar > li::before):
(.content-view.audit-test-group > header > nav > .scope-bar > li.pass::before):
(.content-view.audit-test-group > header > nav > .scope-bar > li.warn::before):
(.content-view.audit-test-group > header > nav > .scope-bar > li.fail::before):
(.content-view.audit-test-group > header > nav > .scope-bar > li.error::before):
(.content-view.audit-test-group > header > nav > .scope-bar > li.unsupported::before):
(.content-view.audit-test-group > header > .percentage-pass):
(.content-view.audit-test-group > header > .percentage-pass:not(:empty)::after):
(.content-view.audit-test-group > section > .audit-test-case:first-child, .content-view.audit-test-group > section > .audit-test-group + .audit-test-case, .content-view.audit-test-group > section > .audit-test-case + .audit-test-group):
(.content-view.audit-test-group > section > .audit-test-case:last-child):

  • UserInterface/Views/ScopeBarItem.js:

(WI.ScopeBarItem):
(WI.ScopeBarItem.prototype.set selected):

  • UserInterface/Views/MultipleScopeBarItem.js:

(WI.MultipleScopeBarItem.prototype.set selectedScopeBarItem):
Add an independent option that prevents selection changes from deselecting other
WI.ScopeBarItems in the same WI.ScopeBar (exclusive takes precedence).

  • UserInterface/Views/DOMTreeElement.js:

(WI.DOMTreeElement):
(WI.DOMTreeElement.prototype.highlightAttribute):
(WI.DOMTreeElement.prototype._buildAttributeDOM):

  • UserInterface/Views/DOMTreeOutline.css:

(.tree-outline.dom li .highlight):

  • UserInterface/Views/ToggleButtonNavigationItem.js:

(WI.ToggleButtonNavigationItem.prototype.set toggled):
Also change the label if the ButtonStyle has text.

  • UserInterface/Base/Setting.js:
  • UserInterface/Views/SettingsTabContentView.js:

(WI.SettingsTabContentView.prototype._createExperimentalSettingsView):

  • UserInterface/Views/DividerNavigationItem.css:

(.navigation-bar .item.divider):

  • UserInterface/Base/Utilities.js:

(Promise.chain): Added.

  • UserInterface/Views/ContentView.js:

(WI.ContentView.createFromRepresentedObject):
(WI.ContentView.isViewable):

  • UserInterface/Main.html:
  • UserInterface/Base/Main.js:

(WI.loaded):
(WI.contentLoaded):

  • UserInterface/Test.html:
  • UserInterface/Base/Test.js:

(WI.loaded):

  • UserInterface/Images/Audit.svg: Added.
  • UserInterface/Images/AuditStart.svg: Added.
  • UserInterface/Images/AuditStop.svg: Added.
  • UserInterface/Images/AuditTestCase.svg: Added.
  • UserInterface/Images/AuditTestCaseResult.svg: Added.
  • UserInterface/Images/AuditTestError.svg: Added.
  • UserInterface/Images/AuditTestFail.svg: Added.
  • UserInterface/Images/AuditTestGroup.svg: Added.
  • UserInterface/Images/AuditTestGroupResult.svg: Added.
  • UserInterface/Images/AuditTestNoResult.svg: Added.
  • UserInterface/Images/AuditTestPass.svg: Added.
  • UserInterface/Images/AuditTestUnsupported.svg: Added.
  • UserInterface/Images/AuditTestWarn.svg: Added.
  • Localizations/en.lproj/localizedStrings.js:

LayoutTests:

  • inspector/audit/resources/audit-utilities.js: Added.
  • inspector/audit/basic-expected.txt: Added.
  • inspector/audit/basic.html: Added.
  • inspector/audit/data-domAttributes-expected.txt: Added.
  • inspector/audit/data-domAttributes.html: Added.
  • inspector/audit/data-domNodes-expected.txt: Added.
  • inspector/audit/data-domNodes.html: Added.
  • inspector/audit/data-errors-expected.txt: Added.
  • inspector/audit/data-errors.html: Added.
  • inspector/model/auditTestCase-expected.txt: Added.
  • inspector/model/auditTestCase.html: Added.
  • inspector/model/auditTestCaseResult-expected.txt: Added.
  • inspector/model/auditTestCaseResult.html: Added.
  • inspector/model/auditTestGroup-expected.txt: Added.
  • inspector/model/auditTestGroup.html: Added.
  • inspector/model/auditTestGroupResult-expected.txt: Added.
  • inspector/model/auditTestGroupResult.html: Added.
  • inspector/unit-tests/promise-utilities-expected.txt: Added.
  • inspector/unit-tests/promise-utilities.html: Added.
  • inspector/audit/audit-manager-expected.txt: Removed.
  • inspector/audit/audit-manager.html: Removed.
  • inspector/audit/audit-report-expected.txt: Removed.
  • inspector/audit/audit-report.html: Removed.
  • inspector/audit/audit-test-case-expected.txt: Removed.
  • inspector/audit/audit-test-case.html: Removed.
  • inspector/audit/audit-test-suite-expected.txt: Removed.
  • inspector/audit/audit-test-suite.html: Removed.
  • inspector/audit/resources/audit-test-fixtures.js: Removed.
Location:
trunk
Files:
46 added
10 deleted
19 edited
2 moved

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r237609 r237613  
     12018-10-30  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: Audit: create Audit Tab
     4        https://bugs.webkit.org/show_bug.cgi?id=190754
     5
     6        Reviewed by Matt Baker.
     7
     8        * inspector/audit/resources/audit-utilities.js: Added.
     9        * inspector/audit/basic-expected.txt: Added.
     10        * inspector/audit/basic.html: Added.
     11        * inspector/audit/data-domAttributes-expected.txt: Added.
     12        * inspector/audit/data-domAttributes.html: Added.
     13        * inspector/audit/data-domNodes-expected.txt: Added.
     14        * inspector/audit/data-domNodes.html: Added.
     15        * inspector/audit/data-errors-expected.txt: Added.
     16        * inspector/audit/data-errors.html: Added.
     17        * inspector/model/auditTestCase-expected.txt: Added.
     18        * inspector/model/auditTestCase.html: Added.
     19        * inspector/model/auditTestCaseResult-expected.txt: Added.
     20        * inspector/model/auditTestCaseResult.html: Added.
     21        * inspector/model/auditTestGroup-expected.txt: Added.
     22        * inspector/model/auditTestGroup.html: Added.
     23        * inspector/model/auditTestGroupResult-expected.txt: Added.
     24        * inspector/model/auditTestGroupResult.html: Added.
     25        * inspector/unit-tests/promise-utilities-expected.txt: Added.
     26        * inspector/unit-tests/promise-utilities.html: Added.
     27
     28        * inspector/audit/audit-manager-expected.txt: Removed.
     29        * inspector/audit/audit-manager.html: Removed.
     30        * inspector/audit/audit-report-expected.txt: Removed.
     31        * inspector/audit/audit-report.html: Removed.
     32        * inspector/audit/audit-test-case-expected.txt: Removed.
     33        * inspector/audit/audit-test-case.html: Removed.
     34        * inspector/audit/audit-test-suite-expected.txt: Removed.
     35        * inspector/audit/audit-test-suite.html: Removed.
     36        * inspector/audit/resources/audit-test-fixtures.js: Removed.
     37
    1382018-10-30  Dawei Fenton  <realdawei@apple.com>
    239
  • trunk/Source/WebInspectorUI/ChangeLog

    r237608 r237613  
     12018-10-30  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: Audit: create Audit Tab
     4        https://bugs.webkit.org/show_bug.cgi?id=190754
     5
     6        Reviewed by Matt Baker.
     7
     8        Create an Audit tab for running audits on the inspected page. Leverage `Runtime.evaluate`
     9        for running the audit tests (arbitrary JavaScript), and use the returned value to generate
     10        a preview UI of the results. All tests/results can be exported/imported to formatted JSON:
     11
     12        `AuditTestCase` JSON:
     13            {
     14                "type": "test-case",
     15                "name": <string>,
     16                <optional> "description": <string>,
     17                "test": <stringified JavaScript function>,
     18            }
     19
     20        `AuditTestGroup` JSON:
     21            {
     22                "type": "test-group",
     23                "name": <string>,
     24                <optional> "description": <string>,
     25                "tests": [...<AuditTestCase, AuditTestGroup>],
     26            }
     27
     28        `AuditTestCaseResult` JSON:
     29            {
     30                "type": "test-case-result",
     31                "name": <string>,
     32                <optional> "description": <string>,
     33                "level": <"pass", "warn", "fail", "error", "unsupported">,
     34                "data": {
     35                    "domNodes": [...<stringified CSS path>],
     36                    "domAttributes": [...<string>],
     37                    "errors": [...<string>],
     38                },
     39            }
     40
     41        `AuditTestGroupResult` JSON:
     42            {
     43                "type": "test-group-result",
     44                "name": <string>,
     45                <optional> "description": <string>,
     46                "results": [...<AuditTestCaseResult, AuditTestGroupResult>],
     47            }
     48
     49        More keys may be added in the future (especially for `AuditTestCaseResult.data`).
     50
     51        * UserInterface/Controllers/AuditManager.js:
     52        (WI.AuditManager):
     53        (WI.AuditManager.synthesizeError): Added.
     54        (WI.AuditManager.prototype.get tests): Added.
     55        (WI.AuditManager.prototype.get results): Added.
     56        (WI.AuditManager.prototype.get runningState): Added.
     57        (WI.AuditManager.prototype.start): Added.
     58        (WI.AuditManager.prototype.stop): Added.
     59        (WI.AuditManager.prototype.import): Added.
     60        (WI.AuditManager.prototype.export): Added.
     61        (WI.AuditManager.prototype._addTest): Added.
     62        (WI.AuditManager.prototype._addResult): Added.
     63        (WI.AuditManager.prototype.get testSuites): Deleted.
     64        (WI.AuditManager.prototype.get reports): Deleted.
     65        (WI.AuditManager.prototype.async runAuditTestByRepresentedObject): Deleted.
     66        (WI.AuditManager.prototype.reportForId): Deleted.
     67        (WI.AuditManager.prototype.removeAllReports): Deleted.
     68        (WI.AuditManager.prototype.async _runTestCase): Deleted.
     69
     70        * UserInterface/Models/AuditTestBase.js: Added.
     71        (WI.AuditTestBases):
     72        (WI.AuditTestBases.prototype.get name):
     73        (WI.AuditTestBases.prototype.get description):
     74        (WI.AuditTestBases.prototype.get runningState):
     75        (WI.AuditTestBases.prototype.get result):
     76        (WI.AuditTestBases.prototype.async start):
     77        (WI.AuditTestBases.prototype.stop):
     78        (WI.AuditTestBases.prototype.clearResult):
     79        (WI.AuditTestBases.prototype.saveIdentityToCookie):
     80        (WI.AuditTestBases.prototype.toJSON):
     81        (WI.AuditTestBases.prototype.async run):
     82
     83        * UserInterface/Models/AuditTestCase.js:
     84        (WI.AuditTestCase):
     85        (WI.AuditTestCase.fromPayload): Added.
     86        (WI.AuditTestCase.prototype.toJSON): Added.
     87        (WI.AuditTestCase.prototype.async run): Added.
     88        (WI.AuditTestCase.prototype.async run.setLevel): Added.
     89        (WI.AuditTestCase.prototype.async run.addError): Added.
     90        (WI.AuditTestCase.prototype.async run.checkResultProperty.addErrorForValueType): Added.
     91        (WI.AuditTestCase.prototype.async run.checkResultProperty): Added.
     92        (WI.AuditTestCase.prototype.async run.async resultArrayForEach): Added.
     93        (WI.AuditTestCase.prototype.get id): Deleted.
     94        (WI.AuditTestCase.prototype.get name): Deleted.
     95        (WI.AuditTestCase.prototype.get suite): Deleted.
     96        (WI.AuditTestCase.prototype.get setup): Deleted.
     97        (WI.AuditTestCase.prototype.get tearDown): Deleted.
     98        (WI.AuditTestCase.prototype.get errorDetails): Deleted.
     99
     100        * UserInterface/Models/AuditTestGroup.js: Added.
     101        (WI.AuditTestGroup):
     102        (WI.AuditTestGroup.fromPayload):
     103        (WI.AuditTestGroup.prototype.get tests):
     104        (WI.AuditTestGroup.prototype.stop):
     105        (WI.AuditTestGroup.prototype.clearResult):
     106        (WI.AuditTestGroup.prototype.async run):
     107        (WI.AuditTestGroup.prototype.toJSON):
     108        (WI.AuditTestGroup.prototype._updateResult):
     109        (WI.AuditTestGroup.prototype._handleTestCompleted):
     110        (WI.AuditTestGroup.prototype._handleTestProgress):
     111
     112        * UserInterface/Models/AuditTestResultBase.js: Added.
     113        (WI.AuditTestResultBase):
     114        (WI.AuditTestResultBase.prototype.get name):
     115        (WI.AuditTestResultBase.prototype.get description):
     116        (WI.AuditTestResultBase.prototype.get result):
     117        (WI.AuditTestResultBase.prototype.get didPass):
     118        (WI.AuditTestResultBase.prototype.get didWarn):
     119        (WI.AuditTestResultBase.prototype.get didFail):
     120        (WI.AuditTestResultBase.prototype.get didError):
     121        (WI.AuditTestResultBase.prototype.get unsupported):
     122        (WI.AuditTestResultBase.prototype.saveIdentityToCookie):
     123        (WI.AuditTestResultBase.prototype.toJSON):
     124
     125        * UserInterface/Models/AuditTestCaseResult.js: Added.
     126        (WI.AuditTestCaseResult):
     127        (WI.AuditTestCaseResult.fromPayload.checkArray):
     128        (WI.AuditTestCaseResult.fromPayload):
     129        (WI.AuditTestCaseResult.prototype.get level):
     130        (WI.AuditTestCaseResult.prototype.get data):
     131        (WI.AuditTestCaseResult.prototype.get didPass):
     132        (WI.AuditTestCaseResult.prototype.get didWarn):
     133        (WI.AuditTestCaseResult.prototype.get didFail):
     134        (WI.AuditTestCaseResult.prototype.get didError):
     135        (WI.AuditTestCaseResult.prototype.get unsupported):
     136        (WI.AuditTestCaseResult.prototype.toJSON):
     137
     138        * UserInterface/Models/AuditTestGroupResult.js: Added.
     139        (WI.AuditTestGroupResult):
     140        (WI.AuditTestGroupResult.fromPayload):
     141        (WI.AuditTestGroupResult.prototype.get results):
     142        (WI.AuditTestGroupResult.prototype.get levelCounts):
     143        (WI.AuditTestGroupResult.prototype.get didPass):
     144        (WI.AuditTestGroupResult.prototype.get didWarn):
     145        (WI.AuditTestGroupResult.prototype.get didFail):
     146        (WI.AuditTestGroupResult.prototype.get didError):
     147        (WI.AuditTestGroupResult.prototype.get unsupported):
     148        (WI.AuditTestGroupResult.prototype.toJSON):
     149
     150        * UserInterface/Views/AuditTabContentView.js: Added.
     151        (WI.AuditTabContentView):
     152        (WI.AuditTabContentView.tabInfo):
     153        (WI.AuditTabContentView.isTabAllowed):
     154        (WI.AuditTabContentView.prototype.get type):
     155        (WI.AuditTabContentView.prototype.get supportsSplitContentBrowser):
     156        (WI.AuditTabContentView.prototype.canShowRepresentedObject):
     157        (WI.AuditTabContentView.prototype.shown):
     158        (WI.AuditTabContentView.prototype.hidden):
     159        (WI.AuditTabContentView.prototype._handleSpace):
     160
     161        * UserInterface/Views/AuditNavigationSidebarPanel.js: Added.
     162        (WI.AuditNavigationSidebarPanel):
     163        (WI.AuditNavigationSidebarPanel.prototype.showDefaultContentView):
     164        (WI.AuditNavigationSidebarPanel.prototype.initialLayout):
     165        (WI.AuditNavigationSidebarPanel.prototype.closed):
     166        (WI.AuditNavigationSidebarPanel.prototype._addTest):
     167        (WI.AuditNavigationSidebarPanel.prototype._addResult):
     168        (WI.AuditNavigationSidebarPanel.prototype._updateStartStopButtonNavigationItemState):
     169        (WI.AuditNavigationSidebarPanel.prototype._handleAuditTestAdded):
     170        (WI.AuditNavigationSidebarPanel.prototype._handleAuditTestCompleted):
     171        (WI.AuditNavigationSidebarPanel.prototype._handleAuditTestScheduled):
     172        (WI.AuditNavigationSidebarPanel.prototype._treeSelectionDidChange):
     173        (WI.AuditNavigationSidebarPanel.prototype._handleStartStopButtonNavigationItemClicked):
     174        (WI.AuditNavigationSidebarPanel.prototype._handleImportButtonNavigationItemClicked):
     175        * UserInterface/Views/AuditNavigationSidebarPanel.css: Added.
     176        (.sidebar > .panel.navigation.audit > .content):
     177
     178        * UserInterface/Views/AuditTreeElement.js: Added.
     179        (WI.AuditTreeElement):
     180        (WI.AuditTreeElement.prototype.get result):
     181        (WI.AuditTreeElement.prototype.onattach):
     182        (WI.AuditTreeElement.prototype.ondetach):
     183        (WI.AuditTreeElement.prototype.onpopulate):
     184        (WI.AuditTreeElement.prototype.populateContextMenu):
     185        (WI.AuditTreeElement.prototype._start):
     186        (WI.AuditTreeElement.prototype._updateLevel):
     187        (WI.AuditTreeElement.prototype._showRunningSpinner):
     188        (WI.AuditTreeElement.prototype._showRunningProgress):
     189        (WI.AuditTreeElement.prototype._handleTestCaseCompleted):
     190        (WI.AuditTreeElement.prototype._handleTestResultCleared):
     191        (WI.AuditTreeElement.prototype._handleTestCaseScheduled):
     192        (WI.AuditTreeElement.prototype._handleTestGroupCompleted):
     193        (WI.AuditTreeElement.prototype._handleTestGroupProgress):
     194        (WI.AuditTreeElement.prototype._handleTestGroupScheduled):
     195        (WI.AuditTreeElement.prototype._handleStatusClick):
     196        * UserInterface/Views/AuditTreeElement.css: Added.
     197        (.tree-outline .item.audit > .status):
     198        (.tree-outline .item.audit > .status > img):
     199        (.tree-outline .item.audit:matches(.test-case, .test-group) > .status:hover > img):
     200        (.tree-outline .item.audit > .status:not(:hover) > img.show-on-hover, .tree-outline .item.audit.test-group.expanded > .status:not(:hover)):
     201        (.tree-outline .item.audit.test-group.expanded > .status:hover > :not(img), .tree-outline .item.audit.test-group-result.expanded > .status):
     202        (.tree-outline .item.audit > .status > img.pass):
     203        (.tree-outline .item.audit > .status > img.warn):
     204        (.tree-outline .item.audit > .status > img.fail):
     205        (.tree-outline .item.audit > .status > img.error):
     206        (.tree-outline .item.audit > .status > img.unsupported):
     207        (.audit.test-case .icon):
     208        (.audit.test-group .icon):
     209        (.audit.test-case-result .icon):
     210        (.audit.test-group-result .icon):
     211
     212        * UserInterface/Views/AuditTestContentView.js: Added.
     213        (WI.AuditTestContentView):
     214        (WI.AuditTestContentView.prototype.get navigationItems):
     215        (WI.AuditTestContentView.prototype.get headerView):
     216        (WI.AuditTestContentView.prototype.get contentView):
     217        (WI.AuditTestContentView.prototype.get supportsSave):
     218        (WI.AuditTestContentView.prototype.get saveData):
     219        (WI.AuditTestContentView.prototype.initialLayout):
     220        (WI.AuditTestContentView.prototype.layout):
     221        (WI.AuditTestContentView.prototype.shown):
     222        (WI.AuditTestContentView.prototype.hidden):
     223        (WI.AuditTestContentView.prototype.get placeholderElement):
     224        (WI.AuditTestContentView.prototype.set placeholderElement):
     225        (WI.AuditTestContentView.prototype.showRunningPlaceholder):
     226        (WI.AuditTestContentView.prototype.showStoppingPlaceholder):
     227        (WI.AuditTestContentView.prototype.showNoResultPlaceholder):
     228        (WI.AuditTestContentView.prototype.showNoResultDataPlaceholder):
     229        (WI.AuditTestContentView.prototype.showFilteredPlaceholder):
     230        (WI.AuditTestContentView.prototype.hidePlaceholder):
     231        (WI.AuditTestContentView.prototype.applyFilter):
     232        (WI.AuditTestContentView.prototype.resetFilter):
     233        (WI.AuditTestContentView.prototype._exportAudit):
     234        (WI.AuditTestContentView.prototype._updateExportButtonNavigationItemState):
     235        (WI.AuditTestContentView.prototype._showPlaceholder):
     236        (WI.AuditTestContentView.prototype._handleExportButtonNavigationItemClicked):
     237        (WI.AuditTestContentView.prototype._handleTestChanged):
     238        * UserInterface/Views/AuditTestContentView.css: Added.
     239        (.content-view-container > .content-view.audit-test):
     240        (.content-view-container > .content-view.audit-test > header):
     241        (.content-view-container > .content-view.audit-test > header h1):
     242        (.content-view-container > .content-view.audit-test > header p):
     243        (.content-view.audit-test):
     244        (.content-view.audit-test h1):
     245        (.content-view.audit-test > header):
     246        (.content-view.audit-test > header p):
     247        (.content-view.audit-test .audit-test.filtered, .content-view.audit-test .audit-test .message-text-view):
     248        (.content-view.audit-test > section):
     249        (.content-view.audit-test > section > .message-text-view):
     250        (.content-view.audit-test.showing-placeholder):
     251        (.content-view.audit-test.showing-placeholder > section):
     252        (.content-view.audit-test.showing-placeholder > section > :not(.message-text-view)):
     253        (@media (prefers-dark-interface) .content-view.audit-test):
     254
     255        * UserInterface/Views/AuditTestCaseContentView.js: Added.
     256        (WI.AuditTestCaseContentView):
     257        (WI.AuditTestCaseContentView.prototype.initialLayout):
     258        (WI.AuditTestCaseContentView.prototype.layout):
     259        (WI.AuditTestCaseContentView.prototype.showRunningPlaceholder):
     260        * UserInterface/Views/AuditTestCaseContentView.css: Added.
     261        (.content-view-container > .content-view.audit-test-case > header):
     262        (.content-view-container > .content-view.audit-test-case > section > :not(.message-text-view):first-child):
     263        (.content-view.audit-test-case > header > h1):
     264        (.content-view.audit-test-case > header > h1 > img):
     265        (.content-view.audit-test-case > section > :not(.message-text-view)):
     266        (.content-view.audit-test-case > section > :not(.message-text-view):last-child):
     267        (.content-view.audit-test-case > section > :not(.message-text-view) + :not(.message-text-view)):
     268        (.content-view.audit-test-case > section h1):
     269        (.content-view.audit-test-case > section table):
     270        (.content-view.audit-test-case > section table > tr + tr > td):
     271        (.content-view.audit-test-case > section table > tr > td > :not(.tree-outline)):
     272        (.content-view.audit-test-case > section table > tr > td:first-child):
     273        (.content-view.audit-test-case > section > .dom-nodes > table > tr > td:first-child):
     274        (.content-view.audit-test-case > section code):
     275        (.content-view.audit-test-case > section mark):
     276
     277        * UserInterface/Views/AuditTestGroupContentView.js: Added.
     278        (WI.AuditTestGroupContentView):
     279        (WI.AuditTestGroupContentView.prototype.initialLayout):
     280        (WI.AuditTestGroupContentView.prototype.layout):
     281        (WI.AuditTestGroupContentView.prototype.shown):
     282        (WI.AuditTestGroupContentView.prototype.hidden):
     283        (WI.AuditTestGroupContentView.prototype.applyFilter):
     284        (WI.AuditTestGroupContentView.prototype.resetFilter):
     285        (WI.AuditTestGroupContentView.prototype.showRunningPlaceholder):
     286        (WI.AuditTestGroupContentView.prototype._subobjects):
     287        (WI.AuditTestGroupContentView.prototype._updateLevelScopeBar):
     288        (WI.AuditTestGroupContentView.prototype._handleTestGroupCompleted):
     289        (WI.AuditTestGroupContentView.prototype._handleTestGroupProgress):
     290        (WI.AuditTestGroupContentView.prototype._handleTestGroupScheduled):
     291        (WI.AuditTestGroupContentView.prototype._handleLevelScopeBarSelectionChanged):
     292        * UserInterface/Views/AuditTestGroupContentView.css: Added.
     293        (.content-view-container > .content-view.audit-test-group > header):
     294        (.content-view.audit-test-group > header):
     295        (.content-view.audit-test-group.no-matches + .audit-test-group > header):
     296        (.content-view.audit-test-group > header, .content-view.audit-test-group:not(.filtered):last-child > header):
     297        (.content-view.audit-test-group.contains-test-case > header):
     298        (.content-view.audit-test-group.contains-test-case + .audit-test-group.contains-test-case):
     299        (.content-view.audit-test-group.contains-test-case:not(.contains-test-group) > section, .content-view.audit-test-group.contains-test-case.contains-test-group > section > .audit-test-case):
     300        (.content-view.audit-test-group > header > .information):
     301        (.content-view.audit-test-group > header > .information > p):
     302        (.content-view.audit-test-group > header > nav):
     303        (.content-view.audit-test-group > header > nav:empty):
     304        (.content-view.audit-test-group > header > nav:not(:empty):before):
     305        (.content-view.audit-test-group > header > nav > .scope-bar > li):
     306        (.content-view.audit-test-group > header > nav > .scope-bar > li:not(:hover, .selected)):
     307        (.content-view.audit-test-group > header > nav > .scope-bar > li:last-child):
     308        (.content-view.audit-test-group > header > nav > .scope-bar > li::before):
     309        (.content-view.audit-test-group > header > nav > .scope-bar > li.pass::before):
     310        (.content-view.audit-test-group > header > nav > .scope-bar > li.warn::before):
     311        (.content-view.audit-test-group > header > nav > .scope-bar > li.fail::before):
     312        (.content-view.audit-test-group > header > nav > .scope-bar > li.error::before):
     313        (.content-view.audit-test-group > header > nav > .scope-bar > li.unsupported::before):
     314        (.content-view.audit-test-group > header > .percentage-pass):
     315        (.content-view.audit-test-group > header > .percentage-pass:not(:empty)::after):
     316        (.content-view.audit-test-group > section > .audit-test-case:first-child, .content-view.audit-test-group > section > .audit-test-group + .audit-test-case, .content-view.audit-test-group > section > .audit-test-case + .audit-test-group):
     317        (.content-view.audit-test-group > section > .audit-test-case:last-child):
     318
     319        * UserInterface/Views/ScopeBarItem.js:
     320        (WI.ScopeBarItem):
     321        (WI.ScopeBarItem.prototype.set selected):
     322        * UserInterface/Views/MultipleScopeBarItem.js:
     323        (WI.MultipleScopeBarItem.prototype.set selectedScopeBarItem):
     324        Add an `independent` option that prevents selection changes from deselecting other
     325        `WI.ScopeBarItem`s in the same `WI.ScopeBar` (`exclusive` takes precedence).
     326
     327        * UserInterface/Views/DOMTreeElement.js:
     328        (WI.DOMTreeElement):
     329        (WI.DOMTreeElement.prototype.highlightAttribute):
     330        (WI.DOMTreeElement.prototype._buildAttributeDOM):
     331        * UserInterface/Views/DOMTreeOutline.css:
     332        (.tree-outline.dom li .highlight):
     333
     334        * UserInterface/Views/ToggleButtonNavigationItem.js:
     335        (WI.ToggleButtonNavigationItem.prototype.set toggled):
     336        Also change the `label` if the `ButtonStyle` has text.
     337
     338        * UserInterface/Base/Setting.js:
     339        * UserInterface/Views/SettingsTabContentView.js:
     340        (WI.SettingsTabContentView.prototype._createExperimentalSettingsView):
     341
     342        * UserInterface/Views/DividerNavigationItem.css:
     343        (.navigation-bar .item.divider):
     344
     345        * UserInterface/Base/Utilities.js:
     346        (Promise.chain): Added.
     347
     348        * UserInterface/Views/ContentView.js:
     349        (WI.ContentView.createFromRepresentedObject):
     350        (WI.ContentView.isViewable):
     351
     352        * UserInterface/Main.html:
     353        * UserInterface/Base/Main.js:
     354        (WI.loaded):
     355        (WI.contentLoaded):
     356
     357        * UserInterface/Test.html:
     358        * UserInterface/Base/Test.js:
     359        (WI.loaded):
     360
     361        * UserInterface/Images/Audit.svg: Added.
     362        * UserInterface/Images/AuditStart.svg: Added.
     363        * UserInterface/Images/AuditStop.svg: Added.
     364        * UserInterface/Images/AuditTestCase.svg: Added.
     365        * UserInterface/Images/AuditTestCaseResult.svg: Added.
     366        * UserInterface/Images/AuditTestError.svg: Added.
     367        * UserInterface/Images/AuditTestFail.svg: Added.
     368        * UserInterface/Images/AuditTestGroup.svg: Added.
     369        * UserInterface/Images/AuditTestGroupResult.svg: Added.
     370        * UserInterface/Images/AuditTestNoResult.svg: Added.
     371        * UserInterface/Images/AuditTestPass.svg: Added.
     372        * UserInterface/Images/AuditTestUnsupported.svg: Added.
     373        * UserInterface/Images/AuditTestWarn.svg: Added.
     374
     375        * Localizations/en.lproj/localizedStrings.js:
     376
    13772018-10-30  Devin Rousso  <drousso@apple.com>
    2378
  • trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

    r237574 r237613  
    2323localizedStrings["%.2fs"] = "%.2fs";
    2424localizedStrings["%.3fms"] = "%.3fms";
     25localizedStrings["%d Error"] = "%d Error";
    2526localizedStrings["%d Errors"] = "%d Errors";
    2627localizedStrings["%d Errors, %d Warnings"] = "%d Errors, %d Warnings";
     28localizedStrings["%d Fail"] = "%d Fail";
    2729localizedStrings["%d Frame"] = "%d Frame";
    2830localizedStrings["%d Frames"] = "%d Frames";
    2931localizedStrings["%d More\u2026"] = "%d More\u2026";
     32localizedStrings["%d Pass"] = "%d Pass";
    3033localizedStrings["%d Threads"] = "%d Threads";
     34localizedStrings["%d Unsupported"] = "%d Unsupported";
     35localizedStrings["%d Warn"] = "%d Warn";
    3136localizedStrings["%d Warnings"] = "%d Warnings";
    3237localizedStrings["%d \xd7 %d pixels"] = "%d \xd7 %d pixels";
     
    4449localizedStrings["%s Fired"] = "%s Fired";
    4550localizedStrings["%s Prototype"] = "%s Prototype";
     51localizedStrings["%s Result"] = "%s Result";
    4652localizedStrings["%s \u2013 %s"] = "%s \u2013 %s";
    4753localizedStrings["%s \u2014 %s"] = "%s \u2014 %s";
     
    8995localizedStrings["All Resources"] = "All Resources";
    9096localizedStrings["All Storage"] = "All Storage";
     97localizedStrings["All items in “%s“ must be error objects"] = "All items in “%s“ must be error objects";
     98localizedStrings["All items in “%s“ must be non-empty strings"] = "All items in “%s“ must be non-empty strings";
     99localizedStrings["All items in “%s“ must be valid DOM nodes"] = "All items in “%s“ must be valid DOM nodes";
    91100localizedStrings["An error occurred trying to load the resource."] = "An error occurred trying to load the resource.";
    92101localizedStrings["An error occurred trying to read the “%s” table."] = "An error occurred trying to read the “%s” table.";
     
    114123localizedStrings["Attribute Modified"] = "Attribute Modified";
    115124localizedStrings["Attributes"] = "Attributes";
     125localizedStrings["Audit"] = "Audit";
     126localizedStrings["Audit test error: %s"] = "Audit test error: %s";
     127localizedStrings["Audit:"] = "Audit:";
     128localizedStrings["Audits"] = "Audits";
    116129localizedStrings["Author Stylesheet"] = "Author Stylesheet";
    117130localizedStrings["Auto Increment"] = "Auto Increment";
     
    245258localizedStrings["DOM Event"] = "DOM Event";
    246259localizedStrings["DOM Events"] = "DOM Events";
     260localizedStrings["DOM Nodes:"] = "DOM Nodes:";
    247261localizedStrings["Damping"] = "Damping";
    248262localizedStrings["Data"] = "Data";
     
    330344localizedStrings["Element overlaps other compositing element"] = "Element overlaps other compositing element";
    331345localizedStrings["Elements"] = "Elements";
     346localizedStrings["Enable Audit Tab"] = "Enable Audit Tab";
    332347localizedStrings["Enable Breakpoint"] = "Enable Breakpoint";
    333348localizedStrings["Enable Breakpoints"] = "Enable Breakpoints";
     
    347362localizedStrings["Error: "] = "Error: ";
    348363localizedStrings["Errors"] = "Errors";
     364localizedStrings["Errors:"] = "Errors:";
    349365localizedStrings["Eval Code"] = "Eval Code";
    350366localizedStrings["Evaluate JavaScript"] = "Evaluate JavaScript";
     
    363379localizedStrings["Export"] = "Export";
    364380localizedStrings["Export HAR"] = "Export HAR";
     381localizedStrings["Export Result"] = "Export Result";
     382localizedStrings["Export Test"] = "Export Test";
    365383localizedStrings["Export recording (%s)"] = "Export recording (%s)";
    366384localizedStrings["Expression"] = "Expression";
     
    454472localizedStrings["Import"] = "Import";
    455473localizedStrings["Import recording from file"] = "Import recording from file";
     474localizedStrings["Imported"] = "Imported";
    456475localizedStrings["Imported Recordings"] = "Imported Recordings";
    457476localizedStrings["Incomplete"] = "Incomplete";
     
    538557localizedStrings["Method"] = "Method";
    539558localizedStrings["Microtask Dispatched"] = "Microtask Dispatched";
     559localizedStrings["Missing result level"] = "Missing result level";
    540560localizedStrings["Mixed"] = "Mixed";
    541561localizedStrings["Module Code"] = "Module Code";
     
    566586localizedStrings["No Request Headers"] = "No Request Headers";
    567587localizedStrings["No Response Headers"] = "No Response Headers";
     588localizedStrings["No Result"] = "No Result";
    568589localizedStrings["No Results Found"] = "No Results Found";
    569590localizedStrings["No Search Results"] = "No Search Results";
    570591localizedStrings["No Watch Expressions"] = "No Watch Expressions";
     592localizedStrings["No audit selected"] = "No audit selected";
    571593localizedStrings["No matching ARIA role"] = "No matching ARIA role";
    572594localizedStrings["No preview available"] = "No preview available";
     
    627649localizedStrings["Prefer indent using:"] = "Prefer indent using:";
    628650localizedStrings["Preserve Log"] = "Preserve Log";
     651localizedStrings["Press %s to import a test or result file"] = "Press %s to import a test or result file";
    629652localizedStrings["Press %s to load a recording from file."] = "Press %s to load a recording from file.";
     653localizedStrings["Press %s to start running the audit"] = "Press %s to start running the audit";
    630654localizedStrings["Pressed"] = "Pressed";
    631655localizedStrings["Pretty print"] = "Pretty print";
     
    700724localizedStrings["Restart (%s)"] = "Restart (%s)";
    701725localizedStrings["Restart animation"] = "Restart animation";
     726localizedStrings["Results"] = "Results";
    702727localizedStrings["Resume Processing"] = "Resume Processing";
    703728localizedStrings["Resume Thread"] = "Resume Thread";
    704729localizedStrings["Retained Size"] = "Retained Size";
     730localizedStrings["Return string must be one of %s"] = "Return string must be one of %s";
    705731localizedStrings["Return type for anonymous function"] = "Return type for anonymous function";
    706732localizedStrings["Return type for function: %s"] = "Return type for function: %s";
     733localizedStrings["Return value is not an object, string, or boolean"] = "Return value is not an object, string, or boolean";
    707734localizedStrings["Reveal Breakpoint"] = "Reveal Breakpoint";
    708735localizedStrings["Reveal in DOM Tree"] = "Reveal in DOM Tree";
     
    714741localizedStrings["Reveal in Resources Tab"] = "Reveal in Resources Tab";
    715742localizedStrings["Role"] = "Role";
     743localizedStrings["Run %d"] = "Run %d";
     744localizedStrings["Running the “%s“ audit"] = "Running the “%s“ audit";
    716745localizedStrings["Same-Site"] = "Same-Site";
    717746localizedStrings["Samples"] = "Samples";
     
    791820localizedStrings["Show warnings logged to the Console"] = "Show warnings logged to the Console";
    792821localizedStrings["Show:"] = "Show:";
     822localizedStrings["Showing:"] = "Showing:";
    793823localizedStrings["Size"] = "Size";
    794824localizedStrings["Size of current object plus all objects it keeps alive"] = "Size of current object plus all objects it keeps alive";
     
    810840localizedStrings["Spelling"] = "Spelling";
    811841localizedStrings["Stalled"] = "Stalled";
     842localizedStrings["Start"] = "Start";
    812843localizedStrings["Start Time"] = "Start Time";
    813844localizedStrings["Start element selection (%s)"] = "Start element selection (%s)";
     
    822853localizedStrings["Step over (%s or %s)"] = "Step over (%s or %s)";
    823854localizedStrings["Stiffness"] = "Stiffness";
     855localizedStrings["Stop"] = "Stop";
    824856localizedStrings["Stop Recording"] = "Stop Recording";
    825857localizedStrings["Stop element selection (%s)"] = "Stop element selection (%s)";
     
    827859localizedStrings["Stop recording (%s)"] = "Stop recording (%s)";
    828860localizedStrings["Stop recording canvas actions"] = "Stop recording canvas actions";
     861localizedStrings["Stopping the “%s“ audit"] = "Stopping the “%s“ audit";
    829862localizedStrings["Storage"] = "Storage";
    830863localizedStrings["Style Attribute"] = "Style Attribute";
     
    850883localizedStrings["Text Node"] = "Text Node";
    851884localizedStrings["The page's content has changed"] = "The page's content has changed";
     885localizedStrings["The “%s“ audit failed"] = "The “%s“ audit failed";
     886localizedStrings["The “%s“ audit is unsupported"] = "The “%s“ audit is unsupported";
     887localizedStrings["The “%s“ audit passed"] = "The “%s“ audit passed";
     888localizedStrings["The “%s“ audit threw an error"] = "The “%s“ audit threw an error";
     889localizedStrings["The “%s“ audit warned"] = "The “%s“ audit warned";
    852890localizedStrings["The “%s”\ntable is empty."] = "The “%s”\ntable is empty.";
    853891localizedStrings["This action causes no visual change"] = "This action causes no visual change";
     
    940978localizedStrings["default"] = "default";
    941979localizedStrings["for changes to take effect"] = "for changes to take effect";
     980localizedStrings["invalid JSON."] = "invalid JSON.";
    942981localizedStrings["key"] = "key";
    943982localizedStrings["line "] = "line ";
     
    952991localizedStrings["value"] = "value";
    953992localizedStrings["“%s“ Event Fired"] = "“%s“ Event Fired";
     993localizedStrings["“%s“ must be a %s"] = "“%s“ must be a %s";
     994localizedStrings["“%s“ must be an %s"] = "“%s“ must be an %s";
    954995localizedStrings["“%s” Profile Recorded"] = "“%s” Profile Recorded";
    955996localizedStrings["“%s” is invalid."] = "“%s” is invalid.";
  • trunk/Source/WebInspectorUI/UserInterface/Base/Main.js

    r237482 r237613  
    128128    this.domDebuggerManager = new WI.DOMDebuggerManager;
    129129    this.canvasManager = new WI.CanvasManager;
     130    this.auditManager = new WI.AuditManager;
    130131
    131132    // Enable the Console Agent after creating the singleton managers.
     
    447448        WI.CanvasTabContentView,
    448449        WI.LayersTabContentView,
     450        WI.AuditTabContentView,
    449451        WI.ConsoleTabContentView,
    450452        WI.SearchTabContentView,
     
    11141116        representedObject instanceof WI.IndexedDatabase || representedObject instanceof WI.IndexedDatabaseObjectStoreIndex)
    11151117        return WI.StorageTabContentView;
     1118
     1119    if (representedObject instanceof WI.AuditTestCase || representedObject instanceof WI.AuditTestGroup
     1120        || representedObject instanceof WI.AuditTestCaseResult || representedObject instanceof WI.AuditTestGroupResult)
     1121        return WI.AuditTabContentView;
    11161122
    11171123    if (representedObject instanceof WI.CanvasCollection)
  • trunk/Source/WebInspectorUI/UserInterface/Base/Setting.js

    r236927 r237613  
    126126    experimentalEnableLayersTab: new WI.Setting("experimental-enable-layers-tab", false),
    127127    experimentalEnableNewTabBar: new WI.Setting("experimental-enable-new-tab-bar", false),
     128    experimentalEnableAuditTab: new WI.Setting("experimental-enable-audit-tab", false),
    128129
    129130    // DebugUI
  • trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js

    r237482 r237613  
    13731373});
    13741374
     1375Object.defineProperty(Promise, "chain",
     1376{
     1377    async value(callbacks, initialValue)
     1378    {
     1379        let results = [];
     1380        for (let i = 0; i < callbacks.length; ++i)
     1381            results.push(await callbacks[i](results.lastValue || initialValue || null, i));
     1382        return results;
     1383    }
     1384});
     1385
    13751386Object.defineProperty(Promise, "delay",
    13761387{
  • trunk/Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js

    r235873 r237613  
    3030        super();
    3131
    32         this._testSuiteConstructors = [];
    33         this._reports = new Map;
     32        this._tests = [];
     33        this._results = [];
    3434
    35         // Transforming all the constructors into AuditTestSuite instances.
    36         this._testSuites = this._testSuiteConstructors.map(suite => {
    37             let newTestSuite = new suite;
     35        this._runningState = WI.AuditManager.RunningState.Inactive;
     36        this._runningTests = [];
     37    }
    3838
    39             if (!(newTestSuite instanceof WI.AuditTestSuite))
    40                 throw new Error("Audit test suites must be of instance WI.AuditTestSuite.");
     39    static synthesizeError(message)
     40    {
     41        let consoleMessage = new WI.ConsoleMessage(WI.mainTarget, WI.ConsoleMessage.MessageSource.Other, WI.ConsoleMessage.MessageLevel.Error, WI.UIString("Audit test error: %s").format(message));
     42        consoleMessage.shouldRevealConsole = true;
    4143
    42             return newTestSuite;
    43         });
     44        WI.consoleLogViewController.appendConsoleMessage(consoleMessage);
    4445    }
    4546
    4647    // Public
    4748
    48     get testSuites() { return this._testSuites.slice(); }
    49     get reports() { return [...this._reports.values()]; }
     49    get tests() { return this._tests; }
     50    get results() { return this._results; }
     51    get runningState() { return this._runningState; }
    5052
    51     async runAuditTestByRepresentedObject(representedObject)
     53    async start(tests)
    5254    {
    53         let auditReport = new WI.AuditReport(representedObject);
     55        console.assert(this._runningState === WI.AuditManager.RunningState.Inactive);
     56        if (this._runningState !== WI.AuditManager.RunningState.Inactive)
     57            return;
    5458
    55         if (representedObject instanceof WI.AuditTestCase) {
    56             let auditResult = await this._runTestCase(representedObject);
    57             auditReport.addResult(auditResult);
    58         } else if (representedObject instanceof WI.AuditTestSuite) {
    59             let testCases = representedObject.testCases;
    60             // Start reducing from testCases[0].
    61             let result = testCases.slice(1).reduce((chain, testCase, index) => {
    62                 if (testCase.setup) {
    63                     let setup = testCase.setup.call(testCase, testCase.suite);
    64                     if (testCase.setup[Symbol.toStringTag] === "AsyncFunction")
    65                         return setup;
    66                     else
    67                         return new Promise(setup);
    68                 }
     59        if (tests && tests.length)
     60            tests = tests.filter((test) => typeof test === "object" && test instanceof WI.AuditTestBase);
     61        else
     62            tests = this._tests;
    6963
    70                 chain = chain.then((auditResult) => {
    71                     auditReport.addResult(auditResult);
    72                     return this._runTestCase(testCase);
    73                 });
     64        if (!tests.length)
     65            return;
    7466
    75                 if (testCase.tearDown) {
    76                     let tearDown = testCase.tearDown.call(testCase, testCase.suite);
    77                     if (testCase.tearDown[Symbol.toStringTag] === "AsyncFunction")
    78                         return tearDown;
    79                     else
    80                         return new Promise(tearDown);
    81                 }
    82                 return chain;
    83             }, this._runTestCase(testCases[0]));
     67        this._runningState = WI.AuditManager.RunningState.Active;
     68        this._runningTests = tests;
     69        for (let test of this._runningTests)
     70            test.clearResult();
    8471
    85             let lastAuditResult = await result;
    86             auditReport.addResult(lastAuditResult);
     72        this.dispatchEventToListeners(WI.AuditManager.Event.TestScheduled);
    8773
    88             // Make AuditReport read-only after all the AuditResults have been received.
    89             auditReport.close();
    90         }
     74        await Promise.chain(this._runningTests.map((test) => () => this._runningState === WI.AuditManager.RunningState.Active ? test.start() : null));
    9175
    92         this._reports.set(representedObject.id, auditReport);
    93         this.dispatchEventToListeners(WI.AuditManager.Event.NewReportAdded, {auditReport});
     76        let result = this._runningTests.map((test) => test.result).filter((result) => !!result);
    9477
    95         return auditReport;
     78        this._runningState = WI.AuditManager.RunningState.Inactive;
     79        this._runningTests = [];
     80
     81        this._addResult(result);
    9682    }
    9783
    98     addTestSuite(auditTestSuiteConstructor)
     84    stop()
    9985    {
    100         if (this._testSuiteConstructors.indexOf(auditTestSuiteConstructor) >= 0)
    101             throw new Error(`class ${auditTestSuiteConstructor.name} already exists.`);
     86        console.assert(this._runningState === WI.AuditManager.RunningState.Active);
     87        if (this._runningState !== WI.AuditManager.RunningState.Active)
     88            return;
    10289
    103         let auditTestSuite = new auditTestSuiteConstructor;
    104         this._testSuiteConstructors.push(auditTestSuiteConstructor);
    105         this._testSuites.push(auditTestSuite);
     90        for (let test of this._runningTests)
     91            test.stop();
     92
     93        this._runningState = WI.AuditManager.RunningState.Stopping;
    10694    }
    10795
    108     reportForId(reportId)
     96    import()
    10997    {
    110         return this._reports.get(reportId);
     98        WI.loadDataFromFile((data, filename) => {
     99            if (!data)
     100                return;
     101
     102            let payload = null;
     103            try {
     104                payload = JSON.parse(data);
     105            } catch (e) {
     106                WI.AuditManager.synthesizeError(e);
     107                return;
     108            }
     109
     110            let object = WI.AuditTestGroup.fromPayload(payload) || WI.AuditTestCase.fromPayload(payload);
     111            if (!object) {
     112                object = WI.AuditTestGroupResult.fromPayload(payload) || WI.AuditTestCaseResult.fromPayload(payload);
     113                if (!object) {
     114                    WI.AuditManager.synthesizeError(WI.UIString("invalid JSON."));
     115                    return;
     116                }
     117            }
     118
     119            if (object instanceof WI.AuditTestBase)
     120                this._addTest(object);
     121            else if (object instanceof WI.AuditTestResultBase)
     122                this._addResult(object);
     123        });
    111124    }
    112125
    113     removeAllReports()
     126    export(object)
    114127    {
    115         this._reports.clear();
     128        console.assert(object instanceof WI.AuditTestCase || object instanceof WI.AuditTestGroup || object instanceof WI.AuditTestCaseResult || object instanceof WI.AuditTestGroupResult, object);
     129
     130        let filename = object.name;
     131        if (object instanceof WI.AuditTestResultBase)
     132            filename = WI.UIString("%s Result").format(filename);
     133
     134        let url = "web-inspector:///" + encodeURI(filename) + ".json";
     135
     136        WI.saveDataToFile({
     137            url,
     138            content: JSON.stringify(object),
     139            forceSaveAs: true,
     140        });
    116141    }
    117142
    118143    // Private
    119144
    120     async _runTestCase(testCase)
     145    _addTest(test)
    121146    {
    122         let didRaiseException = false;
    123         let result;
    124         this.dispatchEventToListeners(WI.AuditManager.Event.TestStarted, {test: testCase});
    125         try {
    126             result = await testCase.test.call(testCase, testCase.suite);
    127         } catch (resultData) {
    128             result = resultData;
    129             didRaiseException = true;
    130         }
    131         this.dispatchEventToListeners(WI.AuditManager.Event.TestEnded, {test: testCase});
    132         return new WI.AuditResult(testCase, {result}, didRaiseException);
     147        this._tests.push(test);
     148
     149        this.dispatchEventToListeners(WI.AuditManager.Event.TestAdded, {test});
     150    }
     151
     152    _addResult(result)
     153    {
     154        if (!result || (Array.isArray(result) && !result.length))
     155            return;
     156
     157        this._results.push(result);
     158
     159        this.dispatchEventToListeners(WI.AuditManager.Event.TestCompleted, {
     160            result,
     161            index: this._results.length - 1,
     162        });
    133163    }
    134164};
    135165
     166WI.AuditManager.RunningState = {
     167    Inactive: "inactive",
     168    Active: "active",
     169    Stopping: "stopping",
     170};
     171
    136172WI.AuditManager.Event = {
    137     TestStarted: Symbol("test-started"),
    138     TestEnded: Symbol("test-ended")
     173    TestAdded: "audit-manager-test-added",
     174    TestCompleted: "audit-manager-test-completed",
     175    TestScheduled: "audit-manager-test-scheduled",
    139176};
  • trunk/Source/WebInspectorUI/UserInterface/Main.html

    r237605 r237613  
    3232
    3333    <link rel="stylesheet" href="Views/ApplicationCacheFrameContentView.css">
     34    <link rel="stylesheet" href="Views/AuditNavigationSidebarPanel.css">
     35    <link rel="stylesheet" href="Views/AuditTestCaseContentView.css">
     36    <link rel="stylesheet" href="Views/AuditTestContentView.css">
     37    <link rel="stylesheet" href="Views/AuditTestGroupContentView.css">
     38    <link rel="stylesheet" href="Views/AuditTreeElement.css">
    3439    <link rel="stylesheet" href="Views/BezierEditor.css">
    3540    <link rel="stylesheet" href="Views/BoxModelDetailsSectionRow.css">
     
    441446    <script src="Models/XHRBreakpoint.js"></script>
    442447
    443     <script src="Models/AuditReport.js"></script>
    444     <script src="Models/AuditResult.js"></script>
     448    <script src="Models/AuditTestBase.js"></script>
    445449    <script src="Models/AuditTestCase.js"></script>
    446     <script src="Models/AuditTestSuite.js"></script>
     450    <script src="Models/AuditTestGroup.js"></script>
     451    <script src="Models/AuditTestResultBase.js"></script>
     452    <script src="Models/AuditTestCaseResult.js"></script>
     453    <script src="Models/AuditTestGroupResult.js"></script>
    447454
    448455    <script src="Proxies/FormatterWorkerProxy.js"></script>
     
    517524    <script src="Views/StyleDetailsPanel.js"></script>
    518525
     526    <script src="Views/AuditTabContentView.js"></script>
    519527    <script src="Views/CanvasTabContentView.js"></script>
    520528    <script src="Views/ConsoleTabContentView.js"></script>
     
    547555    <script src="Views/ApplicationCacheFrameTreeElement.js"></script>
    548556    <script src="Views/ApplicationCacheManifestTreeElement.js"></script>
     557    <script src="Views/AuditNavigationSidebarPanel.js"></script>
     558    <script src="Views/AuditTestContentView.js"></script>
     559    <script src="Views/AuditTestCaseContentView.js"></script>
     560    <script src="Views/AuditTestGroupContentView.js"></script>
     561    <script src="Views/AuditTreeElement.js"></script>
    549562    <script src="Views/BezierEditor.js"></script>
    550563    <script src="Views/BoxModelDetailsSectionRow.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Models/AuditTestCase.js

    r235873 r237613  
    2424 */
    2525
    26 WI.AuditTestCase = class AuditTestCase extends WI.Object
     26WI.AuditTestCase = class AuditTestCase extends WI.AuditTestBase
    2727{
    28     constructor(suite, name, test, setup, tearDown, errorDetails = {})
    29     {
    30         console.assert(suite instanceof WI.AuditTestSuite);
    31         console.assert(typeof name === "string");
    32 
    33         if (setup)
    34             console.assert(setup instanceof Function);
    35 
    36         if (tearDown)
    37             console.assert(tearDown instanceof Function);
    38 
    39         if (test[Symbol.toStringTag] !== "AsyncFunction")
    40             throw new Error("Test functions must be async functions.");
    41 
    42         super();
    43         this._id = Symbol(name);
    44 
    45         this._suite = suite;
    46         this._name = name;
     28    constructor(name, test, {description} = {})
     29    {
     30        console.assert(typeof test === "string");
     31
     32        super(name, {description});
     33
    4734        this._test = test;
    48         this._setup = setup;
    49         this._tearDown = tearDown;
    50         this._errorDetails = errorDetails;
     35    }
     36
     37    // Static
     38
     39    static fromPayload(payload)
     40    {
     41        if (typeof payload !== "object" || payload === null)
     42            return null;
     43
     44        let {type, name, test, description} = payload;
     45
     46        if (type !== WI.AuditTestCase.TypeIdentifier)
     47            return null;
     48
     49        if (typeof name !== "string")
     50            return null;
     51
     52        if (typeof test !== "string")
     53            return null;
     54
     55        let options = {};
     56        if (typeof description === "string")
     57            options.description = description;
     58
     59        return new WI.AuditTestCase(name, test, options);
    5160    }
    5261
    5362    // Public
    5463
    55     get id() { return this._id; }
    56     get name() { return this._name; }
    57     get suite() { return this._suite; }
    5864    get test() { return this._test; }
    59     get setup() { return this._setup; }
    60     get tearDown() { return this._tearDown; }
    61     get errorDetails() { return this._errorDetails; }
     65
     66    toJSON()
     67    {
     68        let json = super.toJSON();
     69        json.test = this._test;
     70        return json;
     71    }
     72
     73    // Protected
     74
     75    async run()
     76    {
     77        const levelStrings = Object.values(WI.AuditTestCaseResult.Level);
     78        let level = null;
     79        let data = {};
     80
     81        function setLevel(newLevel) {
     82            let newLevelIndex = levelStrings.indexOf(newLevel);
     83            if (newLevelIndex < 0) {
     84                addError(WI.UIString("Return string must be one of %s").format(JSON.stringify(levelStrings)));
     85                return;
     86            }
     87
     88            if (newLevelIndex <= levelStrings.indexOf(level))
     89                return;
     90
     91            level = newLevel;
     92        }
     93
     94        function addError(value) {
     95            setLevel(WI.AuditTestCaseResult.Level.Error);
     96
     97            if (!data.errors)
     98                data.errors = [];
     99
     100            data.errors.push(value);
     101        }
     102
     103        try {
     104            let {result, wasThrown} = await RuntimeAgent.evaluate.invoke({
     105                expression: `(function() { "use strict"; return eval(${this._test})(); })()`,
     106                objectGroup: "audit",
     107                doNotPauseOnExceptionsAndMuteConsole: true,
     108            });
     109            let remoteObject = WI.RemoteObject.fromPayload(result, WI.mainTarget);
     110
     111            if (wasThrown || (remoteObject.type === "object" && remoteObject.subtype === "error"))
     112                addError(remoteObject.description);
     113            else if (remoteObject.type === "boolean")
     114                setLevel(remoteObject.value ? WI.AuditTestCaseResult.Level.Pass : WI.AuditTestCaseResult.Level.Fail);
     115            else if (remoteObject.type === "string")
     116                setLevel(remoteObject.value.trim().toLowerCase());
     117            else if (remoteObject.type === "object" && !remoteObject.subtype) {
     118                const options = {
     119                    ownProperties: true,
     120                };
     121
     122                let properties = await new Promise((resolve, reject) => remoteObject.getPropertyDescriptorsAsObject(resolve, options));
     123
     124                function checkResultProperty(key, type, subtype) {
     125                    if (!(key in properties))
     126                        return null;
     127
     128                    let property = properties[key].value;
     129                    if (!property)
     130                        return null;
     131
     132                    function addErrorForValueType(valueType) {
     133                        let value = null;
     134                        if (valueType === "object" || valueType === "array")
     135                            value = WI.UIString("“%s“ must be an %s");
     136                        else
     137                            value = WI.UIString("“%s“ must be a %s");
     138                        addError(value.format(key, valueType));
     139                    }
     140
     141                    if (property.subtype !== subtype) {
     142                        addErrorForValueType(subtype);
     143                        return null;
     144                    }
     145
     146                    if (property.type !== type) {
     147                        addErrorForValueType(type);
     148                        return null;
     149                    }
     150
     151                    if (type === "boolean" || type === "string")
     152                        return property.value;
     153
     154                    return property;
     155                }
     156
     157                async function resultArrayForEach(key, callback) {
     158                    let array = checkResultProperty(key, "object", "array");
     159                    if (!array)
     160                        return;
     161
     162                    // `getPropertyDescriptorsAsObject` returns an object, meaning that if we
     163                    // want to iterate over `array` by index, we have to count.
     164                    let asObject = await new Promise((resolve, reject) => array.getPropertyDescriptorsAsObject(resolve, options));
     165                    for (let i = 0; i < array.size; ++i) {
     166                        if (i in asObject)
     167                            await callback(asObject[i]);
     168                    }
     169                }
     170
     171                let levelString = checkResultProperty("level", "string");
     172                if (levelString)
     173                    setLevel(levelString.trim().toLowerCase());
     174
     175                if (checkResultProperty("pass", "boolean"))
     176                    setLevel(WI.AuditTestCaseResult.Level.Pass);
     177                if (checkResultProperty("warn", "boolean"))
     178                    setLevel(WI.AuditTestCaseResult.Level.Warn);
     179                if (checkResultProperty("fail", "boolean"))
     180                    setLevel(WI.AuditTestCaseResult.Level.Fail);
     181                if (checkResultProperty("error", "boolean"))
     182                    setLevel(WI.AuditTestCaseResult.Level.Error);
     183                if (checkResultProperty("unsupported", "boolean"))
     184                    setLevel(WI.AuditTestCaseResult.Level.Unsupported);
     185
     186                await resultArrayForEach("domNodes", async (item) => {
     187                    if (!item || !item.value || item.value.type !== "object" || item.value.subtype !== "node") {
     188                        addError(WI.UIString("All items in “%s“ must be valid DOM nodes").format(WI.unlocalizedString("domNodes")));
     189                        return;
     190                    }
     191
     192                    let domNodeId = await new Promise((resolve, reject) => item.value.pushNodeToFrontend(resolve));
     193                    let domNode = WI.domManager.nodeForId(domNodeId);
     194                    if (!domNode)
     195                        return;
     196
     197                    if (!data.domNodes)
     198                        data.domNodes = [];
     199                    data.domNodes.push(domNode);
     200                });
     201
     202                await resultArrayForEach("domAttributes", (item) => {
     203                    if (!item || !item.value || item.value.type !== "string" || !item.value.value.length) {
     204                        addError(WI.UIString("All items in “%s“ must be non-empty strings").format(WI.unlocalizedString("domAttributes")));
     205                        return;
     206                    }
     207
     208                    if (!data.domAttributes)
     209                        data.domAttributes = [];
     210                    data.domAttributes.push(item.value.value);
     211                });
     212
     213                await resultArrayForEach("errors", (item) => {
     214                    if (!item || !item.value || item.value.type !== "object" || item.value.subtype !== "error") {
     215                        addError(WI.UIString("All items in “%s“ must be error objects").format(WI.unlocalizedString("errors")));
     216                        return;
     217                    }
     218
     219                    addError(item.value.description);
     220                });
     221            } else
     222                addError(WI.UIString("Return value is not an object, string, or boolean"));
     223        } catch (error) {
     224            addError(error.message);
     225        }
     226
     227        if (!level)
     228            addError(WI.UIString("Missing result level"));
     229
     230        let options = {
     231            description: this.description,
     232        };
     233        if (!isEmptyObject(data))
     234            options.data = data;
     235        this._result = new WI.AuditTestCaseResult(this.name, level, options);
     236    }
    62237};
     238
     239WI.AuditTestCase.TypeIdentifier = "test-case";
  • trunk/Source/WebInspectorUI/UserInterface/Models/AuditTestResultBase.js

    r237612 r237613  
    2424 */
    2525
    26 WI.AuditTestSuite = class AuditTestSuite extends WI.Object
     26WI.AuditTestResultBase = class AuditTestResultBase
    2727{
    28     constructor(identifier, name)
     28    constructor(name, {description} = {})
    2929    {
    30         super();
    31         this._id = Symbol(identifier);
     30        console.assert(typeof name === "string");
     31        console.assert(!description || typeof description === "string");
     32
    3233        this._name = name;
    33         this._testCases = new Map;
    34 
    35         this._buildTestCasesFromDescriptors();
     34        this._description = description || null;
    3635    }
    37 
    38     static testCaseDescriptors() { throw WI.NotImplementedError.subclassMustOverride(); }
    3936
    4037    // Public
    4138
    42     get id() { return this._id; }
    4339    get name() { return this._name; }
    44     get testCases() {
    45         return [...this._testCases.values()];
     40    get description() { return this._description; }
     41
     42    get result()
     43    {
     44        return this;
    4645    }
    4746
    48     // Private
     47    get didPass()
     48    {
     49        throw WI.NotImplementedError.subclassMustOverride();
     50    }
    4951
    50     _buildTestCasesFromDescriptors()
     52    get didWarn()
    5153    {
    52         for (let descriptor of this.constructor.testCaseDescriptors()) {
    53             if (typeof descriptor.name !== "string" || !descriptor.name)
    54                 throw new Error("Test name must be a valid string.");
     54        throw WI.NotImplementedError.subclassMustOverride();
     55    }
    5556
    56             let {name, test, setup, tearDown, errorDetails} = descriptor;
     57    get didFail()
     58    {
     59        throw WI.NotImplementedError.subclassMustOverride();
     60    }
    5761
    58             if (!(test instanceof Function) || test[Symbol.toStringTag] !== "AsyncFunction")
    59                 throw new Error("Test function must be an async function.");
     62    get didError()
     63    {
     64        throw WI.NotImplementedError.subclassMustOverride();
     65    }
    6066
    61             let testCaseInstance = new WI.AuditTestCase(this, name, test, setup, tearDown, errorDetails);
     67    get unsupported()
     68    {
     69        throw WI.NotImplementedError.subclassMustOverride();
     70    }
    6271
    63             this._testCases.set(testCaseInstance.id, testCaseInstance);
    64         }
     72    saveIdentityToCookie(cookie)
     73    {
     74        cookie["audit-" + this.constructor.TypeIdentifier + "-name"] = this._name;
     75    }
     76
     77    toJSON()
     78    {
     79        let json = {
     80            type: this.constructor.TypeIdentifier,
     81            name: this._name,
     82        };
     83        if (this._description)
     84            json.description = this._description;
     85        return json;
    6586    }
    6687};
    67 
    68 WI.AuditTestSuite.Event = {
    69     NewAuditResultAvailable: Symbol("new-audit-result-available")
    70 };
  • trunk/Source/WebInspectorUI/UserInterface/Test.html

    r237151 r237613  
    200200    <script src="Models/XHRBreakpoint.js"></script>
    201201
    202     <script src="Models/AuditReport.js"></script>
    203     <script src="Models/AuditResult.js"></script>
     202    <script src="Models/AuditTestBase.js"></script>
    204203    <script src="Models/AuditTestCase.js"></script>
    205     <script src="Models/AuditTestSuite.js"></script>
     204    <script src="Models/AuditTestGroup.js"></script>
     205    <script src="Models/AuditTestResultBase.js"></script>
     206    <script src="Models/AuditTestCaseResult.js"></script>
     207    <script src="Models/AuditTestGroupResult.js"></script>
    206208
    207209    <script src="Proxies/FormatterWorkerProxy.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Test/Test.js

    r236853 r237613  
    6464    this.domDebuggerManager = new WI.DOMDebuggerManager;
    6565    this.canvasManager = new WI.CanvasManager;
     66    this.auditManager = new WI.AuditManager;
    6667
    6768    document.addEventListener("DOMContentLoaded", this.contentLoaded);
  • trunk/Source/WebInspectorUI/UserInterface/Views/AuditNavigationSidebarPanel.css

    r237612 r237613  
    2424 */
    2525
    26 WI.AuditResult = class AuditResult
    27 {
    28     constructor(testInstance, testResult, failed)
    29     {
    30         this._testResult = testResult;
    31         this._failed = failed || false;
    32         this._testName = testInstance.name;
    33         this._errorDetails = testInstance.errorDetails;
    34         this._logLevel = this._errorDetails.logLevel || WI.AuditResult.LogLevel.Passed;
    35         this._errorTitle = this._errorDetails.title;
    36         this._hint = this._errorDetails.hint;
    37         this._documentation = this._errorDetails.documentation;
    38     }
    39 
    40     // Public
    41 
    42     get testResult() { return this._testResult; }
    43     get name() { return this._testName; }
    44     get logLevel() { return this._logLevel; }
    45     get failed() { return this._failed; }
    46 };
    47 
    48 WI.AuditResult.LogLevel = {
    49     Error: "error",
    50     Warning: "warning",
    51     Passed: "passed"
    52 };
     26.sidebar > .panel.navigation.audit > .content {
     27    top: var(--navigation-bar-height);
     28}
  • trunk/Source/WebInspectorUI/UserInterface/Views/ContentView.js

    r236766 r237613  
    168168            return new WI.ResourceCollectionContentView(representedObject, extraArguments);
    169169
     170        if (representedObject instanceof WI.AuditTestCase || representedObject instanceof WI.AuditTestCaseResult)
     171            return new WI.AuditTestCaseContentView(representedObject, extraArguments);
     172
     173        if (representedObject instanceof WI.AuditTestGroup || representedObject instanceof WI.AuditTestGroupResult)
     174            return new WI.AuditTestGroupContentView(representedObject, extraArguments);
     175
    170176        if (representedObject instanceof WI.Collection)
    171177            return new WI.CollectionContentView(representedObject, extraArguments);
     
    295301            return true;
    296302        if (representedObject instanceof WI.Recording)
     303            return true;
     304        if (representedObject instanceof WI.AuditTestCase || representedObject instanceof WI.AuditTestGroup
     305            || representedObject instanceof WI.AuditTestCaseResult || representedObject instanceof WI.AuditTestGroupResult)
    297306            return true;
    298307        if (representedObject instanceof WI.Collection)
  • trunk/Source/WebInspectorUI/UserInterface/Views/DOMTreeElement.js

    r236766 r237613  
    4848        this._subtreeBreakpointCount = 0;
    4949
     50        this._highlightedAttributes = new Set;
    5051        this._recentlyModifiedAttributes = [];
    5152        this._boundNodeChangedAnimationEnd = this._nodeChangedAnimationEnd.bind(this);
     
    267268    {
    268269        this._recentlyModifiedAttributes.push({name});
     270    }
     271
     272    highlightAttribute(name)
     273    {
     274        this._highlightedAttributes.add(name);
    269275    }
    270276
     
    13281334                attribute.element = hasText ? attrValueElement : attrNameElement;
    13291335        }
     1336
     1337        if (this._highlightedAttributes.has(name))
     1338            attrSpanElement.classList.add("highlight");
    13301339    }
    13311340
  • trunk/Source/WebInspectorUI/UserInterface/Views/DOMTreeOutline.css

    r237150 r237613  
    308308}
    309309
     310.tree-outline.dom li .highlight {
     311    background-color: hsla(53, 83%, 53%, 0.2);
     312    border-bottom: 1px solid hsl(47, 82%, 60%);
     313}
     314
    310315@media (prefers-dark-interface) {
    311316    .tree-outline.dom li.elements-drag-over .selection-area {
  • trunk/Source/WebInspectorUI/UserInterface/Views/DividerNavigationItem.css

    r236237 r237613  
    2626.navigation-bar .item.divider {
    2727    width: 1px;
    28 
     28    height: 100%;
    2929    background-image: linear-gradient(hsl(0, 0%, 74%), hsl(0, 0%, 74%));
    3030    background-size: 100% 16px;
  • trunk/Source/WebInspectorUI/UserInterface/Views/Main.css

    r237078 r237613  
    200200    padding: 0 4px;
    201201    border-bottom: none;
    202     vertical-align: middle;
     202    vertical-align: -3px;
    203203}
    204204
  • trunk/Source/WebInspectorUI/UserInterface/Views/ScopeBarItem.js

    r237593 r237613  
    2626WI.ScopeBarItem = class ScopeBarItem extends WI.Object
    2727{
    28     constructor(id, label, {className, exclusive, hidden} = {})
     28    constructor(id, label, {className, exclusive, independent, hidden} = {})
    2929    {
    3030        super();
     
    4040        this._label = label;
    4141        this._exclusive = !!exclusive;
     42        this._independent = !!independent;
    4243        this._hidden = !!hidden;
    4344
     
    8485
    8586        this.dispatchEventToListeners(WI.ScopeBarItem.Event.SelectionChanged, {
    86             extendSelection: WI.modifierKeys.metaKey && !WI.modifierKeys.ctrlKey && !WI.modifierKeys.altKey && !WI.modifierKeys.shiftKey,
     87            extendSelection: this._independent || (WI.modifierKeys.metaKey && !WI.modifierKeys.ctrlKey && !WI.modifierKeys.altKey && !WI.modifierKeys.shiftKey),
    8788        });
    8889    }
  • trunk/Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js

    r236766 r237613  
    250250        }
    251251
     252        experimentalSettingsView.addSetting(WI.UIString("Audit:"), WI.settings.experimentalEnableAuditTab, WI.UIString("Enable Audit Tab"));
     253        experimentalSettingsView.addSeparator();
     254
    252255        experimentalSettingsView.addSetting(WI.UIString("User Interface:"), WI.settings.experimentalEnableNewTabBar, WI.UIString("Enable New Tab Bar"));
    253256        experimentalSettingsView.addSeparator();
     
    269272        listenForChange(WI.settings.experimentalEnableMultiplePropertiesSelection);
    270273        listenForChange(WI.settings.experimentalEnableLayersTab);
     274        listenForChange(WI.settings.experimentalEnableAuditTab);
    271275        listenForChange(WI.settings.experimentalEnableNewTabBar);
    272276
  • trunk/Source/WebInspectorUI/UserInterface/Views/ToggleButtonNavigationItem.js

    r229543 r237613  
    104104            this.image = this._defaultImage;
    105105        }
     106
     107        if (this.buttonStyle === WI.ButtonNavigationItem.Style.Text || this.buttonStyle === WI.ButtonNavigationItem.Style.ImageAndText)
     108            this.label = this.tooltip;
    106109    }
    107110
Note: See TracChangeset for help on using the changeset viewer.