ui5-test-runner 5.13.1 → 6.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/Npm.js +80 -0
- package/dist/browsers/IBrowser.js +1 -0
- package/dist/browsers/factory.js +9 -0
- package/dist/browsers/puppeteer.js +158 -0
- package/dist/cli.js +17 -0
- package/dist/configuration/CommandLine.js +112 -0
- package/dist/configuration/Configuration.js +1 -0
- package/dist/configuration/ConfigurationValidator.js +79 -0
- package/dist/configuration/Option.js +1 -0
- package/dist/configuration/OptionValidationError.js +15 -0
- package/dist/configuration/indexedOptions.js +13 -0
- package/dist/configuration/options.js +191 -0
- package/dist/configuration/validators/OptionValidator.js +1 -0
- package/dist/configuration/validators/boolean.js +15 -0
- package/dist/configuration/validators/browser.js +11 -0
- package/dist/configuration/validators/fsEntry.js +70 -0
- package/dist/configuration/validators/index.js +20 -0
- package/dist/configuration/validators/integer.js +10 -0
- package/dist/configuration/validators/percent.js +17 -0
- package/dist/configuration/validators/regexp.js +20 -0
- package/dist/configuration/validators/string.js +7 -0
- package/dist/configuration/validators/timeout.js +24 -0
- package/dist/configuration/validators/url.js +8 -0
- package/dist/modes/ModeFunction.js +1 -0
- package/dist/modes/Modes.js +9 -0
- package/dist/modes/execute.js +27 -0
- package/dist/modes/help.js +3 -0
- package/dist/modes/log/ILogStorage.js +1 -0
- package/dist/modes/log/LogMetrics.js +9 -0
- package/dist/modes/log/LogReader.js +37 -0
- package/dist/modes/log/LogStorage.js +68 -0
- package/dist/modes/log/REserve.js +101 -0
- package/dist/modes/log/index.js +58 -0
- package/dist/modes/test/REserve.js +31 -0
- package/dist/modes/test/agent.js +8 -0
- package/dist/modes/test/browser.js +37 -0
- package/dist/modes/test/index.js +66 -0
- package/dist/modes/test/pageTask.js +145 -0
- package/dist/modes/test/report.js +3 -0
- package/dist/modes/test/server.js +109 -0
- package/dist/modes/version.js +11 -0
- package/dist/platform/Exit.js +139 -0
- package/dist/platform/FileSystem.js +13 -0
- package/dist/platform/Host.js +10 -0
- package/dist/platform/Http.js +38 -0
- package/dist/platform/Path.js +5 -0
- package/dist/platform/Process.js +133 -0
- package/dist/platform/Terminal.js +47 -0
- package/dist/platform/Thread.js +43 -0
- package/dist/platform/ZLib.js +7 -0
- package/dist/platform/assert.js +17 -0
- package/dist/platform/constants.js +5 -0
- package/dist/platform/environment.js +28 -0
- package/dist/platform/index.js +13 -0
- package/dist/platform/logger/ILogger.js +1 -0
- package/dist/platform/logger/allCompressed.js +54 -0
- package/dist/platform/logger/compress.js +277 -0
- package/dist/platform/logger/output/BaseLoggerOutput.js +158 -0
- package/dist/platform/logger/output/InteractiveLoggerOutput.js +102 -0
- package/dist/platform/logger/output/StaticLoggerOutput.js +32 -0
- package/dist/platform/logger/output/factory.js +10 -0
- package/dist/platform/logger/output.js +58 -0
- package/dist/platform/logger/proxy.js +6 -0
- package/dist/platform/logger/toInternalLogAttributes.js +22 -0
- package/dist/platform/logger/types.js +7 -0
- package/dist/platform/logger.js +138 -0
- package/dist/platform/mock.js +104 -0
- package/dist/platform/version.js +8 -0
- package/dist/platform/workerBootstrap.js +21 -0
- package/dist/reports/html.js +46 -0
- package/dist/types/AgentState.js +1 -0
- package/dist/types/CommonTestReportFormat.js +50 -0
- package/dist/types/IError.js +1 -0
- package/dist/types/IUserInterfaceController.js +1 -0
- package/dist/types/typeUtilities.js +1 -0
- package/dist/ui/agent.js +3 -0
- package/dist/ui/html-report.js +2 -0
- package/dist/ui/lib.js +1 -0
- package/dist/ui/log-viewer.js +2 -0
- package/dist/utils/node/Folder.js +28 -0
- package/dist/utils/node/FramedStreamReader.js +86 -0
- package/dist/utils/node/FramedStreamWriter.js +27 -0
- package/dist/utils/shared/ProgressBar.js +43 -0
- package/dist/utils/shared/TestReportBuilder.js +48 -0
- package/dist/utils/shared/memoize.js +19 -0
- package/dist/utils/shared/object.js +8 -0
- package/dist/utils/shared/parallelize.js +59 -0
- package/dist/utils/shared/string.js +23 -0
- package/dist/utils/shared/toIError.js +17 -0
- package/package.json +73 -50
- package/.releaserc +0 -5
- package/index.js +0 -175
- package/jest.config.json +0 -31
- package/src/add-test-pages.js +0 -67
- package/src/batch.js +0 -214
- package/src/browsers.js +0 -319
- package/src/capabilities/index.js +0 -204
- package/src/capabilities/tests/basic/iframe.html +0 -8
- package/src/capabilities/tests/basic/index.html +0 -12
- package/src/capabilities/tests/basic/index.js +0 -20
- package/src/capabilities/tests/basic/ui5.html +0 -24
- package/src/capabilities/tests/dynamic-include/index.js +0 -21
- package/src/capabilities/tests/dynamic-include/mix.html +0 -11
- package/src/capabilities/tests/dynamic-include/one.html +0 -11
- package/src/capabilities/tests/dynamic-include/post.js +0 -3
- package/src/capabilities/tests/dynamic-include/test.js +0 -1
- package/src/capabilities/tests/dynamic-include/two.html +0 -11
- package/src/capabilities/tests/index.js +0 -16
- package/src/capabilities/tests/local-storage/index.html +0 -16
- package/src/capabilities/tests/local-storage/index.js +0 -21
- package/src/capabilities/tests/screenshot/index.html +0 -23
- package/src/capabilities/tests/screenshot/index.js +0 -24
- package/src/capabilities/tests/scripts/coverage.html +0 -32
- package/src/capabilities/tests/scripts/iframe.html +0 -18
- package/src/capabilities/tests/scripts/index.js +0 -59
- package/src/capabilities/tests/scripts/qunit.html +0 -22
- package/src/capabilities/tests/scripts/testsuite.html +0 -10
- package/src/capabilities/tests/scripts/testsuite.js +0 -8
- package/src/capabilities/tests/timeout/index.html +0 -21
- package/src/capabilities/tests/timeout/index.js +0 -19
- package/src/capabilities/tests/traces/index.html +0 -18
- package/src/capabilities/tests/traces/index.js +0 -81
- package/src/capabilities/tests/ui5/focus.html +0 -89
- package/src/capabilities/tests/ui5/index.js +0 -39
- package/src/capabilities/tests/ui5/language.html +0 -50
- package/src/capabilities/tests/ui5/timezone.html +0 -27
- package/src/clean.js +0 -22
- package/src/cors.js +0 -21
- package/src/coverage.js +0 -384
- package/src/csv-reader.js +0 -36
- package/src/csv-writer.js +0 -55
- package/src/defaults/.nycrc.json +0 -4
- package/src/defaults/browser.js +0 -217
- package/src/defaults/happy-dom.js +0 -123
- package/src/defaults/jsdom/compatibility.js +0 -163
- package/src/defaults/jsdom/debug.js +0 -23
- package/src/defaults/jsdom/resource-loader.js +0 -44
- package/src/defaults/jsdom/sap.ui.test.matchers.visible.js +0 -39
- package/src/defaults/jsdom.js +0 -95
- package/src/defaults/json-report.js +0 -36
- package/src/defaults/junit-xml-report.js +0 -90
- package/src/defaults/playwright.js +0 -142
- package/src/defaults/puppeteer.js +0 -124
- package/src/defaults/report/common.js +0 -38
- package/src/defaults/report/decompress.js +0 -19
- package/src/defaults/report/default.html +0 -99
- package/src/defaults/report/main.js +0 -69
- package/src/defaults/report/progress.js +0 -60
- package/src/defaults/report/styles.css +0 -66
- package/src/defaults/report.js +0 -91
- package/src/defaults/scan-ui5.js +0 -26
- package/src/defaults/selenium-webdriver/chrome.js +0 -39
- package/src/defaults/selenium-webdriver/edge.js +0 -24
- package/src/defaults/selenium-webdriver/firefox.js +0 -30
- package/src/defaults/selenium-webdriver.js +0 -129
- package/src/defaults/text-report.js +0 -108
- package/src/defaults/webdriverio.js +0 -80
- package/src/end.js +0 -62
- package/src/endpoints.js +0 -219
- package/src/error.js +0 -54
- package/src/get-job-progress.js +0 -78
- package/src/handle.js +0 -43
- package/src/if.js +0 -10
- package/src/inject/jest2qunit.js +0 -289
- package/src/inject/opa-iframe-coverage.js +0 -22
- package/src/inject/post.js +0 -141
- package/src/inject/qunit-hooks.js +0 -107
- package/src/inject/qunit-redirect.js +0 -65
- package/src/inject/ui5-coverage.js +0 -33
- package/src/job-mode.js +0 -65
- package/src/job.js +0 -493
- package/src/npm.js +0 -136
- package/src/options.js +0 -95
- package/src/output.js +0 -739
- package/src/parallelize.js +0 -63
- package/src/qunit-hooks.js +0 -219
- package/src/report.js +0 -89
- package/src/reserve.js +0 -25
- package/src/start.js +0 -133
- package/src/symbols.js +0 -8
- package/src/tests.js +0 -183
- package/src/timeout.js +0 -53
- package/src/tools.js +0 -179
- package/src/ui5.js +0 -199
- package/src/unhandled.js +0 -32
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { __developmentMode, __sourcesRoot, FileSystem, Path } from '../platform/index.js';
|
|
2
|
+
export const generateHtmlReport = async (configuration, report) => {
|
|
3
|
+
const libraryPath = __developmentMode
|
|
4
|
+
? Path.join(__sourcesRoot, '../dist/ui', 'lib.js')
|
|
5
|
+
: Path.join(__sourcesRoot, 'ui/lib.js');
|
|
6
|
+
const libraryJs = await FileSystem.readFile(libraryPath, 'utf8');
|
|
7
|
+
const path = __developmentMode
|
|
8
|
+
? Path.join(__sourcesRoot, '../dist/ui', 'html-report.js')
|
|
9
|
+
: Path.join(__sourcesRoot, 'ui/html-report.js');
|
|
10
|
+
const htmlReportJs = await FileSystem.readFile(path, 'utf8');
|
|
11
|
+
const reportJson = JSON.stringify(report);
|
|
12
|
+
FileSystem.writeFileSync(Path.join(configuration.reportDir, 'report.html'), `<!DOCTYPE html>
|
|
13
|
+
<html lang="en">
|
|
14
|
+
<head>
|
|
15
|
+
<meta charset="UTF-8">
|
|
16
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
17
|
+
<title>UI5 Test Runner Report</title>
|
|
18
|
+
</head>
|
|
19
|
+
<body>
|
|
20
|
+
<div id="app"></div>
|
|
21
|
+
<script>
|
|
22
|
+
async function decompress (base64) {
|
|
23
|
+
const bin = atob(base64)
|
|
24
|
+
const uint8Array = new Uint8Array(bin.length)
|
|
25
|
+
for (let i = 0; i < bin.length; ++i) {
|
|
26
|
+
uint8Array[i] = bin.charCodeAt(i)
|
|
27
|
+
}
|
|
28
|
+
const readableStream = new ReadableStream({
|
|
29
|
+
start (ctl) {
|
|
30
|
+
ctl.enqueue(uint8Array)
|
|
31
|
+
ctl.close()
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
const decompressionStream = new DecompressionStream('gzip')
|
|
35
|
+
const decompressedStream = readableStream.pipeThrough(decompressionStream)
|
|
36
|
+
const jsonString = await new Response(decompressedStream).text()
|
|
37
|
+
return JSON.parse(jsonString)
|
|
38
|
+
}
|
|
39
|
+
</script>
|
|
40
|
+
<script type="module">window.ctrf = ${reportJson} /* await decompress('{base64}') */</script>
|
|
41
|
+
<script type="module">${libraryJs}</script>
|
|
42
|
+
<script type="module">${htmlReportJs}</script>
|
|
43
|
+
</body>
|
|
44
|
+
</html>
|
|
45
|
+
`, 'utf8');
|
|
46
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const agentLogPrefix = '[ui5-test-runner]';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export const SPEC_VERSION = 'pre-1.0';
|
|
2
|
+
export const createEmptyTestResults = () => ({
|
|
3
|
+
tool: {
|
|
4
|
+
name: ''
|
|
5
|
+
},
|
|
6
|
+
summary: {
|
|
7
|
+
failed: 0,
|
|
8
|
+
other: 0,
|
|
9
|
+
passed: 0,
|
|
10
|
+
pending: 0,
|
|
11
|
+
skipped: 0,
|
|
12
|
+
start: 0,
|
|
13
|
+
stop: 0,
|
|
14
|
+
tests: 0,
|
|
15
|
+
duration: 0
|
|
16
|
+
},
|
|
17
|
+
tests: []
|
|
18
|
+
});
|
|
19
|
+
export const createTestResults = (results) => {
|
|
20
|
+
const testResults = createEmptyTestResults();
|
|
21
|
+
if (results.tool?.name) {
|
|
22
|
+
Object.assign(testResults.tool, results.tool);
|
|
23
|
+
}
|
|
24
|
+
if (results.summary) {
|
|
25
|
+
Object.assign(testResults.summary, results.summary);
|
|
26
|
+
}
|
|
27
|
+
if (results.tests) {
|
|
28
|
+
Object.assign(testResults.summary, {
|
|
29
|
+
failed: 0,
|
|
30
|
+
other: 0,
|
|
31
|
+
passed: 0,
|
|
32
|
+
pending: 0,
|
|
33
|
+
skipped: 0,
|
|
34
|
+
tests: 0
|
|
35
|
+
});
|
|
36
|
+
for (const test of results.tests) {
|
|
37
|
+
const resolvedTest = {
|
|
38
|
+
suite: ['suite'],
|
|
39
|
+
name: 'test',
|
|
40
|
+
status: 'passed',
|
|
41
|
+
duration: 0,
|
|
42
|
+
...test
|
|
43
|
+
};
|
|
44
|
+
testResults.tests.push(resolvedTest);
|
|
45
|
+
testResults.summary.tests += 1;
|
|
46
|
+
testResults.summary[resolvedTest.status] += 1;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return testResults;
|
|
50
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/ui/agent.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
!function(){var{top:e}=window;
|
|
2
|
+
/* v8 ignore next -- @preserve */
|
|
3
|
+
null!==e&&(window!==e||window!==window.parent)&&(e=>{const t=e.top;Object.defineProperty(e,"__coverage__",{get:()=>t.__coverage__,set:e=>(t.__coverage__=e,!0)})})(window);var t={done:!1,type:void 0},s="[ui5-test-runner]",n=e=>console.debug(`${s}${e}`);n.error=e=>console.error(`${s}${e}`);var o=class e{static pages;constructor(){n("custom JsUnitTestSuite built"),e.pages=[]}addTestPage(t){n(`addTestPage('${t}')`),e.pages.push(t)}};Object.assign(window,{jsUnitTestSuite:o});var r=new class{_results={tool:{name:""},summary:{failed:0,other:0,passed:0,pending:0,skipped:0,start:0,stop:0,tests:0,duration:0},tests:[]};get results(){return this._results}reset(){this._results={tool:{name:""},summary:{failed:0,other:0,passed:0,pending:0,skipped:0,start:0,stop:0,tests:0,duration:0},tests:[]}}begin(e){const[t,s]=e.split("@");this._results.tool.name=t,void 0!==s&&(this._results.tool.version=s),this._results.summary.start=Date.now()}test(e){const{summary:t}=this._results;++t.tests,++t[e.status],this._results.tests.push(e)}end(){const{summary:e}=this._results;e.stop=Date.now(),e.duration=e.stop-e.start}},i=r.results,a=e=>e??"unknown",d=e=>{Object.assign(t,e)};window.addEventListener("load",()=>{n("DOM load event fired");const e=Date.now();t.loaded=e;let s=100;const i=()=>{"function"==typeof window.suite?(n("suite detected"),(async()=>{n("executing suite"),await(window.suite?.()),n("suite executed");const e=o.pages?{done:!0,type:"suite",pages:o.pages}:{done:!0,type:"unknown"};Object.assign(t,e)})()):void 0!==window.QUnit?(n("QUnit detected"),(()=>{t.type="QUnit";let e=0,s=0;const n={};QUnit.begin(t=>{r.begin(`QUnit@${window.QUnit.version}`),d({isOpa:!!window?.sap?.ui?.test?.Opa5,executed:e,total:t.totalTests,errors:s})}),QUnit.log(e=>{const t=a(e.testId);n[t]??=[],n[t].push(e)}),QUnit.testDone(t=>{let o="passed";const i={id:t.testId,suite:[t.module],name:t.name,duration:t.runtime,status:"other"};if(t.failed>0){++s,o="failed";const e=n[a(t.testId)]?.filter(({result:e})=>!e);e&&e.length>0&&(i.message=e[0].message??"failed",i.trace=e[0].source)}else t.skipped?o="skipped":t.todo&&(o="pending");i.status=o,r.test(i),d({executed:++e,errors:s}),delete n[a(t.testId)]}),QUnit.done(()=>{r.end(),d({done:!0})})})()):Date.now()-e<5e3?(s=Math.min(1e3,2*s),n(`Waiting ${s}ms before next detection attempt`),setTimeout(i,s)):(n.error("Nothing detected"),Object.assign(t,{done:!0,type:"unknown"}))};i()});var u=e=>{if(!(e instanceof Error))return u(new Error(JSON.stringify(e)));const t={name:e.name,message:e.message,stack:e.stack};return e.cause&&(t.cause=u(e.cause)),e instanceof AggregateError&&(t.errors=e.errors.map(e=>u(e))),t},c=(e,s)=>{void 0===t.uncaughtErrors&&(t.uncaughtErrors=[]),t.uncaughtErrors.push({...u(s),event:e})};window.addEventListener("error",e=>c("error",e.error)),window.addEventListener("unhandledrejection",e=>c("unhandledrejection",e.reason)),window["ui5-test-runner"]={state:t,results:i}}();
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(){try{if("undefined"!=typeof document){var e=document.createElement("style");e.appendChild(document.createTextNode('*,:before,:after{box-sizing:border-box}body,#app{min-height:100vh;font-family:var(--sapFontFamily,"72", Arial, sans-serif);background:var(--sapBackgroundColor,#f7f7f7);flex-direction:column;margin:0;display:flex}.panel-header{justify-content:space-between;align-items:center;gap:.5rem;width:100%;display:flex}.panel-header-actions{align-items:center;gap:.5rem;display:flex}.summary-body{flex-direction:column;gap:.75rem;padding:.75rem 1rem;display:flex}.summary-grid{grid-template-columns:repeat(4,1fr);gap:.25rem 1rem;display:grid}.summary-tags{flex-wrap:wrap;align-items:center;gap:.5rem;margin-top:.25rem;display:flex}.filter-bar{background:var(--sapGroup_ContentBackground,#fff);border-bottom:1px solid var(--sapGroup_TitleBorderColor,#d9d9d9);flex-wrap:wrap;align-items:flex-end;gap:1rem;padding:.75rem 1rem;display:flex}.filter-field{flex-direction:column;gap:.25rem;display:flex}.filter-field--grow{flex:1;min-width:200px}.filter-field--grow ui5-input{width:100%}#testList{flex-direction:column;flex:1;display:flex}.test-list-header{background:var(--sapList_HeaderBackground,#f7f7f7);border-bottom:1px solid var(--sapList_BorderColor,#d9d9d9);justify-content:space-between;align-items:center;padding:.5rem 1rem;font-weight:700;display:flex}.test-list-tags{flex-wrap:wrap;gap:.5rem;display:flex}.sort-row{background:var(--sapGroup_ContentBackground,#fff);border-bottom:1px solid var(--sapList_BorderColor,#d9d9d9);align-items:center;gap:.5rem;padding:.5rem 1rem;display:flex}#testRows{overflow-y:auto}.test-row{border-bottom:1px solid var(--sapList_BorderColor,#d9d9d9)}.test-row-header{cursor:pointer;background:var(--sapList_Background,#fff);align-items:center;gap:.5rem;padding:.4rem 1rem;display:flex}.test-row-header:hover{background:var(--sapList_Hover_Background,#f1f1f1)}.test-row-meta{flex-direction:column;flex:1;gap:.15rem;min-width:0;display:flex}.test-name{text-overflow:ellipsis;white-space:nowrap;font-size:.875rem;overflow:hidden}.test-row-status{flex-shrink:0}.test-row-details{background:var(--sapGroup_ContentBackground,#fff);border-top:1px solid var(--sapList_BorderColor,#d9d9d9);padding:.5rem 1rem .5rem 3rem}.test-detail-row{gap:1rem;padding:.2rem 0;font-size:.875rem;display:flex}.test-detail-label{color:var(--sapContent_LabelColor,#6a6d70);flex-shrink:0;min-width:5rem}.test-trace{white-space:pre-wrap;background:var(--sapField_ReadOnly_Background,#f2f2f2);border-radius:.25rem;flex:1;max-height:200px;margin:0;padding:.5rem;font-family:monospace;font-size:.75rem;overflow-y:auto}.open-mode{flex-direction:column;flex:1;justify-content:center;align-items:center;gap:1rem;display:flex}.suite-item{cursor:pointer;border-radius:.25rem;padding:.35rem .75rem;font-size:.875rem}.suite-item:hover{background:var(--sapList_Hover_Background,#f1f1f1)}.suite-item--active{background:var(--sapList_SelectionBackgroundColor,#e8f0fe);font-weight:700}.test-list-warning{padding:.5rem 1rem}\n/*$vite$:1*/')),document.head.appendChild(e)}}catch(r){console.error("vite-plugin-css-injected-by-js",r)}}();
|
|
2
|
+
!function(){var t=class{_state={};get state(){return this._state}_settings={};get settings(){return this._settings}_updateCb=()=>{throw new Error("UI not connected")};_update({...t}){for(const e of Object.keys(t))t[e]===this._state[e]&&delete t[e];return Object.assign(this._state,t),console.log("🎮⏩",t),Object.keys(t).length>0&&this._updateCb({...t}),t}_onConnect(){}connect(t){this._updateCb=t,this._onConnect(),console.log("🎮🔛",{initialState:{...this._state},settings:this._settings})}interaction(t){const{action:e,...s}=t,n=this._update(s);console.log("🎮⏪",{event:t,action:e,stateDiff:n}),(Object.keys(n).length>0||void 0!==e)&&this._onInteraction(n,e)}},e=[{label:"Execution order",key:""},{label:"Name",key:"name"},{label:"Duration",key:"duration"}],s=[{label:"All Statuses",key:""},{label:"Passed",key:"passed"},{label:"Failed",key:"failed"},{label:"Other",key:"other"}],n={uid:"",label:"No suite",suites:[]},i=(t,e)=>{for(const s of t)if(s.uid===e)return s},a=t=>{const e=new URL(t),s=e.searchParams.get("test"),n=e.searchParams.get("testsuite"),i=e.pathname.split("/").findLast(Boolean);return s||n||i||t},r=(t,e)=>!(!t||0===t.length)||(i(e,"")||e.push(n),!1),o=Symbol("breadcrumbs");function l(t){return t<1e3?`${Math.round(t)}ms`:t<6e4?`${Math.round(t/1e3)}s`:`${Math.floor(t/6e4)}m ${Math.round(t%6e4/1e3)}s`}function u(t){return t.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""")}function d(t){const{report:e}=t,{summary:s}=e.results,n=s.failed>0,i=n?"Negative":"Positive",a=n?"failed":"success",r=function(t){if(!t)return"N/A";const e=new Date(t);return Number.isNaN(e.getTime())?"N/A":e.toLocaleString()}(e.timestamp),o=e.reportId?u(e.reportId):"N/A",d=e.generatedBy?u(e.generatedBy):"N/A",c=void 0===s.duration?"N/A":l(s.duration),p=s.pending+s.other,g=p>0?`<ui5-tag design="None">${p} other</ui5-tag>`:"";return`<ui5-panel id="summaryPanel" collapsed no-animation="true">\n <div slot="header" class="panel-header">\n <ui5-title level="H4">Summary</ui5-title>\n <div class="panel-header-actions">\n <ui5-button id="openBtn" design="Transparent">Open Report</ui5-button>\n <ui5-button id="exportBtn" design="Transparent">Export</ui5-button>\n <ui5-tag design="${i}">${a}</ui5-tag>\n </div>\n </div>\n <div class="summary-body">\n <div class="summary-grid">\n <ui5-label>Date</ui5-label>\n <ui5-label>Report ID</ui5-label>\n <ui5-label>Tool</ui5-label>\n <ui5-label>Duration</ui5-label>\n <span>${r}</span>\n <span>${o}</span>\n <span>${d}</span>\n <span>${c}</span>\n </div>\n <div>\n <ui5-label>Tests (${function(t){const{name:e,version:s}=t.results.tool;return s?`${u(e)}@${u(s)}`:u(e)}(e)})</ui5-label>\n <div class="summary-tags">\n <span>${s.tests} total</span>\n <ui5-tag design="Positive">${s.passed} passed</ui5-tag>\n <ui5-tag design="Negative">${s.failed} failed</ui5-tag>\n <ui5-tag design="Neutral">${s.skipped} skipped</ui5-tag>\n ${g}\n </div>\n </div>\n </div>\n</ui5-panel>`}function c(t){return t.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""")}function p(t){return t.flatMap(t=>[t,...p(t.suites)])}function g(t,e){return e?p(t).find(t=>t.uid===e)?.label??"Unknown suite":"All suites"}function f(t,e,s){return t.map(t=>`<div class="suite-item${t.uid===e?" suite-item--active":""}" data-suite-uid="${encodeURIComponent(t.uid)}" style="padding-left:${1.25*s}rem">${c(t.label)}</div>`+f(t.suites,e,s+1)).join("")}function v(t,e){return`<div class="suite-item${""===e?" suite-item--active":""}" data-suite-uid="">All suites</div>`+f(t,e,1)}function h(t){return t.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""")}function m(t,e){const{tests:s}=t,n=s.length>1e3,i=n?s.slice(0,1e3):s,a=s.filter(t=>"passed"===t.status).length,r=s.filter(t=>"failed"===t.status).length,u=s.filter(t=>"skipped"===t.status).length,d=s.length-a-r-u,c=[`<span>${s.length} tests</span>`,`<ui5-tag design="Positive">${a} passed</ui5-tag>`,`<ui5-tag design="Negative">${r} failed</ui5-tag>`,`<ui5-tag design="Neutral">${u} skipped</ui5-tag>`,...d>0?[`<ui5-tag design="None">${d} other</ui5-tag>`]:[]].join(""),p=n?'<div class="test-list-warning"><ui5-message-strip design="Warning" hide-close-button>More than 1000 tests match the current filters. Only the first 1000 are displayed. Use the filters above to narrow down the results.</ui5-message-strip></div>':"",g=i.map((t,e)=>{const s=function(t){return 0===t[o].length?"":`<ui5-breadcrumbs>${t[o].map(t=>`<ui5-breadcrumbs-item data-suite-uid="${encodeURIComponent(t.uid)}">${h(t.label)}</ui5-breadcrumbs-item>`).join("")}</ui5-breadcrumbs>`}(t),n=function(t){const e=[`<div class="test-detail-row"><span class="test-detail-label">Duration</span><span>${l(t.duration)}</span></div>`];return t.message&&e.push(`<div class="test-detail-row"><span class="test-detail-label">Message</span><span>${h(t.message)}</span></div>`),t.trace&&e.push(`<div class="test-detail-row"><span class="test-detail-label">Stack</span><pre class="test-trace">${h(t.trace)}</pre></div>`),e.join("")}(t);return`<div class="test-row" data-key="${e}">\n <div class="test-row-header" data-index="${e}">\n <ui5-button class="test-toggle-btn" design="Transparent" data-index="${e}">[>]</ui5-button>\n <div class="test-row-meta">\n ${s}\n <span class="test-name">${h(t.name)}</span>\n </div>\n <div class="test-row-status">${i=t.status,"passed"===i?'<ui5-tag design="Positive">passed</ui5-tag>':"failed"===i?'<ui5-tag design="Negative">failed</ui5-tag>':"skipped"===i?'<ui5-tag design="Neutral">skipped</ui5-tag>':`<ui5-tag design="None">${h(i)}</ui5-tag>`}</div>\n </div>\n <div class="test-row-details" data-index="${e}" style="display:none">\n ${n}\n </div>\n</div>`;var i}).join("");return`<div class="test-list-header">\n <div class="test-list-tags">${c}</div>\n</div>\n<div id="sortRow" class="sort-row">\n <span>Sort By:</span>\n ${function(t,e){return e.sortBy.map(e=>{const s=""===e.key,n=t.sortBy===e.key;let i=h(e.label);!s&&n&&(i+=t.sortAscending?" ↑":" ↓");const a=n?"Emphasized":"Default";return`<ui5-button class="sort-btn" data-sort-key="${e.key}" design="${a}">${i}</ui5-button>`}).join("")}(t,e)}\n</div>\n${p}\n<div id="testRows">${g}</div>`}function b(){const t=location.hash.slice(1);if(!t)return{};const e=new URLSearchParams(t),s={},n=e.get("suite");null!==n&&(s.filterOnSuiteUid=n);const i=e.get("status");null!==i&&(s.filterOnStatus=i);const a=e.get("q");null!==a&&(s.search=a);const r=e.get("sort");null!==r&&(s.sortBy=r);const o=e.get("sort-order");return null!==o&&(s.sortAscending="desc"!==o),s}function y(t){const e=new URLSearchParams;e.set("suite",t.filterOnSuiteUid),e.set("status",t.filterOnStatus),e.set("q",t.search),e.set("sort",t.sortBy),e.set("sort-order",t.sortAscending?"asc":"desc"),history.pushState(null,"",`#${e.toString()}`)}var S=new class extends t{constructor(){super(),this._settings={filterOnStatus:s,sortBy:e},this._reset()}_reset(){this._state={report:{reportFormat:"CTRF",specVersion:"pre-1.0",results:{tool:{name:""},summary:{failed:0,other:0,passed:0,pending:0,skipped:0,start:0,stop:0,tests:0,duration:0},tests:[]}},filterOnSuiteUid:"",filterOnStatus:"",search:"",sortBy:"",sortAscending:!0,mode:"open",suites:[],tests:[]}}_needFilterOrSort(t){return void 0!==t.filterOnSuiteUid||void 0!==t.filterOnStatus||void 0!==t.search||void 0!==t.sortBy||void 0!==t.sortAscending}_filter(t){const{filterOnSuiteUid:e,filterOnStatus:s,search:n}=this._state;return e&&(t=t.filter(t=>t.suite?.join("\r")?.startsWith(e))),s&&(t="other"===s?t.filter(t=>!["passed","failed"].includes(t.status)):t.filter(t=>t.status===s)),n&&(t=t.filter(t=>t.name.includes(n))),t}_sort(t){const{sortBy:e,sortAscending:s}=this._state;if(!e)return t;const n=t.toSorted((t,s)=>"name"===e?t.name.localeCompare(s.name):"duration"===e?t.duration-s.duration:0);return s||n.reverse(),n}_injectBreadcrumbs(t,e){for(const s of t){const t=[];let n={uid:"",label:"",suites:e};const a=[];for(const e of s.suite??[]){a.push(e);const s=a.join("\r"),r=i(n.suites,s);if(!r)break;t.push(r),n=r}Object.assign(s,{[o]:t})}}_onInteraction(t,e){let s,n={},o=!1;t.report&&(this._reset(),this._state.report=t.report,s=(t=>{const e={uid:"",label:"",suites:[]};for(const s of t){const{suite:t}=s;if(r(t,e.suites)){let s=e;const n=[];for(const e of t){const t=e;n.push(t);const r=n.join("\r");let o=i(s.suites,r);o||(o={uid:r,label:/^https?:/.test(t)?a(t):t,suites:[]},s.suites.push(o)),s=o}}}return e.suites})(this._state.report.results.tests),this._injectBreadcrumbs(this._state.report.results.tests,s),n={...this._state,suites:s,mode:"display"},o=!0);let l=this._state.report.results.tests;this._needFilterOrSort(t)&&(l=this._filter(l),l=this._sort(l),o=!0),o&&this._update({...n,tests:l}),void 0!==e&&this[e]()}export(){const t=document.createElement("a"),e=new Blob([JSON.stringify(this._state.report)],{type:"application/json"});t.setAttribute("href",URL.createObjectURL(e)),t.setAttribute("download","report.json"),t.click()}};function $(){const t=S.state;return{filterOnSuiteUid:t.filterOnSuiteUid,filterOnStatus:t.filterOnStatus,search:t.search,sortBy:t.sortBy,sortAscending:t.sortAscending}}function k(){const t=document.querySelector("#suitePopoverContent");t&&(t.innerHTML=v(S.state.suites,S.state.filterOnSuiteUid))}var O=document.createElement("input");function _(){return d(S.state)+(t=S.state,e=S.settings,`<div class="filter-bar">\n <div class="filter-field">\n <ui5-label>Suite</ui5-label>\n <ui5-button id="suiteFilterBtn" design="Default">${c(g(t.suites,t.filterOnSuiteUid))} ▾</ui5-button>\n </div>\n <div class="filter-field">\n <ui5-label>Status</ui5-label>\n <ui5-select id="statusSelect">${e.filterOnStatus.map(e=>`<ui5-option value="${e.key}"${t.filterOnStatus===e.key?" selected":""}>${c(e.label)}</ui5-option>`).join("")}</ui5-select>\n </div>\n <div class="filter-field filter-field--grow">\n <ui5-label>Search</ui5-label>\n <ui5-input id="searchInput" value="${c(t.search)}" placeholder="Search tests..."></ui5-input>\n </div>\n</div>`)+function(t,e){return`<div id="testList">${m(t,e)}</div>`}(S.state,S.settings);var t,e}function w(){const t=document.querySelector("#app");t&&(t.innerHTML="display"===S.state.mode?_():'<div class="open-mode">\n <ui5-title level="H2">UI5 Test Runner Report</ui5-title>\n <ui5-button id="openBtn" design="Emphasized">Open Report</ui5-button>\n</div>',k(),function(){document.querySelector("#suiteFilterBtn")?.addEventListener("click",t=>{k();const e=document.querySelector("#suitePopover");e&&(e.opener=t.currentTarget,e.open=!0)}),document.querySelector("#suitePopoverContent")?.addEventListener("click",t=>{const e=t.target.closest("[data-suite-uid]");if(!e)return;const s=decodeURIComponent(e.dataset.suiteUid??"");S.interaction({filterOnSuiteUid:s}),y($()),A(),document.querySelector("#suitePopover").open=!1}),document.querySelector("#statusSelect")?.addEventListener("change",t=>{const e=t.detail;S.interaction({filterOnStatus:e.selectedOption.value}),y($())});const t=document.querySelector("#searchInput"),e=function(t,e){let s;return(...n)=>{clearTimeout(s),s=setTimeout(()=>t(...n),e)}}(t=>{S.interaction({search:t}),y($())},250);t?.addEventListener("input",t=>{const s=t.target;e(s.value)}),t?.addEventListener("keydown",t=>{if("Enter"===t.key){const e=t.target;S.interaction({search:e.value}),y($())}})}(),B(),L())}function A(){const t=document.querySelector("#suiteFilterBtn");if(!t)return;const e=S.state.filterOnSuiteUid,s=function t(e){return e.flatMap(e=>[e,...t(e.suites)])}(S.state.suites);t.textContent=`${e?s.find(t=>t.uid===e)?.label??"Unknown suite":"All suites"} ▾`}function B(){document.querySelector("#sortRow")?.addEventListener("click",t=>{const e=t.target.closest(".sort-btn");if(!e)return;const s=e.dataset.sortKey??"",{sortBy:n,sortAscending:i}=S.state;""===s?S.interaction({sortBy:""}):n!==s?S.interaction({sortBy:s}):i?S.interaction({sortAscending:!1}):S.interaction({sortBy:""}),y($())})}function U(t,e){const s=t.querySelector(`.test-row-details[data-index="${e}"]`),n=t.querySelector(`.test-toggle-btn[data-index="${e}"]`);if(!s)return;const i="none"!==s.style.display;s.style.display=i?"none":"block",n&&(n.textContent=i?"[>]":"[V]")}function L(){const t=document.querySelector("#testRows");t&&(t.addEventListener("click",e=>{const s=e.target,n=s.closest(".test-toggle-btn");if(n)return e.stopPropagation(),void U(t,n.dataset.index??"");const i=s.closest(".test-row-header");i&&!s.closest("ui5-breadcrumbs")&&U(t,i.dataset.index??"")}),t.addEventListener("item-click",t=>{const e=t.detail,s=decodeURIComponent(e.item.dataset.suiteUid??"");S.interaction({filterOnSuiteUid:s}),y($()),A(),k()}))}function j(t){"mode"in t?w():"tests"in t&&function(){const t=document.querySelector("#testList");t&&(t.innerHTML=m(S.state,S.settings),L(),B())}()}O.type="file",O.accept=".json",O.style.display="none",O.addEventListener("change",()=>{const t=O.files?.[0];t&&(t.text().then(t=>{const e=JSON.parse(t);S.interaction({report:e})}).catch(()=>{}),O.value="")}),globalThis.addEventListener("popstate",()=>{const t=b();Object.keys(t).length>0&&S.interaction(t),A()}),document.addEventListener("DOMContentLoaded",()=>{document.body.append(O),function(){const t=document.querySelector("#app");t&&t.insertAdjacentHTML("beforebegin",'<ui5-popover id="suitePopover" header-text="Suite filter" placement="Bottom" style="max-width: 320px;">\n <div id="suitePopoverContent" style="padding: 0.5rem; min-width: 240px; max-height: 300px; overflow-y: auto;"></div>\n <div slot="footer" style="display:flex;justify-content:flex-end;padding:0.5rem;">\n <ui5-button id="suitePopoverClose" design="Transparent">Close</ui5-button>\n </div>\n </ui5-popover>')}(),document.addEventListener("click",t=>{const e=t.target;e.closest("#openBtn")&&O.click(),e.closest("#exportBtn")&&S.interaction({action:"export"})}),document.querySelector("#suitePopoverClose")?.addEventListener("click",()=>{document.querySelector("#suitePopover").open=!1}),S.connect(j);const t=b();Object.keys(t).length>0&&S.interaction(t),w()})}();
|