diff mrjunejune/test/theme_and_webp_test.js @ 273:e02e2036ef84 default tip

add Layer 2 JRPG component system Add reusable content and window modals, an isolated component sandbox, shared cyberpunk scroll areas, production-safe cache freshness, and server-rendered JRPG panel state. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Sat, 08 Aug 2026 02:08:08 -0700
parents 41a49c29a28f
children
line wrap: on
line diff
--- a/mrjunejune/test/theme_and_webp_test.js	Fri Aug 07 16:05:29 2026 -0700
+++ b/mrjunejune/test/theme_and_webp_test.js	Sat Aug 08 02:08:08 2026 -0700
@@ -491,6 +491,173 @@
   await context.close();
 }
 
+async function testModalSandbox(browser) {
+  const context = await browser.newContext({
+    serviceWorkers: 'block',
+    viewport: { width: 1280, height: 900 },
+  });
+  const page = await context.newPage();
+  const errors = [];
+  page.on('pageerror', error => errors.push(error.message));
+  page.on('console', message => {
+    if (message.type() === 'error') errors.push(message.text());
+  });
+  await page.goto(`${baseUrl}/public/component-sandbox.html`, {
+    waitUntil: 'networkidle',
+  });
+  await page.waitForFunction(() =>
+    customElements.get('mjj-content-modal') &&
+    customElements.get('mjj-window-modal') &&
+    [...document.querySelectorAll('mjj-content-modal, mjj-window-modal')]
+      .every(modal => modal.hasAttribute('data-ready'))
+  );
+  await page.evaluate(() => document.fonts.ready);
+  const modalCss = await (
+    await fetch(`${baseUrl}/public/mjj-modal.css`, { cache: 'no-store' })
+  ).text();
+  assert.match(modalCss, /clip-path:\s*inset\(49% 45%\)/);
+  assert.match(modalCss, /clip-path:\s*inset\(49% 0\)/);
+  assert.match(modalCss, /scrollbar-gutter:\s*stable/);
+
+  assert.equal(
+    await page.locator('mjj-content-modal[data-invalid], mjj-window-modal[data-invalid]')
+      .count(),
+    0,
+  );
+
+  await page.getByRole('button', { name: 'Open article' }).click();
+  const contentDialog = page.locator('mjj-content-modal dialog');
+  await contentDialog.waitFor({ state: 'visible' });
+  await contentDialog.evaluate(dialog =>
+    Promise.all(dialog.getAnimations().map(animation => animation.finished))
+  );
+  const contentMetrics = await contentDialog.evaluate(dialog => {
+    const body = dialog.querySelector(':scope > [data-modal-body]');
+    const children = [...dialog.children].map(child => {
+      if (child.hasAttribute('data-modal-header')) return 'header';
+      if (child.hasAttribute('data-modal-body')) return 'body';
+      if (child.hasAttribute('data-modal-footer')) return 'footer';
+      return child.localName;
+    });
+    const style = getComputedStyle(dialog);
+    const bodyStyle = getComputedStyle(body);
+    return {
+      bodyScrollable: ['auto', 'scroll'].includes(bodyStyle.overflowY),
+      borderRadius: style.borderRadius,
+      children,
+      fontFamily: style.fontFamily,
+      labelled: dialog.getAttribute('aria-labelledby') ===
+        dialog.querySelector('[data-modal-title]').id,
+      overflowing: body.scrollHeight > body.clientHeight,
+      shadow: style.boxShadow,
+      scrollPrimitive: body.localName,
+      scrollbarColor: bodyStyle.scrollbarColor,
+      scrollbarGutter: bodyStyle.scrollbarGutter,
+      width: dialog.getBoundingClientRect().width,
+    };
+  });
+  assert.deepEqual(contentMetrics.children, ['header', 'body', 'footer']);
+  assert.equal(contentMetrics.bodyScrollable, true);
+  assert.equal(contentMetrics.borderRadius, '0px');
+  assert.equal(contentMetrics.labelled, true);
+  assert.equal(contentMetrics.fontFamily, '"Pixel Mplus"');
+  assert.equal(contentMetrics.overflowing, true);
+  assert.equal(contentMetrics.scrollPrimitive, 'zen-scroll-area');
+  assert.notEqual(contentMetrics.scrollbarColor, 'auto');
+  assert.match(contentMetrics.scrollbarGutter, /stable/);
+  assert.notEqual(contentMetrics.shadow, 'none');
+  assert.ok(contentMetrics.width <= 864);
+  await page.getByRole('button', { name: 'Close article' }).click();
+  await contentDialog.waitFor({ state: 'hidden' });
+
+  await page.getByRole('button', { name: 'Open workspace' }).click();
+  const windowDialog = page.locator('mjj-window-modal dialog');
+  await windowDialog.waitFor({ state: 'visible' });
+  await windowDialog.evaluate(dialog =>
+    Promise.all(dialog.getAnimations().map(animation => animation.finished))
+  );
+  const windowMetrics = await windowDialog.evaluate(dialog => {
+    const body = dialog.querySelector(':scope > [data-modal-body]');
+    const bodyStyle = getComputedStyle(body);
+    const primary = dialog.querySelector('[data-modal-pane="primary"]');
+    return {
+      bodyColumns: bodyStyle.gridTemplateColumns,
+      bodyScrollable: ['auto', 'scroll'].includes(bodyStyle.overflowY),
+      borderRadius: getComputedStyle(dialog).borderRadius,
+      hasStrictRegions:
+        Boolean(dialog.querySelector(':scope > [data-modal-header]')) &&
+        Boolean(body) &&
+        Boolean(dialog.querySelector(':scope > [data-modal-footer]')),
+      height: dialog.getBoundingClientRect().height,
+      primaryShadow: getComputedStyle(primary).boxShadow,
+      primaryOverflowing: primary.scrollHeight > primary.clientHeight,
+      primaryScrollColor: getComputedStyle(primary).scrollbarColor,
+      scrollPrimitive: body.localName,
+      scrollPrimitives:
+        dialog.querySelectorAll('zen-scroll-area[data-cyber-scroll]').length,
+      scrollbarColor: bodyStyle.scrollbarColor,
+      scrollbarGutter: bodyStyle.scrollbarGutter,
+      shadow: getComputedStyle(dialog).boxShadow,
+    };
+  });
+  assert.equal(windowMetrics.bodyScrollable, true);
+  assert.equal(windowMetrics.borderRadius, '0px');
+  assert.equal(windowMetrics.hasStrictRegions, true);
+  assert.equal(windowMetrics.scrollPrimitive, 'zen-scroll-area');
+  assert.notEqual(windowMetrics.scrollbarColor, 'auto');
+  assert.match(windowMetrics.scrollbarGutter, /stable/);
+  assert.match(windowMetrics.bodyColumns, /\d/);
+  assert.ok(windowMetrics.height <= 768);
+  assert.notEqual(windowMetrics.primaryShadow, 'none');
+  assert.equal(windowMetrics.primaryOverflowing, true);
+  assert.notEqual(windowMetrics.primaryScrollColor, 'auto');
+  assert.equal(windowMetrics.scrollPrimitives, 3);
+  assert.notEqual(windowMetrics.shadow, 'none');
+  await page.getByRole('button', { name: 'Close workspace' }).click();
+  await windowDialog.waitFor({ state: 'hidden' });
+  assert.deepEqual(errors, []);
+  await context.close();
+
+  const mobileContext = await browser.newContext({
+    serviceWorkers: 'block',
+    viewport: { width: 390, height: 844 },
+  });
+  const mobilePage = await mobileContext.newPage();
+  await mobilePage.goto(`${baseUrl}/public/component-sandbox.html`, {
+    waitUntil: 'networkidle',
+  });
+  await mobilePage.evaluate(() => document.fonts.ready);
+  await mobilePage.getByRole('button', { name: 'Open workspace' }).click();
+  const mobileDialog = mobilePage.locator('mjj-window-modal dialog');
+  await mobileDialog.waitFor({ state: 'visible' });
+  await mobileDialog.evaluate(dialog =>
+    Promise.all(dialog.getAnimations().map(animation => animation.finished))
+  );
+  const mobileMetrics = await mobileDialog.evaluate(dialog => {
+    const body = dialog.querySelector('[data-modal-body]');
+    const dialogBox = dialog.getBoundingClientRect();
+    return {
+      bodyColumns: getComputedStyle(body).gridTemplateColumns,
+      fits:
+        dialogBox.left >= 0 &&
+        dialogBox.top >= 0 &&
+        dialogBox.right <= innerWidth &&
+        dialogBox.bottom <= innerHeight,
+      pageFits: document.documentElement.scrollWidth <= innerWidth,
+    };
+  });
+  assert.equal(mobileMetrics.fits, true);
+  assert.equal(mobileMetrics.pageFits, true);
+  assert.doesNotMatch(mobileMetrics.bodyColumns, /\s/);
+  assert.equal(
+    await mobilePage.locator('mjj-window-modal').getAttribute(
+      'data-component-layer',
+    ),
+    '2',
+  );
+  await mobileContext.close();
+}
+
 async function testJrpgPage(browser) {
   const context = await browser.newContext({
     colorScheme: 'light',
@@ -547,6 +714,30 @@
     }
   });
 
