Mercurial
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 262:0f45474c1b1a | 263:ee04e4e69fed |
|---|---|
| 181 !message.text().startsWith('Failed to load resource:')) { | 181 !message.text().startsWith('Failed to load resource:')) { |
| 182 errors.push(`console: ${message.text()}`); | 182 errors.push(`console: ${message.text()}`); |
| 183 } | 183 } |
| 184 }); | 184 }); |
| 185 page.on('requestfailed', request => { | 185 page.on('requestfailed', request => { |
| 186 if (request.url().startsWith('blob:') && | |
| 187 request.failure()?.errorText === 'net::ERR_ABORTED') { | |
| 188 return; | |
| 189 } | |
| 190 if (request.method() === 'DELETE' && | |
| 191 request.url().includes('/api/download/') && | |
| 192 request.failure()?.errorText === 'net::ERR_ABORTED') { | |
| 193 return; | |
| 194 } | |
| 195 if (request.method() === 'POST' && | |
| 196 request.url().endsWith('/api/latex/render') && | |
| 197 request.failure()?.errorText === 'net::ERR_ABORTED') { | |
| 198 return; | |
| 199 } | |
| 186 errors.push(`requestfailed: ${request.url()} ${request.failure()?.errorText || ''}`); | 200 errors.push(`requestfailed: ${request.url()} ${request.failure()?.errorText || ''}`); |
| 187 }); | 201 }); |
| 188 page.on('response', response => { | 202 page.on('response', response => { |
| 189 if (response.status() === 404 && | 203 if (response.status() === 404 && |
| 190 response.url().endsWith( | 204 response.url().endsWith( |
| 447 !message.text().startsWith('Failed to load resource:')) { | 461 !message.text().startsWith('Failed to load resource:')) { |
| 448 errors.push(`console: ${message.text()}`); | 462 errors.push(`console: ${message.text()}`); |
| 449 } | 463 } |
| 450 }); | 464 }); |
| 451 page.on('requestfailed', request => { | 465 page.on('requestfailed', request => { |
| 466 if (request.url().startsWith('blob:') && | |
| 467 request.failure()?.errorText === 'net::ERR_ABORTED') { | |
| 468 return; | |
| 469 } | |
| 470 if (request.method() === 'DELETE' && | |
| 471 request.url().includes('/api/download/') && | |
| 472 request.failure()?.errorText === 'net::ERR_ABORTED') { | |
| 473 return; | |
| 474 } | |
| 475 if (request.method() === 'POST' && | |
| 476 request.url().endsWith('/api/latex/render') && | |
| 477 request.failure()?.errorText === 'net::ERR_ABORTED') { | |
| 478 return; | |
| 479 } | |
| 452 errors.push(`requestfailed: ${request.url()} ${request.failure()?.errorText || ''}`); | 480 errors.push(`requestfailed: ${request.url()} ${request.failure()?.errorText || ''}`); |
| 453 }); | 481 }); |
| 454 page.on('response', response => { | 482 page.on('response', response => { |
| 455 if (response.status() === 404 && | 483 if (response.status() === 404 && |
| 456 response.url().endsWith( | 484 response.url().endsWith( |
| 485 .getBoundingClientRect(); | 513 .getBoundingClientRect(); |
| 486 const composerTextarea = document.querySelector('#jrpg-message') | 514 const composerTextarea = document.querySelector('#jrpg-message') |
| 487 .getBoundingClientRect(); | 515 .getBoundingClientRect(); |
| 488 const menu = document.querySelector('mjj-jrpg-menu').getBoundingClientRect(); | 516 const menu = document.querySelector('mjj-jrpg-menu').getBoundingClientRect(); |
| 489 const character = document.querySelector('[data-character]'); | 517 const character = document.querySelector('[data-character]'); |
| 518 const characterBox = character.getBoundingClientRect(); | |
| 519 const sceneBarStyle = getComputedStyle( | |
| 520 document.querySelector('.jrpg-scene'), | |
| 521 '::before', | |
| 522 ); | |
| 490 const messageScroller = document.querySelector('zen-message-scroller') | 523 const messageScroller = document.querySelector('zen-message-scroller') |
| 491 .getBoundingClientRect(); | 524 .getBoundingClientRect(); |
| 492 const messageViewport = document.querySelector('.jrpg-message-viewport') | 525 const messageViewport = document.querySelector('.jrpg-message-viewport') |
| 493 .getBoundingClientRect(); | 526 .getBoundingClientRect(); |
| 527 const workspace = document.querySelector('.jrpg-workspace') | |
| 528 .getBoundingClientRect(); | |
| 529 const brand = document.querySelector('.jrpg-brand').getBoundingClientRect(); | |
| 530 const frameControls = document.querySelector('.jrpg-frame-controls') | |
| 531 .getBoundingClientRect(); | |
| 532 const frameTelemetry = document.querySelector('.jrpg-frame-telemetry') | |
| 533 .getBoundingClientRect(); | |
| 534 const telemetryValues = [ | |
| 535 ...document.querySelectorAll('.jrpg-frame-telemetry > *'), | |
| 536 ].map(value => value.getBoundingClientRect()); | |
| 494 const shell = document.querySelector('.jrpg-shell').getBoundingClientRect(); | 537 const shell = document.querySelector('.jrpg-shell').getBoundingClientRect(); |
| 495 const main = document.querySelector('.jrpg-main').getBoundingClientRect(); | 538 const main = document.querySelector('.jrpg-main').getBoundingClientRect(); |
| 496 const colorProbe = document.createElement('span'); | 539 const colorProbe = document.createElement('span'); |
| 497 colorProbe.style.background = 'var(--zenbu-sys-color-surface-page)'; | 540 colorProbe.style.background = 'var(--zenbu-sys-color-surface-page)'; |
| 498 colorProbe.style.color = | 541 colorProbe.style.color = |
| 564 }; | 607 }; |
| 565 colorProbe.remove(); | 608 colorProbe.remove(); |
| 566 surfaceProbe.remove(); | 609 surfaceProbe.remove(); |
| 567 sunkenProbe.remove(); | 610 sunkenProbe.remove(); |
| 568 panelProbe.remove(); | 611 panelProbe.remove(); |
| 612 const closeTo = (value, expected) => Math.abs(value - expected) < 0.01; | |
| 613 const frameAlignment = [ | |
| 614 closeTo((scene.left - workspace.left) / workspace.width, 0.032), | |
| 615 closeTo((scene.top - workspace.top) / workspace.height, 0.094), | |
| 616 closeTo(scene.width / workspace.width, 0.548), | |
| 617 closeTo(scene.height / workspace.height, 0.57), | |
| 618 closeTo((utility.left - workspace.left) / workspace.width, 0.604), | |
| 619 closeTo(utility.width / workspace.width, 0.365), | |
| 620 closeTo((composer.top - workspace.top) / workspace.height, 0.72), | |
| 621 closeTo(composer.width / workspace.width, 0.682), | |
| 622 closeTo((menu.left - workspace.left) / workspace.width, 0.734), | |
| 623 closeTo(menu.width / workspace.width, 0.234), | |
| 624 ].every(Boolean); | |
| 625 const borderless = [ | |
| 626 '.jrpg-shell', | |
| 627 '.jrpg-scene', | |
| 628 '.jrpg-utility', | |
| 629 'mjj-jrpg-chat', | |
| 630 'mjj-jrpg-composer', | |
| 631 'mjj-jrpg-menu', | |
| 632 '.jrpg-preview-panel', | |
| 633 '.jrpg-message', | |
| 634 '.jrpg-work-showcase a', | |
| 635 '.jrpg-composer textarea', | |
| 636 '.jrpg-composer button[type="submit"]', | |
| 637 'mjj-jrpg-menu button', | |
| 638 ].every(selector => { | |
| 639 const style = getComputedStyle(document.querySelector(selector)); | |
| 640 return style.borderTopWidth === '0px' && | |
| 641 style.borderRightWidth === '0px' && | |
| 642 style.borderBottomWidth === '0px' && | |
| 643 style.borderLeftWidth === '0px'; | |
| 644 }); | |
| 569 return { | 645 return { |
| 570 art: getComputedStyle(document.querySelector('.jrpg-scene')).backgroundImage, | 646 art: getComputedStyle(document.querySelector('.jrpg-workspace')) |
| 647 .backgroundImage, | |
| 648 borderless, | |
| 649 brandInSystemSlot: | |
| 650 closeTo((brand.left - workspace.left) / workspace.width, 0.054) && | |
| 651 closeTo((brand.top - workspace.top) / workspace.height, 0.022) && | |
| 652 document.querySelector('.jrpg-brand').parentElement.classList | |
| 653 .contains('jrpg-workspace'), | |
| 654 brandCoversSystemLabel: | |
| 655 brand.left <= workspace.left + workspace.width * 0.062 && | |
| 656 brand.right >= workspace.left + workspace.width * 0.102 && | |
| 657 brand.top <= workspace.top + workspace.height * 0.03 && | |
| 658 brand.bottom >= workspace.top + workspace.height * 0.06, | |
| 571 characterLoaded: character.complete && character.naturalWidth > 0, | 659 characterLoaded: character.complete && character.naturalWidth > 0, |
| 572 colors, | 660 colors, |
| 573 composerBelowScene: composer.top >= scene.bottom, | 661 composerBelowScene: composer.top >= scene.bottom, |
| 574 composerTextareaFills: ( | 662 composerTextareaFills: ( |
| 575 Math.abs(composerTextarea.left - composerField.left) < 1 && | 663 Math.abs(composerTextarea.left - composerField.left) < 1 && |
| 598 Math.abs(shell.bottom - innerHeight) < 1 && | 686 Math.abs(shell.bottom - innerHeight) < 1 && |
| 599 main.left === 0 && | 687 main.left === 0 && |
| 600 main.top === 0 && | 688 main.top === 0 && |
| 601 Math.abs(main.right - innerWidth) < 1 && | 689 Math.abs(main.right - innerWidth) < 1 && |
| 602 Math.abs(main.bottom - innerHeight) < 1, | 690 Math.abs(main.bottom - innerHeight) < 1, |
| 691 frameAlignment, | |
| 692 frameChromeAligned: | |
| 693 closeTo((frameControls.left - workspace.left) / workspace.width, 0.866) && | |
| 694 closeTo((frameTelemetry.left - workspace.left) / workspace.width, 0.085) && | |
| 695 closeTo((frameTelemetry.top - workspace.top) / workspace.height, 0.928), | |
| 603 fonts: computedFonts, | 696 fonts: computedFonts, |
| 604 fontLoaded: [...document.fonts].some(font => | 697 fontLoaded: [...document.fonts].some(font => |
| 605 font.family.replaceAll('"', '') === 'Pixel Mplus' && | 698 font.family.replaceAll('"', '') === 'Pixel Mplus' && |
| 606 font.status === 'loaded' | 699 font.status === 'loaded' |
| 607 ), | 700 ), |
| 612 messageViewport.height > 192 | 705 messageViewport.height > 192 |
| 613 ), | 706 ), |
| 614 noOverflow: document.documentElement.scrollWidth <= innerWidth, | 707 noOverflow: document.documentElement.scrollWidth <= innerWidth, |
| 615 nonPixelText, | 708 nonPixelText, |
| 616 questLabelAbsent: !document.body.textContent.includes('QUEST 01'), | 709 questLabelAbsent: !document.body.textContent.includes('QUEST 01'), |
| 710 statsRemoved: | |
| 711 !document.querySelector('.jrpg-preview-stats') && | |
| 712 !document.querySelector('[data-preview-type]'), | |
| 713 telemetryCovered: | |
| 714 document.querySelectorAll('.jrpg-frame-telemetry > *').length === 4 && | |
| 715 document.querySelector('[data-frame-network]').textContent | |
| 716 .includes('ONLINE') && | |
| 717 document.querySelector('[data-frame-uptime]').textContent | |
| 718 .match(/^\d{2}:\d{2}:\d{2}$/) && | |
| 719 [...document.querySelectorAll('.jrpg-frame-telemetry > *')] | |
| 720 .every(item => !/TEMP:|USER:|STATUS:|UPTIME:/.test(item.textContent)), | |
| 721 telemetryValuesAligned: | |
| 722 closeTo( | |
| 723 (telemetryValues[0].left + telemetryValues[0].width / 2 - | |
| 724 frameTelemetry.left) / frameTelemetry.width, | |
| 725 0.12, | |
| 726 ) && | |
| 727 closeTo( | |
| 728 (telemetryValues[1].left + telemetryValues[1].width / 2 - | |
| 729 frameTelemetry.left) / frameTelemetry.width, | |
| 730 0.325, | |
| 731 ) && | |
| 732 closeTo( | |
| 733 (telemetryValues[2].left + telemetryValues[2].width / 2 - | |
| 734 frameTelemetry.left) / frameTelemetry.width, | |
| 735 0.605, | |
| 736 ) && | |
| 737 closeTo( | |
| 738 (telemetryValues[3].left + telemetryValues[3].width / 2 - | |
| 739 frameTelemetry.left) / frameTelemetry.width, | |
| 740 0.88, | |
| 741 ), | |
| 742 shibaInFrontOfBar: | |
| 743 sceneBarStyle.backgroundImage.includes('bar-ink.webp') && | |
| 744 Number(getComputedStyle(character.parentElement).zIndex) > | |
| 745 Number(sceneBarStyle.zIndex) && | |
| 746 characterBox.bottom > scene.top + scene.height * 0.65, | |
| 747 terminalScrollbar: ( | |
| 748 getComputedStyle( | |
| 749 document.querySelector('.jrpg-message-viewport'), | |
| 750 ).scrollbarWidth === 'thin' && | |
| 751 getComputedStyle( | |
| 752 document.querySelector('.jrpg-message-viewport'), | |
| 753 ).scrollbarColor !== 'auto' | |
| 754 ), | |
| 617 theme: document.documentElement.dataset.zenTheme, | 755 theme: document.documentElement.dataset.zenTheme, |
| 618 utilityRightOfScene: utility.left >= scene.right, | 756 utilityRightOfScene: utility.left >= scene.right, |
| 757 workspaceFillsViewport: | |
| 758 Math.abs(workspace.left) < 1 && | |
| 759 Math.abs(workspace.top) < 1 && | |
| 760 Math.abs(workspace.right - innerWidth) < 1 && | |
| 761 Math.abs(workspace.bottom - innerHeight) < 1, | |
| 619 }; | 762 }; |
| 620 }); | 763 }); |
| 621 assert.match(desktop.art, /bar-ink\.webp/); | 764 assert.match(desktop.art, /background-frame-2\.webp/); |
| 765 assert.equal(desktop.borderless, true); | |
| 766 assert.equal(desktop.brandInSystemSlot, true); | |
| 767 assert.equal(desktop.brandCoversSystemLabel, true); | |
| 622 assert.equal(desktop.characterLoaded, true); | 768 assert.equal(desktop.characterLoaded, true); |
| 623 assert.equal(desktop.colors.page, desktop.colors.canvas); | 769 assert.equal(desktop.colors.page, desktop.colors.canvas); |
| 624 assert.equal( | 770 assert.equal(desktop.colors.conversation, 'rgba(0, 0, 0, 0)'); |
| 625 desktop.colors.conversation, | |
| 626 desktop.colors.conversationPanel, | |
| 627 ); | |
| 628 assert.equal(desktop.colors.conversationBorder, desktop.colors.frame); | |
| 629 assert.equal(desktop.colors.conversationText, desktop.colors.text); | 771 assert.equal(desktop.colors.conversationText, desktop.colors.text); |
| 630 assert.equal(desktop.colors.menu, desktop.colors.sunken); | 772 assert.equal(desktop.colors.menu, 'rgba(0, 0, 0, 0)'); |
| 631 assert.equal(desktop.colors.primaryText, desktop.colors.primary); | 773 assert.equal(desktop.colors.primaryText, desktop.colors.primary); |
| 632 assert.equal(desktop.colors.submit, desktop.colors.primary); | 774 assert.equal(desktop.colors.submit, desktop.colors.primary); |
| 633 assert.equal(desktop.colors.utility, desktop.colors.surface); | 775 assert.equal(desktop.colors.utility, 'rgba(0, 0, 0, 0)'); |
| 634 assert.equal(desktop.composerBelowScene, true); | 776 assert.equal(desktop.composerBelowScene, true); |
| 635 assert.equal( | 777 assert.equal( |
| 636 desktop.composerTextareaFills, | 778 desktop.composerTextareaFills, |
| 637 true, | 779 true, |
| 638 JSON.stringify(desktop.composerTextareaGeometry), | 780 JSON.stringify(desktop.composerTextareaGeometry), |
| 639 ); | 781 ); |
| 640 assert.equal(desktop.fillsViewport, true); | 782 assert.equal(desktop.fillsViewport, true); |
| 783 assert.equal(desktop.frameAlignment, true); | |
| 784 assert.equal(desktop.frameChromeAligned, true); | |
| 641 assert.ok(desktop.fonts.every(font => font === '"Pixel Mplus"')); | 785 assert.ok(desktop.fonts.every(font => font === '"Pixel Mplus"')); |
| 642 assert.equal(desktop.fontLoaded, true); | 786 assert.equal(desktop.fontLoaded, true); |
| 643 assert.equal(desktop.menuRightOfComposer, true); | 787 assert.equal(desktop.menuRightOfComposer, true); |
| 644 assert.equal(desktop.messageViewportFills, true); | 788 assert.equal(desktop.messageViewportFills, true); |
| 645 assert.equal(desktop.noOverflow, true); | 789 assert.equal(desktop.noOverflow, true); |
| 646 assert.deepEqual(desktop.nonPixelText, []); | 790 assert.deepEqual(desktop.nonPixelText, []); |
| 647 assert.equal(desktop.questLabelAbsent, true); | 791 assert.equal(desktop.questLabelAbsent, true); |
| 792 assert.equal(desktop.statsRemoved, true); | |
| 793 assert.equal(desktop.telemetryCovered, true); | |
| 794 assert.equal(desktop.telemetryValuesAligned, true); | |
| 795 assert.equal(desktop.shibaInFrontOfBar, true); | |
| 796 assert.equal(desktop.terminalScrollbar, true); | |
| 648 assert.equal(desktop.theme, 'cyberpunk'); | 797 assert.equal(desktop.theme, 'cyberpunk'); |
| 649 assert.equal(desktop.utilityRightOfScene, true); | 798 assert.equal(desktop.utilityRightOfScene, true); |
| 799 assert.equal(desktop.workspaceFillsViewport, true); | |
| 650 await page.locator('.jrpg-brand a').focus(); | 800 await page.locator('.jrpg-brand a').focus(); |
| 651 assert.equal( | 801 assert.equal( |
| 652 await page.locator('.jrpg-brand a').evaluate(element => | 802 await page.locator('.jrpg-brand a').evaluate(element => |
| 653 getComputedStyle(element).outlineColor | 803 getComputedStyle(element).outlineColor |
| 654 ), | 804 ), |
| 663 ); | 813 ); |
| 664 assert.equal( | 814 assert.equal( |
| 665 await page.locator('meta[name="theme-color"]').first().getAttribute('content'), | 815 await page.locator('meta[name="theme-color"]').first().getAttribute('content'), |
| 666 desktop.colors.canvas, | 816 desktop.colors.canvas, |
| 667 ); | 817 ); |
| 668 assert.equal(await page.locator('[data-work-showcase] a').count(), 6); | 818 assert.equal( |
| 819 await page.locator('.jrpg-frame-controls :is(button, a)').count(), | |
| 820 3, | |
| 821 ); | |
| 822 assert.equal( | |
| 823 await page.locator('.jrpg-frame-controls a').getAttribute('href'), | |
| 824 '/', | |
| 825 ); | |
| 826 await page.getByRole('button', { name: 'Minimize interface' }).click(); | |
| 827 assert.equal( | |
| 828 await page.locator('.jrpg-scene').evaluate( | |
| 829 scene => getComputedStyle(scene).visibility, | |
| 830 ), | |
| 831 'hidden', | |
| 832 ); | |
| 833 await page.getByRole('button', { name: 'Restore interface' }).click(); | |
| 834 assert.equal( | |
| 835 await page.locator('.jrpg-scene').evaluate( | |
| 836 scene => getComputedStyle(scene).visibility, | |
| 837 ), | |
| 838 'visible', | |
| 839 ); | |
| 840 assert.equal(await page.locator('[data-work-showcase] a').count(), 7); | |
| 841 assert.equal( | |
| 842 await page.getByRole('button', { name: /Inspect/ }).count(), | |
| 843 0, | |
| 844 ); | |
| 669 assert.match( | 845 assert.match( |
| 670 await page.locator('[data-work-showcase]').textContent(), | 846 await page.locator('[data-work-showcase]').textContent(), |
| 671 /Copilot Tasks/, | 847 /Copilot Tasks/, |
| 672 ); | 848 ); |
| 673 assert.match( | 849 assert.match( |
| 699 assert.equal( | 875 assert.equal( |
| 700 await page.getByRole('button', { name: 'Tools', exact: true }) | 876 await page.getByRole('button', { name: 'Tools', exact: true }) |
| 701 .getAttribute('aria-pressed'), | 877 .getAttribute('aria-pressed'), |
| 702 'true', | 878 'true', |
| 703 ); | 879 ); |
| 704 assert.equal(await page.locator('[data-work-showcase] a').count(), 3); | 880 await page.waitForFunction(() => |
| 705 | 881 document.querySelectorAll('[data-latest-tool]').length === 5 |
| 706 await page.getByRole('button', { name: /Inspect/ }).click(); | 882 ); |
| 707 await page.locator('.jrpg-preview-dialog dialog[open]').waitFor(); | 883 assert.match( |
| 884 await page.locator('[data-work-showcase]').textContent(), | |
| 885 /Online LaTeX Editor/, | |
| 886 ); | |
| 887 assert.match( | |
| 888 await page.locator('[data-work-showcase]').textContent(), | |
| 889 /Personal Notes/, | |
| 890 ); | |
| 891 await page.locator('[data-latest-tool]').first().click(); | |
| 892 await page.locator('[data-functional-tool="markdown"]').waitFor(); | |
| 708 assert.equal( | 893 assert.equal( |
| 709 await page.locator('[data-dialog-title]').textContent(), | 894 await page.locator('[data-dialog-title]').textContent(), |
| 710 'Tools', | 895 'MarkDown to HTML', |
| 711 ); | 896 ); |
| 897 assert.equal( | |
| 898 await page.locator('[data-preview-link]').getAttribute('href'), | |
| 899 '/tools/markdown_to_html', | |
| 900 ); | |
| 901 await page.locator('[data-markdown-source]').fill('# Modal Markdown'); | |
| 902 await page.waitForFunction(() => | |
| 903 document.querySelector('[data-markdown-output] h1')?.textContent === | |
| 904 'Modal Markdown' | |
| 905 ); | |
| 906 assert.equal(await page.locator('.jrpg-tool-pane').count(), 2); | |
| 907 const toolWorkspace = await page.evaluate(() => { | |
| 908 const dialogElement = document.querySelector( | |
| 909 '.jrpg-detail-dialog[data-detail-mode="tools"]', | |
| 910 ); | |
| 911 const dialog = dialogElement.getBoundingClientRect(); | |
| 912 const windows = [...document.querySelectorAll('.jrpg-tool-window')] | |
| 913 .map(window => window.getBoundingClientRect()); | |
| 914 const windowShadows = [...document.querySelectorAll('.jrpg-tool-window')] | |
| 915 .map(window => getComputedStyle(window).boxShadow); | |
| 916 const rail = getComputedStyle( | |
| 917 document.querySelector('.jrpg-tool-panes'), | |
| 918 '::before', | |
| 919 ); | |
| 920 return { | |
| 921 dialogHeight: dialogElement.offsetHeight, | |
| 922 dialogWidth: dialogElement.offsetWidth, | |
| 923 fillsViewport: dialogElement.offsetWidth > innerWidth * 0.9 && | |
| 924 dialogElement.offsetHeight > innerHeight * 0.9, | |
| 925 viewportHeight: innerHeight, | |
| 926 viewportWidth: innerWidth, | |
| 927 sideBySide: windows.length === 2 && | |
| 928 windows[0].right < windows[1].left && | |
| 929 Math.abs(windows[0].top - windows[1].top) < 1, | |
| 930 fullPageInHeader: Boolean( | |
| 931 document.querySelector('[data-preview-link]') | |
| 932 .closest('.jrpg-dialog-heading-actions'), | |
| 933 ), | |
| 934 footerHidden: document.querySelector('[data-dialog-actions]').hidden, | |
| 935 metalRail: rail.content !== 'none' && | |
| 936 rail.backgroundImage.includes('bar-ink.webp'), | |
| 937 pixelIcons: document.querySelectorAll('.jrpg-tool-window zen-icon').length >= 2 && | |
| 938 [...document.querySelectorAll('.jrpg-tool-window zen-icon svg')] | |
| 939 .every(icon => | |
| 940 getComputedStyle(icon).shapeRendering.toLowerCase() === 'crispedges' | |
| 941 ), | |
| 942 separateGlows: windowShadows.length === 2 && | |
| 943 windowShadows.every(shadow => shadow !== 'none'), | |
| 944 wideWindows: windows.every(window => window.width > dialog.width * 0.35), | |
| 945 }; | |
| 946 }); | |
| 947 assert.equal( | |
| 948 toolWorkspace.fillsViewport, | |
| 949 true, | |
| 950 JSON.stringify(toolWorkspace), | |
| 951 ); | |
| 952 assert.equal(toolWorkspace.sideBySide, true); | |
| 953 assert.equal(toolWorkspace.fullPageInHeader, true); | |
| 954 assert.equal(toolWorkspace.footerHidden, true); | |
| 955 assert.equal(toolWorkspace.metalRail, true); | |
| 956 assert.equal(toolWorkspace.pixelIcons, true); | |
| 957 assert.equal(toolWorkspace.separateGlows, true); | |
| 958 assert.equal(toolWorkspace.wideWindows, true); | |
| 712 await page.getByRole('button', { | 959 await page.getByRole('button', { |
| 713 name: 'Close destination details', | 960 name: 'Close destination details', |
| 714 }).click(); | 961 }).click(); |
| 715 await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); | 962 await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); |
| 716 | 963 |
| 964 await page.locator('[data-latest-tool]').first().click(); | |
| 965 await page.locator('[data-functional-tool="markdown"]').waitFor(); | |
| 966 await page.getByRole('button', { | |
| 967 name: 'Close destination details', | |
| 968 }).click(); | |
| 969 await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); | |
| 970 | |
| 971 await page.locator('[data-latest-tool]').nth(1).click(); | |
| 972 await page.locator('[data-functional-tool="converter"]').waitFor(); | |
| 973 assert.equal( | |
| 974 await page.locator('[data-converter-input]').count(), | |
| 975 2, | |
| 976 ); | |
| 977 const converterFixture = path.join( | |
| 978 RUNFILES, | |
| 979 WORKSPACE, | |
| 980 'mrjunejune/test/shiba.webp', | |
| 981 ); | |
| 982 assert.equal(fs.existsSync(converterFixture), true); | |
| 983 await page.locator('[data-converter-input="image"]').setInputFiles( | |
| 984 converterFixture, | |
| 985 ); | |
| 986 const conversionResponse = page.waitForResponse(response => | |
| 987 response.url().endsWith('/api/convert/image-to-webp') && | |
| 988 response.request().method() === 'POST' | |
| 989 ); | |
| 990 await page.locator('[data-convert-kind="image"]').click(); | |
| 991 assert.equal((await conversionResponse).status(), 200); | |
| 992 await page.locator('[data-converter-download="image"]').waitFor(); | |
| 993 const cleanupResponse = page.waitForResponse(response => | |
| 994 response.url().includes('/api/download/') && | |
| 995 response.request().method() === 'DELETE' | |
| 996 ); | |
| 997 await page.getByRole('button', { | |
| 998 name: 'Close destination details', | |
| 999 }).click(); | |
| 1000 assert.equal((await cleanupResponse).status(), 204); | |
| 1001 await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); | |
| 1002 | |
| 1003 await page.locator('[data-latest-tool]').nth(1).click(); | |
| 1004 await page.locator('[data-functional-tool="converter"]').waitFor(); | |
| 1005 await page.locator('[data-converter-input="image"]').setInputFiles( | |
| 1006 converterFixture, | |
| 1007 ); | |
| 1008 const lateConversionResponse = page.waitForResponse(response => | |
| 1009 response.url().endsWith('/api/convert/image-to-webp') && | |
| 1010 response.request().method() === 'POST' | |
| 1011 ); | |
| 1012 const lateCleanupResponse = page.waitForResponse(response => | |
| 1013 response.url().includes('/api/download/') && | |
| 1014 response.request().method() === 'DELETE' | |
| 1015 ); | |
| 1016 await page.locator('[data-convert-kind="image"]').click(); | |
| 1017 await page.getByRole('button', { | |
| 1018 name: 'Close destination details', | |
| 1019 }).click(); | |
| 1020 assert.equal((await lateConversionResponse).status(), 200); | |
| 1021 assert.equal((await lateCleanupResponse).status(), 204); | |
| 1022 await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); | |
| 1023 | |
| 1024 await page.locator('[data-latest-tool]').nth(2).click(); | |
| 1025 await page.locator('[data-functional-tool="hls"]').waitFor(); | |
| 1026 assert.equal( | |
| 1027 await page.locator('[data-preview-link]').getAttribute('href'), | |
| 1028 '/tools/hls_player', | |
| 1029 ); | |
| 1030 await page.locator('[data-hls-sample]').click(); | |
| 1031 await page.waitForFunction(() => | |
| 1032 ['ready', 'error'].includes( | |
| 1033 document.querySelector('[data-hls-status]')?.dataset.state, | |
| 1034 ) | |
| 1035 ); | |
| 1036 assert.ok( | |
| 1037 ['ready', 'error'].includes( | |
| 1038 await page.locator('[data-hls-status]').getAttribute('data-state'), | |
| 1039 ), | |
| 1040 ); | |
| 1041 assert.match( | |
| 1042 await page.locator('[data-hls-status]').textContent(), | |
| 1043 /ready|unsupported|failed/i, | |
| 1044 ); | |
| 1045 await page.getByRole('button', { | |
| 1046 name: 'Close destination details', | |
| 1047 }).click(); | |
| 1048 await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); | |
| 1049 | |
| 1050 await page.locator('[data-latest-tool]').nth(3).click(); | |
| 1051 await page.locator('[data-functional-tool="latex"]').waitFor(); | |
| 1052 await page.locator('[data-latex-compile]').click(); | |
| 1053 await page.locator('[data-latex-source]').fill( | |
| 1054 '\\documentclass{article}\\begin{document}Updated modal PDF\\end{document}', | |
| 1055 ); | |
| 1056 assert.equal(await page.locator('[data-latex-download]').isHidden(), true); | |
| 1057 await page.locator('[data-latex-compile]').click(); | |
| 1058 await page.waitForFunction(() => | |
| 1059 document.querySelector('[data-latex-status]')?.dataset.state === 'ready' | |
| 1060 ); | |
| 1061 assert.match( | |
| 1062 await page.locator('[data-latex-preview]').getAttribute('src'), | |
| 1063 /^blob:/, | |
| 1064 ); | |
| 1065 await page.getByRole('button', { | |
| 1066 name: 'Close destination details', | |
| 1067 }).click(); | |
| 1068 await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); | |
| 1069 | |
| 1070 await page.locator('[data-latest-tool]').nth(4).click(); | |
| 1071 await page.waitForFunction(() => | |
| 1072 document.querySelector('[data-tool-content] h1')?.textContent === | |
| 1073 'Personal Notes' | |
| 1074 ); | |
| 1075 assert.match( | |
| 1076 await page.locator('[data-tool-content]').textContent(), | |
| 1077 /private browser-based writing workspace/, | |
| 1078 ); | |
| 1079 assert.equal( | |
| 1080 await page.locator('[data-preview-link]').getAttribute('href'), | |
| 1081 '/notes', | |
| 1082 ); | |
| 1083 await page.getByRole('button', { | |
| 1084 name: 'Close destination details', | |
| 1085 }).click(); | |
| 1086 await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); | |
| 1087 | |
| 717 await page.getByRole('button', { name: 'Resume', exact: true }).click(); | 1088 await page.getByRole('button', { name: 'Resume', exact: true }).click(); |
| 718 await page.getByRole('button', { name: /Inspect/ }).click(); | 1089 await page.locator('[data-resume-modal]').click(); |
| 719 await page.locator('[data-resume-content] .sub-header').first().waitFor(); | 1090 await page.locator('[data-resume-content] .sub-header').first().waitFor(); |
| 720 const resumeDossier = await page.locator('[data-resume-dossier]').evaluate( | 1091 const resumeDossier = await page.locator('[data-resume-dossier]').evaluate( |
| 721 dossier => ({ | 1092 dossier => ({ |
| 722 fontFamily: getComputedStyle(dossier).fontFamily, | 1093 fontFamily: getComputedStyle(dossier).fontFamily, |
| 723 text: dossier.textContent, | 1094 text: dossier.textContent, |
| 767 await page.getByRole('button', { | 1138 await page.getByRole('button', { |
| 768 name: 'Close destination details', | 1139 name: 'Close destination details', |
| 769 }).click(); | 1140 }).click(); |
| 770 await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); | 1141 await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' }); |
| 771 | 1142 |
| 772 await page.getByRole('button', { name: /Inspect/ }).click(); | 1143 await page.locator('[data-blog-archive]').click(); |
| 773 await page.waitForFunction(() => | 1144 await page.waitForFunction(() => |
| 774 document.querySelectorAll('[data-blog-entry]').length === 9 | 1145 document.querySelectorAll('[data-blog-entry]').length === 9 |
| 775 ); | 1146 ); |
| 776 assert.equal(await page.locator('[data-blog-entry]').count(), 9); | 1147 assert.equal(await page.locator('[data-blog-entry]').count(), 9); |
| 777 await page.locator('[data-blog-entry]').nth(1).click(); | 1148 await page.locator('[data-blog-entry]').nth(1).click(); |
| 884 await page.reload({ waitUntil: 'networkidle' }); | 1255 await page.reload({ waitUntil: 'networkidle' }); |
| 885 await page.evaluate(() => document.fonts.ready); | 1256 await page.evaluate(() => document.fonts.ready); |
| 886 const controlledReload = await page.evaluate(() => { | 1257 const controlledReload = await page.evaluate(() => { |
| 887 const shell = document.querySelector('.jrpg-shell').getBoundingClientRect(); | 1258 const shell = document.querySelector('.jrpg-shell').getBoundingClientRect(); |
| 888 return { | 1259 return { |
| 889 art: getComputedStyle(document.querySelector('.jrpg-scene')) | 1260 art: getComputedStyle(document.querySelector('.jrpg-workspace')) |
| 890 .backgroundImage, | 1261 .backgroundImage, |
| 891 fillsViewport: shell.left === 0 && | 1262 fillsViewport: shell.left === 0 && |
| 892 shell.top === 0 && | 1263 shell.top === 0 && |
| 893 Math.abs(shell.right - innerWidth) < 1 && | 1264 Math.abs(shell.right - innerWidth) < 1 && |
| 894 Math.abs(shell.bottom - innerHeight) < 1, | 1265 Math.abs(shell.bottom - innerHeight) < 1, |
| 897 document.querySelector('.jrpg-brand h1'), | 1268 document.querySelector('.jrpg-brand h1'), |
| 898 ).fontFamily, | 1269 ).fontFamily, |
| 899 theme: document.documentElement.dataset.zenTheme, | 1270 theme: document.documentElement.dataset.zenTheme, |
| 900 }; | 1271 }; |
| 901 }); | 1272 }); |
| 902 assert.match(controlledReload.art, /bar-ink\.webp/); | 1273 assert.match(controlledReload.art, /background-frame-2\.webp/); |
| 903 assert.equal(controlledReload.fillsViewport, true); | 1274 assert.equal(controlledReload.fillsViewport, true); |
| 904 assert.equal(controlledReload.fontLoaded, true); | 1275 assert.equal(controlledReload.fontLoaded, true); |
| 905 assert.equal(controlledReload.fontFamily, '"Pixel Mplus"'); | 1276 assert.equal(controlledReload.fontFamily, '"Pixel Mplus"'); |
| 906 assert.equal(controlledReload.theme, 'cyberpunk'); | 1277 assert.equal(controlledReload.theme, 'cyberpunk'); |
| 907 assert.deepEqual(errors, []); | 1278 assert.deepEqual(errors, []); |
| 937 initialScroll: 0, | 1308 initialScroll: 0, |
| 938 noOverflow: true, | 1309 noOverflow: true, |
| 939 utilityBelowScene: true, | 1310 utilityBelowScene: true, |
| 940 }); | 1311 }); |
| 941 await mobileContext.close(); | 1312 await mobileContext.close(); |
| 1313 | |
| 1314 const shortContext = await browser.newContext({ | |
| 1315 colorScheme: 'dark', | |
| 1316 viewport: { width: 900, height: 500 }, | |
| 1317 }); | |
| 1318 const shortPage = await shortContext.newPage(); | |
| 1319 await shortPage.goto(`${baseUrl}/jrpg`, { waitUntil: 'networkidle' }); | |
| 1320 const shortLayout = await shortPage.evaluate(() => { | |
| 1321 const scene = document.querySelector('.jrpg-scene').getBoundingClientRect(); | |
| 1322 const character = document.querySelector('mjj-jrpg-character') | |
| 1323 .getBoundingClientRect(); | |
| 1324 const chat = document.querySelector('mjj-jrpg-chat') | |
| 1325 .getBoundingClientRect(); | |
| 1326 return { | |
| 1327 characterInside: character.left >= scene.left && | |
| 1328 character.right <= scene.right, | |
| 1329 chatInside: chat.left >= scene.left && chat.right <= scene.right, | |
| 1330 noSceneOverflow: document.querySelector('.jrpg-scene').scrollWidth <= | |
| 1331 document.querySelector('.jrpg-scene').clientWidth, | |
| 1332 }; | |
| 1333 }); | |
| 1334 assert.deepEqual(shortLayout, { | |
| 1335 characterInside: true, | |
| 1336 chatInside: true, | |
| 1337 noSceneOverflow: true, | |
| 1338 }); | |
| 1339 await shortContext.close(); | |
| 1340 | |
| 1341 const forcedColorsContext = await browser.newContext({ | |
| 1342 forcedColors: 'active', | |
| 1343 viewport: { width: 1280, height: 900 }, | |
| 1344 }); | |
| 1345 const forcedColorsPage = await forcedColorsContext.newPage(); | |
| 1346 await forcedColorsPage.goto(`${baseUrl}/jrpg`, { waitUntil: 'networkidle' }); | |
| 1347 assert.deepEqual( | |
| 1348 await forcedColorsPage.evaluate(() => ({ | |
| 1349 sceneBar: getComputedStyle( | |
| 1350 document.querySelector('.jrpg-scene'), | |
| 1351 '::before', | |
| 1352 ).backgroundImage, | |
| 1353 workspace: getComputedStyle( | |
| 1354 document.querySelector('.jrpg-workspace'), | |
| 1355 ).backgroundImage, | |
| 1356 })), | |
| 1357 { | |
| 1358 sceneBar: 'none', | |
| 1359 workspace: 'none', | |
| 1360 }, | |
| 1361 ); | |
| 1362 await forcedColorsContext.close(); | |
| 942 } | 1363 } |
| 943 | 1364 |
| 944 async function testHlsPlayer(browser, siteRoot) { | 1365 async function testHlsPlayer(browser, siteRoot) { |
| 945 const page = await browser.newPage(); | 1366 const page = await browser.newPage(); |
| 946 const errors = []; | 1367 const errors = []; |
| 1259 await fetch(`${baseUrl}/sw.js`) | 1680 await fetch(`${baseUrl}/sw.js`) |
| 1260 ).text(); | 1681 ).text(); |
| 1261 for (const source of [home, dogGame, manifest]) { | 1682 for (const source of [home, dogGame, manifest]) { |
| 1262 assert.doesNotMatch(source, /\.png(?:["')]|$)/i); | 1683 assert.doesNotMatch(source, /\.png(?:["')]|$)/i); |
| 1263 } | 1684 } |
| 1264 assert.match(serviceWorker, /v14-blog-browser/); | 1685 assert.match(serviceWorker, /v30-card-driven-details/); |
| 1265 assert.match( | 1686 assert.match( |
| 1266 await ( | 1687 await ( |
| 1267 await fetch(`${baseUrl}/public/pwa-register.js`) | 1688 await fetch(`${baseUrl}/public/pwa-register.js`) |
| 1268 ).text(), | 1689 ).text(), |
| 1269 /register\('\/sw\.js', \{ scope: '\/' \}\)/, | 1690 /register\('\/sw\.js', \{ scope: '\/' \}\)/, |