ui5-test-runner 6.0.0-beta.5 → 6.0.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 +20 -29
- package/dist/Command.js +52 -0
- package/dist/Npm.js +154 -74
- package/dist/browsers/RecoverableBrowserError.js +6 -0
- package/dist/browsers/chromeArguments.js +5 -0
- package/dist/browsers/chromeOptions.js +29 -0
- package/dist/browsers/consoleMessage.js +23 -0
- package/dist/browsers/factory.js +168 -3
- package/dist/browsers/networkResponse.js +15 -0
- package/dist/browsers/playwright.js +104 -0
- package/dist/browsers/puppeteer.js +54 -88
- package/dist/browsers/seleniumWebdriver.js +124 -0
- package/dist/browsers/webdriverio.js +85 -0
- package/dist/cli.js +18 -12
- package/dist/configuration/CommandLine.js +46 -26
- package/dist/configuration/ConfigurationValidator.js +132 -18
- package/dist/configuration/OptionValidationError.js +50 -2
- package/dist/configuration/derivations.js +8 -0
- package/dist/configuration/options.js +380 -23
- package/dist/configuration/validations.js +135 -0
- package/dist/configuration/validators/boolean.js +1 -1
- package/dist/configuration/validators/enumeration.js +7 -0
- package/dist/configuration/validators/fsEntry.js +12 -12
- package/dist/configuration/validators/index.js +8 -2
- package/dist/configuration/validators/integer.js +7 -1
- package/dist/configuration/validators/json.js +23 -0
- package/dist/configuration/validators/libraryMapping.js +28 -0
- package/dist/configuration/validators/percent.js +2 -2
- package/dist/configuration/validators/regexp.js +2 -2
- package/dist/configuration/validators/secret.js +8 -0
- package/dist/configuration/validators/string.js +1 -1
- package/dist/configuration/validators/timeout.js +2 -2
- package/dist/configuration/validators/url.js +3 -3
- package/dist/end.js +58 -0
- package/dist/if.js +22 -0
- package/dist/modes/Modes.js +2 -1
- package/dist/modes/batch/BatchItem.js +1 -0
- package/dist/modes/batch/batchTask.js +159 -0
- package/dist/modes/batch/index.js +98 -0
- package/dist/modes/batch/output.js +22 -0
- package/dist/modes/batch/report.js +45 -0
- package/dist/modes/batch/resolve.js +105 -0
- package/dist/modes/dumpConfig.js +6 -0
- package/dist/modes/execute.js +6 -5
- package/dist/modes/help.js +87 -2
- package/dist/modes/log/LogReader.js +3 -2
- package/dist/modes/log/LogStorage.js +31 -27
- package/dist/modes/log/index.js +38 -6
- package/dist/modes/log/{REserve.js → reserve.js} +25 -2
- package/dist/modes/mcp/IDocument.js +2 -0
- package/dist/modes/mcp/frontmatter.js +142 -0
- package/dist/modes/mcp/index.js +49 -0
- package/dist/modes/mcp/knowledgeBase.js +104 -0
- package/dist/modes/mcp/remoteKnowledgeBase.js +79 -0
- package/dist/modes/mcp/reserve.js +175 -0
- package/dist/modes/mcp/tools/getTopic.js +46 -0
- package/dist/modes/mcp/tools/index.js +6 -0
- package/dist/modes/mcp/tools/listTopics.js +14 -0
- package/dist/modes/test/PageContext.js +1 -0
- package/dist/modes/test/Server.js +99 -0
- package/dist/modes/test/browser.js +31 -17
- package/dist/modes/test/browserConfig.js +17 -0
- package/dist/modes/test/coverage/collect.js +38 -0
- package/dist/modes/test/coverage/index.js +3 -0
- package/dist/modes/test/coverage/instrument.js +70 -0
- package/dist/modes/test/coverage/nyc.js +15 -0
- package/dist/modes/test/coverage/report.js +71 -0
- package/dist/modes/test/coverage/settings.js +53 -0
- package/dist/modes/test/index.js +94 -34
- package/dist/modes/test/pageTask.js +293 -98
- package/dist/modes/test/report.js +20 -3
- package/dist/modes/test/reserve.js +76 -0
- package/dist/modes/test/screenshot.js +49 -0
- package/dist/platform/Crypto.js +9 -0
- package/dist/platform/Exit.js +33 -24
- package/dist/platform/FileSystem.js +6 -2
- package/dist/platform/Host.js +9 -1
- package/dist/platform/Http.js +54 -29
- package/dist/platform/Module.js +2 -1
- package/dist/platform/Path.js +5 -1
- package/dist/platform/Process.js +67 -13
- package/dist/platform/Terminal.js +12 -8
- package/dist/platform/Thread.js +1 -1
- package/dist/platform/assert.js +13 -12
- package/dist/platform/environment.js +9 -18
- package/dist/platform/index.js +1 -0
- package/dist/platform/logger/CompressionContext.js +117 -0
- package/dist/platform/logger/allCompressed.js +8 -7
- package/dist/platform/logger/compress.js +29 -129
- package/dist/platform/logger/output/BaseLoggerOutput.js +49 -31
- package/dist/platform/logger/output/InteractiveLoggerOutput.js +1 -1
- package/dist/platform/logger/output/LlmLoggerOutput.js +29 -0
- package/dist/platform/logger/output/factory.js +5 -1
- package/dist/platform/logger/output.js +24 -6
- package/dist/platform/logger/toInternalLogAttributes.js +10 -11
- package/dist/platform/logger/traceFileName.js +1 -0
- package/dist/platform/logger.js +52 -21
- package/dist/platform/mock.js +110 -19
- package/dist/platform/version.js +1 -2
- package/dist/platform/workerBootstrap.js +1 -1
- package/dist/reports/initReportBuilder.js +18 -0
- package/dist/reports/junit.js +64 -0
- package/dist/reports/saveReport.js +10 -0
- package/dist/reports/textReport.js +92 -0
- package/dist/reserveLogger.js +22 -0
- package/dist/sendToParentProcess.js +6 -0
- package/dist/start.js +79 -0
- package/dist/types/ISecretValue.js +1 -0
- package/dist/types/SecretValue.js +15 -0
- package/dist/ui/agent.js +4 -2
- package/dist/ui/html-report.js +2 -2
- package/dist/ui/lib.js +1 -1
- package/dist/ui/log-viewer.js +1 -1
- package/dist/utils/node/Folder.js +2 -4
- package/dist/utils/node/FramedStreamReader.js +4 -5
- package/dist/utils/node/FramedStreamWriter.js +6 -5
- package/dist/utils/node/anonymize.js +11 -0
- package/dist/utils/node/timeout.js +11 -0
- package/dist/utils/shared/ProgressBar.js +1 -2
- package/dist/utils/shared/TestReportBuilder.js +11 -7
- package/dist/utils/shared/host.js +11 -0
- package/dist/utils/shared/object.js +12 -3
- package/dist/utils/shared/schema.js +20 -0
- package/dist/utils/shared/string.js +1 -0
- package/dist/utils/shared/toIError.js +3 -7
- package/dist/utils/shared/version.js +52 -0
- package/package.json +3 -79
- package/dist/configuration/validators/browser.js +0 -11
- package/dist/modes/test/REserve.js +0 -31
- package/dist/modes/test/server.js +0 -109
package/dist/ui/agent.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
!function(){var{top:
|
|
1
|
+
!function(){var e="ui5-test-runner",{top:t}=window;
|
|
2
2
|
/* v8 ignore next -- @preserve */
|
|
3
|
-
null!==
|
|
3
|
+
null!==t&&(window!==t||window!==window.parent)&&(e=>{const t=e.top;Object.defineProperty(e,"__coverage__",{get:()=>t.__coverage__,set:e=>(t.__coverage__=e,!0)})})(window);var n=()=>window[e].config,s=(e=>{let t,n;return()=>{if(n)throw n;if(void 0===t)try{t=e()}catch(s){throw n=s,s}return t}})(()=>(n().coverageRemoteInstrument??[]).map(e=>new RegExp(e)));function o(e){if(!e.endsWith(".js"))return!1;const t=/(?:^|\/)(?:test-)?resources\/(.*)$/.exec(e);if(t){const e=t[1];return s().some(t=>t.test(e))}return!0}function r(e){const t=new URL(e,document.baseURI);return t.searchParams.set("instrument","true"),t.toString()}var i=HTMLScriptElement.prototype.setAttribute;HTMLScriptElement.prototype.setAttribute=function(e,t){"data-sap-ui-module"===e&&o(new URL(this.src,document.baseURI).pathname)&&(this.src=r(this.src)),i.call(this,e,t)};var a=XMLHttpRequest.prototype.open;XMLHttpRequest.prototype.open=function(e,t,n,s,i){const d=t&&window.sap&&window.sap.ui&&window.sap.ui.loader&&o(new URL(t.toString(),document.baseURI).pathname)?r(t.toString()):t;return a.call(this,e,d,n??!0,s,i)};var d={done:!1,type:void 0},u="[ui5-test-runner]",c=console.debug.bind(console),l=console.warn.bind(console),p=console.error.bind(console),g=e=>c(`${u}${e}`);g.warn=e=>l(`${u}${e}`),g.error=e=>p(`${u}${e}`);var w=class e{static pages;constructor(){g("custom JsUnitTestSuite built"),e.pages=[]}addTestPage(t){g(`addTestPage('${t}')`),e.pages.push(t)}};Object.assign(window,{jsUnitTestSuite:w});var m=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,n]=e.split("@",2);this._results.tool.name=t,void 0!==n&&(this._results.tool.version=n),this._results.summary.start||(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}},f=m.results,h=e=>"object"==typeof e&&null!==e&&"getId"in e&&"function"==typeof e.getId&&"getMetadata"in e&&"function"==typeof e.getMetadata,v=e=>({"ui5:class":e.getMetadata().getName(),"ui5:id":e.getId()}),y=e=>{const t=[],n=[],s=JSON.stringify(e,function(e,s){if("object"==typeof s&&s){if(h(s))return v(s);const e=t.indexOf(s);if(-1!==e)return n[e]=!0,null;t.push(s)}return s});if(0===n.length)return s;const o=[];return JSON.stringify(e,function(e,s){if("object"==typeof s&&s){if(h(s))return v(s);const e=t.indexOf(s);if(n[e])return o[e]?{"circular:ref":e}:(o[e]=!0,Array.isArray(s)?{"circular:id":e,"circular:array":[...s]}:{"circular:id":e,...s})}return s})},$=e=>e??"unknown",U=e=>{Object.assign(d,e)},b=()=>d.done&&"suite"===d.type,I=()=>{const{modules:e}=QUnit.config,t=new URL(window.location.href).searchParams.get("moduleId");if(null!==t)return e.find(e=>e.moduleId===t)?.tests.length??0;let n=0;for(const s of e)n+=s.tests.length;return n},x=()=>{const{agentNoTestsTimeout:e,agentScreenshotTimeout:t,screenshot:s,pageId:o}=n();let r=0,i=0;const a={},u={};let c;U({type:"QUnit",isOpa:!1,executed:r,total:0,errors:i,pendingScreenshot:!1}),QUnit.begin(e=>{g(`QUnit.begin({totalTests: ${e.totalTests}, modules: [...${e.modules.length}...]})`),m.begin(`QUnit@${window.QUnit.version}`);const t=!!window?.sap?.ui?.test?.Opa5;if(t&&n().splitOpa&&QUnit.config.modules.length>1&&!new URL(window.location.href).searchParams.has("moduleId")){const e=QUnit.config.modules.map(({moduleId:e})=>{const t=new URL(window.location.href);return t.searchParams.set("moduleId",e),t.href});return void Object.assign(d,{done:!0,type:"suite",pages:e})}U({isOpa:t,executed:r,total:I(),errors:i})}),QUnit.moduleStart(e=>{if(g(`QUnit.moduleStart({name: "${e.name}"})`),b())return;void 0!==c&&(g("New tests found, cancelling done call"),clearTimeout(c),c=void 0);const t=I();"QUnit"===d.type&&t>d.total&&U({isOpa:!!window?.sap?.ui?.test?.Opa5,total:t})}),QUnit.log(e=>{if(g(`QUnit.log({testId: ${e.testId}, result: ${e.result}, name: "${e.name}", module: "${e.module}}")`),b())return;const n=$(e.testId);if(a[n]??=[],a[n].push(e),!s||"QUnit"!==d.type||!d.isOpa)return;const r=a[n].length-1,i=`${o}-${n}-${r}.png`;u[n]??=[],u[n].push(i),U({pendingScreenshot:i}),(window.sap?.ui?.test?.Opa5)?.prototype.waitFor({timeout:Math.ceil(t/1e3)+1,pollingInterval:50,autoWait:!1,check:()=>"QUnit"===d.type&&!d.pendingScreenshot})});QUnit.testDone(e=>{if(g(`QUnit.testDone({testId: ${e.testId}, passed: ${e.passed}, failed: ${e.failed}, name: "${e.name}", module: "${e.module}}")`),b())return;let t="passed";const n={id:e.testId,suite:[e.module],name:e.name,duration:e.runtime,status:"other"};e.failed>0?(++i,t="failed",((e,t)=>{const n=a[$(t.testId)];if(!n)return void(e.message="No logs");const s=n.find(({result:e})=>!e);s?(e.message=s.message??"failed",e.trace=s.source,e.extra={actual:y(s.actual),expected:y(s.expected)}):e.message="No error log",e.extra??={},e.extra.QUnitLogs=n.map(e=>({...e,actual:y(e.actual),expected:y(e.expected)}))})(n,e)):e.skipped?t="skipped":e.todo&&(t="pending"),n.status=t;const s=$(e.testId),o=u[s];if(void 0!==o&&o.length>0){const e=a[s];n.attachments=o.map((t,n)=>({
|
|
4
|
+
/* v8 ignore next -- @preserve */
|
|
5
|
+
name:e?.[n]?.message??"no message",contentType:"image/png",path:t}))}m.test(n),U({executed:++r,errors:i}),delete a[s],delete u[s]});const l=()=>{m.end(),U({done:!0})};QUnit.done(t=>{g(`QUnit.done({passed: ${t.passed}, failed: ${t.failed}, total: ${t.total}}")`),b()||(0===m.results.summary.tests?(g.warn("QUnit.done triggered but no tests were executed, waiting for asynchronous tests loading"),c=setTimeout(l,e)):l())})};window.addEventListener("load",()=>{g("DOM load event fired"),g(`URL: ${document.URL}`),g(`viewport: ${window.innerWidth}x${window.innerHeight}`),g(`viewport/outer: ${window.outerWidth}x${window.outerHeight}`),g(`viewport/inner: ${window.innerWidth}x${window.innerHeight}`),g(`screen: ${screen.width}x${screen.height}, devicePixelRatio: ${window.devicePixelRatio}`),g(`position: ${window.screenX},${window.screenY}`),g(`userAgent: ${navigator.userAgent}`);const{agentDetectionTimeout:e,agentDetectionInterval:t,agentDetectionMaxInterval:s}=n(),o=Date.now();d.loaded=o;let r=t;const i=()=>{"function"==typeof window.suite?(g("suite detected"),(async()=>{g("executing suite"),await(window.suite?.()),g("suite executed");const e=w.pages?{done:!0,type:"suite",pages:w.pages}:{done:!0,type:"unknown"};Object.assign(d,e)})()):void 0!==window.QUnit?(g("QUnit detected"),x()):Date.now()-o<e?(r=Math.min(s,2*r),g(`Waiting ${r}ms before next detection attempt`),setTimeout(i,r)):(g.error("Nothing detected"),Object.assign(d,{done:!0,type:"unknown"}))};i()});var Q=e=>e instanceof Error?{name:e.name,message:e.message,stack:e.stack,...e.cause?{cause:Q(e.cause)}:{},...e instanceof AggregateError&&{errors:e.errors.map(e=>Q(e))}}:Q(new Error(JSON.stringify(e))),_=(e,t)=>{void 0===d.uncaughtErrors&&(d.uncaughtErrors=[]),d.uncaughtErrors.push({...Q(t),event:e})};window.addEventListener("error",e=>_("error",e.error)),window.addEventListener("unhandledrejection",e=>_("unhandledrejection",e.reason));var O=()=>{throw new Error("[ui5-test-runner] localStorage.addEventListener is not supported in parallel mode")};!function(){if((window["ui5-test-runner"]?.config?.parallel??1)<=1)return;const e=new Map;Object.defineProperty(window,"localStorage",{configurable:!0,value:{getItem:t=>e.get(t)??null,setItem:(t,n)=>e.set(t,n),removeItem:t=>e.delete(t),clear:()=>e.clear(),key:t=>[...e.keys()][t]??null,get length(){return e.size},addEventListener:O,removeEventListener:O}})}(),window[e]={...window[e],state:d,results:f}}();
|
package/dist/ui/html-report.js
CHANGED
|
@@ -1,2 +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()})}();
|
|
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-grid--2{grid-template-columns:repeat(2,1fr)}.summary-tags{flex-wrap:wrap;align-items:center;gap:.5rem;margin-top:.25rem;display:flex}.config-body{padding:.75rem 1rem}.config-json{white-space:pre-wrap;background:var(--sapField_ReadOnly_Background,#f2f2f2);border-radius:.25rem;max-height:400px;margin:0;padding:.75rem;font-family:monospace;font-size:.8rem;overflow-y:auto}.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}.test-detail-row--block{align-items:flex-start}.test-value{background:var(--sapField_ReadOnly_Background,#f2f2f2);border-radius:.2rem;padding:.1rem .3rem;font-family:monospace;font-size:.875rem}.qunit-logs-collapsible{flex:1}.qunit-logs-toggle{cursor:pointer;color:var(--sapLinkColor,#0a6ed1);text-align:left;background:0 0;border:none;padding:0;font-size:.875rem}.qunit-logs-body{margin-top:.25rem}.qunit-log-row{padding:.15rem 0;font-size:.8rem}.qunit-log-pass{color:var(--sapPositiveColor,#107e3e)}.qunit-log-fail{color:var(--sapNegativeColor,#b00)}.qunit-log-values{color:var(--sapContent_LabelColor,#6a6d70);font-size:.75rem}.attachments-list{flex-wrap:wrap;flex:1;gap:.5rem;display:flex}.attachment-item{flex-direction:column;align-items:center;gap:.2rem;display:flex}.attachment-caption{color:var(--sapContent_LabelColor,#6a6d70);text-overflow:ellipsis;white-space:nowrap;max-width:120px;font-size:.75rem;overflow:hidden}.attachment-thumb{object-fit:cover;border:1px solid var(--sapList_BorderColor,#d9d9d9);cursor:pointer;background:var(--sapField_ReadOnly_Background,#f2f2f2);border-radius:.25rem;width:120px;height:80px}.attachment-thumb--error{color:var(--sapContent_LabelColor,#6a6d70);text-align:center;justify-content:center;align-items:center;padding:.25rem;font-size:.7rem;display:flex}\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),Object.keys(t).length>0&&this._updateCb({...t}),t}_onConnect(){}connect(t){this._updateCb=t,this._onConnect()}interaction(t){const{action:e,...s}=t,n=this._update(s);(void 0!==e||Object.keys(n).length>0)&&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`}var u=({machine:t,cpus:e})=>{const s=(t=>{const e={};for(const{model:s}of t)e[s]=(e[s]??0)+1;return Object.entries(e).map(([t,e])=>({model:t,count:e}))})(e).map(({model:t,count:e})=>1===e?t:`${e}x ${t}`);return s.length>0?`${t} / ${s.join(", ")}`:t};function c(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?c(e.reportId):"N/A",d=e.generatedBy?c(e.generatedBy):"N/A",p=void 0===s.duration?"N/A":l(s.duration),g=e.results.environment?.extra?.browserName,v=e.results.environment?.extra?.browserVersion,m=g&&v?`${g} ${v}`:g??"",f=m?c(m):"N/A",h=e.results.environment?.extra?.machine,b=e.results.environment?.extra?.cpus,y=h?c(u({machine:h,cpus:b??[]})):e.results.environment?.osPlatform?c(e.results.environment.osPlatform):"N/A",$=s.pending+s.other,S=$>0?`<ui5-tag design="None">${$} 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>${p}</span>\n </div>\n <div class="summary-grid summary-grid--2">\n <ui5-label>Browser</ui5-label>\n <ui5-label>OS</ui5-label>\n <span>${f}</span>\n <span>${y}</span>\n </div>\n <div>\n <ui5-label>Tests (${function(t){const{name:e,version:s}=t.results.tool;return s?`${c(e)}@${c(s)}`:c(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 ${S}\n </div>\n </div>\n </div>\n</ui5-panel>`}function p(t){return t.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""")}function g(t){return t.flatMap(t=>[t,...g(t.suites)])}function v(t,e){return e?g(t).find(t=>t.uid===e)?.label??"Unknown suite":"All suites"}function m(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">${p(t.label)}</div>`+m(t.suites,e,s+1)).join("")}function f(t,e){return`<div class="suite-item${""===e?" suite-item--active":""}" data-suite-uid="">All suites</div>`+m(t,e,1)}function h(t){return t.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""")}function b(t,e){const s=[`<div class="test-detail-row"><span class="test-detail-label">Duration</span><span>${l(t.duration)}</span></div>`,t.message?`<div class="test-detail-row"><span class="test-detail-label">Message</span><span>${h(t.message)}</span></div>`:"",t.trace?`<div class="test-detail-row"><span class="test-detail-label">Stack</span><pre class="test-trace">${h(t.trace)}</pre></div>`:""];if("failed"===t.status&&t.extra){const{actual:i,expected:a}=t.extra;void 0!==i&&s.push(`<div class="test-detail-row"><span class="test-detail-label">Actual</span><code class="test-value">${h(String(i))}</code></div>`),void 0!==a&&s.push(`<div class="test-detail-row"><span class="test-detail-label">Expected</span><code class="test-value">${h(String(a))}</code></div>`);const r=t.extra.QUnitLogs;r&&r.length>0&&s.push(`<div class="test-detail-row test-detail-row--block">\n <span class="test-detail-label">Assertions</span>\n <div class="qunit-logs-collapsible">\n <button class="qunit-logs-toggle" data-row="${e}">[>] Assertions (${r.length})</button>\n <div class="qunit-logs-body" style="display:none">${n=r,n.map(t=>{const e=t,s=e.result?"✓":"✗",n=e.result?"qunit-log-pass":"qunit-log-fail",i=void 0===e.message?"—":h(String(e.message));let a="";return e.result||void 0===e.actual&&void 0===e.expected||(a=' <span class="qunit-log-values">[',void 0!==e.actual&&(a+=`actual: <code>${h(String(e.actual))}</code> `),void 0!==e.expected&&(a+=`expected: <code>${h(String(e.expected))}</code>`),a+="]</span>"),`<div class="qunit-log-row"><span class="${n}">${s}</span> ${i}${a}</div>`}).join("")}</div>\n </div>\n</div>`)}var n;return t.screenshot&&s.push(`<div class="test-detail-row">\n <span class="test-detail-label">Screenshot</span>\n <img class="attachment-thumb" data-src="${h(t.screenshot)}" data-row="${e}" data-att="screenshot" alt="failure screenshot" title="${h(t.screenshot)}" />\n</div>`),t.attachments&&t.attachments.length>0&&s.push(`<div class="test-detail-row test-detail-row--block">\n <span class="test-detail-label">Attachments</span>\n <div class="attachments-list">${function(t,e){return t.map((t,s)=>"image/png"===t.contentType?`<div class="attachment-item">\n <div class="attachment-caption">${h(t.name)}</div>\n <img class="attachment-thumb" data-src="${h(t.path)}" data-row="${e}" data-att="${s}" alt="${h(t.name)}" title="${h(t.path)}" />\n</div>`:`<div class="attachment-item"><a href="${h(t.path)}" target="_blank">${h(t.name)}</a></div>`).join("")}(t.attachments,e)}</div>\n</div>`),s.join("")}function y(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,l=s.filter(t=>"skipped"===t.status).length,u=s.length-a-r-l,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">${l} skipped</ui5-tag>`,...u>0?[`<ui5-tag design="None">${u} other</ui5-tag>`]:[]].join(""),d=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>':"",p=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=b(t,e);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${d}\n<div id="testRows">${p}</div>`}function $(){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 S(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 w=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=[],r=s.suite??[];for(const e of r){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=!1;if(t.report){this._reset(),this._state.report=t.report;const e=(t=>{const e={uid:"",label:"",suites:[]};for(const s of t){const{suite:t}=s;if(!r(t,e.suites))continue;let n=e;const o=[];for(const e of t){const t=e;o.push(t);const s=o.join("\r");let r=i(n.suites,s);r||(r={uid:s,label:/^https?:/.test(t)?a(t):t,suites:[]},n.suites.push(r)),n=r}}return e.suites})(this._state.report.results.tests);this._injectBreadcrumbs(this._state.report.results.tests,e),s={...this._state,suites:e,mode:"display"},n=!0}let o=this._state.report.results.tests;this._needFilterOrSort(t)&&(o=this._filter(o),o=this._sort(o),n=!0),n&&this._update({...s,tests:o}),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 k(){const t=w.state;return{filterOnSuiteUid:t.filterOnSuiteUid,filterOnStatus:t.filterOnStatus,search:t.search,sortBy:t.sortBy,sortAscending:t.sortAscending}}function x(){const t=document.querySelector("#suitePopoverContent");t&&(t.innerHTML=f(w.state.suites,w.state.filterOnSuiteUid))}var A=document.createElement("input");function O(){return d(w.state)+function(t){const e=t.report.extra?.configuration;return e?`<ui5-panel id="configPanel" collapsed no-animation="true">\n <div slot="header" class="panel-header">\n <ui5-title level="H4">Configuration</ui5-title>\n </div>\n <div class="config-body">\n <pre class="config-json">${JSON.stringify(e,null,2)}</pre>\n </div>\n</ui5-panel>`:""}(w.state)+(t=w.state,e=w.settings,`<div class="filter-bar">\n <div class="filter-field">\n <ui5-label>Suite</ui5-label>\n <ui5-button id="suiteFilterBtn" design="Default">${p(v(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":""}>${p(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="${p(t.search)}" placeholder="Search tests..."></ui5-input>\n </div>\n</div>`)+function(t,e){return`<div id="testList">${y(t,e)}</div>`}(w.state,w.settings);var t,e}function _(){const t=document.querySelector("#app");t&&(t.innerHTML="display"===w.state.mode?O():'<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>',x(),function(){document.querySelector("#suiteFilterBtn")?.addEventListener("click",t=>{x();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??"");w.interaction({filterOnSuiteUid:s}),S(k()),q(),document.querySelector("#suitePopover").open=!1}),document.querySelector("#statusSelect")?.addEventListener("change",t=>{const e=t.detail;w.interaction({filterOnStatus:e.selectedOption.value}),S(k())});const t=document.querySelector("#searchInput"),e=function(t,e){let s;return(...n)=>{clearTimeout(s),s=setTimeout(()=>t(...n),e)}}(t=>{w.interaction({search:t}),S(k())},250);t?.addEventListener("input",t=>{const s=t.target;e(s.value)}),t?.addEventListener("keydown",t=>{if("Enter"!==t.key)return;const e=t.target;w.interaction({search:e.value}),S(k())})}(),j(),L())}function q(){const t=document.querySelector("#suiteFilterBtn");if(!t)return;const e=w.state.filterOnSuiteUid,s=function t(e){return e.flatMap(e=>[e,...t(e.suites)])}(w.state.suites);t.textContent=`${e?s.find(t=>t.uid===e)?.label??"Unknown suite":"All suites"} ▾`}function j(){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}=w.state;""===s?w.interaction({sortBy:""}):n!==s?w.interaction({sortBy:s}):i?w.interaction({sortAscending:!1}):w.interaction({sortBy:""}),S(k())})}function B(t,e){const s=t.querySelector(`.test-row-details[data-index="${CSS.escape(e)}"]`);if(!s)return;const n=t.querySelector(`.test-toggle-btn[data-index="${CSS.escape(e)}"]`),i="none"!==s.style.display;s.style.display=i?"none":"block",n&&(n.textContent=i?"[>]":"[V]"),i?function(t){t.querySelectorAll("img.attachment-thumb").forEach(t=>{t.removeAttribute("src")})}(s):function(t){t.querySelectorAll("img.attachment-thumb[data-src]").forEach(t=>{t.src&&t.src!==window.location.href||(t.src=t.dataset.src??"",t.addEventListener("error",()=>{t.removeAttribute("src"),t.classList.add("attachment-thumb--error"),t.title=`Unavailable: ${t.dataset.src??""}`},{once:!0}),t.addEventListener("click",()=>{window.open(t.dataset.src,"_blank")}))})}(s)}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 B(t,n.dataset.index??"");const i=s.closest(".qunit-logs-toggle");if(i){e.stopPropagation();const t=i.nextElementSibling;if(t){const e="none"!==t.style.display;t.style.display=e?"none":"block",i.textContent=e?i.textContent.replace("[V]","[>]"):i.textContent.replace("[>]","[V]")}return}const a=s.closest(".test-row-header");a&&!s.closest("ui5-breadcrumbs")&&B(t,a.dataset.index??"")}),t.addEventListener("item-click",t=>{const e=t.detail,s=decodeURIComponent(e.item.dataset.suiteUid??"");w.interaction({filterOnSuiteUid:s}),S(k()),q(),x()}))}function U(t){"mode"in t?_():"tests"in t&&function(){const t=document.querySelector("#testList");t&&(t.innerHTML=y(w.state,w.settings),L(),j())}()}A.type="file",A.accept=".json",A.style.display="none",A.addEventListener("change",async()=>{const t=A.files?.[0];if(!t)return;const e=await t.text(),s=JSON.parse(e);w.interaction({report:s}),A.value=""}),window.addEventListener("popstate",()=>{const t=$();Object.keys(t).length>0&&w.interaction(t),q()}),document.addEventListener("DOMContentLoaded",()=>{document.body.append(A),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")&&A.click(),e.closest("#exportBtn")&&w.interaction({action:"export"})}),document.querySelector("#suitePopoverClose")?.addEventListener("click",()=>{document.querySelector("#suitePopover").open=!1}),w.connect(U),"ctrf"in window&&"object"==typeof window.ctrf&&window.ctrf&&w.interaction({report:window.ctrf});const t=$();Object.keys(t).length>0&&w.interaction(t),_()})}();
|