+  for (const expected of [
+    { panel: 'resume', title: 'Resume' },
+    { panel: 'tools', title: 'Tools' },
+    { panel: 'blog', title: 'Blogs' },
+  ]) {
+    const html = await (
+      await fetch(`${baseUrl}/jrpg?panel=${expected.panel}`)
+    ).text();
+    assert.match(
+      html,
+      new RegExp(`data-initial-panel="${expected.panel}"`),
+    );
+    assert.match(html, new RegExp(`data-preview-title>${expected.title}<`));
+    assert.doesNotMatch(html, /__MJJ_[A-Z_]+__/);
+  }
+  const conversationHtml = await (
+    await fetch(`${baseUrl}/jrpg?panel=conversations`)
+  ).text();
+  assert.match(conversationHtml, /data-initial-panel="conversations"/);
+  assert.match(
+    conversationHtml,
+    /<mjj-conversation-archive\s+aria-label="Conversations"/,
+  );
+
   await page.goto(`${baseUrl}/jrpg`, { waitUntil: 'networkidle' });
   await page.waitForFunction(() =>
     customElements.get('mjj-composer') &&
@@ -554,6 +745,26 @@
     customElements.get('mjj-jrpg-chat') &&
     customElements.get('mjj-jrpg-preview')
   );
+  await page.waitForFunction(() =>
+    [...document.querySelectorAll('.jrpg-workspace dialog')].every(dialog => {
+      const owner = dialog.closest('mjj-content-modal, mjj-window-modal');
+      return owner?.hasAttribute('data-ready');
+    })
+  );
+  assert.deepEqual(
+    await page.evaluate(() =>
+      [...document.querySelectorAll('.jrpg-workspace dialog')]
+        .map(dialog =>
+          dialog.closest('mjj-content-modal, mjj-window-modal')?.localName)),
+    [
+      'mjj-window-modal',
+      'mjj-content-modal',
+      'mjj-content-modal',
+      'mjj-window-modal',
+      'mjj-window-modal',
+    ],
+    'every JRPG dialog has a Layer 2 owner',
+  );
   await page.evaluate(() => document.fonts.ready);
   const desktop = await page.evaluate(() => {
     const scene = document.querySelector('.jrpg-scene').getBoundingClientRect();
@@ -1067,6 +1278,36 @@
     await page.locator('button[data-preview="conversations"]').getAttribute('aria-pressed'),
     'true',
   );
+  await page.evaluate(() => {
+    document.querySelector('mjj-conversation-archive')?.addConversation({
+      id: 'eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee',
+      title: 'Modal migration fixture',
+      turn_count: 2,
+    });
+  });
+  await page.getByRole('button', {
+    name: 'Rename "Modal migration fixture"',
+  }).click();
+  await page.locator('[data-archive-rename-dialog]').waitFor({ state: 'visible' });
+  assert.equal(
+    await page.locator('[data-archive-rename-dialog]').evaluate(dialog =>
+      dialog.closest('mjj-window-modal')?.localName),
+    'mjj-window-modal',
+  );
+  await page.locator('[data-archive-rename-cancel]').click();
+  await page.locator('[data-archive-rename-dialog]').waitFor({ state: 'hidden' });
+
+  await page.getByRole('button', {
+    name: 'Delete "Modal migration fixture"',
+  }).click();
+  await page.locator('[data-archive-delete-dialog]').waitFor({ state: 'visible' });
+  assert.equal(
+    await page.locator('[data-archive-delete-dialog]').evaluate(dialog =>
+      dialog.closest('mjj-content-modal')?.localName),
+    'mjj-content-modal',
+  );
+  await page.locator('[data-archive-delete-cancel]').click();
+  await page.locator('[data-archive-delete-dialog]').waitFor({ state: 'hidden' });
 
   // Archive: close via close button, navigates back to resume panel
   await page.locator('[data-archive-close]').click();
@@ -1128,10 +1369,7 @@
     await page.locator('[data-preview-title]').textContent(),
     'Tools',
   );
