ui5-test-runner 5.13.1 → 6.0.0-beta.2
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 +20 -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,2 @@
|
|
|
1
|
+
!function(){try{if("undefined"!=typeof document){var e=document.createElement("style");e.appendChild(document.createTextNode('body,#app{height:100vh;font-family:var(--sapFontFamily,"72", Arial, sans-serif);background:var(--sapBackgroundColor,#f7f7f7);flex-direction:column;margin:0;display:flex;overflow:hidden}#header{background:var(--sapShellBar_Background,#354a5e);color:var(--sapShellBar_TextColor,#fff);flex:none;align-items:center;gap:1rem;padding:.5rem 1rem;display:flex}#header h1{color:inherit;flex:1;margin:0;font-size:1rem;font-weight:700}#toolbar{background:var(--sapGroup_ContentBackground,#fff);border-bottom:1px solid var(--sapGroup_TitleBorderColor,#d9d9d9);flex-wrap:wrap;flex:none;align-items:center;gap:.75rem;padding:.5rem 1rem;display:flex}#toolbar ui5-message-strip{flex:0 0 100%;margin-top:.25rem}#logTableWrapper{flex:1;overflow-y:auto}#logTable{border-collapse:collapse;width:100%;font-size:.875rem}#logTable thead th{background:var(--sapList_HeaderBackground,#f7f7f7);text-align:left;border-bottom:1px solid var(--sapList_BorderColor,#d9d9d9);white-space:nowrap;padding:.4rem .5rem;font-weight:700;position:sticky;top:0}#logTable thead th.col-clickable{cursor:pointer;-webkit-user-select:none;user-select:none}#logTable thead th.col-clickable:hover{background:var(--sapList_Hover_Background,#f1f1f1);text-decoration:underline}#logTable tbody tr{cursor:pointer;border-bottom:1px solid var(--sapList_BorderColor,#d9d9d9)}#logTable tbody tr:hover{background:var(--sapList_Hover_Background,#f1f1f1)}#logTable tbody td{vertical-align:top;text-overflow:ellipsis;white-space:nowrap;max-width:400px;padding:.25rem .5rem;overflow:hidden}#logTable tbody td.col-message{max-width:600px}.log-details-row{align-items:baseline;gap:.5rem;min-width:400px;padding:.2rem 0;display:flex}.log-details-label{color:var(--sapContent_LabelColor,#6a6d70);flex-shrink:0;min-width:6rem;font-size:.8rem}.log-details-value{word-break:break-all;flex:1}.log-details-actions{flex-shrink:0;gap:.25rem;display:flex}.log-details-error{white-space:pre-wrap;background:var(--sapField_ReadOnly_Background,#f2f2f2);border-radius:.25rem;flex:1;max-height:200px;padding:.5rem;font-family:monospace;font-size:.75rem;overflow-y:auto}.log-details-section-label{color:var(--sapContent_LabelColor,#6a6d70);margin-top:.5rem;margin-bottom:.25rem;font-size:.8rem;font-weight:700}.column-filter-row{align-items:center;padding:.2rem 0;display:flex}\n/*$vite$:1*/')),document.head.appendChild(e)}}catch(o){console.error("vite-plugin-css-injected-by-js",o)}}();
|
|
2
|
+
!function(){var e=class{_state={};get state(){return this._state}_settings={};get settings(){return this._settings}_updateCb=()=>{throw new Error("UI not connected")};_update({...e}){for(const t of Object.keys(e))e[t]===this._state[t]&&delete e[t];return Object.assign(this._state,e),console.log("🎮⏩",e),Object.keys(e).length>0&&this._updateCb({...e}),e}_onConnect(){}connect(e){this._updateCb=e,this._onConnect(),console.log("🎮🔛",{initialState:{...this._state},settings:this._settings})}interaction(e){const{action:t,...n}=e,r=this._update(n);console.log("🎮⏪",{event:e,action:t,stateDiff:r}),(Object.keys(r).length>0||void 0!==t)&&this._onInteraction(r,t)}},t=3e5,n=[{label:"5 minutes",key:t},{label:"15 minutes",key:9e5},{label:"30 minutes",key:18e5},{label:"1 hour",key:36e5},{label:"3 hours",key:108e5}],r=[{label:"5 seconds",key:5e3},{label:"10 seconds",key:1e4},{label:"30 seconds",key:3e4},{label:"60 seconds",key:6e4}];function o(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""")}function a(e){return e.toString().padStart(2,"0")}function s(e){const t=new Date(e);return`${a(t.getMonth()+1)}/${a(t.getDate())}/${t.getFullYear()}, ${a(t.getHours())}:${a(t.getMinutes())}:${a(t.getSeconds())}`}function i(e,t){const n="relative"===e.timerangeType,r=n?function(e,t){const n=t.relativeTimerange.map(t=>`<ui5-option value="${t.key}"${e.relativeTimerange===t.key?" selected":""}>${t.label}</ui5-option>`).join(""),r=t.autorefresh.map(t=>`<ui5-option value="${t.key}"${e.autorefreshInterval===t.key?" selected":""}>${t.label}</ui5-option>`).join("");return`<ui5-select id="relativeTimerangeSelect">${n}</ui5-select>\n <span>Auto Refresh:</span>\n <ui5-select id="autorefreshSelect">\n <ui5-option value="none"${e.autorefresh?"":" selected"}>None</ui5-option>\n ${r}\n </ui5-select>`}(e,t):function(e){return`<ui5-datetime-picker id="absoluteFromPicker" value="${s(e.absoluteTimerangeFrom)}" placeholder="From"></ui5-datetime-picker>\n <ui5-datetime-picker id="absoluteToPicker" value="${s(e.absoluteTimerangeTo)}" placeholder="To"></ui5-datetime-picker>`}(e),a=e.errorMessage?`<ui5-message-strip id="errorStrip" design="Negative" hide-close-button>${o(e.errorMessage)}</ui5-message-strip>`:"";return`<ui5-input id="filterInput" value="${o(e.filter)}" placeholder="Filter expression..." style="flex:1;min-width:200px;"></ui5-input>\n <ui5-select id="timerangeTypeSelect">\n <ui5-option value="relative"${n?" selected":""}>Relative</ui5-option>\n <ui5-option value="absolute"${n?"":" selected"}>Absolute</ui5-option>\n </ui5-select>\n ${r}\n <ui5-button id="refreshNowBtn">Refresh now</ui5-button>\n ${a}`}var l={0:"🔍",1:"💬",2:"⚠️",3:"❌",4:"💣"},c={0:"debug",1:"info",2:"warn",3:"error",4:"fatal"},u=["B","KB","MB","GB"];function d(e){return l[e]??"?"}function p(e){return e?new Date(e).toLocaleString():"—"}function g(e){let t=e,n=0;for(;t>=1024&&n<u.length-1;)t/=1024,n++;return`${0===n?t.toString():t.toFixed(1)} ${u[n]}`}function m(e){const t='<thead><tr>\n <th data-col="timestamp" class="col-clickable">Timestamp (local)</th>\n <th data-col="level" class="col-clickable">Level</th>\n <th data-col="source" class="col-clickable">Source</th>\n <th data-col="processId" class="col-clickable">PID</th>\n <th data-col="threadId" class="col-clickable">TID</th>\n <th data-col="pageId" class="col-clickable">Page ID</th>\n <th class="col-message">Message</th>\n</tr></thead>';return 0===e.length?`${t}<tbody><tr><td colspan="7" style="text-align:center;padding:1rem;color:var(--sapContent_LabelColor);">No log entries</td></tr></tbody>`:`${t}<tbody>${e.map((e,t)=>{const n=e.message.length>200?`${e.message.slice(0,200)}…`:e.message;return`<tr data-index="${t}">\n <td>${p(e.timestamp)}</td>\n <td>${d(e.level)}</td>\n <td>${e.source}</td>\n <td>${e.processId}</td>\n <td>${e.threadId}</td>\n <td>${e.pageId??""}</td>\n <td class="col-message">${r=n,r.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""")}</td>\n</tr>`;var r}).join("")}</tbody>`}function v(e,t){const n=JSON.stringify(t);return`<span class="log-details-actions">\n <ui5-button class="filter-add-btn" design="Transparent" data-field="${e}" data-value='${n}' data-op="===">➕</ui5-button>\n <ui5-button class="filter-remove-btn" design="Transparent" data-field="${e}" data-value='${n}' data-op="!==">➖</ui5-button>\n </span>`}function f(e,t,n=""){return`<div class="log-details-row">\n <span class="log-details-label">${e}</span>\n <span class="log-details-value">${t}</span>\n ${n}\n</div>`}function h(e,t="data"){return Object.entries(e).map(([e,n])=>{const r=`${t}.${e}`;return Array.isArray(n)?n.map((e,t)=>{const n=`${r}[${t}]`;return"object"==typeof e&&null!==e?h(e,n):f(n,b(String(e)),v(n,e))}).join(""):"object"==typeof n&&null!==n?h(n,r):f(r,b(String(n)),v(r,n))}).join("")}function b(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""")}function y(e){const t=d(e.level),n=(r=e.level,c[r]??"unknown");var r;let o=f("timestamp",p(e.timestamp));var a;return o+=f("level",`${t} ${n}`,v("level",n)),o+=f("source",b(e.source),v("source",e.source)),o+=f("processId",String(e.processId),v("processId",e.processId)),o+=f("threadId",String(e.threadId),v("threadId",e.threadId)),void 0!==e.pageId&&(o+=f("pageId",String(e.pageId),v("pageId",e.pageId))),o+=f("message",b(e.message)),e.error&&(o+=(a=e.error,`<div class="log-details-row"><span class="log-details-section-label">error (JSON):</span></div>\n<div class="log-details-row"><pre class="log-details-error">${b(JSON.stringify(a,null,2))}</pre></div>`)),void 0!==e.data&&(o+='<div class="log-details-row"><span class="log-details-section-label">data (JSON):</span></div>',o+=h(e.data)),o}function S(e,t,n,r){const o=`${e} ${n} ${"string"==typeof t?`"${t}"`:String(t)}`;return r?`${r} && ${o}`:o}var $=new class extends e{constructor(){super(),this._state={timerangeType:"relative",relativeTimerange:t,absoluteTimerangeFrom:0,absoluteTimerangeTo:0,autorefresh:!1,autorefreshInterval:5e3,filter:"",errorMessage:"",logs:[],metrics:{inputSize:0,chunksCount:0,outputSize:0,minTimestamp:Number.MAX_SAFE_INTEGER,maxTimestamp:0,logCount:0,reading:!0}},this._settings={relativeTimerange:n,autorefresh:r}}async _executeQuery(e){this._update({errorMessage:""});const t=new URLSearchParams;for(const a of Object.keys(e))t.set(a,e[a].toString());const n=await fetch("/query?"+t.toString());if(!n.ok){const e=await n.text()||`${n.status} ${n.statusText}`;return void this._update({errorMessage:e})}const{metrics:r,logs:o}=await n.json();return{metrics:r,logs:o}}async _checkInitialQuery(){const e=await this._executeQuery({});if(!e)return;const{metrics:t,logs:n}=e,r=Date.now(),o={absoluteTimerangeFrom:t.minTimestamp,absoluteTimerangeTo:r,metrics:t,logs:n};t.minTimestamp<=r-3e5&&(o.timerangeType="absolute"),this._update(o)}_onConnect(){this._checkInitialQuery()}_onInteraction(e,t){("autorefresh"in e||"autorefreshInterval"in e)&&this._autorefresh(e),void 0!==t&&this[t]()}async refresh_now(){const e={};"absolute"===this._state.timerangeType?(e.from=this._state.absoluteTimerangeFrom,e.to=this._state.absoluteTimerangeTo):e.from=Date.now()-this._state.relativeTimerange,this._state.filter&&(e.filter=this._state.filter);const t=await this._executeQuery(e);t&&this._update(t)}_autorefreshIntervalId;_startAutorefresh(){this._autorefreshIntervalId=setInterval(()=>{this.refresh_now()},this._state.autorefreshInterval)}_stopAutorefresh(){clearInterval(this._autorefreshIntervalId),this._autorefreshIntervalId=void 0}_autorefresh({autorefresh:e}){!1===e?this._stopAutorefresh():(!0===e||this._stopAutorefresh(),this._startAutorefresh())}};function T(){const e=document.querySelector("#filterInput"),t=function(e,t){let n;return(...r)=>{clearTimeout(n),n=setTimeout(()=>e(...r),t)}}(e=>$.interaction({filter:e}),250);e?.addEventListener("input",e=>{const n=e.target;t(n.value)}),document.querySelector("#timerangeTypeSelect")?.addEventListener("change",e=>{const t=e.detail;$.interaction({timerangeType:t.selectedOption.value})}),document.querySelector("#relativeTimerangeSelect")?.addEventListener("change",e=>{const t=e.detail;$.interaction({relativeTimerange:Number(t.selectedOption.value)})}),document.querySelector("#autorefreshSelect")?.addEventListener("change",e=>{const t=e.detail.selectedOption.value;"none"===t?$.interaction({autorefresh:!1}):$.interaction({autorefresh:!0,autorefreshInterval:Number(t)})}),document.querySelector("#absoluteFromPicker")?.addEventListener("change",e=>{const t=e.target;t.dateValue&&$.interaction({absoluteTimerangeFrom:t.dateValue.getTime()})}),document.querySelector("#absoluteToPicker")?.addEventListener("change",e=>{const t=e.target;t.dateValue&&$.interaction({absoluteTimerangeTo:t.dateValue.getTime()})}),document.querySelector("#refreshNowBtn")?.addEventListener("click",()=>$.interaction({action:"refresh_now"}))}function _(e){e.addEventListener("click",e=>{const t=e.target.closest("th[data-col]");if(!t)return;const n=t.dataset.col;if(!n)return;const r=$.state.logs,o=document.querySelector("#columnFilterPopover"),a=document.querySelector("#columnFilterPopoverContent");if(!o||!a)return;if("timestamp"===n){if(0===r.length)return;const e=r.map(e=>e.timestamp),t=Math.min(...e),n=Math.max(...e);return void $.interaction({timerangeType:"absolute",absoluteTimerangeFrom:t,absoluteTimerangeTo:n})}a.innerHTML="level"===n?["debug","info","warn","error","fatal"].map(e=>`<div class="column-filter-row"><ui5-checkbox data-filter-value="${e}" text="${e}"></ui5-checkbox></div>`).join(""):function(e,t){return[...new Set(t.map(t=>t[e]))].toSorted().map(e=>{const t=void 0===e;return`<div class="column-filter-row"><ui5-checkbox data-filter-value='${t?"__undefined__":JSON.stringify(e)}' text="${t?"none":String(e)}"></ui5-checkbox></div>`}).join("")}(n,r);const s=document.querySelector("#columnFilterApply"),i=s?.cloneNode(!0);s&&i&&(s.replaceWith(i),i.addEventListener("click",()=>function(e){const t=document.querySelector("#columnFilterPopoverContent");if(!t)return;const n=[...t.querySelectorAll("ui5-checkbox")].filter(e=>!0===e.checked);if(0===n.length)return;const r=n.map(e=>{const t=e.dataset.filterValue??"null";if("__undefined__"!==t)try{return JSON.parse(t)}catch{return t}}),o=r.map(t=>S(e,t,"===","")),a=r.length>1?`(${o.join(" || ")})`:o[0],s=$.state.filter?`${$.state.filter} && ${a}`:a;$.interaction({filter:s});const i=document.querySelector("#filterInput");i&&(i.value=s),document.querySelector("#columnFilterPopover").open=!1}("level"===n?"level":n))),o.opener=t,o.open=!0})}function k(){T(),document.querySelector("#statusBtn")?.addEventListener("click",e=>{const t=document.querySelector("#metricsPopover"),n=document.querySelector("#metricsPopoverContent");t&&n&&(n.innerHTML=function(){const e=$.state.metrics;return`<div class="log-details-row"><span class="log-details-label">Status</span>\n <span>${e.reading?"Live (reading)":"Replay (complete)"}</span></div>\n <div class="log-details-row"><span class="log-details-label">Log count</span>\n <span>${e.logCount}</span></div>\n <div class="log-details-row"><span class="log-details-label">Input size</span>\n <span>${g(e.inputSize)}</span></div>\n <div class="log-details-row"><span class="log-details-label">Output size</span>\n <span>${g(e.outputSize)}</span></div>\n <div class="log-details-row"><span class="log-details-label">Chunks</span>\n <span>${e.chunksCount}</span></div>\n <div class="log-details-row"><span class="log-details-label">From</span>\n <span>${p(e.minTimestamp)}</span></div>\n <div class="log-details-row"><span class="log-details-label">To</span>\n <span>${p(e.maxTimestamp)}</span></div>`}(),t.opener=e.currentTarget,t.open=!0)});const e=document.querySelector("#logTable thead");e&&_(e);const t=document.querySelector("#logTable tbody");t&&I(t),document.querySelector("#logDetailsClose")?.addEventListener("click",()=>{const e=document.querySelector("#logDetailsPopover");e&&(e.open=!1)})}function I(e){e.addEventListener("click",e=>{const t=e.target.closest("tr[data-index]");if(!t)return;const n=Number(t.dataset.index),r=$.state.logs[n];if(!r)return;const o=document.querySelector("#logDetailsPopover"),a=document.querySelector("#logDetailsPopoverContent");o&&a&&(a.innerHTML=y(r),function(e){for(const t of e.querySelectorAll(".filter-add-btn, .filter-remove-btn"))t.addEventListener("click",()=>{const e=t,n=e.dataset.field??"",r=e.dataset.value??"null",o=e.dataset.op??"===";let a;try{a=JSON.parse(r)}catch{a=r}const s=S(n,a,o,$.state.filter);$.interaction({filter:s});const i=document.querySelector("#filterInput");i&&(i.value=s)})}(a),o.opener=t,o.open=!0)})}var w=["timerangeType","relativeTimerange","autorefresh","autorefreshInterval","absoluteTimerangeFrom","absoluteTimerangeTo"];function q(e){if(w.some(t=>t in e)&&function(){const e=document.querySelector("#toolbar");e&&(e.innerHTML=i($.state,$.settings),T())}(),"metrics"in e&&function(){const e=document.querySelector("#statusBtn");if(!e)return;const t=$.state.metrics.reading;e.textContent=t?"Status: Live":"Status: Replay",e.setAttribute("design",t?"Positive":"Neutral")}(),"logs"in e&&function(){const e=document.querySelector("#logTable");if(!e)return;e.innerHTML=m($.state.logs);const t=e.querySelector("thead");t&&_(t);const n=e.querySelector("tbody");n&&I(n)}(),"errorMessage"in e&&function(){const e=document.querySelector("#toolbar");if(!e)return;const t=document.querySelector("#errorStrip");$.state.errorMessage?t?t.textContent=$.state.errorMessage:e.insertAdjacentHTML("beforeend",`<ui5-message-strip id="errorStrip" design="Negative" hide-close-button>${$.state.errorMessage}</ui5-message-strip>`):t?.remove()}(),"filter"in e){const e=document.querySelector("#filterInput");e&&document.activeElement!==e&&(e.value=$.state.filter)}}document.addEventListener("DOMContentLoaded",()=>{!function(){const e=document.querySelector("#app");e&&e.insertAdjacentHTML("beforebegin",'<ui5-popover id="metricsPopover" header-text="Metrics" placement="Bottom">\n <div id="metricsPopoverContent" style="padding: 0.5rem;"></div>\n </ui5-popover>\n <ui5-popover id="logDetailsPopover" header-text="Log Details" placement="Bottom" style="max-width: 600px;">\n <div id="logDetailsPopoverContent" style="padding: 0.5rem;"></div>\n <div slot="footer" style="display: flex; justify-content: flex-end; padding: 0.5rem;">\n <ui5-button id="logDetailsClose" design="Transparent">Close</ui5-button>\n </div>\n </ui5-popover>\n <ui5-popover id="columnFilterPopover" placement="Bottom">\n <div id="columnFilterPopoverContent" style="padding: 0.5rem; min-width: 180px;"></div>\n <div slot="footer" style="display: flex; justify-content: flex-end; padding: 0.5rem;">\n <ui5-button id="columnFilterApply">Filter</ui5-button>\n </div>\n </ui5-popover>')}(),$.connect(q),function(){const e=document.querySelector("#app");if(!e)return;const{state:t,settings:n}=$;e.innerHTML=function(e){const t=e.reading;return`<div id="header">\n <h1>UI5 Test Runner Log Viewer</h1>\n <ui5-button id="statusBtn" design="${t?"Positive":"Neutral"}">Status: ${t?"Live":"Replay"}</ui5-button>\n</div>`}(t.metrics)+`<div id="toolbar">${i(t,n)}</div><div id="logTableWrapper"><table id="logTable">${m(t.logs)}</table></div>`,k()}()})}();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { FileSystem, logger } from '../../platform/index.js';
|
|
2
|
+
const recursive = { recursive: true };
|
|
3
|
+
export const Folder = {
|
|
4
|
+
async clean(path) {
|
|
5
|
+
logger.debug({ source: 'job', message: `Cleaning folder: ${path}` });
|
|
6
|
+
try {
|
|
7
|
+
await FileSystem.stat(path);
|
|
8
|
+
await FileSystem.rm(path, recursive);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
async create(path) {
|
|
14
|
+
logger.debug({ source: 'job', message: `Creating folder: ${path}` });
|
|
15
|
+
try {
|
|
16
|
+
await FileSystem.mkdir(path, recursive);
|
|
17
|
+
}
|
|
18
|
+
catch (error_) {
|
|
19
|
+
const error = new Error(`Failed to create folder: ${path}`);
|
|
20
|
+
error.cause = error_;
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
async recreate(path) {
|
|
25
|
+
await Folder.clean(path);
|
|
26
|
+
await Folder.create(path);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Exit, FileSystem, assert } from '../../platform/index.js';
|
|
2
|
+
import { setTimeout } from 'node:timers/promises';
|
|
3
|
+
export class FramedStreamReader {
|
|
4
|
+
static create(fileName, pollIntervalMs = 500) {
|
|
5
|
+
return new this(fileName, pollIntervalMs);
|
|
6
|
+
}
|
|
7
|
+
_fileName;
|
|
8
|
+
_pollIntervalMs;
|
|
9
|
+
constructor(fileName, pollIntervalMs) {
|
|
10
|
+
this._fileName = fileName;
|
|
11
|
+
this._pollIntervalMs = pollIntervalMs;
|
|
12
|
+
}
|
|
13
|
+
*_extractFrames(buffer) {
|
|
14
|
+
let workingBuffer = buffer;
|
|
15
|
+
while (this._reading && workingBuffer.length >= 4) {
|
|
16
|
+
const size = workingBuffer.readUInt32BE();
|
|
17
|
+
if (size === 0) {
|
|
18
|
+
yield { type: 'end' };
|
|
19
|
+
}
|
|
20
|
+
if (workingBuffer.length < size + 4) {
|
|
21
|
+
yield { type: 'incomplete', remaining: workingBuffer };
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
yield {
|
|
25
|
+
type: 'frame',
|
|
26
|
+
frame: workingBuffer.subarray(4, size + 4)
|
|
27
|
+
};
|
|
28
|
+
workingBuffer = Buffer.from(workingBuffer.subarray(size + 4));
|
|
29
|
+
}
|
|
30
|
+
yield { type: 'incomplete', remaining: workingBuffer };
|
|
31
|
+
}
|
|
32
|
+
_startPos = 0;
|
|
33
|
+
_reading = true;
|
|
34
|
+
_buffer = Buffer.alloc(0);
|
|
35
|
+
_task;
|
|
36
|
+
async *_read(end) {
|
|
37
|
+
const fileStream = FileSystem.createReadStream(this._fileName, {
|
|
38
|
+
start: this._startPos,
|
|
39
|
+
end,
|
|
40
|
+
highWaterMark: 64 * 1024
|
|
41
|
+
});
|
|
42
|
+
for await (const chunk of fileStream) {
|
|
43
|
+
assert(chunk instanceof Buffer);
|
|
44
|
+
this._buffer = Buffer.concat([this._buffer, chunk]);
|
|
45
|
+
this._startPos += chunk.length;
|
|
46
|
+
for (const result of this._extractFrames(this._buffer)) {
|
|
47
|
+
if (result.type === 'end') {
|
|
48
|
+
this._reading = false;
|
|
49
|
+
this._task?.[Symbol.dispose]();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (result.type === 'frame') {
|
|
53
|
+
yield result.frame;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
this._buffer = result.remaining;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async *read(signal) {
|
|
62
|
+
assert(this._task === undefined, 'read is already in progress');
|
|
63
|
+
this._task = Exit.registerAsyncTask({
|
|
64
|
+
name: `FramedStreamReader(${this._fileName})`,
|
|
65
|
+
stop: () => {
|
|
66
|
+
this._reading = false;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const { promise: abortSignal, resolve: triggerAbort } = Promise.withResolvers();
|
|
70
|
+
if (signal) {
|
|
71
|
+
signal.addEventListener('abort', () => {
|
|
72
|
+
triggerAbort();
|
|
73
|
+
this._reading = false;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
while (this._reading) {
|
|
77
|
+
const stats = await FileSystem.stat(this._fileName);
|
|
78
|
+
if (stats.size > this._startPos) {
|
|
79
|
+
yield* this._read(stats.size - 1);
|
|
80
|
+
}
|
|
81
|
+
if (this._reading) {
|
|
82
|
+
await Promise.race([setTimeout(this._pollIntervalMs), abortSignal]);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { FileSystem } from '../../platform/index.js';
|
|
2
|
+
export class FramedStreamWriter {
|
|
3
|
+
static create(fileName) {
|
|
4
|
+
return new this(fileName);
|
|
5
|
+
}
|
|
6
|
+
_stream;
|
|
7
|
+
constructor(fileName) {
|
|
8
|
+
this._stream = FileSystem.createWriteStream(fileName);
|
|
9
|
+
}
|
|
10
|
+
async _write(buffer) {
|
|
11
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
12
|
+
this._stream.write(buffer, (error) => (error ? reject(error) : resolve()));
|
|
13
|
+
await promise;
|
|
14
|
+
}
|
|
15
|
+
async write(buffer) {
|
|
16
|
+
if (buffer.length > 0) {
|
|
17
|
+
const size = Buffer.alloc(4);
|
|
18
|
+
size.writeUInt32BE(buffer.length, 0);
|
|
19
|
+
await this._write(size);
|
|
20
|
+
await this._write(buffer);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async end() {
|
|
24
|
+
await this._write(Buffer.from([0, 0, 0, 0]));
|
|
25
|
+
this._stream.end();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export class ProgressBar {
|
|
2
|
+
static WIDTH = 10;
|
|
3
|
+
_value = 0;
|
|
4
|
+
get value() {
|
|
5
|
+
return this._value;
|
|
6
|
+
}
|
|
7
|
+
_max = 0;
|
|
8
|
+
get max() {
|
|
9
|
+
return this._max;
|
|
10
|
+
}
|
|
11
|
+
_label = '';
|
|
12
|
+
get label() {
|
|
13
|
+
return this._label;
|
|
14
|
+
}
|
|
15
|
+
update(attributes) {
|
|
16
|
+
this._value = attributes.data.value;
|
|
17
|
+
this._max = attributes.data.max;
|
|
18
|
+
this._label = attributes.message;
|
|
19
|
+
}
|
|
20
|
+
render(width) {
|
|
21
|
+
let spaceLeft = width;
|
|
22
|
+
let progressBar = [];
|
|
23
|
+
if (this._max !== 0) {
|
|
24
|
+
const ratio = this._value / this._max;
|
|
25
|
+
const filled = Math.floor(ProgressBar.WIDTH * Math.min(ratio, 1));
|
|
26
|
+
spaceLeft = width - ProgressBar.WIDTH - 7;
|
|
27
|
+
progressBar = [
|
|
28
|
+
'[',
|
|
29
|
+
''.padEnd(filled, '#'),
|
|
30
|
+
''.padEnd(ProgressBar.WIDTH - filled, '-'),
|
|
31
|
+
']',
|
|
32
|
+
Math.floor(100 * ratio)
|
|
33
|
+
.toString()
|
|
34
|
+
.padStart(3, ' ')
|
|
35
|
+
.toString(),
|
|
36
|
+
'% '
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
const { length: labelLength } = this._label;
|
|
40
|
+
const label = labelLength > spaceLeft ? `...${this._label.slice(Math.max(0, labelLength - spaceLeft + 3))}` : this._label;
|
|
41
|
+
return [...progressBar, label].join('');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createEmptyTestResults } from '../../types/CommonTestReportFormat.js';
|
|
2
|
+
const SUMMARY_FIELDS = ['tests', 'passed', 'failed', 'skipped', 'pending', 'other'];
|
|
3
|
+
export class TestReportBuilder {
|
|
4
|
+
_report;
|
|
5
|
+
_suites = {};
|
|
6
|
+
get report() {
|
|
7
|
+
return this._report;
|
|
8
|
+
}
|
|
9
|
+
constructor(reportId, generatedBy) {
|
|
10
|
+
this._report = {
|
|
11
|
+
reportFormat: 'CTRF',
|
|
12
|
+
specVersion: 'pre-1.0',
|
|
13
|
+
reportId,
|
|
14
|
+
timestamp: new Date().toISOString(),
|
|
15
|
+
generatedBy,
|
|
16
|
+
results: createEmptyTestResults()
|
|
17
|
+
};
|
|
18
|
+
this._report.results.summary.start = Date.now();
|
|
19
|
+
}
|
|
20
|
+
addSuite(suiteUrl, pageUrls) {
|
|
21
|
+
const parentUrls = this._suites[suiteUrl] ?? [];
|
|
22
|
+
for (const pageUrl of pageUrls) {
|
|
23
|
+
this._suites[pageUrl] = [...parentUrls, suiteUrl];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
merge(url, testResults) {
|
|
27
|
+
const { name: toolName } = this._report.results.tool;
|
|
28
|
+
if (testResults.tool.name && toolName === '') {
|
|
29
|
+
this._report.results.tool = testResults.tool;
|
|
30
|
+
}
|
|
31
|
+
const { results } = this._report;
|
|
32
|
+
const suites = [...(this._suites[url] ?? []), url];
|
|
33
|
+
for (const test of testResults.tests) {
|
|
34
|
+
results.tests.push({
|
|
35
|
+
...test,
|
|
36
|
+
suite: [...suites, ...(test.suite ?? [])]
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
for (const summaryField of SUMMARY_FIELDS) {
|
|
40
|
+
results.summary[summaryField] += testResults.summary[summaryField];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
finalize() {
|
|
44
|
+
const { summary } = this._report.results;
|
|
45
|
+
summary.stop = Date.now();
|
|
46
|
+
summary.duration = summary.stop - summary.start;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const memoize = (compute) => {
|
|
2
|
+
let cachedValue;
|
|
3
|
+
let cachedError;
|
|
4
|
+
return () => {
|
|
5
|
+
if (cachedError) {
|
|
6
|
+
throw cachedError;
|
|
7
|
+
}
|
|
8
|
+
if (cachedValue === undefined) {
|
|
9
|
+
try {
|
|
10
|
+
cachedValue = compute();
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
cachedError = error;
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return cachedValue;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const literalPrototype = Object.getPrototypeOf({});
|
|
2
|
+
export const toPlainObject = (object) => {
|
|
3
|
+
const prototype = Object.getPrototypeOf(object);
|
|
4
|
+
if (Object.getPrototypeOf(object) === literalPrototype) {
|
|
5
|
+
return object;
|
|
6
|
+
}
|
|
7
|
+
return Object.assign({}, toPlainObject(prototype), object);
|
|
8
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export const parallelize = async (processor, queue, options) => {
|
|
2
|
+
const { parallel = 1, on } = options ?? {};
|
|
3
|
+
const results = [];
|
|
4
|
+
let index = 0;
|
|
5
|
+
const context = {
|
|
6
|
+
stop(reason) {
|
|
7
|
+
context.stopRequested = true;
|
|
8
|
+
throw reason;
|
|
9
|
+
},
|
|
10
|
+
stopRequested: false
|
|
11
|
+
};
|
|
12
|
+
let active = 0;
|
|
13
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
14
|
+
const fiber = async () => {
|
|
15
|
+
++active;
|
|
16
|
+
while (!context.stopRequested && index < queue.length) {
|
|
17
|
+
const current = index++;
|
|
18
|
+
if (active < parallel && index < queue.length) {
|
|
19
|
+
void fiber();
|
|
20
|
+
}
|
|
21
|
+
const input = queue[current];
|
|
22
|
+
try {
|
|
23
|
+
on?.({
|
|
24
|
+
type: 'started',
|
|
25
|
+
index: current,
|
|
26
|
+
input
|
|
27
|
+
});
|
|
28
|
+
const output = await processor.call(context, input, current, queue);
|
|
29
|
+
results[current] = {
|
|
30
|
+
status: 'fulfilled',
|
|
31
|
+
value: output
|
|
32
|
+
};
|
|
33
|
+
on?.({
|
|
34
|
+
type: 'completed',
|
|
35
|
+
index: current,
|
|
36
|
+
input,
|
|
37
|
+
output
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
on?.({
|
|
42
|
+
type: 'failed',
|
|
43
|
+
index: current,
|
|
44
|
+
input,
|
|
45
|
+
error
|
|
46
|
+
});
|
|
47
|
+
results[current] = {
|
|
48
|
+
status: 'rejected',
|
|
49
|
+
reason: error
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (--active === 0) {
|
|
54
|
+
resolve(results);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
void fiber();
|
|
58
|
+
return promise;
|
|
59
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function split(string, ...lengthes) {
|
|
2
|
+
const result = [];
|
|
3
|
+
let from = 0;
|
|
4
|
+
for (const length of lengthes) {
|
|
5
|
+
result.push(string.slice(from, from + length));
|
|
6
|
+
from += length;
|
|
7
|
+
}
|
|
8
|
+
if (from < string.length) {
|
|
9
|
+
result.push(string.slice(from));
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
export const formatDuration = (ms) => {
|
|
14
|
+
if (ms <= 0) {
|
|
15
|
+
return '00:00';
|
|
16
|
+
}
|
|
17
|
+
if (ms < 1000) {
|
|
18
|
+
return '0.' + ms.toString().padStart(3, '0');
|
|
19
|
+
}
|
|
20
|
+
const seconds = Math.floor(ms / 1000);
|
|
21
|
+
const minutes = Math.floor(seconds / 60);
|
|
22
|
+
return minutes.toString().padStart(2, '0') + ':' + (seconds % 60).toString().padStart(2, '0');
|
|
23
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const toIError = (error) => {
|
|
2
|
+
if (!(error instanceof Error)) {
|
|
3
|
+
return toIError(new Error(JSON.stringify(error)));
|
|
4
|
+
}
|
|
5
|
+
const attributes = {
|
|
6
|
+
name: error.name,
|
|
7
|
+
message: error.message,
|
|
8
|
+
stack: error.stack
|
|
9
|
+
};
|
|
10
|
+
if (error.cause) {
|
|
11
|
+
attributes.cause = toIError(error.cause);
|
|
12
|
+
}
|
|
13
|
+
if (error instanceof AggregateError) {
|
|
14
|
+
attributes.errors = error.errors.map((item) => toIError(item));
|
|
15
|
+
}
|
|
16
|
+
return attributes;
|
|
17
|
+
};
|
package/package.json
CHANGED
|
@@ -1,33 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ui5-test-runner",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0-beta.2",
|
|
4
4
|
"description": "Standalone test runner for UI5",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "dist/cli.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/"
|
|
9
|
+
],
|
|
6
10
|
"bin": {
|
|
7
|
-
"ui5-test-runner": "./
|
|
11
|
+
"ui5-test-runner": "./dist/cli.js"
|
|
8
12
|
},
|
|
9
13
|
"publishConfig": {
|
|
10
14
|
"access": "public",
|
|
11
15
|
"provenance": true
|
|
12
16
|
},
|
|
13
17
|
"engines": {
|
|
14
|
-
"node": ">=
|
|
18
|
+
"node": ">=24.11.1"
|
|
15
19
|
},
|
|
16
20
|
"scripts": {
|
|
17
|
-
"
|
|
18
|
-
"
|
|
21
|
+
"ts-run": "node --no-warnings --experimental-strip-types --import ./src/platform/js2ts.mjs",
|
|
22
|
+
"cli": "make precli && npm run ts-run -- src/cli.ts",
|
|
23
|
+
"test:cli1": "npm run cli -- --url https://ui5.sap.com/test-resources/sap/m/demokit/cart/webapp/test/testsuite.qunit.html --report-dir tmp",
|
|
24
|
+
"test:cli2": "npm run cli -- --url \"https://ui5.sap.com/test-resources/sap/m/demokit/cart/webapp/test/Test.qunit.html?testsuite=test-resources/sap/ui/demo/cart/testsuite.qunit&test=unit/unitTests\" --report-dir tmp",
|
|
25
|
+
"test:cli3": "npm run cli -- --url \"https://ui5.sap.com/test-resources/sap/m/demokit/cart/webapp/test/Test.qunit.html?testsuite=test-resources%2Fsap%2Fui%2Fdemo%2Fcart%2Ftestsuite.qunit&test=integration%2FopaTestsComponent#/categories\" --report-dir tmp",
|
|
26
|
+
"test:cli4": "npm run cli -- --url \"https://ui5.sap.com/test-resources/sap/m/demokit/cart/webapp/test/Test.qunit.html?testsuite=test-resources/sap/ui/demo/cart/testsuite.qunit&test=integration/opaTestsIFrame\" --report-dir tmp",
|
|
27
|
+
"test:cli5": "npm run cli -- --cwd test/sample.js",
|
|
28
|
+
"test:openui5": "npm run cli -- --url http://localhost:8000/test-resources/sap/ui/core/qunit/testsuite.qunit.html --parallel 4",
|
|
29
|
+
"test:openui5.uncaught": "npm run cli -- --url \"http://localhost:8000/resources/sap/ui/test/starter/Test.qunit.html?testsuite=test-resources/sap/ui/core/qunit/odata/v2/testsuite.odatav2.qunit&test=ODataV2TreeBindingFlat_MockSrv\"",
|
|
30
|
+
"lint": "dpdm src/cli.ts --circular --tree false --warning false --skip-dynamic-imports circular && eslint \"**/*.mjs\" \"./src/**/*.ts\" --fix --cache --cache-strategy content && tsc --build --noEmit",
|
|
31
|
+
"build:options": "node build/options.mjs > ./src/configuration/options.ts && npm run lint",
|
|
32
|
+
"build:agent": "vite build",
|
|
33
|
+
"build:ui:report": "vite build -c src/ui/report/vite.config.mjs",
|
|
34
|
+
"start:ui:report": "vite -c src/ui/report/vite.config.mjs",
|
|
35
|
+
"build:ui:log": "vite build -c src/ui/log/vite.config.mjs",
|
|
36
|
+
"build:ui:lib": "vite build -c src/ui/lib/vite.config.mjs",
|
|
37
|
+
"start:ui:log": "vite -c src/ui/log/vite.config.mjs",
|
|
38
|
+
"test": "npm run lint && npm run test:unit && npm run test:e2e",
|
|
19
39
|
"test:samples": "npm run test:samples:js && npm run test:samples:ts",
|
|
20
40
|
"test:samples:js": "npm run test:sample:js:legacy && npm run test:sample:js:coverage:legacy && npm run test:sample:js:legacy-remote && npm run test:sample:js:coverage:legacy-remote && npm run test:sample:js:remote && npm run test:sample:js:coverage:remote && npm run test:sample:js:basic-authent && npm run test:sample:js:legacy:split-opa && npm run test:sample:js:remote:split-opa",
|
|
21
41
|
"test:coverall": "rimraf .nyc_output && jest --coverageDirectory .nyc_output --coverageReporters json && nyc --silent --no-clean npm run test:e2e && nyc merge .nyc_output .nyc_output/final/coverage.json && nyc report --temp-dir .nyc_output/final/ --report-dir coverage --branches 80 --functions 80 --lines 80 --statements 80",
|
|
22
|
-
"test:unit": "
|
|
42
|
+
"test:unit": "vitest run --coverage",
|
|
43
|
+
"test:unit:watch": "vitest",
|
|
23
44
|
"test:unit:debug": "node --inspect node_modules/jest/bin/jest.js --runInBand --no-coverage",
|
|
24
|
-
"pretest:
|
|
25
|
-
"test:
|
|
26
|
-
"
|
|
27
|
-
"test:e2e:legacy-only": "node . --batch \"test/e2e/JS_LEGACY_[\\w_]*\\.json\" --report-dir e2e --start serve:e2e --start-wait-url http://localhost:8081 --start-wait-method HEAD --start-timeout 30s --debug-verbose start handle --ci",
|
|
45
|
+
"pretest:e2e0": "npm install -g puppeteer selenium-webdriver playwright webdriverio",
|
|
46
|
+
"test:e2e0": "npm run cli -- --batch \"test/e2e/[\\w_]*\\.json\" --report-dir e2e --start \"node test/e2e/serve.js\" --start-wait-url http://localhost:8081 --start-wait-method HEAD --start-timeout 30s",
|
|
47
|
+
"test:e2e": "echo No E2E tests until the runner is ready",
|
|
28
48
|
"test:report": "node ./src/defaults/report.js ./test/report && reserve --config ./test/report/reserve.json",
|
|
29
49
|
"test:text-report": "node ./src/defaults/text-report.js ./test/report",
|
|
30
50
|
"build:doc": "node build/doc",
|
|
51
|
+
"build": "make precli && npx tsc --project tsconfig.cli.json",
|
|
31
52
|
"clean": "npm uninstall -g ui5-test-runner puppeteer nyc selenium-webdriver playwright webdriverio jsdom",
|
|
32
53
|
"update": "ncu -i --format group",
|
|
33
54
|
"semantic-release": "semantic-release"
|
|
@@ -45,52 +66,54 @@
|
|
|
45
66
|
"coverage",
|
|
46
67
|
"ui5"
|
|
47
68
|
],
|
|
48
|
-
"author": "Arnaud Buchholz",
|
|
69
|
+
"author": "Arnaud Buchholz <arnaud.buchholz@free.fr>",
|
|
49
70
|
"license": "MIT",
|
|
50
71
|
"bugs": {
|
|
51
72
|
"url": "https://github.com/ArnaudBuchholz/ui5-test-runner/issues"
|
|
52
73
|
},
|
|
53
74
|
"homepage": "https://github.com/ArnaudBuchholz/ui5-test-runner#readme",
|
|
54
75
|
"dependencies": {
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"punyexpr": "1.2.0",
|
|
58
|
-
"reserve": "2.3.4"
|
|
76
|
+
"punyexpr": "1.3.1",
|
|
77
|
+
"reserve": "2.3.5"
|
|
59
78
|
},
|
|
60
79
|
"devDependencies": {
|
|
61
|
-
"@openui5/types": "^1.
|
|
62
|
-
"@
|
|
63
|
-
"@
|
|
64
|
-
"@
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
"
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
"
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
"
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
80
|
+
"@openui5/types": "^1.148.0",
|
|
81
|
+
"@stylistic/eslint-plugin": "^5.10.0",
|
|
82
|
+
"@stylistic/eslint-plugin-js": "^4.4.1",
|
|
83
|
+
"@types/node": "^24.12.2",
|
|
84
|
+
"@types/qunit": "^2.19.14",
|
|
85
|
+
"@ui5/cli": "^4.0.55",
|
|
86
|
+
"@ui5/middleware-code-coverage": "^2.0.3",
|
|
87
|
+
"@ui5/webcomponents": "^2.22.0",
|
|
88
|
+
"@ui5/webcomponents-fiori": "^2.22.0",
|
|
89
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
90
|
+
"baseline-browser-mapping": "^2.10.33",
|
|
91
|
+
"dotenv": "^17.4.2",
|
|
92
|
+
"dpdm": "^4.2.0",
|
|
93
|
+
"eslint": "^9.39.2",
|
|
94
|
+
"eslint-config-prettier": "^10.1.8",
|
|
95
|
+
"eslint-plugin-import": "^2.32.0",
|
|
96
|
+
"eslint-plugin-no-only-tests": "^3.4.0",
|
|
97
|
+
"eslint-plugin-prettier": "^5.5.6",
|
|
98
|
+
"eslint-plugin-security": "^4.0.0",
|
|
99
|
+
"eslint-plugin-sonarjs": "^4.0.3",
|
|
100
|
+
"eslint-plugin-unicorn": "^64.0.0",
|
|
101
|
+
"globals": "^17.6.0",
|
|
102
|
+
"jsdom": "^29.1.1",
|
|
103
|
+
"markdownlint-cli": "^0.48.0",
|
|
104
|
+
"npm-check-updates": "^22.2.1",
|
|
105
|
+
"puppeteer": "^25.1.0",
|
|
106
|
+
"qunit": "^2.26.0",
|
|
107
|
+
"rimraf": "^6.1.3",
|
|
108
|
+
"semantic-release": "^25.0.3",
|
|
109
|
+
"stylelint": "^17.12.0",
|
|
110
|
+
"stylelint-config-standard": "^40.0.0",
|
|
111
|
+
"terser": "^5.48.0",
|
|
112
|
+
"typescript": "^6.0.3",
|
|
113
|
+
"typescript-eslint": "^8.60.0",
|
|
114
|
+
"ui5-tooling-transpile": "^3.11.2",
|
|
115
|
+
"vite": "^8.0.16",
|
|
116
|
+
"vite-plugin-css-injected-by-js": "^5.0.1",
|
|
117
|
+
"vitest": "^4.1.8"
|
|
95
118
|
}
|
|
96
119
|
}
|