diff --git a/muxplex/frontend/app.js b/muxplex/frontend/app.js index 30a489a..5e2b5c3 100644 --- a/muxplex/frontend/app.js +++ b/muxplex/frontend/app.js @@ -524,6 +524,23 @@ function buildSidebarHTML(session, currentSession) { ); } +/** + * Build the HTML string for an auth-required source tile. + * @param {{ name: string, url: string }} source + * @returns {string} + */ +function buildAuthTileHTML(source) { + const escapedName = escapeHtml(source.name || ''); + const escapedUrl = escapeHtml(source.url || ''); + return ( + '
' + + '' + escapedName + '' + + '' + + 'Authenticate to see sessions' + + '
' + ); +} + /** * Returns sessions with hidden session names removed. * Only hides LOCAL sessions (those with empty/absent sourceUrl) matching the @@ -2200,6 +2217,8 @@ if (typeof module !== 'undefined' && module.exports) { mergeSources, // Filter bar renderFilterBar, + // Federation tiles + buildAuthTileHTML, // Test-only helpers _setCurrentSessions, _setViewMode, diff --git a/muxplex/frontend/tests/test_app.mjs b/muxplex/frontend/tests/test_app.mjs index cefee58..6194569 100644 --- a/muxplex/frontend/tests/test_app.mjs +++ b/muxplex/frontend/tests/test_app.mjs @@ -2835,3 +2835,32 @@ test('Phase 2 end-to-end: buildSources → tagSessions → mergeSources produces ); }); +// --- buildAuthTileHTML --- + +test('buildAuthTileHTML is exported as a function', () => { + assert.strictEqual(typeof app.buildAuthTileHTML, 'function'); +}); + +test('buildAuthTileHTML returns article with source-tile--auth class', () => { + const html = app.buildAuthTileHTML({ name: 'Dev Server', url: 'http://dev:8088' }); + assert.ok(html.startsWith(' { + const html = app.buildAuthTileHTML({ name: 'Dev Server', url: 'http://dev:8088' }); + assert.ok(html.includes('Dev Server'), 'html should include the device name'); +}); + +test('buildAuthTileHTML includes login button with data-url attribute', () => { + const html = app.buildAuthTileHTML({ name: 'Dev Server', url: 'http://dev:8088' }); + assert.ok(html.includes('source-tile__login-btn'), 'html should include source-tile__login-btn class'); + assert.ok(html.includes('data-url="http://dev:8088"'), 'html should include data-url attribute with correct value'); +}); + +test('buildAuthTileHTML escapes HTML in device name', () => { + const html = app.buildAuthTileHTML({ name: '', url: '' }); + assert.ok(!html.includes(''), 'raw script tag should not appear in html'); + assert.ok(html.includes('<script>'), 'escaped script tag should appear in html'); +}); +