From 3e9c6852631d74ee3349ebf57cac2eee088dec3f Mon Sep 17 00:00:00 2001 From: "Suleyman C. Ataman" Date: Sat, 28 Mar 2026 14:38:44 +0000 Subject: [PATCH 1/2] chore: version bump to 2.9.1 --- README.md | 5 ++++- app/desktop/package.json | 2 +- app/desktop/version.json | 2 +- app/website/src/data/siteContent.ts | 2 +- docs/USER_GUIDE.md | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b35c734..28b30a7 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,9 @@ Your Markdown workspace for real projects, docs, and personal knowledge bases, b [![Windows](https://img.shields.io/github/actions/workflow/status/scabir/NoteBranch/windows-installer.yml?branch=main&label=Windows)](https://github.com/scabir/notebranch/actions/workflows/windows-installer.yml) [![Linux](https://img.shields.io/github/actions/workflow/status/scabir/NoteBranch/linux-packages.yml?branch=main&label=Linux)](https://github.com/scabir/notebranch/actions/workflows/linux-packages.yml) -**Version**: 2.8.5 +Official website: [notebranch.app](https://notebranch.app) + +**Version**: 2.9.1 **License**: MIT ## Built for daily note workflows @@ -68,6 +70,7 @@ All releases: [github.com/scabir/notebranch/releases](https://github.com/scabir/ ## Website +Live website: [notebranch.app](https://notebranch.app) Static product website source: [app/website/README.md](app/website/README.md) diff --git a/app/desktop/package.json b/app/desktop/package.json index 76e2342..4a332aa 100644 --- a/app/desktop/package.json +++ b/app/desktop/package.json @@ -1,6 +1,6 @@ { "name": "NoteBranch", - "version": "2.8.5", + "version": "2.9.1", "packageManager": "pnpm@10.28.0", "description": "A Git-backed Markdown note-taking desktop application", "homepage": "https://github.com/scabir/notebranch", diff --git a/app/desktop/version.json b/app/desktop/version.json index 0e4879e..302d4b9 100644 --- a/app/desktop/version.json +++ b/app/desktop/version.json @@ -1,3 +1,3 @@ { - "version": "2.8.5" + "version": "2.9.1" } diff --git a/app/website/src/data/siteContent.ts b/app/website/src/data/siteContent.ts index 0a1d3dd..8f6e219 100644 --- a/app/website/src/data/siteContent.ts +++ b/app/website/src/data/siteContent.ts @@ -231,7 +231,7 @@ export const latestRelease = { apiUrl: githubLatestReleaseApi }; -export const desktopReleaseVersion = "2.8.5"; +export const desktopReleaseVersion = "2.9.1"; export const releasesPageUrl = `${githubBase}/releases`; diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md index 895a854..5ce3153 100644 --- a/docs/USER_GUIDE.md +++ b/docs/USER_GUIDE.md @@ -2,7 +2,7 @@ How to use NoteBranch as an end user. -**Version**: 2.8.5 +**Version**: 2.9.1 **Last Updated**: March 7, 2026 ## Table of Contents From d1f54995bc674547c0bbcb99ed297eb5d0bf109f Mon Sep 17 00:00:00 2001 From: "Suleyman C. Ataman" Date: Sat, 28 Mar 2026 23:50:20 +0000 Subject: [PATCH 2/2] test: consolidate integration tests --- .../git/repo-setup-screen.integration.spec.ts | 102 -------- .../status-bar-actions.integration.spec.ts | 66 +----- .../s3/app-lifecycle.integration.spec.ts | 49 ---- ...ditor-shell-navigation.integration.spec.ts | 123 ---------- .../s3/history-panel.integration.spec.ts | 67 ------ .../s3/repo-setup-screen.integration.spec.ts | 218 ------------------ .../s3/status-bar-actions.integration.spec.ts | 82 ------- 7 files changed, 2 insertions(+), 705 deletions(-) delete mode 100644 app/desktop/integration-tests/s3/editor-shell-navigation.integration.spec.ts diff --git a/app/desktop/integration-tests/git/repo-setup-screen.integration.spec.ts b/app/desktop/integration-tests/git/repo-setup-screen.integration.spec.ts index 62533f3..41404d2 100644 --- a/app/desktop/integration-tests/git/repo-setup-screen.integration.spec.ts +++ b/app/desktop/integration-tests/git/repo-setup-screen.integration.spec.ts @@ -3,7 +3,6 @@ import { DEFAULT_PAT, DEFAULT_REMOTE_URL, apiCreateProfile, - apiGetActiveProfileId, apiGetFullConfig, apiGetProfiles, apiSetActiveProfile, @@ -47,30 +46,6 @@ const readProfilesFromDisk = async (userDataDir: string): Promise => { return JSON.parse(content); }; -test("(git) connect to git repo (happy path)", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - - try { - const launched = await launchIntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - - await connectGitRepo(page); - - const repoInfo = await getRepoInfo(page); - expect(repoInfo.provider).toBe("git"); - expect( - path.resolve(repoInfo.localPath).startsWith(path.resolve(userDataDir)), - ).toBe(true); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - test("(git) connect using non-default branch", async ({ request: _request, }, testInfo) => { @@ -160,54 +135,6 @@ test("(git) invalid URL connect shows error and stays on setup", async ({ } }); -test("(git) active profile id persists across restart", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let firstApp: ElectronApplication | null = null; - let secondApp: ElectronApplication | null = null; - try { - const firstLaunch = await launchIntegrationApp(userDataDir); - firstApp = firstLaunch.app; - const firstPage = firstLaunch.page; - await connectGitRepo(firstPage); - - const firstRepoInfo = await getRepoInfo(firstPage); - const profile = await apiCreateProfile(firstPage, "Profile Switch B", { - provider: "git", - remoteUrl: "https://github.com/mock/profile-switch-b.git", - branch: "main", - pat: "token-switch-b", - authMethod: "pat", - }); - await apiSetActiveProfile(firstPage, profile.id); - - await closeAppIfOpen(firstApp); - firstApp = null; - - const secondLaunch = await launchIntegrationApp(userDataDir); - secondApp = secondLaunch.app; - const secondPage = secondLaunch.page; - - await expect( - secondPage.getByTestId("status-bar-commit-push-action"), - ).toBeVisible(); - await expect( - secondPage.getByRole("button", { name: "Connect to Repository" }), - ).toHaveCount(0); - - const activeProfileId = await apiGetActiveProfileId(secondPage); - expect(activeProfileId).toBe(profile.id); - - const secondRepoInfo = await getRepoInfo(secondPage); - expect(secondRepoInfo.localPath).not.toBe(firstRepoInfo.localPath); - } finally { - await closeAppIfOpen(firstApp); - await closeAppIfOpen(secondApp); - await cleanupUserDataDir(userDataDir); - } -}); - test("(git) invalid active profile id still allows workspace to load", async ({ request: _request, }, testInfo) => { @@ -286,35 +213,6 @@ test("(git) empty remote branch bootstrap succeeds", async ({ } }); -test("(git) re-open app with existing repo skips setup", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let firstApp: ElectronApplication | null = null; - let secondApp: ElectronApplication | null = null; - try { - const firstLaunch = await launchIntegrationApp(userDataDir); - firstApp = firstLaunch.app; - await connectGitRepo(firstLaunch.page); - await closeAppIfOpen(firstApp); - firstApp = null; - - const secondLaunch = await launchIntegrationApp(userDataDir); - secondApp = secondLaunch.app; - - await expect( - secondLaunch.page.getByRole("button", { name: "Connect to Repository" }), - ).toHaveCount(0); - await expect( - secondLaunch.page.getByTestId("status-bar-commit-push-action"), - ).toBeVisible(); - } finally { - await closeAppIfOpen(firstApp); - await closeAppIfOpen(secondApp); - await cleanupUserDataDir(userDataDir); - } -}); - test("(git) provider lock rejects switching from git to local", async ({ request: _request, }, testInfo) => { diff --git a/app/desktop/integration-tests/git/status-bar-actions.integration.spec.ts b/app/desktop/integration-tests/git/status-bar-actions.integration.spec.ts index 19b65bd..7846fe3 100644 --- a/app/desktop/integration-tests/git/status-bar-actions.integration.spec.ts +++ b/app/desktop/integration-tests/git/status-bar-actions.integration.spec.ts @@ -53,6 +53,7 @@ test("(git) create file, edit content, commit and push", async ({ return status.ahead; }) .toBe(0); + await expect(page.getByTestId("status-bar-push-action")).toBeDisabled(); const repoInfo = await getRepoInfo(page); await fs.access(path.join(repoInfo.localPath, "integration-note.md")); @@ -177,49 +178,6 @@ test("(git) auto-generated commit message truncates with 'and N more'", async ({ } }); -test("(git) save then commit+push ends in synced status", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - try { - const launched = await launchIntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - await connectGitRepo(page); - - await createMarkdownFile(page, "sync-state.md"); - await appendToCurrentEditor(page, "\ncontent\n"); - await saveCurrentFile(page); - await commitAndPushAll(page); - - const status = await getRepoStatus(page); - expect(status.ahead).toBe(0); - expect(status.hasUncommitted).toBe(false); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - -test("(git) push button is disabled when no pending commits", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - try { - const launched = await launchIntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - await connectGitRepo(page); - - await expect(page.getByTestId("status-bar-push-action")).toBeDisabled(); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - test("(git) push button enables after commit and disables after push", async ({ request: _request, }, testInfo) => { @@ -262,6 +220,7 @@ test("(git) pull button enabled when behind and disabled after pull", async ({ await connectGitRepo(page); await expect(page.getByTestId("status-bar-pull-action")).toBeEnabled(); + await expect(page.getByTestId("status-bar-push-action")).toBeDisabled(); await clickPull(page); await expectSavedStatus(page); await expect(page.getByTestId("status-bar-pull-action")).toBeDisabled(); @@ -506,27 +465,6 @@ test("(git) commit failure is surfaced and app stays responsive", async ({ } }); -test("(git) push remains disabled when repo is only behind", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - try { - const launched = await launchIntegrationApp(userDataDir, { - env: { NOTEBRANCH_MOCK_GIT_INITIAL_BEHIND: "2" }, - }); - app = launched.app; - const page = launched.page; - await connectGitRepo(page); - - await expect(page.getByTestId("status-bar-pull-action")).toBeEnabled(); - await expect(page.getByTestId("status-bar-push-action")).toBeDisabled(); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - test("(git) git unavailable warning is shown for existing git config", async ({ request: _request, }, testInfo) => { diff --git a/app/desktop/integration-tests/s3/app-lifecycle.integration.spec.ts b/app/desktop/integration-tests/s3/app-lifecycle.integration.spec.ts index d6cc62a..d89a5db 100644 --- a/app/desktop/integration-tests/s3/app-lifecycle.integration.spec.ts +++ b/app/desktop/integration-tests/s3/app-lifecycle.integration.spec.ts @@ -71,55 +71,6 @@ test("(S3) restart app and recover s3 workspace", async ({ } }); -test("(S3) parallel app instances stay isolated by userData path", async ({ - request: _request, -}) => { - const userDataDirA = await fs.mkdtemp( - path.join(os.tmpdir(), `NoteBranch-s3-isolation-a-${Date.now()}-`), - ); - const userDataDirB = await fs.mkdtemp( - path.join(os.tmpdir(), `NoteBranch-s3-isolation-b-${Date.now()}-`), - ); - - let appA: ElectronApplication | null = null; - let appB: ElectronApplication | null = null; - - try { - const launchA = await launchS3IntegrationApp(userDataDirA); - appA = launchA.app; - const pageA = launchA.page; - await connectS3Repo(pageA, { bucket: "s3-isolation-a" }); - await createMarkdownFile(pageA, "only-a.md"); - - const launchB = await launchS3IntegrationApp(userDataDirB); - appB = launchB.app; - const pageB = launchB.page; - await connectS3Repo(pageB, { bucket: "s3-isolation-b" }); - await createMarkdownFile(pageB, "only-b.md"); - - const repoInfoA = await getRepoInfo(pageA); - const repoInfoB = await getRepoInfo(pageB); - expect( - path.resolve(repoInfoA.localPath).startsWith(path.resolve(userDataDirA)), - ).toBe(true); - expect( - path.resolve(repoInfoB.localPath).startsWith(path.resolve(userDataDirB)), - ).toBe(true); - - const pathsA = flattenTreePaths(await listTree(pageA)); - const pathsB = flattenTreePaths(await listTree(pageB)); - expect(pathsA).toContain("only-a.md"); - expect(pathsA).not.toContain("only-b.md"); - expect(pathsB).toContain("only-b.md"); - expect(pathsB).not.toContain("only-a.md"); - } finally { - await closeAppIfOpen(appA); - await closeAppIfOpen(appB); - await cleanupUserDataDir(userDataDirA); - await cleanupUserDataDir(userDataDirB); - } -}); - test("(S3) restart preserves unsynced local changes", async ({ request: _request, }, testInfo) => { diff --git a/app/desktop/integration-tests/s3/editor-shell-navigation.integration.spec.ts b/app/desktop/integration-tests/s3/editor-shell-navigation.integration.spec.ts deleted file mode 100644 index a17721e..0000000 --- a/app/desktop/integration-tests/s3/editor-shell-navigation.integration.spec.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { - appendToCurrentEditor, - cleanupUserDataDir, - closeAppIfOpen, - connectS3Repo, - createIsolatedUserDataDir, - createMarkdownFile, - launchS3IntegrationApp, - saveCurrentFile, -} from "../helpers/gitIntegration"; -import { expect, test } from "@playwright/test"; -import type { ElectronApplication, Page } from "@playwright/test"; - -const getModKey = () => (process.platform === "darwin" ? "Meta" : "Control"); - -const readEditorContent = async (page: Page): Promise => { - return await page.locator(".cm-content").first().innerText(); -}; - -test("(S3) keyboard back/forward navigates between previously opened files", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - try { - const launched = await launchS3IntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - await connectS3Repo(page); - - await createMarkdownFile(page, "nav-a.md"); - await appendToCurrentEditor(page, "\nAAA_NAV\n"); - await saveCurrentFile(page); - - await createMarkdownFile(page, "nav-b.md"); - await appendToCurrentEditor(page, "\nBBB_NAV\n"); - await saveCurrentFile(page); - - await page.getByTestId("status-bar-header-title").click(); - await page.keyboard.press(`${getModKey()}+ArrowLeft`); - await expect - .poll(async () => (await readEditorContent(page)).includes("AAA_NAV")) - .toBe(true); - - await page.getByTestId("status-bar-header-title").click(); - await page.keyboard.press(`${getModKey()}+ArrowRight`); - await expect - .poll(async () => (await readEditorContent(page)).includes("BBB_NAV")) - .toBe(true); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - -test("(S3) navigation shortcuts are ignored while dialog input is focused", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - try { - const launched = await launchS3IntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - await connectS3Repo(page); - - await createMarkdownFile(page, "nav-focus-a.md"); - await appendToCurrentEditor(page, "\nAAA_FOCUS_NAV\n"); - await saveCurrentFile(page); - - await createMarkdownFile(page, "nav-focus-b.md"); - await appendToCurrentEditor(page, "\nBBB_FOCUS_NAV\n"); - await saveCurrentFile(page); - - await page.locator(".tree-container").click({ button: "right" }); - await page.getByRole("menuitem", { name: "New File" }).click(); - const createDialog = page.getByTestId("create-file-dialog"); - await expect(createDialog).toBeVisible(); - await createDialog.getByLabel("File Name").click(); - - await page.keyboard.press(`${getModKey()}+ArrowLeft`); - await expect(createDialog.getByLabel("File Name")).toBeFocused(); - await expect - .poll(async () => - (await readEditorContent(page)).includes("BBB_FOCUS_NAV"), - ) - .toBe(true); - - await createDialog.getByRole("button", { name: "Cancel" }).click(); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - -test("(S3) back navigation with a single entry is a no-op", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - try { - const launched = await launchS3IntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - await connectS3Repo(page); - - await createMarkdownFile(page, "nav-single.md"); - await appendToCurrentEditor(page, "\nSINGLE_NAV_ENTRY\n"); - await saveCurrentFile(page); - - await page.getByTestId("status-bar-header-title").click(); - await page.keyboard.press(`${getModKey()}+ArrowLeft`); - - await expect - .poll(async () => - (await readEditorContent(page)).includes("SINGLE_NAV_ENTRY"), - ) - .toBe(true); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); diff --git a/app/desktop/integration-tests/s3/history-panel.integration.spec.ts b/app/desktop/integration-tests/s3/history-panel.integration.spec.ts index 7f7fc92..611cf2f 100644 --- a/app/desktop/integration-tests/s3/history-panel.integration.spec.ts +++ b/app/desktop/integration-tests/s3/history-panel.integration.spec.ts @@ -113,73 +113,6 @@ test("(S3) history panel shows empty state for unsynced file", async ({ } }); -test("(S3) history entry opens read-only history viewer dialog", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - try { - const launched = await launchS3IntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - await connectS3Repo(page); - - await createMarkdownFile(page, "history-open-viewer.md"); - await appendToCurrentEditor(page, "\nviewer content\n"); - await saveCurrentFile(page); - await syncAll(page); - - await page.getByTestId("status-bar-history-action").click(); - const history = await apiGetHistory(page, "history-open-viewer.md"); - const shortHash = history[0].hash.slice(0, 7); - await page.getByText(shortHash, { exact: true }).click(); - - await expect(page.getByText("READ ONLY")).toBeVisible(); - await expect( - page.getByRole("button", { name: "Copy Content" }), - ).toBeVisible(); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - -test("(S3) history panel can be closed and reopened", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - try { - const launched = await launchS3IntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - await connectS3Repo(page); - - await createMarkdownFile(page, "history-toggle.md"); - await appendToCurrentEditor(page, "\nhistory toggle\n"); - await saveCurrentFile(page); - await syncAll(page); - - await page.getByTestId("status-bar-history-action").click(); - await expect( - page.getByRole("heading", { name: "File History" }), - ).toBeVisible(); - - await page.getByTestId("status-bar-history-action").click(); - await expect( - page.getByRole("heading", { name: "File History" }), - ).toHaveCount(0); - - await page.getByTestId("status-bar-history-action").click(); - await expect( - page.getByRole("heading", { name: "File History" }), - ).toBeVisible(); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - test("(S3) history ordering is newest-first", async ({ request: _request, }, testInfo) => { diff --git a/app/desktop/integration-tests/s3/repo-setup-screen.integration.spec.ts b/app/desktop/integration-tests/s3/repo-setup-screen.integration.spec.ts index 2f409c8..b52a8a4 100644 --- a/app/desktop/integration-tests/s3/repo-setup-screen.integration.spec.ts +++ b/app/desktop/integration-tests/s3/repo-setup-screen.integration.spec.ts @@ -4,7 +4,6 @@ import { DEFAULT_S3_REGION, DEFAULT_S3_SECRET_ACCESS_KEY, apiCreateProfile, - apiGetActiveProfileId, apiGetFullConfig, apiGetProfiles, apiSetActiveProfile, @@ -20,7 +19,6 @@ import { } from "../helpers/gitIntegration"; import { expect, test } from "@playwright/test"; import type { ElectronApplication, Page } from "@playwright/test"; -import * as path from "path"; const fillAndSubmitS3ConnectForm = async ( page: Page, @@ -50,82 +48,6 @@ const fillAndSubmitS3ConnectForm = async ( await page.getByRole("button", { name: "Connect" }).click(); }; -test("(S3) connect to s3 repo (happy path)", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - - try { - const launched = await launchS3IntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - - await connectS3Repo(page); - - const repoInfo = await getRepoInfo(page); - expect(repoInfo.provider).toBe("s3"); - expect( - path.resolve(repoInfo.localPath).startsWith(path.resolve(userDataDir)), - ).toBe(true); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - -test("(S3) connect dialog validates required s3 fields", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - try { - const launched = await launchS3IntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - - await page.getByRole("button", { name: "Connect to Repository" }).click(); - await page.getByRole("button", { name: "S3" }).click(); - await page.getByRole("button", { name: "Connect" }).click(); - - await expect( - page.getByText("Please fill in all required S3 fields"), - ).toBeVisible(); - await expect(page.getByLabel("Bucket")).toBeVisible(); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - -test("(S3) canceling setup dialog keeps app on welcome screen", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - try { - const launched = await launchS3IntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - - await page.getByRole("button", { name: "Connect to Repository" }).click(); - await page.getByRole("button", { name: "S3" }).click(); - await expect(page.getByLabel("Bucket")).toBeVisible(); - await page.getByRole("button", { name: "Cancel" }).click(); - - await expect(page.getByLabel("Bucket")).toHaveCount(0); - await expect( - page.getByRole("button", { name: "Connect to Repository" }), - ).toBeVisible(); - await expect(page.getByTestId("status-bar-commit-push-action")).toHaveCount( - 0, - ); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - test("(S3) bucket versioning disabled blocks setup and stays on setup", async ({ request: _request, }, testInfo) => { @@ -174,35 +96,6 @@ test("(S3) connect with prefix succeeds and reflects prefixed bucket label", asy } }); -test("(S3) re-open app with existing s3 repo skips setup", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let firstApp: ElectronApplication | null = null; - let secondApp: ElectronApplication | null = null; - try { - const firstLaunch = await launchS3IntegrationApp(userDataDir); - firstApp = firstLaunch.app; - await connectS3Repo(firstLaunch.page); - await closeAppIfOpen(firstApp); - firstApp = null; - - const secondLaunch = await launchS3IntegrationApp(userDataDir); - secondApp = secondLaunch.app; - - await expect( - secondLaunch.page.getByRole("button", { name: "Connect to Repository" }), - ).toHaveCount(0); - await expect( - secondLaunch.page.getByTestId("status-bar-commit-push-action"), - ).toBeVisible(); - } finally { - await closeAppIfOpen(firstApp); - await closeAppIfOpen(secondApp); - await cleanupUserDataDir(userDataDir); - } -}); - test("(S3) provider lock rejects switching from s3 to git", async ({ request: _request, }, testInfo) => { @@ -231,32 +124,6 @@ test("(S3) provider lock rejects switching from s3 to git", async ({ } }); -test("(S3) provider lock rejects switching from s3 to local", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - try { - const launched = await launchS3IntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - await connectS3Repo(page); - - const result = await apiUpdateRepoSettings(page, { - provider: "local", - localPath: "local-test-repo", - }); - - expect(result.ok).toBe(false); - expect(result.error?.message || "").toContain( - "Repository provider cannot be changed", - ); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - test("(S3) create s3 profile and activate it", async ({ request: _request, }, testInfo) => { @@ -294,88 +161,3 @@ test("(S3) create s3 profile and activate it", async ({ await cleanupUserDataDir(userDataDir); } }); - -test("(S3) active profile id persists across restart", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let firstApp: ElectronApplication | null = null; - let secondApp: ElectronApplication | null = null; - try { - const firstLaunch = await launchS3IntegrationApp(userDataDir); - firstApp = firstLaunch.app; - const firstPage = firstLaunch.page; - await connectS3Repo(firstPage); - - const firstRepoInfo = await getRepoInfo(firstPage); - const profile = await apiCreateProfile(firstPage, "S3 Profile Switch B", { - provider: "s3", - bucket: "s3-profile-switch-b", - region: "us-east-1", - prefix: "workspace-b", - accessKeyId: "switch-b-key", - secretAccessKey: "switch-b-secret", - sessionToken: "", - }); - await apiSetActiveProfile(firstPage, profile.id); - - await closeAppIfOpen(firstApp); - firstApp = null; - - const secondLaunch = await launchS3IntegrationApp(userDataDir); - secondApp = secondLaunch.app; - const secondPage = secondLaunch.page; - - await expect( - secondPage.getByTestId("status-bar-commit-push-action"), - ).toBeVisible(); - await expect( - secondPage.getByRole("button", { name: "Connect to Repository" }), - ).toHaveCount(0); - - const activeProfileId = await apiGetActiveProfileId(secondPage); - expect(activeProfileId).toBe(profile.id); - - const secondRepoInfo = await getRepoInfo(secondPage); - expect(secondRepoInfo.localPath).not.toBe(firstRepoInfo.localPath); - } finally { - await closeAppIfOpen(firstApp); - await closeAppIfOpen(secondApp); - await cleanupUserDataDir(userDataDir); - } -}); - -test("(S3) invalid active profile id still allows workspace to load", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let firstApp: ElectronApplication | null = null; - let secondApp: ElectronApplication | null = null; - try { - const firstLaunch = await launchS3IntegrationApp(userDataDir); - firstApp = firstLaunch.app; - await connectS3Repo(firstLaunch.page); - await apiSetActiveProfile(firstLaunch.page, "profile-missing-integration"); - - await closeAppIfOpen(firstApp); - firstApp = null; - - const secondLaunch = await launchS3IntegrationApp(userDataDir); - secondApp = secondLaunch.app; - const secondPage = secondLaunch.page; - - await expect( - secondPage.getByTestId("status-bar-commit-push-action"), - ).toBeVisible(); - await expect( - secondPage.getByRole("button", { name: "Connect to Repository" }), - ).toHaveCount(0); - - const repoInfo = await getRepoInfo(secondPage); - expect(repoInfo.provider).toBe("s3"); - } finally { - await closeAppIfOpen(firstApp); - await closeAppIfOpen(secondApp); - await cleanupUserDataDir(userDataDir); - } -}); diff --git a/app/desktop/integration-tests/s3/status-bar-actions.integration.spec.ts b/app/desktop/integration-tests/s3/status-bar-actions.integration.spec.ts index ccca858..71dddeb 100644 --- a/app/desktop/integration-tests/s3/status-bar-actions.integration.spec.ts +++ b/app/desktop/integration-tests/s3/status-bar-actions.integration.spec.ts @@ -1,5 +1,4 @@ import { - apiCommitAndPushAll, apiCreateFile, apiFetch, apiPull, @@ -79,62 +78,6 @@ test("(S3) local-only changes are visible before sync", async ({ } }); -test("(S3) sync all from mixed changes succeeds", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - - try { - const launched = await launchS3IntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - - await connectS3Repo(page); - await apiCreateFile(page, "", "mixed-a.md"); - await apiCreateFile(page, "", "mixed-b.md"); - - await syncAll(page); - - await expectTreeToContainPath(page, "mixed-a.md"); - await expectTreeToContainPath(page, "mixed-b.md"); - - await expect - .poll(async () => { - const status = await getRepoStatus(page); - return status.hasUncommitted || status.pendingPushCount > 0; - }) - .toBe(false); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - -test("(S3) sync with no local changes returns synced successfully", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - - try { - const launched = await launchS3IntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - - await connectS3Repo(page); - - const message = await apiCommitAndPushAll(page); - expect(message).toBe("Synced successfully"); - - const status = await getRepoStatus(page); - expect(status.hasUncommitted).toBe(false); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - test("(S3) save-all action persists unsaved buffer before sync", async ({ request: _request, }, testInfo) => { @@ -219,31 +162,6 @@ test("(S3) status bar uses bucket label and hides git-only actions", async ({ } }); -test("(S3) sync chip transitions from pending changes to synced", async ({ - request: _request, -}, testInfo) => { - const userDataDir = await createIsolatedUserDataDir(testInfo); - let app: ElectronApplication | null = null; - - try { - const launched = await launchS3IntegrationApp(userDataDir); - app = launched.app; - const page = launched.page; - - await connectS3Repo(page); - await apiCreateFile(page, "", "chip-transition.md"); - - const statusBeforeSync = await getRepoStatus(page); - expect(statusBeforeSync.pendingPushCount).toBeGreaterThan(0); - - await syncAll(page); - await expectSyncChipText(page, "Synced"); - } finally { - await closeAppIfOpen(app); - await cleanupUserDataDir(userDataDir); - } -}); - test("(S3) fetch reports status without changing tree", async ({ request: _request, }, testInfo) => {