Skip to content

Commit 90bed9e

Browse files
authored
Fix popup re-enable toolbar lifecycle (#56)
## Summary - keep `EventsManager` mounted while inspector UI modules remain gated by active state - restore `EXTENSION_TOGGLE` handling inside `EventsManager` with source/state guards so popup off/on works without reload - add a patch changeset and regression coverage for popup off -> on toolbar recovery ## Test plan - [x] `pnpm check` - [x] `pnpm lint` - [x] `pnpm test:e2e --grep \"popup toggle off and on restores toolbar without reload\"` Closes #55 Made with [Cursor](https://cursor.com)
1 parent 6eabbe5 commit 90bed9e

10 files changed

Lines changed: 211 additions & 149 deletions

File tree

.changeset/brave-schools-matter.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"amgiflol": patch
3+
---
4+
5+
Fix popup disable/re-enable so the toolbar restores without requiring a page reload.

.cursor/skills/wxt-svelte-extension/SKILL.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ description: Provides project context for the amgiflol WXT + Svelte 5 browser ex
1313

1414
## Scripts
1515

16-
| Command | Purpose |
17-
| --------------------------- | ------------------------------------------------------------------------------------------- |
18-
| `pnpm dev` | Chrome dev (MV3) |
19-
| `pnpm dev:firefox` | Firefox dev (MV2 in dev) |
20-
| `pnpm build` | Chrome build (MV3) |
21-
| `pnpm build:firefox` | Firefox build (MV3) |
22-
| `pnpm build:all` | Both targets |
23-
| `pnpm zip` / `pnpm zip:all` | Distribution zips |
16+
| Command | Purpose |
17+
| --------------------------- | ------------------------ |
18+
| `pnpm dev` | Chrome dev (MV3) |
19+
| `pnpm dev:firefox` | Firefox dev (MV2 in dev) |
20+
| `pnpm build` | Chrome build (MV3) |
21+
| `pnpm build:firefox` | Firefox build (MV3) |
22+
| `pnpm build:all` | Both targets |
23+
| `pnpm zip` / `pnpm zip:all` | Distribution zips |
2424

2525
## Constraints
2626

.github/workflows/ci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ jobs:
4949
- name: Package all
5050
run: pnpm zip:all
5151

52-
5352
- name: Dry-Run Submit to stores
5453
# run: |
5554
# pnpm wxt submit --dry-run \

.github/workflows/playwright.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ jobs:
4444
- name: Run Playwright tests
4545
run: xvfb-run -a pnpm test:e2e
4646

47-
4847
- uses: actions/upload-artifact@v4
4948
if: ${{ !cancelled() }}
5049
with:

.github/workflows/release.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ jobs:
145145
run: |
146146
pnpm run zip:all
147147
148-
149148
- name: Get Version
150149
id: version
151150
run: |

e2e/pages/web.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export async function enableDomainInStorage(context: BrowserContext, domain: str
2727
if (!worker) {
2828
worker = await context.waitForEvent("serviceworker");
2929
}
30-
const setStorage = (d: string) => {
30+
const setAndVerifyStorage = async (d: string) => {
3131
const toBooleanRecord = (value: unknown): Record<string, boolean> => {
3232
if (typeof value !== "object" || value === null) return {};
3333
const result: Record<string, boolean> = {};
@@ -46,15 +46,17 @@ export async function enableDomainInStorage(context: BrowserContext, domain: str
4646
}
4747
return result;
4848
};
49-
return chrome.storage.local.get(["amg-state"]).then((result) => {
49+
const maxAttempts = 5;
50+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
51+
const result = await chrome.storage.local.get(["amg-state"]);
5052
const currentState = result["amg-state"];
5153
const stateRecord = toUnknownRecord(currentState);
5254
const baseState = {
5355
analytics: toUnknownRecord(stateRecord.analytics),
5456
domains: toBooleanRecord(stateRecord.domains),
5557
votes: toBooleanRecord(stateRecord.votes),
5658
};
57-
return chrome.storage.local.set({
59+
await chrome.storage.local.set({
5860
"amg-state": {
5961
...baseState,
6062
domains: {
@@ -63,9 +65,17 @@ export async function enableDomainInStorage(context: BrowserContext, domain: str
6365
},
6466
},
6567
});
66-
});
68+
const verifyResult = await chrome.storage.local.get(["amg-state"]);
69+
const verifyState = toUnknownRecord(verifyResult["amg-state"]);
70+
const verifyDomains = toBooleanRecord(verifyState.domains);
71+
if (verifyDomains[d] === true) {
72+
return;
73+
}
74+
await new Promise((resolve) => setTimeout(resolve, 50));
75+
}
76+
throw new Error(`Failed to enable domain state for ${d}`);
6777
};
68-
await worker.evaluate(setStorage, domain);
78+
await worker.evaluate(setAndVerifyStorage, domain);
6979
}
7080

7181
export async function enableStableDomainInStorage(context: BrowserContext) {

e2e/tests/popup.spec.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
import pkg from "../../package.json" assert { type: "json" };
22
import { expect, test } from "../fixtures";
3+
import { getExtensionToolbar } from "../pages/extension";
4+
import {
5+
enableStableDomainInStorage,
6+
expectSvelteAppLoaded,
7+
openStableTestPage,
8+
} from "../pages/web";
9+
10+
declare const chrome: {
11+
tabs: {
12+
query: (queryInfo: {
13+
active: boolean;
14+
currentWindow: boolean;
15+
}) => Promise<Array<{ id?: number }>>;
16+
sendMessage: (tabId: number, message: unknown) => Promise<void>;
17+
};
18+
};
319

420
function toUnknownRecord(value: unknown): Record<string, unknown> {
521
if (typeof value !== "object" || value === null) return {};
@@ -73,4 +89,47 @@ test.describe("Popup", () => {
7389
expect(beforeDomainValue === undefined || beforeDomainValue === false).toBeTruthy();
7490
expect(afterDomainValue).toBeTruthy();
7591
});
92+
93+
test("popup toggle off and on restores toolbar without reload", async ({
94+
context,
95+
extensionId: _extensionId,
96+
page,
97+
}) => {
98+
await enableStableDomainInStorage(context);
99+
await openStableTestPage(page);
100+
await expectSvelteAppLoaded(page);
101+
await expect(getExtensionToolbar(page)).toBeVisible();
102+
let [worker] = context.serviceWorkers();
103+
if (!worker) {
104+
worker = await context.waitForEvent("serviceworker");
105+
}
106+
await worker.evaluate(async () => {
107+
const [tab] = await chrome.tabs.query({
108+
active: true,
109+
currentWindow: true,
110+
});
111+
if (!tab?.id) return;
112+
await chrome.tabs.sendMessage(tab.id, {
113+
type: "EXTENSION_TOGGLE",
114+
payload: { isActive: false },
115+
source: { popup: true },
116+
timestamp: Date.now(),
117+
});
118+
});
119+
await expect(getExtensionToolbar(page)).toHaveCount(0);
120+
await worker.evaluate(async () => {
121+
const [tab] = await chrome.tabs.query({
122+
active: true,
123+
currentWindow: true,
124+
});
125+
if (!tab?.id) return;
126+
await chrome.tabs.sendMessage(tab.id, {
127+
type: "EXTENSION_TOGGLE",
128+
payload: { isActive: true },
129+
source: { popup: true },
130+
timestamp: Date.now(),
131+
});
132+
});
133+
await expect(getExtensionToolbar(page)).toBeVisible();
134+
});
76135
});

src/lib/Main.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@
4444
},
4545
]}
4646
>
47+
<EventsManager />
4748
{#if uiStore.isActive}
48-
<EventsManager />
4949
<SvgManager style="z-index: 1000000004" />
5050
<SelectorManager
5151
enabled={uiStore.isActive}

0 commit comments

Comments
 (0)