Files
helder/test/app-interactions.test.tsx
Jonathan van Rij 42defcc7cd
Some checks failed
CI / check (push) Has been cancelled
copy paste
2026-06-17 13:40:49 +02:00

97 lines
3.7 KiB
TypeScript

// @vitest-environment jsdom
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
import { cleanup, fireEvent, render, waitFor } from '@testing-library/react'
import React from 'react'
vi.mock('../src/renderer/src/terminals', () => {
let n = 0
return { Terminal: () => null, lid: () => ++n }
})
import { App } from '../src/renderer/src/App'
import { ProjectProvider } from '../src/renderer/src/project'
beforeAll(() => {
globalThis.ResizeObserver = class { observe() {} unobserve() {} disconnect() {} } as never
if (!Element.prototype.scrollIntoView) Element.prototype.scrollIntoView = () => {}
})
afterEach(() => { cleanup(); localStorage.clear() })
function renderApp(): HTMLElement {
return render(<ProjectProvider><App /></ProjectProvider>).container
}
function find(c: HTMLElement, sel: string, text: string): HTMLElement | undefined {
return Array.from(c.querySelectorAll<HTMLElement>(sel)).find((el) => el.textContent?.includes(text))
}
// The tree opens fully collapsed, so expand each ancestor folder before reaching
// a nested file. Each folder is clicked exactly once (a second click collapses).
async function expandTo(c: HTMLElement, ...folders: string[]): Promise<void> {
for (const name of folders) {
const row = await waitFor(() => {
const r = find(c, '.tree-row', name)
if (!r) throw new Error(`folder ${name} not ready`)
return r
})
fireEvent.click(row)
}
}
describe('Pass on to Agent', () => {
it('inserts "<note> <path:line>" via the agentPaste event', async () => {
const received: string[] = []
const handler = (e: Event): void => { received.push((e as CustomEvent<string>).detail) }
window.addEventListener('agentPaste', handler)
try {
const c = renderApp()
await expandTo(c, 'public', 'assets')
const treeRow = await waitFor(() => {
const r = find(c, '.tree-row', 'store.js')
if (!r) throw new Error('tree not ready')
return r
})
fireEvent.click(treeRow)
const ta = await waitFor(() => {
const t = c.querySelector<HTMLTextAreaElement>('.ce-ta')
if (!t) throw new Error('editor not ready')
return t
})
fireEvent.contextMenu(ta)
const pass = await waitFor(() => {
const item = find(c, '.ctx-item', 'Pass on to Agent')
if (!item) throw new Error('menu not open')
return item
})
fireEvent.click(pass)
const input = await waitFor(() => {
const i = c.querySelector<HTMLInputElement>('.pass-input')
if (!i) throw new Error('popup not open')
return i
})
fireEvent.change(input, { target: { value: 'look here' } })
fireEvent.keyDown(input, { key: 'Enter' })
await waitFor(() => expect(received.length).toBeGreaterThan(0))
expect(received[0]).toBe('look here public/assets/store.js:1')
} finally {
window.removeEventListener('agentPaste', handler)
}
})
})
describe('Stage + commit', () => {
it('stages a file and commits via Shift+Enter (no commit button)', async () => {
const c = renderApp()
const row = await waitFor(() => {
const r = find(c, '.git-row', 'UserController.php')
if (!r) throw new Error('git not ready')
return r
})
fireEvent.click(row.querySelector<HTMLButtonElement>('button[title="Stage changes"]')!)
// the commit button is intentionally gone — committing is keyboard-only
expect(c.querySelector('.commit-btn')).toBeNull()
const msg = c.querySelector<HTMLTextAreaElement>('.commit-input')!
fireEvent.change(msg, { target: { value: 'wire up balance' } })
fireEvent.keyDown(msg, { key: 'Enter', shiftKey: true })
await waitFor(() => expect(find(c, '.toast', 'Committed')).toBeTruthy())
})
})