diff --git a/packages/extension-chrome/e2e/mvp.spec.ts b/packages/extension-chrome/e2e/mvp.spec.ts index 5b09cf8..21dd438 100644 --- a/packages/extension-chrome/e2e/mvp.spec.ts +++ b/packages/extension-chrome/e2e/mvp.spec.ts @@ -9,7 +9,14 @@ test.describe("MVP smoke", () => { await expect(page.getByRole("button", { name: "Set up gitmarks" })).toBeVisible(); }); - test("options page saves settings and popup switches to save view", async ({ context, extensionId }) => { + // SKIPPED: requires the popup's getActiveTab fallback that scanned all tabs + // (which needed the broader 'tabs' manifest permission). Permission was dropped + // (issue #3) — production code now relies on the activeTab permission granted + // on toolbar-icon click. Playwright opens popup.html as a normal tab, which + // doesn't qualify as the action gesture, so chrome.tabs.query({active,currentWindow}) + // returns the popup tab itself. Same family as the Playwright SW-dispatch gap + // (issue #5). The popup save flow is covered by unit tests in test/save-flow.test.ts. + test.skip("options page saves settings and popup switches to save view", async ({ context, extensionId }) => { await installGitHubMock(context); const options = await context.newPage(); @@ -44,7 +51,12 @@ test.describe("MVP smoke", () => { await expect(options.locator("#status")).toContainText("valid PAT"); }); - test("save flow writes to mocked GitHub", async ({ context, extensionId }) => { + // SKIPPED: same Playwright/activeTab gap as the "popup switches to save view" + // test above (issue #3, #5). Save-flow unit coverage lives in + // test/save-flow.test.ts and test/bookmarks-file.test.ts; the GitHub-write + // round-trip is verified by sync.spec.ts which exercises the algorithm + // inline in the service-worker context. + test.skip("save flow writes to mocked GitHub", async ({ context, extensionId }) => { // The popup calls the GitHub API directly (page context), so context.route() // interception is sufficient — no service worker fetch patching needed. const mock = await installGitHubMock(context); diff --git a/packages/extension-chrome/manifest.config.ts b/packages/extension-chrome/manifest.config.ts index 36df5df..583c8b0 100644 --- a/packages/extension-chrome/manifest.config.ts +++ b/packages/extension-chrome/manifest.config.ts @@ -5,7 +5,7 @@ export default defineManifest({ name: "gitmarks", version: "0.0.1", description: "Save bookmarks to your own GitHub repo.", - permissions: ["storage", "activeTab", "tabs", "bookmarks", "alarms"], + permissions: ["storage", "activeTab", "bookmarks", "alarms"], host_permissions: ["https://api.github.com/*"], action: { default_popup: "src/popup.html", diff --git a/packages/extension-chrome/src/popup.ts b/packages/extension-chrome/src/popup.ts index 759e77c..26594d2 100644 --- a/packages/extension-chrome/src/popup.ts +++ b/packages/extension-chrome/src/popup.ts @@ -8,20 +8,16 @@ const root = document.getElementById("root"); if (root == null) throw new Error("#root not found"); async function getActiveTab(): Promise { - // When opened as a real extension popup, currentWindow refers to the browser - // window the user was in (not the popup's own floating window), so this gives - // the tab the user was viewing. + // When opened as a real extension popup, currentWindow refers to the + // browser window the user was in (not the popup's own floating window), + // so this returns the tab they were viewing. activeTab grants access to + // title + url for that one tab on user-gesture popup open; no broader + // tabs permission is required. const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); if (tab != null && tab.url != null && !tab.url.startsWith("chrome-extension://")) { return tab; } - // Fallback for cases where the popup is opened as a tab (e.g., in tests): - // return the most recently accessed regular tab. - const allTabs = await chrome.tabs.query({}); - const regularTabs = allTabs - .filter(t => t.url != null && !t.url.startsWith("chrome-extension://") && !t.url.startsWith("about:")) - .sort((a, b) => (b.lastAccessed ?? 0) - (a.lastAccessed ?? 0)); - return regularTabs[0] ?? null; + return null; } async function render(): Promise {