feat: add buildAuthTileHTML for auth-required federation tiles
- Add buildAuthTileHTML(source) function to frontend/app.js after buildSidebarHTML - Function renders an <article class='source-tile source-tile--auth'> with device name, login button (with data-url), and hint text - HTML-escapes both name and url to prevent XSS - Export buildAuthTileHTML under '// Federation tiles' comment in module.exports - Add 5 tests covering: export, article class, device name, login button data-url, HTML escaping Task: task-2-build-auth-tile-html
This commit is contained in:
@@ -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 (
|
||||
'<article class="source-tile source-tile--auth">' +
|
||||
'<span class="source-tile__name">' + escapedName + '</span>' +
|
||||
'<button class="source-tile__login-btn" data-url="' + escapedUrl + '">Log in</button>' +
|
||||
'<span class="source-tile__hint">Authenticate to see sessions</span>' +
|
||||
'</article>'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
|
||||
@@ -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('<article'), 'html should start with <article');
|
||||
assert.ok(html.includes('source-tile--auth'), 'html should include source-tile--auth class');
|
||||
});
|
||||
|
||||
test('buildAuthTileHTML includes device name', () => {
|
||||
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: '<script>alert(1)</script>', url: '' });
|
||||
assert.ok(!html.includes('<script>alert(1)</script>'), 'raw script tag should not appear in html');
|
||||
assert.ok(html.includes('<script>'), 'escaped script tag should appear in html');
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user