Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions packages/extension-chrome/e2e/mvp.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion packages/extension-chrome/manifest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
16 changes: 6 additions & 10 deletions packages/extension-chrome/src/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,16 @@ const root = document.getElementById("root");
if (root == null) throw new Error("#root not found");

async function getActiveTab(): Promise<chrome.tabs.Tab | null> {
// 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<void> {
Expand Down
Loading