fix: migrate test_app.mjs sourceUrl tests to remoteId API

Update 9 test_app.mjs tests that regressed when task-17 migrated
from sourceUrl to remoteId throughout app.js:

- buildTileHTML: assert data-remote-id instead of data-source-url
- buildTileHTML data-session-key: assert data-remote-id
- buildSidebarHTML: assert data-remote-id instead of data-source-url
- buildSidebarHTML empty: assert data-remote-id="" not data-source-url=""
- _previewClickHandler: assert remoteId forwarded not sourceUrl
- openSession with remoteId: assert federation proxy URL instead of remote URL
  (no credentials: include - same-origin calls don't need it)
- openSession passes remoteId to _openTerminal: assert fed-abc123
- closeSession remote: use remoteId to set _viewingRemoteId state
- cycleViewMode state pollution fixed as side-effect of above fixes
  (failing tests no longer leave document.getElementById mocked)
This commit is contained in:
Brian Krabach
2026-04-01 13:35:26 -07:00
parent 38300fcf79
commit 939a20e8b5
+29 -30
View File
@@ -562,10 +562,10 @@ test('buildTileHTML wraps snapshot in .tile-body with <pre> as direct child', ()
); );
}); });
test('buildTileHTML includes data-source-url attribute when session has sourceUrl', () => { test('buildTileHTML includes data-remote-id attribute when session has remoteId', () => {
const session = { name: 'work-project', sourceUrl: 'http://work:8088', snapshot: '' }; const session = { name: 'work-project', remoteId: 'fed-abc123', snapshot: '' };
const html = app.buildTileHTML(session, 0, false); const html = app.buildTileHTML(session, 0, false);
assert.ok(html.includes('data-source-url="http://work:8088"'), 'article should have data-source-url with the session sourceUrl'); assert.ok(html.includes('data-remote-id="fed-abc123"'), 'article should have data-remote-id with the session remoteId');
}); });
test('buildTileHTML does not include data-source-url for local sessions (empty sourceUrl)', () => { test('buildTileHTML does not include data-source-url for local sessions (empty sourceUrl)', () => {
@@ -724,15 +724,15 @@ test('renderGrid shows offline tile and hides empty-state when no sessions but s
app._setSources([]); app._setSources([]);
}); });
test('_previewClickHandler looks up sourceUrl from _currentSessions before calling openSession', () => { test('_previewClickHandler looks up remoteId from _currentSessions before calling openSession', () => {
const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8'); const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8');
const handlerIdx = source.indexOf('function _previewClickHandler'); const handlerIdx = source.indexOf('function _previewClickHandler');
assert.ok(handlerIdx >= 0, '_previewClickHandler must exist'); assert.ok(handlerIdx >= 0, '_previewClickHandler must exist');
// Extract from function declaration to its closing brace // Extract from function declaration to its closing brace
const handlerEnd = source.indexOf('\n}', handlerIdx) + 2; const handlerEnd = source.indexOf('\n}', handlerIdx) + 2;
const handlerBody = source.slice(handlerIdx, handlerEnd); const handlerBody = source.slice(handlerIdx, handlerEnd);
assert.ok(handlerBody.includes('_currentSessions'), '_previewClickHandler must look up session from _currentSessions to recover sourceUrl'); assert.ok(handlerBody.includes('_currentSessions'), '_previewClickHandler must look up session from _currentSessions to recover remoteId');
assert.ok(handlerBody.includes('sourceUrl'), '_previewClickHandler must forward sourceUrl when calling openSession'); assert.ok(handlerBody.includes('remoteId'), '_previewClickHandler must forward remoteId when calling openSession');
}); });
// --- requestNotificationPermission --- // --- requestNotificationPermission ---
@@ -1191,7 +1191,7 @@ test('openSession shows toast and calls closeSession on connect failure', async
globalThis.setTimeout = origSetTimeout; globalThis.setTimeout = origSetTimeout;
}); });
test('openSession with sourceUrl POSTs connect to remote instance URL', async () => { test('openSession with remoteId POSTs connect to federation proxy URL', async () => {
const fetchCalls = []; const fetchCalls = [];
const origFetch = globalThis.fetch; const origFetch = globalThis.fetch;
const origGetById = globalThis.document.getElementById; const origGetById = globalThis.document.getElementById;
@@ -1203,19 +1203,18 @@ test('openSession with sourceUrl POSTs connect to remote instance URL', async ()
globalThis.setTimeout = () => {}; globalThis.setTimeout = () => {};
globalThis.window._openTerminal = () => {}; globalThis.window._openTerminal = () => {};
await app.openSession('work-project', { sourceUrl: 'http://work:8088' }); await app.openSession('work-project', { remoteId: 'fed-abc123' });
const connectCall = fetchCalls.find((c) => c.url === 'http://work:8088/api/sessions/work-project/connect'); const connectCall = fetchCalls.find((c) => c.url === '/api/federation/fed-abc123/connect/work-project');
assert.ok(connectCall, 'should POST to http://work:8088/api/sessions/work-project/connect'); assert.ok(connectCall, 'should POST to /api/federation/fed-abc123/connect/work-project');
assert.strictEqual(connectCall.opts.method, 'POST'); assert.strictEqual(connectCall.opts.method, 'POST');
assert.strictEqual(connectCall.opts.credentials, 'include');
globalThis.fetch = origFetch; globalThis.fetch = origFetch;
globalThis.document.getElementById = origGetById; globalThis.document.getElementById = origGetById;
globalThis.document.querySelector = origQS; globalThis.document.querySelector = origQS;
globalThis.setTimeout = origSetTimeout; globalThis.setTimeout = origSetTimeout;
}); });
test('openSession with sourceUrl passes sourceUrl to window._openTerminal', async () => { test('openSession with remoteId passes remoteId to window._openTerminal', async () => {
let openTerminalArgs = null; let openTerminalArgs = null;
const origFetch = globalThis.fetch; const origFetch = globalThis.fetch;
const origGetById = globalThis.document.getElementById; const origGetById = globalThis.document.getElementById;
@@ -1227,11 +1226,11 @@ test('openSession with sourceUrl passes sourceUrl to window._openTerminal', asyn
globalThis.setTimeout = (fn) => { fn(); }; globalThis.setTimeout = (fn) => { fn(); };
globalThis.window._openTerminal = (...args) => { openTerminalArgs = args; }; globalThis.window._openTerminal = (...args) => { openTerminalArgs = args; };
await app.openSession('my-session', { sourceUrl: 'http://work:8088' }); await app.openSession('my-session', { remoteId: 'fed-abc123' });
assert.ok(openTerminalArgs !== null, '_openTerminal should have been called'); assert.ok(openTerminalArgs !== null, '_openTerminal should have been called');
assert.strictEqual(openTerminalArgs[0], 'my-session', '_openTerminal first arg should be session name'); assert.strictEqual(openTerminalArgs[0], 'my-session', '_openTerminal first arg should be session name');
assert.strictEqual(openTerminalArgs[1], 'http://work:8088', '_openTerminal second arg should be sourceUrl'); assert.strictEqual(openTerminalArgs[1], 'fed-abc123', '_openTerminal second arg should be remoteId');
globalThis.fetch = origFetch; globalThis.fetch = origFetch;
globalThis.document.getElementById = origGetById; globalThis.document.getElementById = origGetById;
globalThis.document.querySelector = origQS; globalThis.document.querySelector = origQS;
@@ -1313,13 +1312,13 @@ test('closeSession fires DELETE /api/sessions/current', async () => {
globalThis.document.getElementById = origGetById; globalThis.document.getElementById = origGetById;
}); });
test('closeSession does NOT fire DELETE for remote session (non-empty _viewingSourceUrl)', async () => { test('closeSession does NOT fire DELETE for remote session (non-empty _viewingRemoteId)', async () => {
const origFetch = globalThis.fetch; const origFetch = globalThis.fetch;
const origGetById = globalThis.document.getElementById; const origGetById = globalThis.document.getElementById;
const origQS = globalThis.document.querySelector; const origQS = globalThis.document.querySelector;
const origSetTimeout = globalThis.setTimeout; const origSetTimeout = globalThis.setTimeout;
// Setup to call openSession with remote sourceUrl // Setup to call openSession with remote remoteId
globalThis.fetch = async () => ({ ok: true }); globalThis.fetch = async () => ({ ok: true });
globalThis.document.getElementById = () => ({ textContent: '', style: {}, classList: { remove: () => {}, add: () => {} } }); globalThis.document.getElementById = () => ({ textContent: '', style: {}, classList: { remove: () => {}, add: () => {} } });
globalThis.document.querySelector = () => null; globalThis.document.querySelector = () => null;
@@ -1327,8 +1326,8 @@ test('closeSession does NOT fire DELETE for remote session (non-empty _viewingSo
globalThis.window._openTerminal = () => {}; globalThis.window._openTerminal = () => {};
globalThis.window._closeTerminal = () => {}; globalThis.window._closeTerminal = () => {};
// Open a remote session - this sets _viewingSourceUrl = 'http://work:8088' // Open a remote session - this sets _viewingRemoteId = 'fed-abc123'
await app.openSession('remote-sess', { sourceUrl: 'http://work:8088' }); await app.openSession('remote-sess', { remoteId: 'fed-abc123' });
// Restore setTimeout so Promise-based yielding works // Restore setTimeout so Promise-based yielding works
globalThis.setTimeout = origSetTimeout; globalThis.setTimeout = origSetTimeout;
@@ -1365,7 +1364,7 @@ test('closeSession still fires DELETE /api/sessions/current for local session',
globalThis.window._openTerminal = () => {}; globalThis.window._openTerminal = () => {};
globalThis.window._closeTerminal = () => {}; globalThis.window._closeTerminal = () => {};
// Open a local session - this sets _viewingSourceUrl = '' // Open a local session - this sets _viewingRemoteId = ''
await app.openSession('local-sess', {}); await app.openSession('local-sess', {});
// Restore setTimeout so Promise-based yielding works // Restore setTimeout so Promise-based yielding works
@@ -2595,23 +2594,23 @@ test('buildSidebarHTML omits device-badge when session has no deviceName', () =>
app._setSources([]); app._setSources([]);
}); });
test('buildSidebarHTML includes data-source-url attribute on article element', () => { test('buildSidebarHTML includes data-remote-id attribute on article element', () => {
const session = { const session = {
name: 'work', name: 'work',
deviceName: 'Laptop', deviceName: 'Laptop',
sourceUrl: 'https://remote.example.com', remoteId: 'fed-abc123',
sessionKey: 'https://remote.example.com::work', sessionKey: 'fed-abc123::work',
snapshot: '', snapshot: '',
bell: { unseen_count: 0 }, bell: { unseen_count: 0 },
}; };
const html = app.buildSidebarHTML(session, null); const html = app.buildSidebarHTML(session, null);
assert.ok(html.includes('data-source-url="https://remote.example.com"'), 'article should have data-source-url with correct value'); assert.ok(html.includes('data-remote-id="fed-abc123"'), 'article should have data-remote-id with correct value');
}); });
test('buildSidebarHTML data-source-url is empty string when session has no sourceUrl', () => { test('buildSidebarHTML data-remote-id is empty string when session has no remoteId', () => {
const session = { name: 'work', snapshot: '', bell: { unseen_count: 0 } }; const session = { name: 'work', snapshot: '', bell: { unseen_count: 0 } };
const html = app.buildSidebarHTML(session, null); const html = app.buildSidebarHTML(session, null);
assert.ok(html.includes('data-source-url=""'), 'article should have data-source-url as empty string when no sourceUrl'); assert.ok(html.includes('data-remote-id=""'), 'article should have data-remote-id as empty string when no remoteId');
}); });
test('buildSidebarHTML escapes HTML in deviceName within device-badge', () => { test('buildSidebarHTML escapes HTML in deviceName within device-badge', () => {
@@ -2650,17 +2649,17 @@ test('buildTileHTML omits device-badge when only one source configured', () => {
app._setSources([]); app._setSources([]);
}); });
test('buildTileHTML includes data-session-key and data-source-url attributes on article element', () => { test('buildTileHTML includes data-session-key and data-remote-id attributes on article element', () => {
const session = { const session = {
name: 'work', name: 'work',
deviceName: 'Laptop', deviceName: 'Laptop',
sourceUrl: 'https://remote.example.com', remoteId: 'fed-abc123',
sessionKey: 'https://remote.example.com::work', sessionKey: 'fed-abc123::work',
snapshot: '', snapshot: '',
}; };
const html = app.buildTileHTML(session, 0, false); const html = app.buildTileHTML(session, 0, false);
assert.ok(html.includes('data-session-key="https://remote.example.com::work"'), 'article should have data-session-key with correct value'); assert.ok(html.includes('data-session-key="fed-abc123::work"'), 'article should have data-session-key with correct value');
assert.ok(html.includes('data-source-url="https://remote.example.com"'), 'article should have data-source-url with correct value'); assert.ok(html.includes('data-remote-id="fed-abc123"'), 'article should have data-remote-id with correct value');
}); });
test('buildTileHTML escapes HTML in deviceName within device-badge', () => { test('buildTileHTML escapes HTML in deviceName within device-badge', () => {