Mercurial
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: '\/' \}\)/, |