feat: add openLoginPopup function and delegated click handler for auth tile login buttons

- Add openLoginPopup(remoteUrl) function after buildOfflineTileHTML that strips
  trailing slashes from remoteUrl before appending /login and opens a popup
  window with width=500,height=600
- Wire up delegated click handler for .source-tile__login-btn buttons inside
  bindStaticEventListeners, right after the existing delegated kill-session handler
- Export openLoginPopup from module.exports

Tests: 4 new tests covering openLoginPopup export, window.open call
with correct URL and dimensions, trailing slash handling (with and without)
This commit is contained in:
Brian Krabach
2026-03-31 03:20:21 -07:00
parent 2862f494f5
commit d955c90c0b
2 changed files with 65 additions and 0 deletions
+19
View File
@@ -572,6 +572,16 @@ function buildOfflineTileHTML(source) {
);
}
/**
* Open a login popup window for a remote muxplex instance.
* Strips trailing slashes from remoteUrl before appending /login.
* @param {string} remoteUrl - The base URL of the remote instance
*/
function openLoginPopup(remoteUrl) {
var baseUrl = remoteUrl.replace(/\/+$/, '');
window.open(baseUrl + '/login', '_blank', 'width=500,height=600');
}
/**
* Returns sessions with hidden session names removed.
* Only hides LOCAL sessions (those with empty/absent sourceUrl) matching the
@@ -1874,6 +1884,14 @@ function bindStaticEventListeners() {
if (name) killSession(name);
});
document.addEventListener('click', function(e) {
var loginBtn = e.target.closest && e.target.closest('.source-tile__login-btn');
if (!loginBtn) return;
e.stopPropagation();
var url = loginBtn.dataset.url;
if (url) openLoginPopup(url);
});
on($('back-btn'), 'click', closeSession);
var newSessionBtn = $('new-session-btn');
if (newSessionBtn) on(newSessionBtn, 'click', function() { showNewSessionInput(newSessionBtn); });
@@ -2262,6 +2280,7 @@ if (typeof module !== 'undefined' && module.exports) {
// Federation tiles
buildAuthTileHTML,
buildOfflineTileHTML,
openLoginPopup,
formatLastSeen,
// Test-only helpers
_setCurrentSessions,
+46
View File
@@ -2977,3 +2977,49 @@ test('formatLastSeen returns days ago for diff >= 86400', () => {
assert.match(app.formatLastSeen(twoDaysAgo), /^\d+d ago$/, 'should return Xd ago for diff >= 86400s');
});
// --- openLoginPopup (task-5-login-popup-flow) ---
test('openLoginPopup is exported as a function', () => {
assert.strictEqual(typeof app.openLoginPopup, 'function', 'openLoginPopup should be exported as a function');
});
test('openLoginPopup calls window.open with correct URL and dimensions', () => {
const openCalls = [];
const origOpen = globalThis.window.open;
globalThis.window.open = (url, target, features) => { openCalls.push({ url, target, features }); };
app.openLoginPopup('http://work:8088');
globalThis.window.open = origOpen;
assert.strictEqual(openCalls.length, 1, 'window.open should be called exactly once');
assert.strictEqual(openCalls[0].url, 'http://work:8088/login', 'url should be remoteUrl + /login');
assert.strictEqual(openCalls[0].target, '_blank', 'target should be _blank');
assert.ok(openCalls[0].features.includes('width=500'), 'features should include width=500');
assert.ok(openCalls[0].features.includes('height=600'), 'features should include height=600');
});
test('openLoginPopup appends /login to URL without trailing slash', () => {
const openCalls = [];
const origOpen = globalThis.window.open;
globalThis.window.open = (url) => { openCalls.push(url); };
app.openLoginPopup('http://work:8088');
globalThis.window.open = origOpen;
assert.strictEqual(openCalls[0], 'http://work:8088/login', 'should append /login when no trailing slash');
});
test('openLoginPopup handles URL with trailing slash', () => {
const openCalls = [];
const origOpen = globalThis.window.open;
globalThis.window.open = (url) => { openCalls.push(url); };
app.openLoginPopup('http://work:8088/');
globalThis.window.open = origOpen;
assert.strictEqual(openCalls[0], 'http://work:8088/login', 'should strip trailing slash then append /login');
});