-  assert.equal(
-    await page.locator('[data-preview-link]').getAttribute('href'),
-    '/tools',
-  );
+  assert.equal(await page.locator('[data-preview-link]').count(), 0);
   assert.equal(
     await page.getByRole('button', { name: 'Tools', exact: true })
       .getAttribute('aria-pressed'),
@@ -1151,13 +1389,15 @@
   await page.locator('[data-latest-tool]').first().click();
   await page.locator('[data-functional-tool="markdown"]').waitFor();
   assert.equal(
+    await page.locator('.jrpg-detail-dialog').evaluate(dialog =>
+      dialog.closest('mjj-content-modal, mjj-window-modal')?.localName),
+    'mjj-window-modal',
+    'tool details use the window modal',
+  );
+  assert.equal(
     await page.locator('[data-dialog-title]').textContent(),
     '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 ===
@@ -1173,27 +1413,18 @@
       .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,
+      compact: dialogElement.offsetWidth <= innerWidth * 0.9 &&
+        dialogElement.offsetHeight <= innerHeight * 0.8,
       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'),
+      stacked: windows.length === 2 &&
+        windows[0].bottom < windows[1].top &&
+        Math.abs(windows[0].left - windows[1].left) < 1,
+      noOpenPageAction: !document.querySelector('[data-preview-link]'),
+      noFooter: !document.querySelector('[data-dialog-actions]'),
       pixelIcons: document.querySelectorAll('.jrpg-tool-window zen-icon').length >= 2 &&
         [...document.querySelectorAll('.jrpg-tool-window zen-icon svg')]
           .every(icon =>
@@ -1201,21 +1432,19 @@
           ),
       separateGlows: windowShadows.length === 2 &&
         windowShadows.every(shadow => shadow !== 'none'),
-      wideWindows: windows.every(window => window.width > dialog.width * 0.35),
+      wideWindows: windows.every(window => window.width > dialog.width * 0.8),
     };
   });
