Files
deepseek-harness/apps/web/tests/plugin-recommendations.e2e.ts
T
ZiyaZhang 50be7d37c9 feat(experimental): recommend official plugins through dsh-action answer blocks
Replaces list_official_plugins, suggest_plugin and the routing prompt
section with the official-plugins skill, whose load appends the live
catalog, and an enable_official_plugin block at the end of the answer.
The card reads catalog text from the Host, Enable goes through the plugin
manager, and each outcome reaches the model as a status notice on the
next request. Adds the queuedMessageLimit option, en/zh/ja card copy and
a re-recorded persistence change and Web replay.
2026-10-21 10:50:48 -07:00

88 lines
4.9 KiB
TypeScript

/** Replay model output through the shipped Web profile and decide its real card. */
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { chromium, type Browser, type Page } from 'playwright'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import type {} from '@deepseek-ai/dsh-plugin-manager'
import {
acknowledgeReloadConnectionLoss, assertFixtureInventory, captureStableAria, compareOrRefreshGolden,
fixtureUserPrompts, launchWebScaffold, watchConsole, type WebScaffold,
} from './scaffold.ts'
import { connectFreshWorkspace, newEnglishPage } from './support.ts'
const directory = fileURLToPath(new URL('../../../snapshots/web/plugin-recommendations', import.meta.url))
const fixture = join(directory, 'session.v4.jsonl')
const voiceBundle = '@deepseek-ai/dsh-experimental-voice-input-bundle'
describe('official plugin recommendation in a conversation', () => {
let scaffold: WebScaffold
let browser: Browser
let page: Page
beforeAll(async () => {
scaffold = await launchWebScaffold({ replayFixture: fixture, compareReplaySession: true,
profile: { packages: [{ dir: fileURLToPath(new URL('../../../packages/experimental/plugin-recommendations', import.meta.url)), enabled: true }] },
})
browser = await chromium.launch()
page = await newEnglishPage(browser)
})
afterAll(async () => {
await browser?.close()
await scaffold?.close()
})
it('offers the answer block as one card, keeps the fence out of the answer, and reports a dismissal on the next request', async () => {
const tripwire = watchConsole(page)
await page.goto(scaffold.authenticatedUrl, { waitUntil: 'load' })
await connectFreshWorkspace(page, scaffold.workspaceCwd)
const input = page.locator('[data-composer-input]').first()
const settled = scaffold.whenTurnSettled()
const prompts = fixtureUserPrompts(await readFile(fixture, 'utf8'))
await input.fill(prompts[0]!)
await input.press('Enter')
await settled
const card = page.locator('[data-plugin-recommendation]')
await card.waitFor({ state: 'visible' })
expect(await card.count()).toBe(1)
expect(await page.locator('[data-turn-tail] [data-plugin-recommendation]').count()).toBe(1)
const answer = page.getByText('Voice input transcribes your recordings locally into the composer. You can enable it below.', { exact: true })
const answerBox = await answer.boundingBox()
const cardBox = await card.boundingBox()
expect(answerBox).not.toBeNull()
expect(cardBox!.y).toBeGreaterThanOrEqual(answerBox!.y + answerBox!.height)
expect(await page.getByText('enable_official_plugin').count()).toBe(0)
await card.getByText('Voice input', { exact: true }).waitFor()
expect(await card.getByRole('button', { name: 'Enable', exact: true }).isEnabled()).toBe(true)
expect((await scaffold.ctx.pluginManager.listBundles()).find(bundle => bundle.name === voiceBundle)?.enabled).toBe(false)
await compareOrRefreshGolden(join(directory, 'offered.expected.md'), await captureStableAria(page, '[data-plugin-recommendation]', scaffold.workspaceCwd), scaffold.mode)
await page.setViewportSize({ width: 620, height: 900 })
expect(await card.evaluate(element => element.scrollWidth <= element.clientWidth)).toBe(true)
await card.getByRole('button', { name: 'Not now', exact: true }).click()
await card.getByRole('status').getByText('Dismissed', { exact: true }).waitFor()
const warnings = tripwire.warnings.length
await page.reload({ waitUntil: 'load' })
await card.getByRole('status').getByText('Dismissed', { exact: true }).waitFor()
acknowledgeReloadConnectionLoss(tripwire, warnings)
expect(await card.count()).toBe(1)
expect(await card.getByRole('button').count()).toBe(1)
expect((await scaffold.ctx.pluginManager.listBundles()).find(bundle => bundle.name === voiceBundle)?.enabled).toBe(false)
await compareOrRefreshGolden(join(directory, 'dismissed.expected.md'), await captureStableAria(page, '[data-plugin-recommendation]', scaffold.workspaceCwd), scaffold.mode)
const statusSettled = scaffold.whenTurnSettled()
await input.fill(prompts[1]!)
await input.press('Enter')
await statusSettled
await page.getByText('Voice input is not enabled: you dismissed the card.', { exact: true }).waitFor()
expect(await card.count()).toBe(1)
await card.getByRole('button', { name: 'View details: Voice input', exact: true }).click()
await page.locator(`[data-plugin-detail="${voiceBundle}"]`).waitFor({ state: 'visible' })
expect((await scaffold.ctx.pluginManager.listBundles()).find(bundle => bundle.name === voiceBundle)?.enabled).toBe(false)
expect(tripwire.pageErrors).toEqual([])
expect(tripwire.warnings).toEqual([])
if (scaffold.mode === 'replay') await assertFixtureInventory(directory, [
'session.v4.jsonl', 'offered.expected.md', 'dismissed.expected.md', 'system-prompt.expected.md', 'tool-schemas.expected.json',
])
})
})