q-component q-test-accumulator { q-property manifest: q-map { } q-property results: q-map { } q-property manualStatus: q-map { } q-signal refreshRequested() q-signal resultReported(testNo, value) q-signal expectedReported(testNo, expectedList) q-signal passingReported(testNo, isPassing) q-signal outcomeReported(testNo, isPassing, value) function normalize(v) { return String(v == null ? "" : v).replace(/\s+/g, " ").trim(); } function mapKeys(store) { if (store && typeof store.keys === "function") { return store.keys(); } return []; } function mapGet(store, key) { if (store && typeof store.value === "function") { return store.value(key); } return undefined; } function ensureModelStore(targetProp) { var store = this.component[targetProp]; if (store && typeof store.set === "function" && typeof store.value === "function" && typeof store.keys === "function") { return store; } var seed = {}; if (store && typeof store === "object") { var keys = Object.keys(store); for (var i = 0; i < keys.length; i += 1) { seed[keys[i]] = store[keys[i]]; } } this.component[targetProp] = QModel(seed); return this.component[targetProp]; } function mapSet(targetProp, key, value) { var store = this.component.ensureModelStore(targetProp); store.set(key, value); } function doneMarkerState(testNo) { var key = String(testNo || ""); var host = document.querySelector("#host-t" + key); if (!host) { return "none"; } var doneNode = host.querySelector("#t" + key + "-done"); if (!doneNode) { return "none"; } var token = String(doneNode.textContent == null ? "" : doneNode.textContent).trim().toLowerCase(); if (token === "1" || token === "true" || token === "done" || token === "ok" || token === "pass") { return "done"; } return "pending"; } function bootstrapManifest() { var cards = document.querySelectorAll(".test-card"); var data = {}; for (var i = 0; i < cards.length; i += 1) { var card = cards[i]; var cardId = String(card.getAttribute("id") || ""); if (cardId.indexOf("card-") !== 0) { continue; } var key = cardId.slice(5); if (!key) { continue; } var titleNode = card.querySelector(".test-head h2"); var rawTitle = titleNode ? String(titleNode.textContent || "") : ("Test " + key); var title = rawTitle.replace(/^\s*\d+\.\s*/, ""); var expectedNode = card.querySelector(".test-expected"); var expectedText = expectedNode ? String(expectedNode.textContent || "") : ""; expectedText = expectedText.replace(/^Expected:\s*/i, "").trim(); var expected = []; if (expectedText) { expected = expectedText.split("|").map(function(part) { return String(part || "").trim(); }).filter(function(part) { return part.length > 0; }); } var deprecated = expected.length === 1 && expected[0].toLowerCase() === "test has been deprecated"; data[key] = { title: title, expected: expected, deprecated: deprecated }; } this.component.manifest = QModel(data); } function ensureManifest() { var manifest = this.component.ensureModelStore("manifest"); var currentKeys = this.component.mapKeys(manifest); var cardCount = document.querySelectorAll(".test-card[id^='card-']").length; if (currentKeys.length === 0 || cardCount > currentKeys.length) { this.component.bootstrapManifest(); } } function setExpected(testNo, expectedList) { var key = String(testNo); var entry = this.component.mapGet(this.component.manifest, key) || {}; if (!entry || typeof entry !== "object") { entry = {}; } entry.expected = Array.isArray(expectedList) ? expectedList.slice() : [String(expectedList || "")]; this.component.mapSet("manifest", key, entry); } function setResult(testNo, value) { this.component.mapSet("results", String(testNo), String(value == null ? "" : value)); } function setPassing(testNo, isPassing) { var key = String(testNo); var value = isPassing ? "pass" : "fail"; this.component.mapSet("manualStatus", key, value); } function report(testNo, isPassing, value) { this.component.setPassing(testNo, isPassing); if (value !== undefined) { this.component.setResult(testNo, value); } this.component.showResults(); } function evaluateOne(testNo) { var key = String(testNo); var entry = this.component.mapGet(this.component.manifest, key) || {}; var expected = Array.isArray(entry.expected) ? entry.expected : []; var deprecated = entry.deprecated === true; var title = String(entry.title || ""); var actual = String(this.component.mapGet(this.component.results, key) || ""); var manual = this.component.ensureModelStore("manualStatus"); var status = ""; var hasResult = false; hasResult = this.component.mapKeys(this.component.ensureModelStore("results")).indexOf(key) >= 0; status = this.component.mapGet(manual, key); if (status !== "pass" && status !== "fail" && status !== "deprecated" && deprecated) { status = "deprecated"; } if (status !== "pass" && status !== "fail" && status !== "deprecated") { status = hasResult ? "pass" : "pending"; } var doneState = this.component.doneMarkerState(key); if (status !== "pass" && status !== "fail" && status !== "deprecated" && doneState === "pending") { status = "pending"; } if (status === "pass" && doneState === "pending") { status = "pending"; } if (status === "pass" && !deprecated) { var normalizedActual = this.component.normalize(actual); for (var i = 0; i < expected.length; i += 1) { var token = this.component.normalize(expected[i]); if (token && normalizedActual.indexOf(token) < 0) { status = "fail"; break; } } } var badge = document.querySelector("#badge-" + key); if (badge) { badge.className = "badge " + status; badge.textContent = status; } return { key: key, title: title, status: status, actual: actual, expected: expected }; } function evaluateAll() { var hosts = document.querySelectorAll("q-html[data-test-no]"); for (var i = 0; i < hosts.length; i += 1) { var host = hosts[i]; var no = String(host.getAttribute("data-test-no") || ""); if (!no) { continue; } this.component.setResult(no, host.innerHTML); } this.component.showResults(); } function refresh() { this.component.evaluateAll(); } function showResults() { this.component.ensureManifest(); var keys = this.component.mapKeys(this.component.manifest); var passed = 0; var failed = 0; var pending = 0; var lines = []; for (var i = 0; i < keys.length; i += 1) { var rec = this.component.evaluateOne(keys[i]); if (rec.status === "pass") { passed += 1; } if (rec.status === "fail") { failed += 1; lines.push(rec.key + ". " + rec.title + "\n expected: " + rec.expected.join(" | ") + "\n actual: " + this.component.normalize(rec.actual)); } if (rec.status !== "pass" && rec.status !== "fail") { pending += 1; } } document.querySelector("#sum-pass").textContent = "Passes: " + String(passed); document.querySelector("#sum-fail").textContent = "Failures: " + String(failed); document.querySelector("#sum-pending").textContent = "Pending: " + String(pending); document.querySelector("#failed-list").textContent = lines.length > 0 ? lines.join("\n\n") : "none"; } onrefreshRequested { this.component.refresh(); } onresultReported { var p = event && event.detail && event.detail.params ? event.detail.params : {}; this.component.setResult(p.testNo, p.value); this.component.showResults(); } onexpectedReported { var p = event && event.detail && event.detail.params ? event.detail.params : {}; this.component.setExpected(p.testNo, p.expectedList); this.component.showResults(); } onpassingReported { var p = event && event.detail && event.detail.params ? event.detail.params : {}; this.component.setPassing(p.testNo, p.isPassing); this.component.showResults(); } onoutcomeReported { var p = event && event.detail && event.detail.params ? event.detail.params : {}; this.component.report(p.testNo, p.isPassing, p.value); } onReady { try { if (!this.component) { return; } if (typeof this.component.bootstrapManifest === "function") { this.component.bootstrapManifest(); } if (typeof this.refreshRequested === "function") { this.refreshRequested(); } else if (typeof this.component.refresh === "function") { this.component.refresh(); } setTimeout(function() { try { if (this.component && typeof this.component.refresh === "function") { this.component.refresh(); } } catch (err1) { console.warn("accumulator delayed refresh(250) failed", err1); } }.bind(this), 250); setTimeout(function() { try { if (this.component && typeof this.component.refresh === "function") { this.component.refresh(); } } catch (err2) { console.warn("accumulator delayed refresh(1000) failed", err2); } }.bind(this), 1000); } catch (err) { console.warn("accumulator onReady failed", err); } } div.panel { div.summary { span#sum-fail.fail { text { Failures: 0 } } span#sum-pending.pending { text { Pending: 0 } } span#sum-pass.pass { text { Passes: 0 } } } div.actions { button#btn-refresh { type: "button" onclick { var acc = document.querySelector("#acc-main"); if (!acc) { return; } if (typeof acc.refreshRequested === "function") { acc.refreshRequested(); } } text { Refresh } } button#btn-copy { type: "button" onclick { var txt = document.querySelector("#failed-list").textContent; if (navigator.clipboard && typeof navigator.clipboard.writeText === "function") { navigator.clipboard.writeText(txt); } } text { Copy Failures } } button#btn-loop { type: "button" onclick { QHtml.printEventLoopSnapshot({ limit: 80, includePayload: false }); } text { Print Event Loop } } } pre#failed-list { text { none } } } } q-test-accumulator#acc-main { } q-timer accRefreshTimer { interval: 5000 repeat: true running: false ontimeout { var acc = document.querySelector("#acc-main"); if (!acc) { return; } if (typeof acc.refreshRequested === "function") { acc.refreshRequested(); } } }

q-style, q-theme, and q-painter

section
Includes: tests 4, 14, 15, 18, 41, 42, 43, 115, 117, 118
Style blocks, q-style, q-theme, q-style-class, default themes, q-painter, and q-transition coverage. Back to test index.

4. Style block + text

pending
Expected: Plain text content | font-size: 20px
p { style { font-size: 20px; margin: 0; } text { Plain text content } } onReady { var acc = document.querySelector("#acc-main"); if (acc) { acc.resultReported(4, this.innerHTML); } }

14. q-style direct apply

pending
Expected: Styled panel | border: 1px solid #93c5fd
q-style panel { backgroundColor: #eff6ff color: #1e293b border: 1px solid #93c5fd } panel,div { text { Styled panel } } onReady { var acc = document.querySelector("#acc-main"); if (acc) { acc.resultReported(14, this.innerHTML); } }

15. q-theme selector mapping

pending
Expected: Title | Description
q-style title-accent { color: #1d4ed8 } q-style body-muted { backgroundColor: #64748b } q-theme article-theme { h3 { title-accent } p { body-muted } } article-theme { div { h3 { text { Title } } p { text { Description } } } } onReady { var acc = document.querySelector("#acc-main"); if (acc) { acc.resultReported(15, this.innerHTML); } }

18. q-default-theme fallback + override

pending
Expected: themed card
q-style panel-base { backgroundColor: #eef3fb color: #0f172a } q-style panel-override { backgroundColor: #ffedd5 color: #7c2d12 } q-default-theme card-theme { .card { panel-base } } q-theme card-demo-theme { card-theme { } .card { panel-override } } card-demo-theme { div.card { text { themed card } } } onReady { var acc = document.querySelector("#acc-main"); if (acc) { acc.resultReported(18, this.innerHTML); } }

41. q-style-class merge

pending
Expected: class-merge | w3-card
q-style card-shell { q-style-class { w3-card w3-round-large w3-padding } borderColor: #cbd5e1 } card-shell,div#card41 { text { class-merge } } onReady { var acc = document.querySelector("#acc-main"); if (acc) { acc.resultReported(41, this.innerHTML); } }

42. q-theme composition

pending
Expected: Remove
q-style button-base { backgroundColor: #0f766e color: #ffffff } q-style button-danger { backgroundColor: #b91c1c color: #ffffff } q-theme base-theme { button { button-base } } q-theme admin-theme { base-theme { } .danger { button-danger } } admin-theme { button.danger { text { Remove } } } onReady { var acc = document.querySelector("#acc-main"); if (acc) { acc.resultReported(42, this.innerHTML); } }

43. scoped theme application

pending
Expected: Scoped Title
q-style title-accent { color: #1d4ed8 } q-theme article-theme { h3 { title-accent } } article-theme { div { h3 { text { Scoped Title } } } } onReady { var acc = document.querySelector("#acc-main"); if (acc) { acc.resultReported(43, this.innerHTML); } }

115. q-painter + q-style-painter declarative worklet mapping

pending
Expected: t115-ok
q-painter painter115 { q-property someprop: "rgba(40,80,160,0.9)" onpaint { this.fillStyle = this.someprop; this.fillRect(0, 0, this.width, this.height); } } q-style style115 { width: 84px height: 26px border: 1px solid #0f172a q-style-painter { background { painter115 } } } q-theme theme115 { .t115-panel { style115 } } theme115 { div#t115-target.t115-panel { text { } } } div#t115-out { text { waiting } } onReady { var host = this; setTimeout(function() { var acc = document.querySelector("#acc-main"); var out = host.querySelector("#t115-out"); var target = host.querySelector("#t115-target"); var support = !!(CSS && CSS.paintWorklet && typeof CSS.paintWorklet.addModule === "function"); var bg = target ? String(target.style.backgroundImage || "").trim() : ""; var prop = target ? String(target.style.getPropertyValue("--someprop") || "").trim() : ""; var ok = support ? (bg.indexOf("paint(") >= 0 && prop.length > 0) : true; var value = (ok ? "t115-ok" : "t115-fail") + "|support=" + String(support ? 1 : 0) + "|bg=" + bg + "|var=" + prop; if (out) { out.textContent = value; } if (acc && typeof acc.outcomeReported === "function") { acc.outcomeReported(115, ok, value); } else if (acc && typeof acc.resultReported === "function") { acc.resultReported(115, value); } }, 520); }

117. q-transition + q-style-transition declarative transition mapping

pending
Expected: t117-ok | opacity 3000ms ease-in-out 50ms
q-transition fade117 { property { opacity } delay { 50 } timing { ease-in-out } duration { 3000 } } q-style style117 { q-style-transition { fade117 } } q-theme theme117 { .t117-box { style117 } } theme117 { div#t117-target.t117-box { text { transition-target } } } button#t117-toggle { text { toggle opacity } onclick { var target = this.closest("q-html").querySelector("#t117-target"); if (!target) { return; } var current = String(target.style.opacity || "1").trim(); target.style.opacity = (current === "1" || current === "") ? "0.2" : "1"; } } div#t117-out { text { waiting } } onReady { var host = this; setTimeout(function() { var acc = document.querySelector("#acc-main"); var out = host.querySelector("#t117-out"); var target = host.querySelector("#t117-target"); if (target) { target.style.opacity = "1"; } var transitionValue = target ? String(target.style.transition || "").trim() : ""; var ok = transitionValue.indexOf("opacity") >= 0 && transitionValue.indexOf("3000ms") >= 0 && transitionValue.indexOf("ease-in-out") >= 0 && transitionValue.indexOf("50ms") >= 0; var value = (ok ? "t117-ok" : "t117-fail") + " | " + transitionValue; if (out) { out.textContent = value; } if (acc && typeof acc.outcomeReported === "function") { acc.outcomeReported(117, ok, value); } else if (acc && typeof acc.resultReported === "function") { acc.resultReported(117, value); } }, 420); }

118. q-theme anonymous q-style rule blocks

pending
Expected: t118-ok | color: #1d4ed8 | color: #334155 | background-color: #e2e8f0
q-style body-panel-118 { backgroundColor: #e2e8f0 } q-theme anon-theme-118 { h3 { q-style { color: #1d4ed8 } } .summary118 { q-style { color: #334155 } body-panel-118 } } anon-theme-118 { div { h3#t118-title { text { Theme heading } } p#t118-summary.summary118 { text { This paragraph uses anonymous and named q-styles. } } } } div#t118-out { text { waiting } } onReady { var host = this; setTimeout(function() { var acc = document.querySelector("#acc-main"); var out = host.querySelector("#t118-out"); var title = host.querySelector("#t118-title"); var summary = host.querySelector("#t118-summary"); var titleStyle = title ? String(title.getAttribute("style") || "").trim() : ""; var summaryStyle = summary ? String(summary.getAttribute("style") || "").trim() : ""; var ok = titleStyle.indexOf("color: #1d4ed8") >= 0 && summaryStyle.indexOf("color: #334155") >= 0 && summaryStyle.indexOf("background-color: #e2e8f0") >= 0; var value = (ok ? "t118-ok" : "t118-fail") + " | " + titleStyle + " | " + summaryStyle; if (out) { out.textContent = value; } if (acc && typeof acc.outcomeReported === "function") { acc.outcomeReported(118, ok, value); } else if (acc && typeof acc.resultReported === "function") { acc.resultReported(118, value); } }, 180); }