feat(schedule): add durable after reminders

This commit is contained in:
pku-xht
2026-08-05 19:00:02 +08:00
committed by Tianyi Cui
parent a229b42e24
commit f7e7851e3f
102 changed files with 2619 additions and 122 deletions

View File

@@ -0,0 +1,75 @@
import { Context } from 'cordis'
import { describe, expect, it } from 'vitest'
import { LocaleService } from '@deepseek-ai/dsh-client-locale/client'
import { apply, inject } from '../src/client/index.ts'
import { ReminderRow } from '../src/client/ReminderRow.tsx'
import { apply as nodeApply } from '../src/index.ts'
import {
apply as invariantApply,
inject as invariantInject,
name as invariantName,
} from '../src/invariant.ts'
interface CapturedEntry {
name: string
key?: string
locale?: string
component: unknown
}
function bench() {
const ctx = new Context()
let entry: CapturedEntry | undefined
ctx.provide('slots', {
register(options: Omit<CapturedEntry, 'component'>, component: unknown) {
entry = { ...options, component }
return () => { entry = undefined }
},
})
ctx.provide('conversation', {})
ctx.provide('locale', new LocaleService(ctx))
const fiber = ctx.plugin({ inject: [...inject], apply })
return { ctx, fiber, entry: () => entry }
}
describe('ui-schedule browser plugin', () => {
it('registers the keyed reminder renderer and unloads it with the fiber', async () => {
const b = bench()
await b.fiber.await()
expect(b.entry()).toEqual({
name: 'conversation.chat.eventview',
key: 'schedule/reminder',
locale: 'schedule',
component: ReminderRow,
})
await b.fiber.dispose()
expect(b.entry()).toBeUndefined()
})
})
describe('ui-schedule node and invariant companions', () => {
it('keeps the node half inert', () => {
expect(() => { nodeApply() }).not.toThrow()
})
it('registers exact package ownership and returns its disposer', async () => {
const ctx = new Context()
let owner: string | undefined
let disposed = false
ctx.provide('invariants', {
register(packageName: string, install: unknown) {
expect(install).toBeTypeOf('function')
owner = packageName
return () => { disposed = true }
},
})
expect(invariantName).toBe('client-ui-schedule-invariant')
expect(invariantInject).toEqual(['invariants'])
const dispose = await invariantApply(ctx)
expect(owner).toBe('@deepseek-ai/dsh-client-ui-schedule')
dispose()
expect(disposed).toBe(true)
})
})

View File

@@ -0,0 +1,55 @@
// @vitest-environment jsdom
import { cleanup, render, screen } from '@testing-library/react'
import { afterEach, describe, expect, it } from 'vitest'
import { makeTranslate } from '@deepseek-ai/dsh-client-test-runtime'
import type { PresentedEventNode } from '@deepseek-ai/dsh-client-runtime/client'
import { ReminderRow, type ReminderRowProps } from '../src/client/ReminderRow.tsx'
import { zh } from '../src/client/locales.ts'
const t: ReminderRowProps['t'] = makeTranslate(zh)
afterEach(cleanup)
function props(view: unknown): ReminderRowProps {
const node: PresentedEventNode = {
kind: 'presented-event',
seq: 4,
time: Date.parse('2026-08-05T08:00:00.000Z'),
presentationKey: 'schedule/reminder',
view,
}
return { node, t } as ReminderRowProps
}
describe('ReminderRow', () => {
it('shows the durable reminder payload and its session-local boundary', () => {
render(<ReminderRow {...props({
scheduleId: 'schedule-7',
prompt: 'Check the deploy',
occurrenceAt: '2026-08-05T08:00:00.000Z',
deliveryMode: 'session-local',
})} />)
expect(screen.getByRole('note')).toBeTruthy()
expect(screen.getByText('定时提醒')).toBeTruthy()
expect(screen.getByText('仅在当前会话中交付')).toBeTruthy()
expect(screen.getByText('Check the deploy')).toBeTruthy()
expect(screen.getByText('编号 schedule-7')).toBeTruthy()
const time = screen.getByText('触发时间 2026-08-05T08:00:00.000Z')
expect(time.getAttribute('datetime')).toBe('2026-08-05T08:00:00.000Z')
})
it('contains an incompatible sidecar as a visible unavailable receipt', () => {
render(<ReminderRow {...props({
scheduleId: '',
prompt: 'not trusted',
occurrenceAt: 123,
deliveryMode: 'external',
})} />)
expect(screen.getByText('提醒回执不可用 · schedule/reminder')).toBeTruthy()
expect(screen.queryByText('not trusted')).toBeNull()
expect(screen.queryByText('仅在当前会话中交付')).toBeNull()
})
})