mirror of https://github.com/garrytan/gstack.git
feat: sidebar debug visibility + auth race tests
- Show attempt count in loading screen ("Connecting... attempt 3")
- After 5 failed attempts, show debug details (port, connected, token)
so stuck users can see exactly what's failing
- Add 4 tests: getPort includes token, tryConnect uses token,
dead state exists with MAX_RECONNECT_ATTEMPTS, reconnectAttempts visible
This commit is contained in:
parent
7aa3973564
commit
f04e48457e
|
|
@ -1323,3 +1323,41 @@ describe('extension dispatches gstack-extension-ready event', () => {
|
||||||
expect(contentSrc).toContain("new CustomEvent('gstack-extension-ready')");
|
expect(contentSrc).toContain("new CustomEvent('gstack-extension-ready')");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('sidebar auth race prevention', () => {
|
||||||
|
const bgSrc = fs.readFileSync(path.join(ROOT, '..', 'extension', 'background.js'), 'utf-8');
|
||||||
|
const spSrc = fs.readFileSync(path.join(ROOT, '..', 'extension', 'sidepanel.js'), 'utf-8');
|
||||||
|
|
||||||
|
test('getPort response includes authToken (not just port + connected)', () => {
|
||||||
|
// The auth race: sidepanel calls getPort, gets {port, connected} but no token.
|
||||||
|
// All subsequent requests fail 401. Token must be in the getPort response.
|
||||||
|
const getPortHandler = bgSrc.slice(
|
||||||
|
bgSrc.indexOf("msg.type === 'getPort'"),
|
||||||
|
bgSrc.indexOf("msg.type === 'setPort'"),
|
||||||
|
);
|
||||||
|
expect(getPortHandler).toContain('token: authToken');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('tryConnect uses token from getPort response', () => {
|
||||||
|
// Sidepanel must pass resp.token to updateConnection, not null
|
||||||
|
const start = spSrc.indexOf('function tryConnect()');
|
||||||
|
const end = spSrc.indexOf('\ntryConnect();', start); // top-level call after the function
|
||||||
|
const tryConnectFn = spSrc.slice(start, end);
|
||||||
|
expect(tryConnectFn).toContain('resp.token');
|
||||||
|
expect(tryConnectFn).not.toContain('updateConnection(url, null)');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sidebar debug visibility when stuck', () => {
|
||||||
|
const spSrc = fs.readFileSync(path.join(ROOT, '..', 'extension', 'sidepanel.js'), 'utf-8');
|
||||||
|
|
||||||
|
test('connection state machine has a dead state with user-visible message', () => {
|
||||||
|
expect(spSrc).toContain("'dead'");
|
||||||
|
expect(spSrc).toContain('MAX_RECONNECT_ATTEMPTS');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reconnect attempt counter is visible in the UI', () => {
|
||||||
|
// The banner should show attempt count so user knows something is happening
|
||||||
|
expect(spSrc).toContain('reconnectAttempts');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1392,12 +1392,31 @@ document.getElementById('conn-copy').addEventListener('click', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Try to connect immediately, retry every 2s until connected
|
// Try to connect immediately, retry every 2s until connected
|
||||||
|
let connectAttempts = 0;
|
||||||
function tryConnect() {
|
function tryConnect() {
|
||||||
|
connectAttempts++;
|
||||||
|
const loadingEl = document.getElementById('chat-loading');
|
||||||
|
if (loadingEl) {
|
||||||
|
const detail = connectAttempts <= 1 ? 'Connecting...'
|
||||||
|
: `Connecting... (attempt ${connectAttempts})`;
|
||||||
|
const p = loadingEl.querySelector('p');
|
||||||
|
if (p) p.textContent = detail;
|
||||||
|
}
|
||||||
chrome.runtime.sendMessage({ type: 'getPort' }, (resp) => {
|
chrome.runtime.sendMessage({ type: 'getPort' }, (resp) => {
|
||||||
if (resp && resp.port && resp.connected) {
|
if (resp && resp.port && resp.connected) {
|
||||||
const url = `http://127.0.0.1:${resp.port}`;
|
const url = `http://127.0.0.1:${resp.port}`;
|
||||||
updateConnection(url, resp.token || null);
|
updateConnection(url, resp.token || null);
|
||||||
} else {
|
} else {
|
||||||
|
// Show debug info after 5 failed attempts
|
||||||
|
if (connectAttempts >= 5 && loadingEl) {
|
||||||
|
const p = loadingEl.querySelector('p');
|
||||||
|
if (p) {
|
||||||
|
const port = resp?.port || '?';
|
||||||
|
const connected = resp?.connected || false;
|
||||||
|
const hasToken = !!(resp?.token);
|
||||||
|
p.textContent = `Waiting for server... port:${port} connected:${connected} token:${hasToken} (attempt ${connectAttempts})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
setTimeout(tryConnect, 2000);
|
setTimeout(tryConnect, 2000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue