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.
Files changed (186) hide show
  1. package/README.md +3 -2
  2. package/dist/Npm.js +80 -0
  3. package/dist/browsers/IBrowser.js +1 -0
  4. package/dist/browsers/factory.js +9 -0
  5. package/dist/browsers/puppeteer.js +158 -0
  6. package/dist/cli.js +17 -0
  7. package/dist/configuration/CommandLine.js +112 -0
  8. package/dist/configuration/Configuration.js +1 -0
  9. package/dist/configuration/ConfigurationValidator.js +79 -0
  10. package/dist/configuration/Option.js +1 -0
  11. package/dist/configuration/OptionValidationError.js +15 -0
  12. package/dist/configuration/indexedOptions.js +13 -0
  13. package/dist/configuration/options.js +191 -0
  14. package/dist/configuration/validators/OptionValidator.js +1 -0
  15. package/dist/configuration/validators/boolean.js +15 -0
  16. package/dist/configuration/validators/browser.js +11 -0
  17. package/dist/configuration/validators/fsEntry.js +70 -0
  18. package/dist/configuration/validators/index.js +20 -0
  19. package/dist/configuration/validators/integer.js +10 -0
  20. package/dist/configuration/validators/percent.js +17 -0
  21. package/dist/configuration/validators/regexp.js +20 -0
  22. package/dist/configuration/validators/string.js +7 -0
  23. package/dist/configuration/validators/timeout.js +24 -0
  24. package/dist/configuration/validators/url.js +8 -0
  25. package/dist/modes/ModeFunction.js +1 -0
  26. package/dist/modes/Modes.js +9 -0
  27. package/dist/modes/execute.js +27 -0
  28. package/dist/modes/help.js +3 -0
  29. package/dist/modes/log/ILogStorage.js +1 -0
  30. package/dist/modes/log/LogMetrics.js +9 -0
  31. package/dist/modes/log/LogReader.js +37 -0
  32. package/dist/modes/log/LogStorage.js +68 -0
  33. package/dist/modes/log/REserve.js +101 -0
  34. package/dist/modes/log/index.js +58 -0
  35. package/dist/modes/test/REserve.js +31 -0
  36. package/dist/modes/test/agent.js +8 -0
  37. package/dist/modes/test/browser.js +37 -0
  38. package/dist/modes/test/index.js +66 -0
  39. package/dist/modes/test/pageTask.js +145 -0
  40. package/dist/modes/test/report.js +3 -0
  41. package/dist/modes/test/server.js +109 -0
  42. package/dist/modes/version.js +11 -0
  43. package/dist/platform/Exit.js +139 -0
  44. package/dist/platform/FileSystem.js +13 -0
  45. package/dist/platform/Host.js +10 -0
  46. package/dist/platform/Http.js +38 -0
  47. package/dist/platform/Path.js +5 -0
  48. package/dist/platform/Process.js +133 -0
  49. package/dist/platform/Terminal.js +47 -0
  50. package/dist/platform/Thread.js +43 -0
  51. package/dist/platform/ZLib.js +7 -0
  52. package/dist/platform/assert.js +17 -0
  53. package/dist/platform/constants.js +5 -0
  54. package/dist/platform/environment.js +28 -0
  55. package/dist/platform/index.js +13 -0
  56. package/dist/platform/logger/ILogger.js +1 -0
  57. package/dist/platform/logger/allCompressed.js +54 -0
  58. package/dist/platform/logger/compress.js +277 -0
  59. package/dist/platform/logger/output/BaseLoggerOutput.js +158 -0
  60. package/dist/platform/logger/output/InteractiveLoggerOutput.js +102 -0
  61. package/dist/platform/logger/output/StaticLoggerOutput.js +32 -0
  62. package/dist/platform/logger/output/factory.js +10 -0
  63. package/dist/platform/logger/output.js +58 -0
  64. package/dist/platform/logger/proxy.js +6 -0
  65. package/dist/platform/logger/toInternalLogAttributes.js +22 -0
  66. package/dist/platform/logger/types.js +7 -0
  67. package/dist/platform/logger.js +138 -0
  68. package/dist/platform/mock.js +104 -0
  69. package/dist/platform/version.js +8 -0
  70. package/dist/platform/workerBootstrap.js +21 -0
  71. package/dist/reports/html.js +46 -0
  72. package/dist/types/AgentState.js +1 -0
  73. package/dist/types/CommonTestReportFormat.js +50 -0
  74. package/dist/types/IError.js +1 -0
  75. package/dist/types/IUserInterfaceController.js +1 -0
  76. package/dist/types/typeUtilities.js +1 -0
  77. package/dist/ui/agent.js +3 -0
  78. package/dist/ui/html-report.js +2 -0
  79. package/dist/ui/lib.js +1 -0
  80. package/dist/ui/log-viewer.js +2 -0
  81. package/dist/utils/node/Folder.js +28 -0
  82. package/dist/utils/node/FramedStreamReader.js +86 -0
  83. package/dist/utils/node/FramedStreamWriter.js +27 -0
  84. package/dist/utils/shared/ProgressBar.js +43 -0
  85. package/dist/utils/shared/TestReportBuilder.js +48 -0
  86. package/dist/utils/shared/memoize.js +19 -0
  87. package/dist/utils/shared/object.js +8 -0
  88. package/dist/utils/shared/parallelize.js +59 -0
  89. package/dist/utils/shared/string.js +23 -0
  90. package/dist/utils/shared/toIError.js +17 -0
  91. package/package.json +73 -50
  92. package/.releaserc +0 -5
  93. package/index.js +0 -175
  94. package/jest.config.json +0 -31
  95. package/src/add-test-pages.js +0 -67
  96. package/src/batch.js +0 -214
  97. package/src/browsers.js +0 -319
  98. package/src/capabilities/index.js +0 -204
  99. package/src/capabilities/tests/basic/iframe.html +0 -8
  100. package/src/capabilities/tests/basic/index.html +0 -12
  101. package/src/capabilities/tests/basic/index.js +0 -20
  102. package/src/capabilities/tests/basic/ui5.html +0 -24
  103. package/src/capabilities/tests/dynamic-include/index.js +0 -21
  104. package/src/capabilities/tests/dynamic-include/mix.html +0 -11
  105. package/src/capabilities/tests/dynamic-include/one.html +0 -11
  106. package/src/capabilities/tests/dynamic-include/post.js +0 -3
  107. package/src/capabilities/tests/dynamic-include/test.js +0 -1
  108. package/src/capabilities/tests/dynamic-include/two.html +0 -11
  109. package/src/capabilities/tests/index.js +0 -16
  110. package/src/capabilities/tests/local-storage/index.html +0 -16
  111. package/src/capabilities/tests/local-storage/index.js +0 -21
  112. package/src/capabilities/tests/screenshot/index.html +0 -23
  113. package/src/capabilities/tests/screenshot/index.js +0 -24
  114. package/src/capabilities/tests/scripts/coverage.html +0 -32
  115. package/src/capabilities/tests/scripts/iframe.html +0 -18
  116. package/src/capabilities/tests/scripts/index.js +0 -59
  117. package/src/capabilities/tests/scripts/qunit.html +0 -22
  118. package/src/capabilities/tests/scripts/testsuite.html +0 -10
  119. package/src/capabilities/tests/scripts/testsuite.js +0 -8
  120. package/src/capabilities/tests/timeout/index.html +0 -21
  121. package/src/capabilities/tests/timeout/index.js +0 -19
  122. package/src/capabilities/tests/traces/index.html +0 -18
  123. package/src/capabilities/tests/traces/index.js +0 -81
  124. package/src/capabilities/tests/ui5/focus.html +0 -89
  125. package/src/capabilities/tests/ui5/index.js +0 -39
  126. package/src/capabilities/tests/ui5/language.html +0 -50
  127. package/src/capabilities/tests/ui5/timezone.html +0 -27
  128. package/src/clean.js +0 -22
  129. package/src/cors.js +0 -21
  130. package/src/coverage.js +0 -384
  131. package/src/csv-reader.js +0 -36
  132. package/src/csv-writer.js +0 -55
  133. package/src/defaults/.nycrc.json +0 -4
  134. package/src/defaults/browser.js +0 -217
  135. package/src/defaults/happy-dom.js +0 -123
  136. package/src/defaults/jsdom/compatibility.js +0 -163
  137. package/src/defaults/jsdom/debug.js +0 -23
  138. package/src/defaults/jsdom/resource-loader.js +0 -44
  139. package/src/defaults/jsdom/sap.ui.test.matchers.visible.js +0 -39
  140. package/src/defaults/jsdom.js +0 -95
  141. package/src/defaults/json-report.js +0 -36
  142. package/src/defaults/junit-xml-report.js +0 -90
  143. package/src/defaults/playwright.js +0 -142
  144. package/src/defaults/puppeteer.js +0 -124
  145. package/src/defaults/report/common.js +0 -38
  146. package/src/defaults/report/decompress.js +0 -19
  147. package/src/defaults/report/default.html +0 -99
  148. package/src/defaults/report/main.js +0 -69
  149. package/src/defaults/report/progress.js +0 -60
  150. package/src/defaults/report/styles.css +0 -66
  151. package/src/defaults/report.js +0 -91
  152. package/src/defaults/scan-ui5.js +0 -26
  153. package/src/defaults/selenium-webdriver/chrome.js +0 -39
  154. package/src/defaults/selenium-webdriver/edge.js +0 -24
  155. package/src/defaults/selenium-webdriver/firefox.js +0 -30
  156. package/src/defaults/selenium-webdriver.js +0 -129
  157. package/src/defaults/text-report.js +0 -108
  158. package/src/defaults/webdriverio.js +0 -80
  159. package/src/end.js +0 -62
  160. package/src/endpoints.js +0 -219
  161. package/src/error.js +0 -54
  162. package/src/get-job-progress.js +0 -78
  163. package/src/handle.js +0 -43
  164. package/src/if.js +0 -10
  165. package/src/inject/jest2qunit.js +0 -289
  166. package/src/inject/opa-iframe-coverage.js +0 -22
  167. package/src/inject/post.js +0 -141
  168. package/src/inject/qunit-hooks.js +0 -107
  169. package/src/inject/qunit-redirect.js +0 -65
  170. package/src/inject/ui5-coverage.js +0 -33
  171. package/src/job-mode.js +0 -65
  172. package/src/job.js +0 -493
  173. package/src/npm.js +0 -136
  174. package/src/options.js +0 -95
  175. package/src/output.js +0 -739
  176. package/src/parallelize.js +0 -63
  177. package/src/qunit-hooks.js +0 -219
  178. package/src/report.js +0 -89
  179. package/src/reserve.js +0 -25
  180. package/src/start.js +0 -133
  181. package/src/symbols.js +0 -8
  182. package/src/tests.js +0 -183
  183. package/src/timeout.js +0 -53
  184. package/src/tools.js +0 -179
  185. package/src/ui5.js +0 -199
  186. 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 {};
@@ -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("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}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("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}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("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}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}">[&gt;]</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?" &#8593;":" &#8595;");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))} &#9662;</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()})}();