140 lines
5.3 KiB
TypeScript
140 lines
5.3 KiB
TypeScript
/**
|
|
* E2E test for the saved-note "Delete" button inside a diff view.
|
|
*
|
|
* Why: the saved note card (DiffCommentCard) is rendered into a Monaco view
|
|
* zone. Monaco routes mouse events to the editor by default, so the delete
|
|
* button relies on `suppressMouseDown: false` plus per-element
|
|
* stopPropagation handlers in `useDiffCommentDecorator.tsx`. A regression
|
|
* anywhere in that chain (e.g. flipping suppressMouseDown, adding an
|
|
* overlay that eats pointer events, removing the always-visible styling)
|
|
* would make the button uninteractive with no type-check or unit-test
|
|
* signal — this spec is the guard.
|
|
*
|
|
* The test seeds a DiffComment directly through the renderer store so it
|
|
* does not depend on the hover-to-"+" affordance working, which is a
|
|
* separate interaction path. It then opens the diff tab that will display
|
|
* the saved note's view zone, clicks the trash button, and asserts the
|
|
* store list is empty and the DOM zone is gone.
|
|
*/
|
|
|
|
import { test, expect } from './helpers/orca-app'
|
|
import { waitForSessionReady, waitForActiveWorktree } from './helpers/store'
|
|
|
|
test.describe('Diff note delete', () => {
|
|
test.beforeEach(async ({ orcaPage }) => {
|
|
await waitForSessionReady(orcaPage)
|
|
await waitForActiveWorktree(orcaPage)
|
|
})
|
|
|
|
test('clicking the trash button removes the saved note', async ({ orcaPage }) => {
|
|
const worktreeId = await waitForActiveWorktree(orcaPage)
|
|
|
|
// Why: modify a tracked file so opening a diff shows real added/removed
|
|
// content rather than an empty "no changes" state. `src/index.ts` is
|
|
// seeded by global-setup with a single line, so rewriting it produces a
|
|
// diff the modified Monaco editor will render against.
|
|
const { relativePath } = await orcaPage.evaluate(async (wId) => {
|
|
const store = window.__store
|
|
if (!store) {
|
|
throw new Error('window.__store is not available — is the app in dev mode?')
|
|
}
|
|
const state = store.getState()
|
|
const worktree = Object.values(state.worktreesByRepo)
|
|
.flat()
|
|
.find((entry) => entry.id === wId)
|
|
if (!worktree) {
|
|
throw new Error('active worktree not found')
|
|
}
|
|
const separator = worktree.path.includes('\\') ? '\\' : '/'
|
|
const rel = `src${separator}index.ts`
|
|
const absolutePath = `${worktree.path}${separator}${rel}`
|
|
await window.api.fs.writeFile({
|
|
filePath: absolutePath,
|
|
content: 'export const hello = "note-test"\n'
|
|
})
|
|
return { relativePath: rel }
|
|
}, worktreeId)
|
|
|
|
// Seed a diff comment directly through the store. This avoids depending
|
|
// on the hover-to-"+" interaction (a separate code path) so this spec
|
|
// stays focused on the delete affordance.
|
|
const addResult = await orcaPage.evaluate(
|
|
async ({ wId, rel }) => {
|
|
const store = window.__store
|
|
if (!store) {
|
|
throw new Error('window.__store is not available')
|
|
}
|
|
const comment = await store.getState().addDiffComment({
|
|
worktreeId: wId,
|
|
filePath: rel,
|
|
source: 'diff',
|
|
lineNumber: 1,
|
|
body: 'delete-me note',
|
|
side: 'modified'
|
|
})
|
|
return comment
|
|
},
|
|
{ wId: worktreeId, rel: relativePath }
|
|
)
|
|
expect(addResult, 'addDiffComment returned null').not.toBeNull()
|
|
const commentId = addResult!.id
|
|
|
|
// Open the diff tab for this file so the decorator mounts a view zone
|
|
// for the seeded note.
|
|
await orcaPage.evaluate(
|
|
({ wId, rel }) => {
|
|
const store = window.__store
|
|
if (!store) {
|
|
throw new Error('window.__store is not available')
|
|
}
|
|
const state = store.getState()
|
|
const worktree = Object.values(state.worktreesByRepo)
|
|
.flat()
|
|
.find((entry) => entry.id === wId)
|
|
if (!worktree) {
|
|
throw new Error('active worktree not found')
|
|
}
|
|
const separator = worktree.path.includes('\\') ? '\\' : '/'
|
|
state.openDiff(wId, `${worktree.path}${separator}${rel}`, rel, 'typescript', false)
|
|
},
|
|
{ wId: worktreeId, rel: relativePath }
|
|
)
|
|
|
|
// The note card is rendered into a Monaco view zone. Wait for the
|
|
// trash button itself rather than any parent container so we know the
|
|
// React root inside the zone has actually mounted.
|
|
const deleteButton = orcaPage.getByTitle('Delete note').first()
|
|
await expect(deleteButton).toBeVisible({ timeout: 15_000 })
|
|
|
|
await deleteButton.click()
|
|
|
|
// Store must drop the comment …
|
|
await expect
|
|
.poll(
|
|
async () =>
|
|
orcaPage.evaluate((id: string) => {
|
|
const store = window.__store
|
|
if (!store) {
|
|
return null
|
|
}
|
|
const all = Object.values(store.getState().worktreesByRepo)
|
|
.flat()
|
|
.flatMap((w) => w.diffComments ?? [])
|
|
return all.some((c) => c.id === id)
|
|
}, commentId),
|
|
{
|
|
timeout: 5_000,
|
|
message: 'deleteDiffComment did not remove the comment from the store'
|
|
}
|
|
)
|
|
.toBe(false)
|
|
|
|
// …and the view zone must be unmounted, proving the click actually
|
|
// reached the React handler (not just some other listener that
|
|
// happened to mutate the store).
|
|
await expect(orcaPage.getByTitle('Delete note')).toHaveCount(0, {
|
|
timeout: 5_000
|
|
})
|
|
})
|
|
})
|