import { test, expect } from '@playwright/test' import { createFixtureVaultCopy, openFixtureVault, removeFixtureVaultCopy, } from '../helpers/fixtureVault' let tempVaultDir: string async function openNote(page: import('@playwright/test').Page, title: string) { await page.getByTestId('note-list-container').getByText(title, { exact: true }).click() await expect(page.getByRole('heading', { name: title, level: 1 })).toBeVisible({ timeout: 5_000 }) } async function openPropertiesPanel(page: import('@playwright/test').Page) { const openPanelButton = page.getByRole('button', { name: 'Open the properties panel' }) if (await openPanelButton.count()) { await openPanelButton.click() } } async function openFirstRelationshipInput(page: import('@playwright/test').Page) { const belongsToLabel = page.getByTestId('relationship-section-label').filter({ hasText: 'Belongs to' }).first() await expect(belongsToLabel).toBeVisible({ timeout: 5_000 }) const addButton = page.getByTestId('add-relation-ref') await expect(addButton.first()).toBeVisible() await addButton.first().click() const input = page.getByTestId('add-relation-ref-input') await expect(input).toBeVisible() return input } function slugFromTitle(title: string): string { return title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '') } test.describe('Create & open note from relationship input', () => { test.beforeEach(async ({ page }) => { await page.setViewportSize({ width: 1600, height: 900 }) tempVaultDir = createFixtureVaultCopy() await openFixtureVault(page, tempVaultDir, { expectedReadyTitle: 'Team Meeting' }) }) test.afterEach(() => { removeFixtureVaultCopy(tempVaultDir) }) test('creates note from relationship input without crash', async ({ page }) => { const pageErrors: string[] = [] page.on('pageerror', (err) => pageErrors.push(err.message)) await openNote(page, 'Team Meeting') await openPropertiesPanel(page) const input = await openFirstRelationshipInput(page) const uniqueTitle = `Test Note ${Date.now()}` await input.fill(uniqueTitle) await page.waitForTimeout(300) const createOption = page.getByTestId('create-and-open-option') await expect(createOption).toBeVisible() await createOption.click() await page.waitForTimeout(2000) // No uncaught errors (especially no "Maximum update depth exceeded") const fatal = pageErrors.filter(e => e.includes('Maximum update depth')) expect(fatal).toHaveLength(0) // App is still visible — not blank/crashed await expect(page.locator('.app__editor')).toBeVisible() }) test('only the new note tab is active after creation', async ({ page }) => { await openNote(page, 'Team Meeting') await openPropertiesPanel(page) const input = await openFirstRelationshipInput(page) const uniqueTitle = `Tab Test ${Date.now()}` await input.fill(uniqueTitle) await page.waitForTimeout(300) await page.getByTestId('create-and-open-option').click() await page.waitForTimeout(2000) // The newly created note should be the active single-note editor. await expect(page.locator('.app__editor')).toBeVisible() await expect(page.getByTestId('breadcrumb-filename-trigger')).toContainText(slugFromTitle(uniqueTitle), { timeout: 5_000, }) }) // TODO: fix relationship wikilink persistence in single-note model — the wikilink // write to the original note may race with navigation to the new note. test.skip('relationship wikilink is added to original note after creation', async ({ page }) => { await openNote(page, 'Team Meeting') await openPropertiesPanel(page) const input = await openFirstRelationshipInput(page) const uniqueTitle = `Link Test ${Date.now()}` await input.fill(uniqueTitle) await page.waitForTimeout(300) await page.getByTestId('create-and-open-option').click() await page.waitForTimeout(3000) // Navigate back to the original note (single-note model: replaces the newly created note) await openNote(page, 'Team Meeting') await page.waitForTimeout(2000) // The new wikilink should appear in the relationships const newRef = page.locator(`text=${uniqueTitle}`) await expect(newRef.first()).toBeVisible({ timeout: 8000 }) }) })