comparison mrjunejune/test/theme_and_webp_test.js @ 261:b401627fc49e

Add JRPG mock flows and interactive previews Add scripted mock SSE commands, custom event forwarding, animated chat turns, full-height message navigation, and a cyberpunk resume dossier. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <mrjunejune@users.noreply.github.com>
date Wed, 05 Aug 2026 20:38:32 -0700
parents 1f9877b637e9
children 0f45474c1b1a
comparison
equal deleted inserted replaced
260:1f9877b637e9 261:b401627fc49e
474 const desktop = await page.evaluate(() => { 474 const desktop = await page.evaluate(() => {
475 const scene = document.querySelector('.jrpg-scene').getBoundingClientRect(); 475 const scene = document.querySelector('.jrpg-scene').getBoundingClientRect();
476 const utility = document.querySelector('.jrpg-utility').getBoundingClientRect(); 476 const utility = document.querySelector('.jrpg-utility').getBoundingClientRect();
477 const composer = document.querySelector('mjj-jrpg-composer') 477 const composer = document.querySelector('mjj-jrpg-composer')
478 .getBoundingClientRect(); 478 .getBoundingClientRect();
479 const composerField = document.querySelector('.jrpg-composer zen-field')
480 .getBoundingClientRect();
481 const composerFieldStyle = getComputedStyle(
482 document.querySelector('.jrpg-composer zen-field'),
483 );
484 const composerLabel = document.querySelector('.jrpg-composer label')
485 .getBoundingClientRect();
486 const composerTextarea = document.querySelector('#jrpg-message')
487 .getBoundingClientRect();
479 const menu = document.querySelector('mjj-jrpg-menu').getBoundingClientRect(); 488 const menu = document.querySelector('mjj-jrpg-menu').getBoundingClientRect();
480 const character = document.querySelector('[data-character]'); 489 const character = document.querySelector('[data-character]');
490 const messageScroller = document.querySelector('zen-message-scroller')
491 .getBoundingClientRect();
492 const messageViewport = document.querySelector('.jrpg-message-viewport')
493 .getBoundingClientRect();
481 const shell = document.querySelector('.jrpg-shell').getBoundingClientRect(); 494 const shell = document.querySelector('.jrpg-shell').getBoundingClientRect();
482 const main = document.querySelector('.jrpg-main').getBoundingClientRect(); 495 const main = document.querySelector('.jrpg-main').getBoundingClientRect();
483 const colorProbe = document.createElement('span'); 496 const colorProbe = document.createElement('span');
484 colorProbe.style.background = 'var(--zenbu-sys-color-surface-page)'; 497 colorProbe.style.background = 'var(--zenbu-sys-color-surface-page)';
485 colorProbe.style.color = 498 colorProbe.style.color =
556 return { 569 return {
557 art: getComputedStyle(document.querySelector('.jrpg-scene')).backgroundImage, 570 art: getComputedStyle(document.querySelector('.jrpg-scene')).backgroundImage,
558 characterLoaded: character.complete && character.naturalWidth > 0, 571 characterLoaded: character.complete && character.naturalWidth > 0,
559 colors, 572 colors,
560 composerBelowScene: composer.top >= scene.bottom, 573 composerBelowScene: composer.top >= scene.bottom,
574 composerTextareaFills: (
575 Math.abs(composerTextarea.left - composerField.left) < 1 &&
576 Math.abs(composerTextarea.right - composerField.right) < 1 &&
577 Math.abs(composerTextarea.bottom - composerField.bottom) < 1 &&
578 composerTextarea.height > 60
579 ),
580 composerTextareaGeometry: {
581 fieldBottom: composerField.bottom,
582 fieldDisplay: composerFieldStyle.display,
583 fieldGridRows: composerFieldStyle.gridTemplateRows,
584 fieldLeft: composerField.left,
585 fieldRight: composerField.right,
586 fieldTop: composerField.top,
587 labelBottom: composerLabel.bottom,
588 labelTop: composerLabel.top,
589 textareaBottom: composerTextarea.bottom,
590 textareaHeight: composerTextarea.height,
591 textareaLeft: composerTextarea.left,
592 textareaRight: composerTextarea.right,
593 textareaTop: composerTextarea.top,
594 },
561 fillsViewport: shell.left === 0 && 595 fillsViewport: shell.left === 0 &&
562 shell.top === 0 && 596 shell.top === 0 &&
563 Math.abs(shell.right - innerWidth) < 1 && 597 Math.abs(shell.right - innerWidth) < 1 &&
564 Math.abs(shell.bottom - innerHeight) < 1 && 598 Math.abs(shell.bottom - innerHeight) < 1 &&
565 main.left === 0 && 599 main.left === 0 &&
570 fontLoaded: [...document.fonts].some(font => 604 fontLoaded: [...document.fonts].some(font =>
571 font.family.replaceAll('"', '') === 'Pixel Mplus' && 605 font.family.replaceAll('"', '') === 'Pixel Mplus' &&
572 font.status === 'loaded' 606 font.status === 'loaded'
573 ), 607 ),
574 menuRightOfComposer: menu.left >= composer.right, 608 menuRightOfComposer: menu.left >= composer.right,
609 messageViewportFills: (
610 Math.abs(messageViewport.top - messageScroller.top) < 1 &&
611 Math.abs(messageViewport.bottom - messageScroller.bottom) < 1 &&
612 messageViewport.height > 192
613 ),
575 noOverflow: document.documentElement.scrollWidth <= innerWidth, 614 noOverflow: document.documentElement.scrollWidth <= innerWidth,
576 nonPixelText, 615 nonPixelText,
616 questLabelAbsent: !document.body.textContent.includes('QUEST 01'),
577 theme: document.documentElement.dataset.zenTheme, 617 theme: document.documentElement.dataset.zenTheme,
578 utilityRightOfScene: utility.left >= scene.right, 618 utilityRightOfScene: utility.left >= scene.right,
579 }; 619 };
580 }); 620 });
581 assert.match(desktop.art, /bar-ink\.webp/); 621 assert.match(desktop.art, /bar-ink\.webp/);
590 assert.equal(desktop.colors.menu, desktop.colors.sunken); 630 assert.equal(desktop.colors.menu, desktop.colors.sunken);
591 assert.equal(desktop.colors.primaryText, desktop.colors.primary); 631 assert.equal(desktop.colors.primaryText, desktop.colors.primary);
592 assert.equal(desktop.colors.submit, desktop.colors.primary); 632 assert.equal(desktop.colors.submit, desktop.colors.primary);
593 assert.equal(desktop.colors.utility, desktop.colors.surface); 633 assert.equal(desktop.colors.utility, desktop.colors.surface);
594 assert.equal(desktop.composerBelowScene, true); 634 assert.equal(desktop.composerBelowScene, true);
635 assert.equal(
636 desktop.composerTextareaFills,
637 true,
638 JSON.stringify(desktop.composerTextareaGeometry),
639 );
595 assert.equal(desktop.fillsViewport, true); 640 assert.equal(desktop.fillsViewport, true);
596 assert.ok(desktop.fonts.every(font => font === '"Pixel Mplus"')); 641 assert.ok(desktop.fonts.every(font => font === '"Pixel Mplus"'));
597 assert.equal(desktop.fontLoaded, true); 642 assert.equal(desktop.fontLoaded, true);
598 assert.equal(desktop.menuRightOfComposer, true); 643 assert.equal(desktop.menuRightOfComposer, true);
644 assert.equal(desktop.messageViewportFills, true);
599 assert.equal(desktop.noOverflow, true); 645 assert.equal(desktop.noOverflow, true);
600 assert.deepEqual(desktop.nonPixelText, []); 646 assert.deepEqual(desktop.nonPixelText, []);
647 assert.equal(desktop.questLabelAbsent, true);
601 assert.equal(desktop.theme, 'cyberpunk'); 648 assert.equal(desktop.theme, 'cyberpunk');
602 assert.equal(desktop.utilityRightOfScene, true); 649 assert.equal(desktop.utilityRightOfScene, true);
603 await page.locator('.jrpg-brand a').focus(); 650 await page.locator('.jrpg-brand a').focus();
604 assert.equal( 651 assert.equal(
605 await page.locator('.jrpg-brand a').evaluate(element => 652 await page.locator('.jrpg-brand a').evaluate(element =>
616 ); 663 );
617 assert.equal( 664 assert.equal(
618 await page.locator('meta[name="theme-color"]').first().getAttribute('content'), 665 await page.locator('meta[name="theme-color"]').first().getAttribute('content'),
619 desktop.colors.canvas, 666 desktop.colors.canvas,
620 ); 667 );
668 assert.equal(await page.locator('[data-work-showcase] a').count(), 6);
669 assert.match(
670 await page.locator('[data-work-showcase]').textContent(),
671 /Copilot Tasks/,
672 );
673 assert.match(
674 await page.locator('[data-work-showcase]').textContent(),
675 /Copilot SuperApp/,
676 );
677 assert.equal(
678 await page.locator('[data-turn-position]').textContent(),
679 'START',
680 );
681 assert.equal(
682 await page.getByRole('button', { name: 'Previous turn' }).isDisabled(),
683 true,
684 );
685 assert.equal(
686 await page.getByRole('button', { name: 'Next turn' }).isDisabled(),
687 true,
688 );
621 689
622 await page.getByRole('button', { name: 'Tools', exact: true }).click(); 690 await page.getByRole('button', { name: 'Tools', exact: true }).click();
623 assert.equal( 691 assert.equal(
624 await page.locator('[data-preview-title]').textContent(), 692 await page.locator('[data-preview-title]').textContent(),
625 'Tools', 693 'Tools',
631 assert.equal( 699 assert.equal(
632 await page.getByRole('button', { name: 'Tools', exact: true }) 700 await page.getByRole('button', { name: 'Tools', exact: true })
633 .getAttribute('aria-pressed'), 701 .getAttribute('aria-pressed'),
634 'true', 702 'true',
635 ); 703 );
704 assert.equal(await page.locator('[data-work-showcase] a').count(), 3);
636 705
637 await page.getByRole('button', { name: /Inspect/ }).click(); 706 await page.getByRole('button', { name: /Inspect/ }).click();
638 await page.locator('.jrpg-preview-dialog dialog[open]').waitFor(); 707 await page.locator('.jrpg-preview-dialog dialog[open]').waitFor();
639 assert.equal( 708 assert.equal(
640 await page.locator('[data-dialog-title]').textContent(), 709 await page.locator('[data-dialog-title]').textContent(),
641 'Tools', 710 'Tools',
642 ); 711 );
643 await page.getByRole('button', { 712 await page.getByRole('button', {
644 name: 'Close destination details', 713 name: 'Close destination details',
645 }).click(); 714 }).click();
646 715 await page.locator('.jrpg-preview-dialog dialog').waitFor({ state: 'hidden' });
716
717 await page.getByRole('button', { name: 'Resume', exact: true }).click();
718 await page.getByRole('button', { name: /Inspect/ }).click();
719 await page.locator('[data-resume-content] .sub-header').first().waitFor();
720 const resumeDossier = await page.locator('[data-resume-dossier]').evaluate(
721 dossier => ({
722 fontFamily: getComputedStyle(dossier).fontFamily,
723 text: dossier.textContent,
724 }),
725 );
726 assert.equal(resumeDossier.fontFamily, '"Pixel Mplus"');
727 assert.match(resumeDossier.text, /Copilot Tasks/);
728 assert.match(resumeDossier.text, /Microsoft/);
729 assert.match(resumeDossier.text, /Meta/);
730 assert.equal(
731 await page.locator('[data-resume-content] a[target="_blank"]').count() > 0,
732 true,
733 );
734 await page.getByRole('button', {
735 name: 'Close destination details',
736 }).click();
737
738 await page.evaluate(() => {
739 window.__jrpgStreamEvents = [];
740 document.querySelector('mjj-jrpg-shell').addEventListener(
741 'mjj-jrpg-stream-event',
742 event => window.__jrpgStreamEvents.push(event.detail),
743 );
744 });
647 await page.locator('#jrpg-message').fill('Where should I begin?'); 745 await page.locator('#jrpg-message').fill('Where should I begin?');
648 await page.locator('#jrpg-message').press('Enter'); 746 await page.locator('#jrpg-message').press('Enter');
649 await page.locator('.jrpg-message[data-speaker="June"]').last().waitFor(); 747 await page.locator('.jrpg-message[data-speaker="June"]').last().waitFor();
748 const streamingMessage = page.locator(
749 '.jrpg-message[data-speaker="Epi"][data-streaming="true"]',
750 ).last();
751 await streamingMessage.waitFor();
752 await page.locator('[data-cancel-control]').waitFor({ state: 'hidden' });
753 await streamingMessage.waitFor();
754 assert.notEqual(
755 await streamingMessage.locator('p').textContent(),
756 'hello traveler',
757 );
650 await page.getByRole('button', { name: 'Blogs', exact: true }).click(); 758 await page.getByRole('button', { name: 'Blogs', exact: true }).click();
651 assert.equal( 759 assert.equal(
652 await page.locator('.jrpg-message[data-speaker="June"] p').last() 760 await page.locator('.jrpg-message[data-speaker="June"] p').last()
653 .textContent(), 761 .textContent(),
654 'Where should I begin?', 762 'Where should I begin?',
655 ); 763 );
656 await page.waitForFunction(() => 764 await page.waitForFunction(() =>
657 document.querySelector('mjj-jrpg-character')?.getAttribute('state') === 765 document.querySelector('mjj-jrpg-character')?.getAttribute('state') ===
658 'default' && 766 'default' &&
659 document.querySelectorAll('.jrpg-message[data-speaker="Epi"]').length > 1 767 document.querySelectorAll('.jrpg-message[data-speaker="Epi"]').length > 1
768 );
769 assert.equal(
770 await page.locator('.jrpg-message[data-speaker="Epi"] p').last()
771 .textContent(),
772 'hello traveler',
773 );
774 assert.equal(
775 await page.locator(
776 '.jrpg-message[data-speaker="Epi"][data-streaming="true"]',
777 ).count(),
778 0,
779 );
780 assert.deepEqual(
781 await page.evaluate(() => {
782 const event = window.__jrpgStreamEvents.find(
783 item => item.type === 'tool.started',
784 );
785 return {
786 name: event?.data.tool?.name,
787 type: event?.type,
788 };
789 }),
790 {
791 name: 'mock-search',
792 type: 'tool.started',
793 },
794 );
795 assert.equal(
796 await page.locator('[data-turn-position]').textContent(),
797 'TURN 1 / 1',
798 );
799 assert.equal(
800 await page.getByRole('button', { name: 'Previous turn' }).isEnabled(),
801 true,
802 );
803 assert.equal(
804 await page.getByRole('button', { name: 'Next turn' }).isDisabled(),
805 true,
806 );
807 await page.getByRole('button', { name: 'Previous turn' }).click();
808 assert.equal(
809 await page.locator('[data-turn-position]').textContent(),
810 'START',
811 );
812 assert.equal(
813 await page.locator('.jrpg-message[data-speaker="June"]:visible').count(),
814 0,
815 );
816 assert.equal(
817 await page.locator('.jrpg-message[data-speaker="System"]:visible').count(),
818 1,
819 );
820 await page.getByRole('button', { name: 'Next turn' }).click();
821 assert.equal(
822 await page.locator('[data-turn-position]').textContent(),
823 'TURN 1 / 1',
824 );
825 assert.equal(
826 await page.locator('.jrpg-message[data-speaker="June"]:visible').count(),
827 1,
660 ); 828 );
661 assert.equal(await page.locator('#jrpg-message').isEnabled(), true); 829 assert.equal(await page.locator('#jrpg-message').isEnabled(), true);
662 830
663 await page.waitForFunction(() => navigator.serviceWorker?.controller); 831 await page.waitForFunction(() => navigator.serviceWorker?.controller);
664 await page.reload({ waitUntil: 'networkidle' }); 832 await page.reload({ waitUntil: 'networkidle' });
1039 await fetch(`${baseUrl}/sw.js`) 1207 await fetch(`${baseUrl}/sw.js`)
1040 ).text(); 1208 ).text();
1041 for (const source of [home, dogGame, manifest]) { 1209 for (const source of [home, dogGame, manifest]) {
1042 assert.doesNotMatch(source, /\.png(?:["')]|$)/i); 1210 assert.doesNotMatch(source, /\.png(?:["')]|$)/i);
1043 } 1211 }
1044 assert.match(serviceWorker, /v10-inference-chat/); 1212 assert.match(serviceWorker, /v13-turn-resume/);
1045 assert.match( 1213 assert.match(
1046 await ( 1214 await (
1047 await fetch(`${baseUrl}/public/pwa-register.js`) 1215 await fetch(`${baseUrl}/public/pwa-register.js`)
1048 ).text(), 1216 ).text(),
1049 /register\('\/sw\.js', \{ scope: '\/' \}\)/, 1217 /register\('\/sw\.js', \{ scope: '\/' \}\)/,