mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-08-19 15:46:15 +00:00
fix(extension): keep the chosen tab selected when activation fails
Selecting a tab and reopening the popup showed no target again. The selection only ever existed as currentTabId, which means "the tab we successfully injected into". Any activation failure — a player frame needing host access, a page still loading, a document that navigated mid-injection — cleared it, so the user's choice disappeared along with the failure. The choice is now stored in its own right, persisted before activation starts and kept across a failed one. GET_STATUS reports it as targetTabId with a terminal state next to it: ready, activating, access_required or error, plus the underlying message. The popup already highlights targetTabId, so the tab stays visibly selected and can explain itself instead of silently vanishing. Nothing retries on its own. Reactivation happens only when the user selects again or grants the missing host access, which is what turned the previous attempt at this into an endless reinjection loop on every popup open. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -607,6 +607,55 @@ test('rejects a hidden cross-origin player after its iframe URL redirects', asyn
|
||||
expect(await redirectedFrame.locator('video').getAttribute('data-koala-attached')).toBeNull();
|
||||
});
|
||||
|
||||
test('keeps the tab selected when its activation fails', async ({ context, extensionId }) => {
|
||||
// A page the extension is not allowed to script stands in for any activation
|
||||
// failure the user can act on. Losing the selection here is what made the
|
||||
// popup come back empty after it was closed and reopened.
|
||||
const page = await context.newPage();
|
||||
await page.goto('chrome://version');
|
||||
|
||||
const { tabId, response } = await selectTargetTab(context, extensionId, 'chrome://version/*');
|
||||
expect(response?.status).not.toBe('ok');
|
||||
|
||||
const status = await getExtensionState(context, extensionId, { type: 'GET_STATUS' });
|
||||
expect(status).toMatchObject({
|
||||
targetTabId: tabId,
|
||||
targetReady: false,
|
||||
targetActivationState: 'error'
|
||||
});
|
||||
expect(status.targetActivationError).toBeTruthy();
|
||||
|
||||
// Reopening the popup must not quietly retry and must not lose the choice.
|
||||
const second = await getExtensionState(context, extensionId, { type: 'GET_STATUS' });
|
||||
expect(second).toMatchObject({ targetTabId: tabId, targetActivationState: 'error' });
|
||||
});
|
||||
|
||||
test('drops the selection only when the user clears it', async ({ context, extensionId, baseURL }) => {
|
||||
const url = `${baseURL}/pages/simple-player.html`;
|
||||
const page = await context.newPage();
|
||||
await page.goto(url);
|
||||
await page.waitForFunction(() => window.__fixtureReady === true);
|
||||
|
||||
const { tabId } = await selectTargetTab(context, extensionId, url);
|
||||
await expect
|
||||
.poll(() => getExtensionState(context, extensionId, { type: 'GET_STATUS' })
|
||||
.then(state => state.targetTabId))
|
||||
.toBe(tabId);
|
||||
|
||||
const cleared = await getExtensionState(context, extensionId, {
|
||||
type: 'SET_TARGET_TAB',
|
||||
tabId: null
|
||||
});
|
||||
expect(cleared).toMatchObject({ status: 'ok', tabId: null });
|
||||
|
||||
const status = await getExtensionState(context, extensionId, { type: 'GET_STATUS' });
|
||||
expect(status).toMatchObject({
|
||||
targetTabId: null,
|
||||
targetReady: false,
|
||||
targetActivationState: 'none'
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Reads one global from the top document and from the player frame separately,
|
||||
* so a test can prove which frame a script was installed in.
|
||||
|
||||
Reference in New Issue
Block a user