Mercurial
comparison mrjunejune/test/theme_and_webp_test.js @ 265:056790c4fb0d
add role-aware Epi assistant prompts
Add verified June knowledge, guest/member/admin Copilot profiles, profile-isolated session recovery, animated Epi greetings, and a single authoritative runtime config workflow for inference.
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 07 Aug 2026 10:50:30 -0700 |
| parents | 04fee26ecce0 |
| children | 41a49c29a28f |
comparison
equal
deleted
inserted
replaced
| 264:04fee26ecce0 | 265:056790c4fb0d |
|---|---|
| 1940 | 1940 |
| 1941 /* ------------------------------------------------------------------ */ | 1941 /* ------------------------------------------------------------------ */ |
| 1942 /* Conversation URL routing regression tests (Fixes 1ā4) */ | 1942 /* Conversation URL routing regression tests (Fixes 1ā4) */ |
| 1943 /* ------------------------------------------------------------------ */ | 1943 /* ------------------------------------------------------------------ */ |
| 1944 | 1944 |
| 1945 async function testRoleGreetings(browser) { | |
| 1946 const cases = [ | |
| 1947 { | |
| 1948 name: 'guest', | |
| 1949 session: { | |
| 1950 kind: 'guest', | |
| 1951 csrfToken: 'guest-greeting-csrf', | |
| 1952 quota: null, | |
| 1953 }, | |
| 1954 expected: 'Hi, my name is Epi. June usually calls me Epi-chan.', | |
| 1955 }, | |
| 1956 { | |
| 1957 name: 'invited', | |
| 1958 session: { | |
| 1959 kind: 'user', | |
| 1960 username: 'friend', | |
| 1961 role: 'member', | |
| 1962 mustChangePassword: false, | |
| 1963 csrfToken: 'friend-greeting-csrf', | |
| 1964 quota: null, | |
| 1965 }, | |
| 1966 expected: 'Since June invited you, I can be a little more casual.', | |
| 1967 }, | |
| 1968 { | |
| 1969 name: 'admin', | |
| 1970 session: { | |
| 1971 kind: 'user', | |
| 1972 username: 'june', | |
| 1973 role: 'admin', | |
| 1974 mustChangePassword: false, | |
| 1975 csrfToken: 'admin-greeting-csrf', | |
| 1976 quota: null, | |
| 1977 }, | |
| 1978 expected: 'Hi June! Iām Epi-chan.', | |
| 1979 }, | |
| 1980 ]; | |
| 1981 | |
| 1982 for (const testCase of cases) { | |
| 1983 const context = await browser.newContext({ serviceWorkers: 'block' }); | |
| 1984 const page = await context.newPage(); | |
| 1985 await page.route('**/api/auth/session', route => route.fulfill({ | |
| 1986 status: 200, | |
| 1987 contentType: 'application/json', | |
| 1988 body: JSON.stringify(testCase.session), | |
| 1989 })); | |
| 1990 await page.route('**/api/conversations**', route => route.fulfill({ | |
| 1991 status: 200, | |
| 1992 contentType: 'application/json', | |
| 1993 body: JSON.stringify({ conversations: [], cursor: null }), | |
| 1994 })); | |
| 1995 await page.goto(`${baseUrl}/jrpg`, { waitUntil: 'domcontentloaded' }); | |
| 1996 const greeting = page.locator('[data-greeting]'); | |
| 1997 await greeting.waitFor({ state: 'visible' }); | |
| 1998 if (testCase.name === 'guest') { | |
| 1999 await page.locator('[data-greeting][data-streaming="true"]').waitFor({ | |
| 2000 state: 'visible', | |
| 2001 }); | |
| 2002 } | |
| 2003 await page.waitForFunction(() => { | |
| 2004 const item = document.querySelector('[data-greeting]'); | |
| 2005 return item && !item.hasAttribute('data-streaming'); | |
| 2006 }); | |
| 2007 assert.match( | |
| 2008 await greeting.locator('p').textContent(), | |
| 2009 new RegExp(testCase.expected.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')), | |
| 2010 `${testCase.name} receives its role-specific Epi greeting`, | |
| 2011 ); | |
| 2012 await context.close(); | |
| 2013 } | |
| 2014 } | |
| 2015 | |
| 1945 async function testConversationRouting(browser) { | 2016 async function testConversationRouting(browser) { |
| 1946 const CONV_A = 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa'; | 2017 const CONV_A = 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa'; |
| 1947 const CONV_B = 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb'; | 2018 const CONV_B = 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb'; |
| 1948 const GUEST_SESSION = { | 2019 const GUEST_SESSION = { |
| 1949 kind: 'guest', csrfToken: 'test-csrf', | 2020 kind: 'guest', csrfToken: 'test-csrf', |
| 2197 assert.ok( | 2268 assert.ok( |
| 2198 !finalUrl.includes('conversation='), | 2269 !finalUrl.includes('conversation='), |
| 2199 `Fix 1: URL must not have ?conversation after popstate back: ${finalUrl}`, | 2270 `Fix 1: URL must not have ?conversation after popstate back: ${finalUrl}`, |
| 2200 ); | 2271 ); |
| 2201 assert.equal( | 2272 assert.equal( |
| 2202 await page.locator('.jrpg-message[data-streaming="true"]').count(), 0, | 2273 await page.locator( |
| 2274 '.jrpg-message[data-streaming="true"]:not([data-greeting])', | |
| 2275 ).count(), | |
| 2276 0, | |
| 2203 'Fix 1: no streaming message must remain after popstate back', | 2277 'Fix 1: no streaming message must remain after popstate back', |
| 2204 ); | 2278 ); |
| 2205 | 2279 |
| 2206 releaseStream(); /* clean up the held route */ | 2280 releaseStream(); /* clean up the held route */ |
| 2207 assert.deepEqual(pageErrors, [], 'no page errors in popstate-during-stream test'); | 2281 assert.deepEqual(pageErrors, [], 'no page errors in popstate-during-stream test'); |
| 2366 ...process.env, | 2440 ...process.env, |
| 2367 MRJUNEJUNE_PORT: port, | 2441 MRJUNEJUNE_PORT: port, |
| 2368 MRJUNEJUNE_DB_PATH: path.join(tempDirectory, 'site.db'), | 2442 MRJUNEJUNE_DB_PATH: path.join(tempDirectory, 'site.db'), |
| 2369 MRJUNEJUNE_INFERENCE_SIDECAR_PATH: fakeSidecar, | 2443 MRJUNEJUNE_INFERENCE_SIDECAR_PATH: fakeSidecar, |
| 2370 MRJUNEJUNE_COPILOT_CLI_PATH: fakeSidecar, | 2444 MRJUNEJUNE_COPILOT_CLI_PATH: fakeSidecar, |
| 2371 MRJUNEJUNE_ALLOW_ANONYMOUS_INFERENCE: '1', | 2445 MRJUNEJUNE_ALLOW_GUEST_INFERENCE: '1', |
| 2372 }, | 2446 }, |
| 2373 stdio: ['ignore', 'pipe', 'pipe'], | 2447 stdio: ['ignore', 'pipe', 'pipe'], |
| 2374 }); | 2448 }); |
| 2375 server.stdout.on('data', chunk => serverLogs.push(chunk.toString())); | 2449 server.stdout.on('data', chunk => serverLogs.push(chunk.toString())); |
| 2376 server.stderr.on('data', chunk => serverLogs.push(chunk.toString())); | 2450 server.stderr.on('data', chunk => serverLogs.push(chunk.toString())); |
| 2387 await fetch(`${baseUrl}/sw.js`) | 2461 await fetch(`${baseUrl}/sw.js`) |
| 2388 ).text(); | 2462 ).text(); |
| 2389 for (const source of [home, dogGame, manifest]) { | 2463 for (const source of [home, dogGame, manifest]) { |
| 2390 assert.doesNotMatch(source, /\.png(?:["')]|$)/i); | 2464 assert.doesNotMatch(source, /\.png(?:["')]|$)/i); |
| 2391 } | 2465 } |
| 2392 assert.match(serviceWorker, /v34-login-modal/); | 2466 assert.match(serviceWorker, /v35-role-greeting/); |
| 2393 assert.match( | 2467 assert.match( |
| 2394 await ( | 2468 await ( |
| 2395 await fetch(`${baseUrl}/public/pwa-register.js`) | 2469 await fetch(`${baseUrl}/public/pwa-register.js`) |
| 2396 ).text(), | 2470 ).text(), |
| 2397 /register\('\/sw\.js', \{ scope: '\/' \}\)/, | 2471 /register\('\/sw\.js', \{ scope: '\/' \}\)/, |
| 2456 await testDynamicButtonOwnership(browser); | 2530 await testDynamicButtonOwnership(browser); |
| 2457 await testResumePrint(browser); | 2531 await testResumePrint(browser); |
| 2458 } | 2532 } |
| 2459 if (runsSuite('jrpg')) await testJrpgPage(browser); | 2533 if (runsSuite('jrpg')) await testJrpgPage(browser); |
| 2460 if (runsSuite('routing')) await testConversationRouting(browser); | 2534 if (runsSuite('routing')) await testConversationRouting(browser); |
| 2535 if (runsSuite('greeting')) await testRoleGreetings(browser); | |
| 2461 if (runsSuite('hls')) await testHlsPlayer(browser, siteRoot); | 2536 if (runsSuite('hls')) await testHlsPlayer(browser, siteRoot); |
| 2462 } finally { | 2537 } finally { |
| 2463 if (browser) await browser.close(); | 2538 if (browser) await browser.close(); |
| 2464 await stopProcess(server); | 2539 await stopProcess(server); |
| 2465 fs.rmSync(tempDirectory, { recursive: true, force: true }); | 2540 fs.rmSync(tempDirectory, { recursive: true, force: true }); |