Mercurial
diff mrjunejune/test/theme_and_webp_test.js @ 263:ee04e4e69fed
Add functional JRPG frame and tools
Add full-screen background_2 apertures, functional frame chrome, card-driven details, dual-window tools, live conversion workflows, and bounded cleanup for generated downloads.
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <mrjunejune@users.noreply.github.com> |
|---|---|
| date | Thu, 06 Aug 2026 11:31:30 -0700 |
| parents | 0f45474c1b1a |
| children | 04fee26ecce0 |
line wrap: on
line diff
--- a/mrjunejune/test/theme_and_webp_test.js Thu Aug 06 04:08:45 2026 -0700 +++ b/mrjunejune/test/theme_and_webp_test.js Thu Aug 06 11:31:30 2026 -0700 @@ -183,6 +183,20 @@ } }); page.on('requestfailed', request => { + if (request.url().startsWith('blob:') && + request.failure()?.errorText === 'net::ERR_ABORTED') { + return; + } + if (request.method() === 'DELETE' && + request.url().includes('/api/download/') && + request.failure()?.errorText === 'net::ERR_ABORTED') { + return; + } + if (request.method() === 'POST' && + request.url().endsWith('/api/latex/render') && + request.failure()?.errorText === 'net::ERR_ABORTED') { + return; + } errors.push(`requestfailed: ${request.url()} ${request.failure()?.errorText || ''}`); }); page.on('response', response => { @@ -449,6 +463,20 @@ } }); page.on('requestfailed', request => { + if (request.url().startsWith('blob:') && + request.failure()?.errorText === 'net::ERR_ABORTED') { + return; + } + if (request.method() === 'DELETE' && + request.url().includes('/api/download/') && + request.failure()?.errorText === 'net::ERR_ABORTED') { + return; + } + if (request.method() === 'POST' && + request.url().endsWith('/api/latex/render') && + request.failure()?.errorText === 'net::ERR_ABORTED') { + return; + } errors.push(`requestfailed: ${request.url()} ${request.failure()?.errorText || ''}`); }); page.on('response', response => { @@ -487,10 +515,25 @@ .getBoundingClientRect(); const menu = document.querySelector('mjj-jrpg-menu').getBoundingClientRect(); const character = document.querySelector('[data-character]'); + const characterBox = character.getBoundingClientRect(); + const sceneBarStyle = getComputedStyle( + document.querySelector('.jrpg-scene'), + '::before', + ); const messageScroller = document.querySelector('zen-message-scroller') .getBoundingClientRect(); const messageViewport = document.querySelector('.jrpg-message-viewport') .getBoundingClientRect(); + const workspace = document.querySelector('.jrpg-workspace') + .getBoundingClientRect(); + const brand = document.querySelector('.jrpg-brand').getBoundingClientRect(); + const frameControls = document.querySelector('.jrpg-frame-controls') + .getBoundingClientRect(); + const frameTelemetry = document.querySelector('.jrpg-frame-telemetry') + .getBoundingClientRect(); + const telemetryValues = [ + ...document.querySelectorAll('.jrpg-frame-telemetry > *'), + ].map(value => value.getBoundingClientRect()); const shell = document.querySelector('.jrpg-shell').getBoundingClientRect(); const main = document.querySelector('.jrpg-main').getBoundingClientRect(); const colorProbe = document.createElement('span'); @@ -566,8 +609,53 @@ surfaceProbe.remove(); sunkenProbe.remove(); panelProbe.remove(); + const closeTo = (value, expected) => Math.abs(value - expected) < 0.01; + const frameAlignment = [ + closeTo((scene.left - workspace.left) / workspace.width, 0.032), + closeTo((scene.top - workspace.top) / workspace.height, 0.094), + closeTo(scene.width / workspace.width, 0.548), + closeTo(scene.height / workspace.height, 0.57), + closeTo((utility.left - workspace.left) / workspace.width, 0.604), + closeTo(utility.width / workspace.width, 0.365), + closeTo((composer.top - workspace.top) / workspace.height, 0.72), + closeTo(composer.width / workspace.width, 0.682), + closeTo((menu.left - workspace.left) / workspace.width, 0.734), + closeTo(menu.width / workspace.width, 0.234), + ].every(Boolean); + const borderless = [ + '.jrpg-shell', + '.jrpg-scene', + '.jrpg-utility', + 'mjj-jrpg-chat', + 'mjj-jrpg-composer', + 'mjj-jrpg-menu', + '.jrpg-preview-panel', + '.jrpg-message', + '.jrpg-work-showcase a', + '.jrpg-composer textarea', + '.jrpg-composer button[type="submit"]', + 'mjj-jrpg-menu button', + ].every(selector => { + const style = getComputedStyle(document.querySelector(selector)); + return style.borderTopWidth === '0px' && + style.borderRightWidth === '0px' && + style.borderBottomWidth === '0px' && + style.borderLeftWidth === '0px'; + }); return { - art: getComputedStyle(document.querySelector('.jrpg-scene')).backgroundImage, + art: getComputedStyle(document.querySelector('.jrpg-workspace')) + .backgroundImage, + borderless, + brandInSystemSlot: + closeTo((brand.left - workspace.left) / workspace.width, 0.054) && + closeTo((brand.top - workspace.top) / workspace.height, 0.022) && + document.querySelector('.jrpg-brand').parentElement.classList + .contains('jrpg-workspace'), + brandCoversSystemLabel: + brand.left <= workspace.left + workspace.width * 0.062 && + brand.right >= workspace.left + workspace.width * 0.102 && + brand.top <= workspace.top + workspace.height * 0.03 && + brand.bottom >= workspace.top + workspace.height * 0.06, characterLoaded: character.complete && character.naturalWidth > 0, colors, composerBelowScene: composer.top >= scene.bottom, @@ -600,6 +688,11 @@ main.top === 0 && Math.abs(main.right - innerWidth) < 1 && Math.abs(main.bottom - innerHeight) < 1, + frameAlignment, + frameChromeAligned: + closeTo((frameControls.left - workspace.left) / workspace.width, 0.866) && + closeTo((frameTelemetry.left - workspace.left) / workspace.width, 0.085) && + closeTo((frameTelemetry.top - workspace.top) / workspace.height, 0.928), fonts: computedFonts, fontLoaded: [...document.fonts].some(font => font.family.replaceAll('"', '') === 'Pixel Mplus' && @@ -614,23 +707,72 @@ noOverflow: document.documentElement.scrollWidth <= innerWidth, nonPixelText, questLabelAbsent: !document.body.textContent.includes('QUEST 01'), + statsRemoved: + !document.querySelector('.jrpg-preview-stats') && + !document.querySelector('[data-preview-type]'), + telemetryCovered: + document.querySelectorAll('.jrpg-frame-telemetry > *').length === 4 && + document.querySelector('[data-frame-network]').textContent + .includes('ONLINE') && + document.querySelector('[data-frame-uptime]').textContent + .match(/^\d{2}:\d{2}:\d{2}$/) && + [...document.querySelectorAll('.jrpg-frame-telemetry > *')] + .every(item => !/TEMP:|USER:|STATUS:|UPTIME:/.test(item.textContent)), + telemetryValuesAligned: + closeTo( + (telemetryValues[0].left + telemetryValues[0].width / 2 - + frameTelemetry.left) / frameTelemetry.width, + 0.12, + ) && + closeTo( + (telemetryValues[1].left + telemetryValues[1].width / 2 - + frameTelemetry.left) / frameTelemetry.width, + 0.325, + ) && + closeTo( + (telemetryValues[2].left + telemetryValues[2].width / 2 - + frameTelemetry.left) / frameTelemetry.width, + 0.605, + ) && + closeTo( + (telemetryValues[3].left + telemetryValues[3].width / 2 - + frameTelemetry.left) / frameTelemetry.width, + 0.88, + ), + shibaInFrontOfBar: + sceneBarStyle.backgroundImage.includes('bar-ink.webp') && + Number(getComputedStyle(character.parentElement).zIndex) > + Number(sceneBarStyle.zIndex) && + characterBox.bottom > scene.top + scene.height * 0.65, + terminalScrollbar: ( + getComputedStyle( + document.querySelector('.jrpg-message-viewport'), + ).scrollbarWidth === 'thin' && + getComputedStyle( + document.querySelector('.jrpg-message-viewport'), + ).scrollbarColor !== 'auto' + ), theme: document.documentElement.dataset.zenTheme, utilityRightOfScene: utility.left >= scene.right, + workspaceFillsViewport: + Math.abs(workspace.left) < 1 && + Math.abs(workspace.top) < 1 && + Math.abs(workspace.right - innerWidth) < 1 && + Math.abs(workspace.bottom - innerHeight) < 1, }; }); - assert.match(desktop.art, /bar-ink\.webp/); + assert.match(desktop.art, /background-frame-2\.webp/); + assert.equal(desktop.borderless, true); + assert.equal(desktop.brandInSystemSlot, true); + assert.equal(desktop.brandCoversSystemLabel, true); assert.equal(desktop.characterLoaded, true); assert.equal(desktop.colors.page, desktop.colors.canvas); - assert.equal( - desktop.colors.conversation, - desktop.colors.conversationPanel, - ); - assert.equal(desktop.colors.conversationBorder, desktop.colors.frame); + assert.equal(desktop.colors.conversation, 'rgba(0, 0, 0, 0)'); assert.equal(desktop.colors.conversationText, desktop.colors.text); - assert.equal(desktop.colors.menu, desktop.colors.sunken); + assert.equal(desktop.colors.menu, 'rgba(0, 0, 0, 0)'); assert.equal(desktop.colors.primaryText, desktop.colors.primary); assert.equal(desktop.colors.submit, desktop.colors.primary); - assert.equal(desktop.colors.utility, desktop.colors.surface); + assert.equal(desktop.colors.utility, 'rgba(0, 0, 0, 0)'); assert.equal(desktop.composerBelowScene, true); assert.equal( desktop.composerTextareaFills, @@ -638,6 +780,8 @@ JSON.stringify(desktop.composerTextareaGeometry), ); assert.equal(desktop.fillsViewport, true); + assert.equal(desktop.frameAlignment, true); + assert.equal(desktop.frameChromeAligned, true); assert.ok(desktop.fonts.every(font => font === '"Pixel Mplus"')); assert.equal(desktop.fontLoaded, true); assert.equal(desktop.menuRightOfComposer, true); @@ -645,8 +789,14 @@ assert.equal(desktop.noOverflow, true); assert.deepEqual(desktop.nonPixelText, []); assert.equal(desktop.questLabelAbsent, true); + assert.equal(desktop.statsRemoved, true); + assert.equal(desktop.telemetryCovered, true); + assert.equal(desktop.telemetryValuesAligned, true); + assert.equal(desktop.shibaInFrontOfBar, true); + assert.equal(desktop.terminalScrollbar, true); assert.equal(desktop.theme, 'cyberpunk'); assert.equal(desktop.utilityRightOfScene, true); + assert.equal(desktop.workspaceFillsViewport, true); await page.locator('.jrpg-brand a').focus(); assert.equal( await page.locator('.jrpg-brand a').evaluate(element => @@ -665,7 +815,33 @@ await page.locator('meta[name="theme-color"]').first().getAttribute('content'), desktop.colors.canvas, ); - assert.equal(await page.locator('[data-work-showcase] a').count(), 6); + assert.equal( + await page.locator('.jrpg-frame-controls :is(button, a)').count(), + 3, + ); + assert.equal( + await page.locator('.jrpg-frame-controls a').getAttribute('href'), + '/', + ); + await page.getByRole('button', { name: 'Minimize interface' }).click(); + assert.equal( + await page.locator('.jrpg-scene').evaluate( + scene => getComputedStyle(scene).visibility, + ), + 'hidden', + ); + await page.getByRole('button', { name: 'Restore interface' }).click(); + assert.equal( + await page.locator('.jrpg-scene').evaluate( + scene => getComputedStyle(scene).visibility, + ), + 'visible', + ); + assert.equal(await page.locator('[data-work-showcase] a').count(), 7); + assert.equal( + await page.getByRole('button', { name: /Inspect/ }).count(), + 0, + ); assert.match( await page.locator('[data-work-showcase]').textContent(), /Copilot Tasks/, @@ -701,13 +877,208 @@ .getAttribute('aria-pressed'), 'true', ); - assert.equal(await page.locator('[data-work-showcase] a').count(), 3); - - await page.getByRole('button', { name: /Inspect/ }).click(); - await page.locator('.jrpg-preview-dialog dialog[open]').waitFor(); + await page.waitForFunction(() => + document.querySelectorAll('[data-latest-tool]').length === 5 + ); + assert.match( + await page.locator('[data-work-showcase]').textContent(), + /Online LaTeX Editor/, + ); + assert.match( + await page.locator('[data-work-showcase]').textContent(), + /Personal Notes/, + ); + await page.locator('[data-latest-tool]').first().click(); + await page.locator('[data-functional-tool="markdown"]').waitFor(); assert.equal( await page.locator('[data-dialog-title]').textContent(), - 'Tools', + 'MarkDown to HTML', + ); + assert.equal( + await page.locator('[data-preview-link]').getAttribute('href'), + '/tools/markdown_to_html', + ); + await page.locator('[data-markdown-source]').fill('# Modal Markdown'); + await page.waitForFunction(() => + document.querySelector('[data-markdown-output] h1')?.textContent === + 'Modal Markdown' + ); + assert.equal(await page.locator('.jrpg-tool-pane').count(), 2); + const toolWorkspace = await page.evaluate(() => { + const dialogElement = document.querySelector( + '.jrpg-detail-dialog[data-detail-mode="tools"]', + ); + const dialog = dialogElement.getBoundingClientRect(); + const windows = [...document.querySelectorAll('.jrpg-tool-window')] + .map(window => window.getBoundingClientRect()); + const windowShadows = [...document.querySelectorAll('.jrpg-tool-window')] + .map(window => getComputedStyle(window).boxShadow); + const rail = getComputedStyle( + document.querySelector('.jrpg-tool-panes'), + '::before', + ); + return { + dialogHeight: dialogElement.offsetHeight, + dialogWidth: dialogElement.offsetWidth, + fillsViewport: dialogElement.offsetWidth > innerWidth * 0.9 && + dialogElement.offsetHeight > innerHeight * 0.9, + viewportHeight: innerHeight, + viewportWidth: innerWidth, + sideBySide: windows.length === 2 && + windows[0].right < windows[1].left && + Math.abs(windows[0].top - windows[1].top) < 1, + fullPageInHeader: Boolean( + document.querySelector('[data-preview-link]') + .closest('.jrpg-dialog-heading-actions'), + ), + footerHidden: document.querySelector('[data-dialog-actions]').hidden, + metalRail: rail.content !== 'none' && + rail.backgroundImage.includes('bar-ink.webp'), + pixelIcons: document.querySelectorAll('.jrpg-tool-window zen-icon').length >= 2 && + [...document.querySelectorAll('.jrpg-tool-window zen-icon svg')] + .every(icon => + getComputedStyle(icon).shapeRendering.toLowerCase() === 'crispedges' + ), + separateGlows: windowShadows.length === 2 && + windowShadows.every(shadow => shadow !== 'none'), + wideWindows: windows.every(window => window.width > dialog.width * 0.35), + }; + }); + assert.equal( + toolWorkspace.fillsViewport, + true, + JSON.stringify(toolWorkspace), + ); + assert.equal(toolWorkspace.sideBySide, true); + assert.equal(toolWorkspace.fullPageInHeader, true); + assert.equal(toolWorkspace.footerHidden, true); + assert.equal(toolWorkspace.metalRail, true); + assert.equal(toolWorkspace.pixelIcons, true); + assert.equal(toolWorkspace.separateGlows, true); + assert.equal(toolWorkspace.wideWindows, true); + await page.getByRole('button', { + name: 'Close destination details', + }).click(); + await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); + + await page.locator('[data-latest-tool]').first().click(); + await page.locator('[data-functional-tool="markdown"]').waitFor(); + await page.getByRole('button', { + name: 'Close destination details', + }).click(); + await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); + + await page.locator('[data-latest-tool]').nth(1).click(); + await page.locator('[data-functional-tool="converter"]').waitFor(); + assert.equal( + await page.locator('[data-converter-input]').count(), + 2, + ); + const converterFixture = path.join( + RUNFILES, + WORKSPACE, + 'mrjunejune/test/shiba.webp', + ); + assert.equal(fs.existsSync(converterFixture), true); + await page.locator('[data-converter-input="image"]').setInputFiles( + converterFixture, + ); + const conversionResponse = page.waitForResponse(response => + response.url().endsWith('/api/convert/image-to-webp') && + response.request().method() === 'POST' + ); + await page.locator('[data-convert-kind="image"]').click(); + assert.equal((await conversionResponse).status(), 200); + await page.locator('[data-converter-download="image"]').waitFor(); + const cleanupResponse = page.waitForResponse(response => + response.url().includes('/api/download/') && + response.request().method() === 'DELETE' + ); + await page.getByRole('button', { + name: 'Close destination details', + }).click(); + assert.equal((await cleanupResponse).status(), 204); + await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); + + await page.locator('[data-latest-tool]').nth(1).click(); + await page.locator('[data-functional-tool="converter"]').waitFor(); + await page.locator('[data-converter-input="image"]').setInputFiles( + converterFixture, + ); + const lateConversionResponse = page.waitForResponse(response => + response.url().endsWith('/api/convert/image-to-webp') && + response.request().method() === 'POST' + ); + const lateCleanupResponse = page.waitForResponse(response => + response.url().includes('/api/download/') && + response.request().method() === 'DELETE' + ); + await page.locator('[data-convert-kind="image"]').click(); + await page.getByRole('button', { + name: 'Close destination details', + }).click(); + assert.equal((await lateConversionResponse).status(), 200); + assert.equal((await lateCleanupResponse).status(), 204); + await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); + + await page.locator('[data-latest-tool]').nth(2).click(); + await page.locator('[data-functional-tool="hls"]').waitFor(); + assert.equal( + await page.locator('[data-preview-link]').getAttribute('href'), + '/tools/hls_player', + ); + await page.locator('[data-hls-sample]').click(); + await page.waitForFunction(() => + ['ready', 'error'].includes( + document.querySelector('[data-hls-status]')?.dataset.state, + ) + ); + assert.ok( + ['ready', 'error'].includes( + await page.locator('[data-hls-status]').getAttribute('data-state'), + ), + ); + assert.match( + await page.locator('[data-hls-status]').textContent(), + /ready|unsupported|failed/i, + ); + await page.getByRole('button', { + name: 'Close destination details', + }).click(); + await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); + + await page.locator('[data-latest-tool]').nth(3).click(); + await page.locator('[data-functional-tool="latex"]').waitFor(); + await page.locator('[data-latex-compile]').click(); + await page.locator('[data-latex-source]').fill( + '\\documentclass{article}\\begin{document}Updated modal PDF\\end{document}', + ); + assert.equal(await page.locator('[data-latex-download]').isHidden(), true); + await page.locator('[data-latex-compile]').click(); + await page.waitForFunction(() => + document.querySelector('[data-latex-status]')?.dataset.state === 'ready' + ); + assert.match( + await page.locator('[data-latex-preview]').getAttribute('src'), + /^blob:/, + ); + await page.getByRole('button', { + name: 'Close destination details', + }).click(); + await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); + + await page.locator('[data-latest-tool]').nth(4).click(); + await page.waitForFunction(() => + document.querySelector('[data-tool-content] h1')?.textContent === + 'Personal Notes' + ); + assert.match( + await page.locator('[data-tool-content]').textContent(), + /private browser-based writing workspace/, + ); + assert.equal( + await page.locator('[data-preview-link]').getAttribute('href'), + '/notes', ); await page.getByRole('button', { name: 'Close destination details', @@ -715,7 +1086,7 @@ await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); await page.getByRole('button', { name: 'Resume', exact: true }).click(); - await page.getByRole('button', { name: /Inspect/ }).click(); + await page.locator('[data-resume-modal]').click(); await page.locator('[data-resume-content] .sub-header').first().waitFor(); const resumeDossier = await page.locator('[data-resume-dossier]').evaluate( dossier => ({ @@ -769,7 +1140,7 @@ }).click(); await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); - await page.getByRole('button', { name: /Inspect/ }).click(); + await page.locator('[data-blog-archive]').click(); await page.waitForFunction(() => document.querySelectorAll('[data-blog-entry]').length === 9 ); @@ -886,7 +1257,7 @@ const controlledReload = await page.evaluate(() => { const shell = document.querySelector('.jrpg-shell').getBoundingClientRect(); return { - art: getComputedStyle(document.querySelector('.jrpg-scene')) + art: getComputedStyle(document.querySelector('.jrpg-workspace')) .backgroundImage, fillsViewport: shell.left === 0 && shell.top === 0 && @@ -899,7 +1270,7 @@ theme: document.documentElement.dataset.zenTheme, }; }); - assert.match(controlledReload.art, /bar-ink\.webp/); + assert.match(controlledReload.art, /background-frame-2\.webp/); assert.equal(controlledReload.fillsViewport, true); assert.equal(controlledReload.fontLoaded, true); assert.equal(controlledReload.fontFamily, '"Pixel Mplus"'); @@ -939,6 +1310,56 @@ utilityBelowScene: true, }); await mobileContext.close(); + + const shortContext = await browser.newContext({ + colorScheme: 'dark', + viewport: { width: 900, height: 500 }, + }); + const shortPage = await shortContext.newPage(); + await shortPage.goto(`${baseUrl}/jrpg`, { waitUntil: 'networkidle' }); + const shortLayout = await shortPage.evaluate(() => { + const scene = document.querySelector('.jrpg-scene').getBoundingClientRect(); + const character = document.querySelector('mjj-jrpg-character') + .getBoundingClientRect(); + const chat = document.querySelector('mjj-jrpg-chat') + .getBoundingClientRect(); + return { + characterInside: character.left >= scene.left && + character.right <= scene.right, + chatInside: chat.left >= scene.left && chat.right <= scene.right, + noSceneOverflow: document.querySelector('.jrpg-scene').scrollWidth <= + document.querySelector('.jrpg-scene').clientWidth, + }; + }); + assert.deepEqual(shortLayout, { + characterInside: true, + chatInside: true, + noSceneOverflow: true, + }); + await shortContext.close(); + + const forcedColorsContext = await browser.newContext({ + forcedColors: 'active', + viewport: { width: 1280, height: 900 }, + }); + const forcedColorsPage = await forcedColorsContext.newPage(); + await forcedColorsPage.goto(`${baseUrl}/jrpg`, { waitUntil: 'networkidle' }); + assert.deepEqual( + await forcedColorsPage.evaluate(() => ({ + sceneBar: getComputedStyle( + document.querySelector('.jrpg-scene'), + '::before', + ).backgroundImage, + workspace: getComputedStyle( + document.querySelector('.jrpg-workspace'), + ).backgroundImage, + })), + { + sceneBar: 'none', + workspace: 'none', + }, + ); + await forcedColorsContext.close(); } async function testHlsPlayer(browser, siteRoot) { @@ -1261,7 +1682,7 @@ for (const source of [home, dogGame, manifest]) { assert.doesNotMatch(source, /\.png(?:["')]|$)/i); } - assert.match(serviceWorker, /v14-blog-browser/); + assert.match(serviceWorker, /v30-card-driven-details/); assert.match( await ( await fetch(`${baseUrl}/public/pwa-register.js`)