(function () { "use strict"; const DEFAULT_TIMEOUT = 10000; const DEFAULT_INTERVAL = 50; function sleep(ms) { return new Promise((resolve) => { window.setTimeout(resolve, ms); }); } async function waitFor(predicate, options = {}) { const timeout = Number.isFinite(Number(options.timeout)) ? Number(options.timeout) : DEFAULT_TIMEOUT; const interval = Number.isFinite(Number(options.interval)) ? Number(options.interval) : DEFAULT_INTERVAL; const deadline = Date.now() + timeout; while (Date.now() < deadline) { try { if (await predicate()) { return true; } } catch { } await sleep(interval); } return false; } function textById(id) { const element = document.getElementById(id); return element instanceof HTMLElement ? String(element.textContent || "").trim() : ""; } function getSectionElement(selector) { const element = document.querySelector(selector); return element instanceof HTMLElement ? element : null; } function getLayoutState(selector) { const layout = getSectionElement(selector); const classes = layout ? Array.from(layout.classList) : []; return { classes, detailOnly: classes.includes("layout-sidebar-collapsed") && !classes.includes("layout-detail-collapsed") }; } function getSelectedText(sectionSelector) { const section = getSectionElement(sectionSelector); if (!section) { return ""; } const selected = section.querySelector('[aria-selected="true"]'); return selected instanceof HTMLElement ? String(selected.textContent || "").trim() : ""; } function dispatchNavigationEvent(eventName, detail) { document.dispatchEvent(new CustomEvent(eventName, { detail, bubbles: true })); } function createSnapshot(test) { const section = getSectionElement(test.sectionSelector); const layoutState = getLayoutState(test.layoutSelector); return { sectionHidden: section ? section.hidden : true, layoutClasses: layoutState.classes, detailOnly: layoutState.detailOnly, detailName: textById(test.detailNameId), detailSub: test.detailSubId ? textById(test.detailSubId) : "", detailBody: test.detailBodyId ? textById(test.detailBodyId) : "", selectedText: getSelectedText(test.sectionSelector) }; } async function waitForAppReady() { return waitFor(() => { if (document.readyState !== "complete") { return false; } const referenceData = window.TarotAppRuntime?.getReferenceData?.(); const magickDataset = window.TarotAppRuntime?.getMagickDataset?.(); return Boolean( typeof window.TarotChromeUi?.showDetailOnly === "function" && referenceData && magickDataset ); }); } const TESTS = { tarotTrumpNav: { eventName: "nav:tarot-trump", detail: { trumpNumber: 0 }, sectionSelector: "#tarot-section", layoutSelector: "#tarot-browse-view .tarot-layout", detailNameId: "tarot-detail-name", matches: (state) => state.detailName === "The Fool" }, kabViewTrump: { eventName: "kab:view-trump", detail: { trumpNumber: 0 }, sectionSelector: "#tarot-section", layoutSelector: "#tarot-browse-view .tarot-layout", detailNameId: "tarot-detail-name", matches: (state) => state.detailName === "The Fool" }, ichingHexagramNav: { eventName: "nav:iching", detail: { hexagramNumber: 1 }, sectionSelector: "#iching-section", layoutSelector: "#iching-section .planet-layout", detailNameId: "iching-detail-name", matches: (state) => /Creative Force/i.test(state.detailName) && /#1/i.test(state.selectedText) }, zodiacSignNav: { eventName: "nav:zodiac", detail: { signId: "aries" }, sectionSelector: "#zodiac-section", layoutSelector: "#zodiac-section .planet-layout", detailNameId: "zodiac-detail-name", detailSubId: "zodiac-detail-sub", matches: (state) => /Aries/i.test(state.detailSub) && /aries/i.test(state.selectedText) }, planetNav: { eventName: "nav:planet", detail: { planetId: "mars" }, sectionSelector: "#planet-section", layoutSelector: "#planet-section .planet-layout", detailNameId: "planet-detail-name", matches: (state) => state.detailName !== "--" && /mars/i.test(state.selectedText) }, numberNav: { eventName: "nav:number", detail: { value: 1 }, sectionSelector: "#numbers-section", layoutSelector: "#numbers-section .numbers-main-layout", detailNameId: "numbers-detail-name", matches: (state) => /Number 1|One/i.test(state.detailName) && /One/i.test(state.selectedText) }, elementNav: { eventName: "nav:elements", detail: { elementId: "fire" }, sectionSelector: "#elements-section", layoutSelector: "#elements-section .planet-layout", detailNameId: "elements-detail-name", matches: (state) => state.detailName !== "--" && /fire/i.test(state.selectedText) }, calendarMonthNav: { eventName: "nav:calendar-month", detail: { calendarId: "gregorian", monthId: "january" }, sectionSelector: "#calendar-section", layoutSelector: "#calendar-section .planet-layout", detailNameId: "calendar-detail-name", matches: (state) => /january/i.test(state.detailName) && /january/i.test(state.selectedText) }, tarotViewKabPath: { eventName: "tarot:view-kab-path", detail: { pathNumber: 11 }, sectionSelector: "#kabbalah-tree-section", layoutSelector: "#kabbalah-tree-section .kab-layout", detailNameId: "kab-detail-name", detailSubId: "kab-detail-sub", detailBodyId: "kab-detail-body", matches: (state) => /Path 11/i.test(state.detailName) && /The Fool/i.test(state.detailSub) } }; async function runTest(testId, options = {}) { const test = TESTS[testId]; if (!test) { throw new Error(`Unknown navigation detail test: ${testId}`); } const ready = await waitForAppReady(); if (!ready) { return { id: testId, pass: false, error: "App data did not finish loading in time." }; } dispatchNavigationEvent(test.eventName, test.detail); const timeout = Number.isFinite(Number(options.timeout)) ? Number(options.timeout) : DEFAULT_TIMEOUT; const pass = await waitFor(() => { const state = createSnapshot(test); return !state.sectionHidden && state.detailOnly && test.matches(state); }, { timeout }); const state = createSnapshot(test); return { id: testId, eventName: test.eventName, detail: test.detail, pass, ...state, error: pass ? "" : "Destination detail pane did not settle into detail-only mode." }; } async function runAll(testIds = Object.keys(TESTS), options = {}) { const results = []; for (const testId of testIds) { results.push(await runTest(testId, options)); } const summary = { total: results.length, passed: results.filter((result) => result.pass).length, failed: results.filter((result) => !result.pass).length }; const payload = { summary, results }; window.__TAROT_NAVIGATION_DETAIL_TEST_RESULTS__ = payload; return payload; } window.TarotNavigationDetailTestHarness = { runAll, runTest, listTests: () => Object.keys(TESTS) }; if (new URLSearchParams(window.location.search).has("navtest")) { waitForAppReady().then((ready) => { if (!ready) { return; } runAll().then((result) => { window.__TAROT_NAVIGATION_DETAIL_TEST_LAST_RESULT__ = result; console.info("Navigation detail test harness results", result); }); }); } })();