diff --git a/apps/desktop-e2e/src/example.spec.ts b/apps/desktop-e2e/src/example.spec.ts index 0fa7a8fbd..7deb51482 100644 --- a/apps/desktop-e2e/src/example.spec.ts +++ b/apps/desktop-e2e/src/example.spec.ts @@ -1,5 +1,6 @@ -import { test, expect, _electron as electron, type ElectronApplication } from '@playwright/test'; +import { test, expect, _electron as electron, type ElectronApplication, request } from '@playwright/test'; import { join } from 'path'; +import App from './support'; let app: ElectronApplication; @@ -12,20 +13,54 @@ test.beforeAll(async () => { }); test.afterAll(async () => { + try { + const pid = app.process().pid; + + if (pid) { + // Double-check process is dead + try { + process.kill(pid, 0); // throws if process doesn't exist + process.kill(pid, 'SIGKILL'); // force kill if still alive + } catch (e) { + // Process already dead + } + } + } catch (err) { + console.warn('Failed to close Electron app cleanly:', err); + } + await app.close(); }); test('First setup', async () => { - // Get the main window - const setupWindow = await app.firstWindow(); - await expect(setupWindow).toHaveTitle("Setup"); - await expect(setupWindow.locator('h1')).toHaveText("Trilium Notes setup"); - await setupWindow.locator(`input[type="radio"]`).first().click(); + // Get the main window + const setupWindow = await app.firstWindow(); + await expect(setupWindow).toHaveTitle("Setup"); + await expect(setupWindow.locator('h1')).toHaveText("Trilium Notes setup"); + await setupWindow.locator(`input[type="radio"]`).first().click(); - // Wait for the finish. - const newWindowPromise = app.waitForEvent('window'); - await setupWindow.locator(`button[type="submit"]`, { hasText: "Next" }).click(); + // Wait for the finish. + const newWindowPromise = app.waitForEvent('window'); + await setupWindow.locator(`button[type="submit"]`, { hasText: "Next" }).click(); - const mainWindow = await newWindowPromise; - await expect(mainWindow).toHaveTitle("Trilium Notes"); + const mainWindow = await newWindowPromise; + await expect(mainWindow).toHaveTitle("Trilium Notes"); + + const support = new App(mainWindow); + await support.selectNoteInNoteTree("Trilium Demo"); + await support.setNoteShared(true); + + const sharedInfoWidget = support.currentNoteSplit.locator(".shared-info-widget"); + await expect(sharedInfoWidget).toBeVisible(); + + const sharedInfoLink = sharedInfoWidget.locator("a.shared-link"); + const linkUrl = await sharedInfoLink.getAttribute("href"); + expect(linkUrl).toBeDefined(); + + // Verify the shared link is valid + const requestContext = await request.newContext(); + const response = await requestContext.get(linkUrl!); + expect(response).toBeOK(); + + await mainWindow.waitForTimeout(5000); }); diff --git a/apps/desktop-e2e/src/support.ts b/apps/desktop-e2e/src/support.ts new file mode 100644 index 000000000..88e69f1a3 --- /dev/null +++ b/apps/desktop-e2e/src/support.ts @@ -0,0 +1,53 @@ +import { expect, Locator, Page } from "@playwright/test"; + +export default class App { + + readonly noteTree: Locator; + readonly currentNoteSplit: Locator; + readonly currentNoteSplitTitle: Locator; + readonly currentNoteSplitRibbon: Locator; + readonly currentNoteSplitContent: Locator; + page: Page; + + constructor(page: Page) { + this.page = page; + this.noteTree = page.locator(".tree-wrapper"); + this.currentNoteSplit = page.locator(".note-split:not(.hidden-ext)"); + this.currentNoteSplitTitle = this.currentNoteSplit.locator(".note-title"); + this.currentNoteSplitRibbon = this.currentNoteSplit.locator(".ribbon-container"); + this.currentNoteSplitContent = this.currentNoteSplit.locator(".note-detail-printable.visible"); + } + + async selectNoteInNoteTree(noteTitle: string) { + const item = this.noteTree.locator(`span.fancytree-node`, { hasText: noteTitle }); + await item.click(); + await expect(this.currentNoteSplitTitle).toHaveValue(noteTitle); + } + + async goToRibbonTab(tabName: string) { + await this.currentNoteSplitRibbon.locator(`.ribbon-tab-title`, { hasText: tabName }).click(); + } + + async setNoteShared(shared: boolean) { + await this.goToRibbonTab("Basic Properties"); + + // Ensure the initial state. + const switchButton = this.currentNoteSplitRibbon.locator(`.shared-switch-container .switch-button`); + if (shared) { + await expect(switchButton.locator("input")).not.toBeChecked(); + } else { + await expect(switchButton.locator("input")).toBeChecked(); + } + + // Click the switch to change the state. + await switchButton.click(); + + // Verify the state after clicking. + if (shared) { + await expect(switchButton.locator("input")).toBeChecked(); + } else { + await expect(switchButton.locator("input")).not.toBeChecked(); + } + } + +}