-  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.compact, true, JSON.stringify(toolWorkspace));
+  assert.equal(toolWorkspace.stacked, true);
+  assert.equal(toolWorkspace.noOpenPageAction, true);
+  assert.equal(toolWorkspace.noFooter, true);
   assert.equal(toolWorkspace.pixelIcons, true);
   assert.equal(toolWorkspace.separateGlows, true);
   assert.equal(toolWorkspace.wideWindows, true);
+  await page.locator('.jrpg-detail-dialog').evaluate(dialog =>
+    Promise.all(dialog.getAnimations().map(animation => animation.finished))
+  );
   await page.getByRole('button', {
     name: 'Close destination details',
   }).click();
@@ -1283,10 +1512,6 @@
 
   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(
@@ -1336,10 +1561,6 @@
     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',
   }).click();
@@ -1347,6 +1568,12 @@
 
   await page.getByRole('button', { name: 'Resume', exact: true }).click();
   await page.locator('[data-resume-modal]').click();
+  assert.equal(
+    await page.locator('.jrpg-detail-dialog').evaluate(dialog =>
+      dialog.closest('mjj-content-modal, mjj-window-modal')?.localName),
+    'mjj-content-modal',
+    'resume details use the content modal',
+  );
   await page.locator('[data-resume-content] .sub-header').first().waitFor();
   const resumeDossier = await page.locator('[data-resume-dossier]').evaluate(
     dossier => ({
@@ -1358,6 +1585,46 @@
   assert.match(resumeDossier.text, /Copilot Tasks/);
   assert.match(resumeDossier.text, /Microsoft/);
   assert.match(resumeDossier.text, /Meta/);
+  const resumeModalGeometry = await page.locator('.jrpg-detail-dialog').evaluate(
+    dialog => {
+      const body = dialog.querySelector('[data-modal-body]').getBoundingClientRect();
+      const dossier = dialog.querySelector('[data-resume-dossier]')
+        .getBoundingClientRect();
+      const buttons = [...dialog.querySelectorAll(
+        '[data-modal-header] zen-button > :is(button, a)',
+      )].map(control => control.getBoundingClientRect());
+      return {
+        bodyFilled:
+          dossier.width / body.width > 0.9 &&
+          Math.abs(dossier.left - body.left) < 32,
+        equalHeaderControls:
+          buttons.length === 2 &&
+          Math.abs(buttons[0].height - buttons[1].height) < 1,
+        headerControlHeights: buttons.map(button => button.height),
+        footerAbsent: !dialog.querySelector('[data-modal-footer]'),
+        headerPdfLinks: dialog.querySelectorAll(
+          '[data-modal-header] a[href="/public/resume.pdf"]',
+        ).length,
+        visibleBodyPdfLinks: [...dialog.querySelectorAll(
+          '[data-modal-body] a[href="/public/resume.pdf"]',
+        )].filter(link => getComputedStyle(link).display !== 'none').length,
+        pdfLinks: dialog.querySelectorAll('a[href="/public/resume.pdf"]').length,
+      };
+    },
+  );
+  assert.equal(resumeModalGeometry.bodyFilled, true);
+  assert.equal(
+    resumeModalGeometry.equalHeaderControls,
+    true,
+    JSON.stringify(resumeModalGeometry),
+  );
+  assert.equal(resumeModalGeometry.footerAbsent, true);
+  assert.equal(resumeModalGeometry.headerPdfLinks, 1);
+  assert.equal(resumeModalGeometry.visibleBodyPdfLinks, 0);
+  assert.equal(resumeModalGeometry.pdfLinks, 1);
+  await page.locator('.jrpg-detail-dialog').evaluate(dialog =>
+    Promise.all(dialog.getAnimations().map(animation => animation.finished))
+  );
   assert.equal(
     await page.locator('[data-resume-content] a[target="_blank"]').count() > 0,
     true,
@@ -1386,15 +1653,21 @@
     'WebSocket Demystified',
   );
   assert.equal(
-    await page.locator('[data-preview-link]').getAttribute('href'),
-    '/blog/websocket-demystified',
-  );
-  assert.equal(
     await page.locator('[data-blog-content]').evaluate(
       article => getComputedStyle(article).fontFamily,
     ),
     '"Pixel Mplus"',
   );
+  const blogWidth = await page.locator('.jrpg-detail-dialog').evaluate(dialog => {
+    const body = dialog.querySelector('[data-modal-body]').getBoundingClientRect();
+    const browser = dialog.querySelector('[data-blog-browser]')
+      .getBoundingClientRect();
+    return browser.width / body.width;
+  });
+  assert.ok(blogWidth > 0.9);
+  await page.locator('.jrpg-detail-dialog').evaluate(dialog =>
+    Promise.all(dialog.getAnimations().map(animation => animation.finished))
+  );
   await page.getByRole('button', {
     name: 'Close destination details',
   }).click();
@@ -1410,10 +1683,6 @@
     document.querySelector('[data-blog-content] h1')?.textContent ===
       'Creating Network Library in C'
   );
-  assert.equal(
-    await page.locator('[data-preview-link]').getAttribute('href'),
-    '/blog/my-seobeo-journey',
-  );
   await page.getByRole('button', {
     name: 'Close destination details',
   }).click();
@@ -1689,6 +1958,7 @@
       ).matches,
       menuInModal: menuElement.parentElement === destinationContent,
       menuHidden: menu.width === 0 && menu.height === 0,
+      modalOwner: destinationContent.closest('mjj-window-modal')?.localName,
       mobileArtIsCorrect,
       noOverflow: document.documentElement.scrollWidth <= innerWidth,
       sendButtonCompact:
@@ -1720,6 +1990,10 @@
       utilityHidden: utility.width === 0 && utility.height === 0,
       utilityInModal: document.querySelector('.jrpg-utility').parentElement ===
         destinationContent,
+      usesCyberScroll:
+        destinationContent.localName === 'zen-scroll-area' &&
+        destinationContent.hasAttribute('data-cyber-scroll') &&
+        getComputedStyle(destinationContent).scrollbarColor !== 'auto',
       usesDesignSystemControls:
         !composerField.hasAttribute('appearance') &&
         !sendOwner.hasAttribute('appearance'),
@@ -1762,6 +2036,8 @@
   );
   assert.equal(mobile.utilityHidden, true, 'destination view stays out of the frame');
   assert.equal(mobile.utilityInModal, true, 'destination view is hosted by the mobile modal');
