| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- // @vitest-environment jsdom
- import { cleanup, fireEvent, render, waitFor } from '@testing-library/react'
- import { afterEach, describe, expect, it, vi } from 'vitest'
- import { useSyncExternalStore } from 'react'
- import type { SessionId } from '@deepseek-ai/dsh-session/types'
- import { SessionLogDownloadController } from '../src/client/controller.ts'
- import { SessionLogDownloadHeaderAction } from '../src/client/HeaderAction.tsx'
- import type { SessionLogDownloadDialogProps } from '../src/client/Dialog.tsx'
- import { en } from '../src/client/locales.ts'
- const SID = 'session-export-header' as SessionId
- function bindSessionExport(controller: SessionLogDownloadController) {
- return function useSessionLogDownload<T>(selector: (state: ReturnType<typeof controller.store.getSnapshot>) => T): T {
- return useSyncExternalStore(
- listener => controller.store.subscribe(listener),
- () => selector(controller.store.getSnapshot()),
- )
- }
- }
- function bench() {
- const controller = new SessionLogDownloadController(async () => new Response('zip'), vi.fn())
- const request = vi.fn((sessionId: SessionId) => controller.download(sessionId))
- const dismiss = vi.fn((sessionId: SessionId) => { controller.dismiss(sessionId) })
- const useSessionLogDownload = bindSessionExport(controller)
- const props = {
- sessionId: SID,
- useSessionLogDownload,
- request,
- dismiss,
- t: (key: keyof typeof en): string => en[key],
- } as unknown as SessionLogDownloadDialogProps
- const view = render(<SessionLogDownloadHeaderAction {...props} />)
- return { controller, request, view }
- }
- afterEach(cleanup)
- describe('Session export Header action', () => {
- it('opens the more-actions menu and downloads through the shared controller', async () => {
- const b = bench()
- const button = b.view.getByRole('button', { name: 'More actions' })
- expect(button.querySelector('svg')).not.toBeNull()
- expect(button.getAttribute('aria-expanded')).toBe('false')
- fireEvent.click(button)
- expect(button.getAttribute('aria-expanded')).toBe('true')
- fireEvent.click(b.view.getByRole('menuitem', { name: 'Download session log' }))
- await waitFor(() => { expect(b.request).toHaveBeenCalledWith(SID) })
- expect(b.view.queryByRole('menuitem', { name: 'Download session log' })).toBeNull()
- expect(await b.view.findByRole('dialog', { name: 'Session download started' })).toBeTruthy()
- })
- it('closes the menu on Escape without downloading', () => {
- const b = bench()
- fireEvent.click(b.view.getByRole('button', { name: 'More actions' }))
- expect(b.view.getByRole('menuitem', { name: 'Download session log' })).toBeTruthy()
- fireEvent.keyDown(document, { key: 'Escape' })
- expect(b.view.queryByRole('menuitem', { name: 'Download session log' })).toBeNull()
- expect(b.request).not.toHaveBeenCalled()
- })
- it('disables the download row while either entry path downloads this Session', async () => {
- const b = bench()
- let release!: (response: Response) => void
- const pending = new Promise<Response>((resolve) => { release = resolve })
- const controller = new SessionLogDownloadController(() => pending, vi.fn())
- const useSessionLogDownload = bindSessionExport(controller)
- b.view.rerender(<SessionLogDownloadHeaderAction {...({
- sessionId: SID,
- useSessionLogDownload,
- request: (sessionId: SessionId) => controller.download(sessionId),
- dismiss: (sessionId: SessionId) => { controller.dismiss(sessionId) },
- t: (key: keyof typeof en): string => en[key],
- } as unknown as SessionLogDownloadDialogProps)} />)
- const download = controller.download(SID)
- const button = b.view.getByRole('button', { name: 'More actions' })
- await waitFor(() => { expect(button.getAttribute('aria-busy')).toBe('true') })
- fireEvent.click(button)
- const item = b.view.getByRole('menuitem', { name: 'Download session log' })
- expect((item as HTMLButtonElement).disabled).toBe(true)
- release(new Response('zip'))
- await download
- await waitFor(() => { expect(button.getAttribute('aria-busy')).toBe('false') })
- expect((item as HTMLButtonElement).disabled).toBe(false)
- })
- })
|