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 });