diff --git a/packages/app/tests/stress/_helpers/content-reset.ts b/packages/app/tests/stress/_helpers/content-reset.ts new file mode 100644 index 000000000..29aeba3f2 --- /dev/null +++ b/packages/app/tests/stress/_helpers/content-reset.ts @@ -0,0 +1,38 @@ +import { readdirSync, rmSync } from 'node:fs'; +import { join } from 'node:path'; +import { REQUIRED_FIXTURE_ENTRY_NAMES } from './fixtures.ts'; + +async function deletePathIfExists( + baseURL: string, + kind: 'file' | 'folder', + path: string, +): Promise { + const res = await fetch(`${baseURL}/api/delete-path`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ kind, path }), + }); + if (res.ok || res.status === 404) return; + throw new Error(`delete-path failed for ${kind}:${path}: ${res.status} ${await res.text()}`); +} + +export async function resetContentToFixtureBaseline( + baseURL: string, + contentDir: string, +): Promise { + const preserved = new Set(REQUIRED_FIXTURE_ENTRY_NAMES); + for (const entry of readdirSync(contentDir, { withFileTypes: true })) { + if (entry.name.startsWith('.')) continue; + if (preserved.has(entry.name)) continue; + if (entry.isDirectory()) { + await deletePathIfExists(baseURL, 'folder', entry.name); + continue; + } + const docName = entry.name.replace(/\.(md|mdx)$/i, ''); + if (docName !== entry.name) { + await deletePathIfExists(baseURL, 'file', docName); + continue; + } + rmSync(join(contentDir, entry.name), { recursive: true, force: true }); + } +} diff --git a/packages/app/tests/stress/_helpers/index.ts b/packages/app/tests/stress/_helpers/index.ts index dc53a8314..ae996e2b1 100644 --- a/packages/app/tests/stress/_helpers/index.ts +++ b/packages/app/tests/stress/_helpers/index.ts @@ -1,4 +1,5 @@ export { simulateCopyAndRead, simulateCutAndRead } from './clipboard.ts'; +export { resetContentToFixtureBaseline } from './content-reset.ts'; export { focusEditor, selectAllAndWaitForSelection, diff --git a/packages/app/tests/stress/file-tree-deselect-to-root.e2e.ts b/packages/app/tests/stress/file-tree-deselect-to-root.e2e.ts index 60b2fa129..f82bc1c6c 100644 --- a/packages/app/tests/stress/file-tree-deselect-to-root.e2e.ts +++ b/packages/app/tests/stress/file-tree-deselect-to-root.e2e.ts @@ -12,7 +12,7 @@ * still lands inside the active folder. */ import type { Page } from '@playwright/test'; -import { expect, test } from './_helpers'; +import { expect, resetContentToFixtureBaseline, test } from './_helpers'; const SIDEBAR = '[data-slot="sidebar-container"]'; @@ -50,10 +50,14 @@ async function createFileAndGetDocName( } test.describe('file-tree deselect-to-root', () => { + test.beforeEach(async ({ workerServer }) => { + await resetContentToFixtureBaseline(workerServer.baseURL, workerServer.contentDir); + }); + test('empty-space click deselects the row for creation but leaves the editor view', async ({ page, api, - baseURL, + workerServer, }) => { await api.seedDocs([ { name: 'folder/note', markdown: '# Note\n' }, @@ -93,7 +97,7 @@ test.describe('file-tree deselect-to-root', () => { await expect(sidebar.getByRole('treeitem', { name: 'note.md', exact: true })).toBeVisible(); await expect(page).toHaveURL(/folder\/note$/); - const names = await createFileAndGetDocName(page, baseURL, 'created-at-root'); + const names = await createFileAndGetDocName(page, workerServer.baseURL, 'created-at-root'); expect(names).toContain('created-at-root'); expect(names).not.toContain('folder/created-at-root'); }); @@ -101,7 +105,7 @@ test.describe('file-tree deselect-to-root', () => { test('without the empty-space click, New File still lands in the active folder', async ({ page, api, - baseURL, + workerServer, }) => { await api.seedDocs([ { name: 'folder/note', markdown: '# Note\n' }, @@ -114,7 +118,7 @@ test.describe('file-tree deselect-to-root', () => { sidebar.getByRole('treeitem', { name: 'note.md', exact: true, selected: true }), ).toBeVisible({ timeout: 20_000 }); - const names = await createFileAndGetDocName(page, baseURL, 'created-in-folder'); + const names = await createFileAndGetDocName(page, workerServer.baseURL, 'created-in-folder'); expect(names).toContain('folder/created-in-folder'); expect(names).not.toContain('created-in-folder'); }); diff --git a/packages/app/tests/stress/file-tree-drag-to-root.e2e.ts b/packages/app/tests/stress/file-tree-drag-to-root.e2e.ts index 783e0c3ec..9136ed6ac 100644 --- a/packages/app/tests/stress/file-tree-drag-to-root.e2e.ts +++ b/packages/app/tests/stress/file-tree-drag-to-root.e2e.ts @@ -1,5 +1,5 @@ import type { Page } from '@playwright/test'; -import { expect, test } from './_helpers'; +import { expect, resetContentToFixtureBaseline, test } from './_helpers'; async function dragRowToEmptyRootArea( page: Page, @@ -114,10 +114,14 @@ async function expectDocNames( } test.describe('file-tree drag-to-root (PRD-7043)', () => { + test.beforeEach(async ({ workerServer }) => { + await resetContentToFixtureBaseline(workerServer.baseURL, workerServer.contentDir); + }); + test('dropping a nested folder on empty tree space promotes it to the project root', async ({ page, api, - baseURL, + workerServer, }) => { await api.seedDocs([ { name: 'parent/child/note', markdown: '# Note\n\nNested.\n' }, @@ -136,7 +140,10 @@ test.describe('file-tree drag-to-root (PRD-7043)', () => { await dragRowToEmptyRootArea(page, 'parent/child', true); - await expectDocNames(page, baseURL, { present: 'child/note', absent: 'parent/child/note' }); + await expectDocNames(page, workerServer.baseURL, { + present: 'child/note', + absent: 'parent/child/note', + }); await expect(sidebar.getByRole('treeitem', { name: 'child', exact: true })).toBeVisible({ timeout: 10_000, @@ -146,7 +153,7 @@ test.describe('file-tree drag-to-root (PRD-7043)', () => { test('dropping a nested file on empty tree space promotes it to the project root', async ({ page, api, - baseURL, + workerServer, }) => { await api.seedDocs([ { name: 'folder/note', markdown: '# Note\n\nNested file.\n' }, @@ -165,7 +172,7 @@ test.describe('file-tree drag-to-root (PRD-7043)', () => { await dragRowToEmptyRootArea(page, 'folder/note', false); - await expectDocNames(page, baseURL, { present: 'note', absent: 'folder/note' }); + await expectDocNames(page, workerServer.baseURL, { present: 'note', absent: 'folder/note' }); await expect(sidebar.getByRole('treeitem', { name: 'note.md', exact: true })).toBeVisible({ timeout: 10_000,