+  assert.equal(mobile.modalOwner, 'mjj-window-modal');
+  assert.equal(mobile.usesCyberScroll, true, 'JRPG modal uses the shared cyber scroll area');
   assert.equal(mobile.usesDesignSystemControls, true, 'composer uses styled Zenbu controls');
   assert.equal(mobile.workspaceFills, true, 'workspace fills full viewport');
   assert.equal(mobile.topLeftClear, true, 'hidden dialog triggers do not cover frame art');
@@ -1771,6 +2047,9 @@
   await mobilePage.locator('[data-mobile-destination-dialog]').waitFor({
     state: 'visible',
   });
+  await mobilePage.locator('[data-mobile-destination-dialog]').evaluate(dialog =>
+    Promise.all(dialog.getAnimations().map(animation => animation.finished))
+  );
   assert.equal(
     await mobilePage.locator('[data-mobile-menu-toggle]')
       .getAttribute('aria-expanded'),
@@ -2810,7 +3089,7 @@
     for (const source of [home, dogGame, manifest]) {
       assert.doesNotMatch(source, /\.png(?:["')]|$)/i);
     }
-    assert.match(serviceWorker, /v35-role-greeting/);
+    assert.match(serviceWorker, /v37-network-first/);
     assert.match(
       await (
         await fetch(`${baseUrl}/public/pwa-register.js`)
@@ -2820,6 +3099,18 @@
     assert.ok(serviceWorker.includes("startsWith('/tools/hls_player')"));
     assert.ok(serviceWorker.includes("url.hostname === 'localhost'"));
     assert.ok(serviceWorker.includes("url.hostname === '127.0.0.1'"));
+    assert.ok(serviceWorker.includes("DEVELOPMENT_SANDBOX_PATHS"));
+    assert.ok(serviceWorker.includes("'/public/component-sandbox'"));
+    assert.ok(serviceWorker.includes("'/public/mjj-modal.'"));
+    assert.ok(serviceWorker.includes('networkFirst(request, acceptsHtml)'));
+    assert.ok(serviceWorker.includes('staleWhileRevalidate(event, request)'));
+    assert.ok(serviceWorker.includes("fetch(request, { cache: 'no-cache' })"));
+    assert.doesNotMatch(
+      await (
+        await fetch(`${baseUrl}/public/pwa-register.js`)
+      ).text(),
+      /confirm\(/,
+    );
     assert.ok(!serviceWorker.includes("startsWith('/jrpg')"));
 
     for (const asset of [
@@ -2877,6 +3168,7 @@
       await testDynamicButtonOwnership(browser);
       await testResumePrint(browser);
       await testComposerLab(browser);
+      await testModalSandbox(browser);
     }
     if (runsSuite('jrpg')) await testJrpgPage(browser);
     if (runsSuite('routing')) await testConversationRouting(browser);