198 lines
7.1 KiB
JavaScript
198 lines
7.1 KiB
JavaScript
const { test, expect } = require('@playwright/test');
|
|
|
|
const APP_ORIGIN = process.env.FRONTEND_URL || 'http://www.jamkazam.test:3000';
|
|
const APP_HOST = new URL(APP_ORIGIN).hostname;
|
|
|
|
// Default from active local logs; can be overridden at runtime:
|
|
// REMEMBER_TOKEN=... npx playwright test ...
|
|
const REMEMBER_TOKEN = process.env.REMEMBER_TOKEN || 'xAkhA3BiUZTjTM7hovMP_g';
|
|
|
|
test.describe('Quick Start Private Session', () => {
|
|
test('enters session and stays there (no immediate auto-leave)', async ({ page, context }) => {
|
|
const jsErrors = [];
|
|
const consoleErrors = [];
|
|
const participantDeletes = [];
|
|
|
|
page.on('pageerror', (err) => {
|
|
jsErrors.push(String(err));
|
|
});
|
|
|
|
page.on('console', (msg) => {
|
|
if (msg.type() === 'error') {
|
|
consoleErrors.push(msg.text());
|
|
}
|
|
});
|
|
|
|
page.on('request', (request) => {
|
|
const method = request.method();
|
|
const url = request.url();
|
|
if (method === 'DELETE' && /\/api\/participants\//.test(url)) {
|
|
const event = { method, url, ts: new Date().toISOString() };
|
|
participantDeletes.push(event);
|
|
page.evaluate((e) => {
|
|
if (!window.__jkParticipantDeleteRequests) window.__jkParticipantDeleteRequests = [];
|
|
window.__jkParticipantDeleteRequests.push(e);
|
|
}, event).catch(() => {});
|
|
}
|
|
});
|
|
|
|
// Must exist before first request so server computes gon.isNativeClient=true.
|
|
await context.addCookies([
|
|
{
|
|
name: 'remember_token',
|
|
value: REMEMBER_TOKEN,
|
|
domain: APP_HOST,
|
|
path: '/',
|
|
httpOnly: false,
|
|
secure: false,
|
|
},
|
|
{
|
|
name: 'act_as_native_client',
|
|
value: 'true',
|
|
domain: APP_HOST,
|
|
path: '/',
|
|
httpOnly: false,
|
|
secure: false,
|
|
},
|
|
]);
|
|
|
|
await context.addInitScript(() => {
|
|
try {
|
|
window.localStorage.setItem('jk.webClient.webrtc', '1');
|
|
} catch (e) {
|
|
// ignore
|
|
}
|
|
|
|
// Test-only instrumentation: capture leaveSession callers with stack.
|
|
window.__jkLeaveActionTraces = [];
|
|
window.__jkParticipantDeleteRequests = [];
|
|
|
|
const patchLeaveAction = () => {
|
|
const actions = window.SessionActions;
|
|
if (!actions || !actions.leaveSession) return;
|
|
const action = actions.leaveSession;
|
|
if (action.__jkPlaywrightPatched) return;
|
|
|
|
const pushTrace = (source, argsLike) => {
|
|
const args = Array.prototype.slice.call(argsLike || []);
|
|
window.__jkLeaveActionTraces.push({
|
|
ts: new Date().toISOString(),
|
|
source,
|
|
args,
|
|
stack: (new Error('playwright leaveSession trace')).stack,
|
|
});
|
|
};
|
|
|
|
const wrapped = function wrappedLeaveAction(...args) {
|
|
pushTrace('call', args);
|
|
return action.apply(this, args);
|
|
};
|
|
Object.assign(wrapped, action);
|
|
|
|
if (typeof action.trigger === 'function') {
|
|
const originalTrigger = action.trigger.bind(action);
|
|
wrapped.trigger = (...args) => {
|
|
pushTrace('trigger', args);
|
|
return originalTrigger(...args);
|
|
};
|
|
}
|
|
|
|
wrapped.__jkPlaywrightPatched = true;
|
|
actions.leaveSession = wrapped;
|
|
};
|
|
|
|
setInterval(patchLeaveAction, 100);
|
|
});
|
|
|
|
let joinedUrl = null;
|
|
try {
|
|
await page.goto('/client#', { waitUntil: 'domcontentloaded' });
|
|
|
|
await page.waitForFunction(() => {
|
|
return !!(window.gon && window.gon.isNativeClient);
|
|
}, { timeout: 20000 });
|
|
|
|
await expect(page.locator('h2', { hasText: /create session/i })).toBeVisible({ timeout: 20000 });
|
|
await page.locator('.createsession').first().click();
|
|
|
|
const quickStartSolo = page.locator('.quick-start-solo', { hasText: /quick start private/i });
|
|
await expect(quickStartSolo).toBeVisible({ timeout: 20000 });
|
|
await quickStartSolo.click();
|
|
|
|
await page.waitForURL(/\/client#\/session\/[0-9a-f-]+/i, { timeout: 30000 });
|
|
joinedUrl = page.url();
|
|
|
|
const myTrack = page.locator('#session-screen .session-my-tracks .session-track.my-track');
|
|
await expect(myTrack).toBeVisible({ timeout: 15000 });
|
|
|
|
// Guard against immediate auto-leave (the current regression).
|
|
await page.waitForTimeout(7000);
|
|
|
|
const leaveTraces = await page.evaluate(() => window.__jkLeaveActionTraces || []);
|
|
await expect(page).toHaveURL(/\/client#\/session\/[0-9a-f-]+/i);
|
|
expect(page.url(), 'URL changed after join').toBe(joinedUrl);
|
|
expect(
|
|
participantDeletes,
|
|
`Unexpected DELETE /api/participants requests: ${JSON.stringify(participantDeletes, null, 2)}\nleave traces=${JSON.stringify(leaveTraces, null, 2)}`
|
|
).toHaveLength(0);
|
|
|
|
expect(jsErrors, `Page JS errors: ${JSON.stringify(jsErrors, null, 2)}`).toEqual([]);
|
|
} finally {
|
|
const diagnostics = await page.evaluate(() => {
|
|
const collector = window.JK && window.JK.DebugLogCollector;
|
|
return {
|
|
leaveActionTraces: window.__jkLeaveActionTraces || [],
|
|
joinAborts: window.__jkJoinAborts || [],
|
|
participantDeleteRequests: window.__jkParticipantDeleteRequests || [],
|
|
debugCollectorBuffer: (collector && collector.getBuffer && collector.getBuffer()) || [],
|
|
url: window.location.href,
|
|
};
|
|
}).catch(() => ({
|
|
leaveActionTraces: [],
|
|
joinAborts: [],
|
|
participantDeleteRequests: [],
|
|
debugCollectorBuffer: [],
|
|
url: page.url(),
|
|
}));
|
|
|
|
await test.info().attach('leave-traces.json', {
|
|
body: JSON.stringify(diagnostics.leaveActionTraces, null, 2),
|
|
contentType: 'application/json',
|
|
});
|
|
await test.info().attach('participant-delete-requests.json', {
|
|
body: JSON.stringify(diagnostics.participantDeleteRequests, null, 2),
|
|
contentType: 'application/json',
|
|
});
|
|
await test.info().attach('join-aborts.json', {
|
|
body: JSON.stringify(diagnostics.joinAborts, null, 2),
|
|
contentType: 'application/json',
|
|
});
|
|
await test.info().attach('debug-log-collector-buffer.json', {
|
|
body: JSON.stringify(diagnostics.debugCollectorBuffer, null, 2),
|
|
contentType: 'application/json',
|
|
});
|
|
await test.info().attach('browser-errors.json', {
|
|
body: JSON.stringify({ jsErrors, consoleErrors, participantDeletes, joinedUrl, finalUrl: diagnostics.url }, null, 2),
|
|
contentType: 'application/json',
|
|
});
|
|
// Emit to stdout for quick local triage when attachments are inconvenient to inspect.
|
|
// eslint-disable-next-line no-console
|
|
console.log('PW_DIAGNOSTICS_START');
|
|
// eslint-disable-next-line no-console
|
|
console.log(JSON.stringify({
|
|
leaveActionTraces: diagnostics.leaveActionTraces,
|
|
joinAborts: diagnostics.joinAborts,
|
|
participantDeleteRequests: diagnostics.participantDeleteRequests,
|
|
debugCollectorTail: diagnostics.debugCollectorBuffer.slice(-40),
|
|
jsErrors,
|
|
consoleErrors,
|
|
participantDeletes,
|
|
joinedUrl,
|
|
finalUrl: diagnostics.url,
|
|
}));
|
|
// eslint-disable-next-line no-console
|
|
console.log('PW_DIAGNOSTICS_END');
|
|
}
|
|
});
|
|
});
|