| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- // @vitest-environment jsdom
- import { act, cleanup, renderHook } from '@testing-library/react'
- import type { SessionEventEntry } from '@deepseek-ai/dsh-api-session-controller/types'
- import { createSnapshotStore } from '@deepseek-ai/dsh-client-store'
- import { EMPTY_CHAT_SNAPSHOT } from '@deepseek-ai/dsh-client-ui-chat/client'
- import { EMPTY_CONVERSATION_SNAPSHOT } from '@deepseek-ai/dsh-client-ui-conversation/client'
- import type { SessionId } from '@deepseek-ai/dsh-session/types'
- import { afterAll, afterEach, describe, expect, it, vi } from 'vitest'
- import {
- bindSnapshotSelector,
- chatSnapshot,
- conversationSnapshot,
- SlotTestRuntime,
- usePinnedBrowserLanguages,
- } from '../src/index.ts'
- const originalLanguages = [...navigator.languages]
- const originalLanguage = navigator.language
- usePinnedBrowserLanguages('zh-CN', 'en-US')
- afterEach(cleanup)
- afterAll(() => {
- expect(navigator.languages).toEqual(originalLanguages)
- expect(navigator.language).toBe(originalLanguage)
- })
- function entry(seq: number): SessionEventEntry {
- return {
- event: {
- type: 'fixture/event',
- seq,
- time: seq,
- data: { seq },
- ignorable: true,
- },
- }
- }
- describe('fixture helpers', () => {
- it('builds independent Conversation and Chat snapshots with optional overrides', () => {
- const conversation = conversationSnapshot()
- expect(conversation).toEqual(EMPTY_CONVERSATION_SNAPSHOT)
- expect(conversation).not.toBe(EMPTY_CONVERSATION_SNAPSHOT)
- const activeTargets = new Set(['chat'])
- expect(conversationSnapshot({ activeTargets }).activeTargets).toBe(activeTargets)
- const chat = chatSnapshot()
- expect(chat).toEqual(EMPTY_CHAT_SNAPSHOT)
- expect(chat).not.toBe(EMPTY_CHAT_SNAPSHOT)
- const order = ['node-1']
- expect(chatSnapshot({ order }).order).toBe(order)
- })
- it('binds an observable snapshot through the production selector hook', () => {
- const source = createSnapshotStore({ value: 1 })
- const useValue = bindSnapshotSelector(source)
- const view = renderHook(() => useValue(snapshot => snapshot.value))
- expect(view.result.current).toBe(1)
- act(() => { source.update((draft) => { draft.value = 2 }) })
- expect(view.result.current).toBe(2)
- })
- it('pins both browser language fields for the calling suite', () => {
- expect(navigator.languages).toEqual(['zh-CN', 'en-US'])
- expect(navigator.language).toBe('zh-CN')
- })
- })
- describe('Session fixture lifecycle', () => {
- it('initializes and drives complete event windows through replace, prepend, and append', async () => {
- const runtime = await SlotTestRuntime.create()
- const first = entry(1)
- const older = entry(0)
- const live = entry(2)
- await runtime.sessions.add({ id: 'events', events: [first] }, { current: false })
- expect(runtime.sessions.behavior('events').eventSource.getSnapshot()).toMatchObject({
- entries: [first],
- hasMore: false,
- change: { kind: 'replace', entries: [first] },
- })
- await runtime.sessions.add({ id: 'has-more', hasMore: true }, { current: false })
- expect(runtime.sessions.behavior('has-more').eventSource.getSnapshot()).toMatchObject({
- entries: [],
- hasMore: true,
- })
- await runtime.sessions.replaceEvents('events', [first])
- await runtime.sessions.prependEvents('events', [older])
- await runtime.sessions.appendEvent('events', live)
- expect(runtime.sessions.behavior('events').eventSource.getSnapshot()).toMatchObject({
- entries: [older, first, live],
- hasMore: false,
- change: { kind: 'append', entries: [live] },
- })
- await runtime.dispose()
- })
- it('requires an explicit create stub and records successful create and refresh calls', async () => {
- const runtime = await SlotTestRuntime.create()
- await expect(runtime.sessions.create()).rejects.toThrow(/create is not stubbed/)
- await runtime.sessions.add({ id: 'created' }, { current: false })
- const create = vi.fn(() => Promise.resolve('created' as SessionId))
- runtime.sessions.stubCreate(create)
- await expect(runtime.sessions.create({ cwd: '/workspace' })).resolves.toBe('created')
- await expect(runtime.sessions.refresh()).resolves.toBeUndefined()
- expect(create).toHaveBeenCalledWith({ cwd: '/workspace' })
- expect(runtime.sessions.calls.slice(-2)).toEqual([
- { method: 'create', args: [{ cwd: '/workspace' }] },
- { method: 'refresh', args: [] },
- ])
- await runtime.dispose()
- })
- it('disposes a scope without materializing a binding', async () => {
- const runtime = await SlotTestRuntime.create()
- await runtime.sessions.add({ id: 'scope-only' }, { current: false })
- const scope = runtime.sessions.scope('scope-only')
- expect(scope).toBeDefined()
- const release = vi.fn()
- scope?.effect(() => release, 'fixture scope release')
- runtime.releaseWorkspaceSource()
- await runtime.dispose()
- expect(release).toHaveBeenCalledOnce()
- })
- })
|