|
|
@@ -1,4 +1,4 @@
|
|
|
-// Web e2e scenario: the plugin manager tab in Plugins settings over a scaffold
|
|
|
+// Web e2e scenario: the plugin manager page behind the sidebar's Plugins entry over a scaffold
|
|
|
// profile runtime — one installed bundle switched on, one plain plugin added to
|
|
|
// an agent preset and switched off there, and the configuration tab writing one
|
|
|
// field under that preset's settings scope. Zero model calls: everything is
|
|
|
@@ -57,29 +57,37 @@ describe('web e2e: plugin manager', () => {
|
|
|
await rm(presetRoot, { recursive: true, force: true })
|
|
|
})
|
|
|
|
|
|
- /** Open the settings dialog on the Plugins section and select one of its tabs. */
|
|
|
- async function openPluginsTab(tab: string) {
|
|
|
+ /** Close any open settings dialog, so the sidebar and the main column are clickable. */
|
|
|
+ async function closeSettings() {
|
|
|
if (await page.getByRole('dialog', { name: '设置' }).count() > 0) {
|
|
|
await page.keyboard.press('Escape')
|
|
|
await expect.poll(() => page.getByRole('dialog', { name: '设置' }).count(), { timeout: 5_000 }).toBe(0)
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Select the sidebar's Plugins entry and wait for the management page in the main column. */
|
|
|
+ async function openPluginsPanel() {
|
|
|
+ await closeSettings()
|
|
|
+ await page.getByRole('navigation', { name: '全局面板' }).getByRole('button', { name: '插件', exact: true }).click()
|
|
|
+ const panel = page.locator('[data-plugin-panel]')
|
|
|
+ await panel.getByRole('heading', { name: '插件管理' }).waitFor({ timeout: 10_000 })
|
|
|
+ return panel
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Open the settings dialog on the Plugins section: the configuration page. */
|
|
|
+ async function openPluginsSettings() {
|
|
|
+ await closeSettings()
|
|
|
await page.getByRole('button', { name: '设置', exact: true }).click()
|
|
|
const dialog = page.getByRole('dialog', { name: '设置' })
|
|
|
await dialog.waitFor({ timeout: 10_000 })
|
|
|
await dialog.getByRole('button', { name: '插件', exact: true }).click()
|
|
|
- await dialog.getByRole('tab', { name: tab, exact: true }).click()
|
|
|
- await expect
|
|
|
- .poll(() => dialog.getByRole('tab', { name: tab, exact: true }).getAttribute('aria-selected'), { timeout: 5_000 })
|
|
|
- .toBe('true')
|
|
|
+ await dialog.getByRole('heading', { name: '插件', exact: true }).waitFor({ timeout: 10_000 })
|
|
|
return dialog
|
|
|
}
|
|
|
|
|
|
/** Open the settings dialog on one preset's detail page. */
|
|
|
async function openPresetDetail(name: string) {
|
|
|
- if (await page.getByRole('dialog', { name: '设置' }).count() > 0) {
|
|
|
- await page.keyboard.press('Escape')
|
|
|
- await expect.poll(() => page.getByRole('dialog', { name: '设置' }).count(), { timeout: 5_000 }).toBe(0)
|
|
|
- }
|
|
|
+ await closeSettings()
|
|
|
await page.getByRole('button', { name: '设置', exact: true }).click()
|
|
|
const dialog = page.getByRole('dialog', { name: '设置' })
|
|
|
await dialog.waitFor({ timeout: 10_000 })
|
|
|
@@ -96,33 +104,33 @@ describe('web e2e: plugin manager', () => {
|
|
|
|
|
|
it('lists the profile packages with their switches', async () => {
|
|
|
onTestFailed(() => saveFailureShot(page, 'web-e2e-plugin-manager-list'))
|
|
|
- const dialog = await openPluginsTab('插件管理')
|
|
|
+ const panel = await openPluginsPanel()
|
|
|
|
|
|
- await dialog.getByText('示例组合包', { exact: true }).waitFor({ timeout: 20_000 })
|
|
|
- expect(await dialog.getByText('示例插件', { exact: true }).count()).toBe(1)
|
|
|
+ await panel.getByText('示例组合包', { exact: true }).waitFor({ timeout: 20_000 })
|
|
|
+ expect(await panel.getByText('示例插件', { exact: true }).count()).toBe(1)
|
|
|
// The bundle is installed but not enabled; the plain plugin carries no
|
|
|
// switch, only its **Add to…** menu; uninstall waits on the plugin's page,
|
|
|
// which names the module and where it is composed, and the crumb leads back.
|
|
|
- const toggle = dialog.getByRole('switch', { name: '启用 示例组合包' })
|
|
|
+ const toggle = panel.getByRole('switch', { name: '启用 示例组合包' })
|
|
|
expect(await toggle.getAttribute('aria-checked')).toBe('false')
|
|
|
- expect(await dialog.getByRole('switch', { name: '启用 示例插件' }).count()).toBe(0)
|
|
|
- expect(await dialog.getByRole('button', { name: '加入到…' }).count()).toBe(1)
|
|
|
- expect(await dialog.getByRole('button', { name: '卸载 示例插件' }).count()).toBe(0)
|
|
|
- await dialog.getByRole('button', { name: '查看 示例插件' }).click()
|
|
|
- await dialog.getByRole('button', { name: '卸载 示例插件' }).waitFor({ timeout: 5_000 })
|
|
|
- await dialog.getByText('尚未加入', { exact: true }).waitFor({ timeout: 5_000 })
|
|
|
- await dialog.getByRole('button', { name: '返回插件列表' }).click()
|
|
|
- await expect.poll(() => dialog.getByRole('button', { name: '卸载 示例插件' }).count(), { timeout: 5_000 }).toBe(0)
|
|
|
-
|
|
|
- const snapshot = await captureStableAria(page, '[role="dialog"]', scaffold.workspaceCwd)
|
|
|
+ expect(await panel.getByRole('switch', { name: '启用 示例插件' }).count()).toBe(0)
|
|
|
+ expect(await panel.getByRole('button', { name: '加入到…' }).count()).toBe(1)
|
|
|
+ expect(await panel.getByRole('button', { name: '卸载 示例插件' }).count()).toBe(0)
|
|
|
+ await panel.getByRole('button', { name: '查看 示例插件' }).click()
|
|
|
+ await panel.getByRole('button', { name: '卸载 示例插件' }).waitFor({ timeout: 5_000 })
|
|
|
+ await panel.getByText('尚未加入', { exact: true }).waitFor({ timeout: 5_000 })
|
|
|
+ await panel.getByRole('button', { name: '返回插件列表' }).click()
|
|
|
+ await expect.poll(() => panel.getByRole('button', { name: '卸载 示例插件' }).count(), { timeout: 5_000 }).toBe(0)
|
|
|
+
|
|
|
+ const snapshot = await captureStableAria(page, '[data-plugin-panel]', scaffold.workspaceCwd)
|
|
|
await compareOrRefreshGolden(MANAGER_EXPECTED, snapshot, MODE)
|
|
|
expect(tripwire.pageErrors).toEqual([])
|
|
|
}, 60_000)
|
|
|
|
|
|
it('enables a bundle into the profile manifest and reports the restart it waits for', async () => {
|
|
|
onTestFailed(() => saveFailureShot(page, 'web-e2e-plugin-manager-enable'))
|
|
|
- const dialog = await openPluginsTab('插件管理')
|
|
|
- const toggle = dialog.getByRole('switch', { name: '启用 示例组合包' })
|
|
|
+ const panel = await openPluginsPanel()
|
|
|
+ const toggle = panel.getByRole('switch', { name: '启用 示例组合包' })
|
|
|
await toggle.waitFor({ timeout: 20_000 })
|
|
|
|
|
|
await toggle.click()
|
|
|
@@ -136,39 +144,37 @@ describe('web e2e: plugin manager', () => {
|
|
|
expect(manifest.dsh.profile.bundles).toEqual(['@fixture/bundle'])
|
|
|
// A startup-applied profile: the switch is on, and the banner names the package.
|
|
|
await expect.poll(() => toggle.getAttribute('aria-checked'), { timeout: 10_000 }).toBe('true')
|
|
|
- await dialog.getByText('以下更改会在下次启动生效:示例组合包').waitFor({ timeout: 10_000 })
|
|
|
- expect(await dialog.getByText('需重启', { exact: true }).count()).toBe(1)
|
|
|
+ await panel.getByText('以下更改会在下次启动生效:示例组合包').waitFor({ timeout: 10_000 })
|
|
|
+ expect(await panel.getByText('需重启', { exact: true }).count()).toBe(1)
|
|
|
// The pack's page lists its rows from the probe. A profile that applies
|
|
|
// patches at its next start keeps them read-only: no row switch.
|
|
|
- await dialog.getByRole('button', { name: '查看 示例组合包' }).click()
|
|
|
- await dialog.locator('[data-plugin-row]', { hasText: 'fixture-row' }).waitFor({ timeout: 10_000 })
|
|
|
- expect(await dialog.getByRole('switch', { name: '启用组件 fixture-row' }).count()).toBe(0)
|
|
|
- await dialog.getByRole('button', { name: '返回插件列表' }).click()
|
|
|
+ await panel.getByRole('button', { name: '查看 示例组合包' }).click()
|
|
|
+ await panel.locator('[data-plugin-row]', { hasText: 'fixture-row' }).waitFor({ timeout: 10_000 })
|
|
|
+ expect(await panel.getByRole('switch', { name: '启用组件 fixture-row' }).count()).toBe(0)
|
|
|
+ await panel.getByRole('button', { name: '返回插件列表' }).click()
|
|
|
expect(tripwire.pageErrors).toEqual([])
|
|
|
}, 60_000)
|
|
|
|
|
|
it('adds a plain plugin to a preset, then switches it off and deletes it on the preset\'s detail page', async () => {
|
|
|
onTestFailed(() => saveFailureShot(page, 'web-e2e-plugin-manager-preset-row'))
|
|
|
- const dialog = await openPluginsTab('插件管理')
|
|
|
- await dialog.getByRole('button', { name: '加入到…' }).click()
|
|
|
+ const panel = await openPluginsPanel()
|
|
|
+ await panel.getByRole('button', { name: '加入到…' }).click()
|
|
|
await page.getByRole('menuitem', { name: '预设:标准模式' }).click()
|
|
|
|
|
|
const overlay = (): Promise<string> => readFile(join(presetRoot, 'standard', 'cordis.patch.yml'), 'utf8').catch(() => '')
|
|
|
await expect.poll(async () => (await overlay()).includes('@fixture/plain-plugin'), { timeout: 10_000 }).toBe(true)
|
|
|
expect(await overlay()).toContain('id: fixture/plain-plugin')
|
|
|
- // The **Add to…** menu now marks that preset as joined. Escape would
|
|
|
- // close the settings dialog as well, so the menu closes through its anchor.
|
|
|
- await dialog.getByRole('button', { name: '加入到…' }).click()
|
|
|
+ // The **Add to…** menu now marks that preset as joined; the menu closes
|
|
|
+ // through its anchor.
|
|
|
+ await panel.getByRole('button', { name: '加入到…' }).click()
|
|
|
await page.getByRole('menuitem', { name: '预设:标准模式(已加入)' }).waitFor({ timeout: 5_000 })
|
|
|
- await dialog.getByRole('button', { name: '加入到…' }).click()
|
|
|
+ await panel.getByRole('button', { name: '加入到…' }).click()
|
|
|
await expect.poll(() => page.getByRole('menuitem').count(), { timeout: 5_000 }).toBe(0)
|
|
|
|
|
|
// The preset's detail page, behind the gear on its card, lists the new
|
|
|
// row under the package's title as the user's, switched on, with the
|
|
|
// local mark.
|
|
|
- await dialog.getByRole('button', { name: 'Agent 预设', exact: true }).click()
|
|
|
- await dialog.getByRole('button', { name: '配置: 标准模式' }).click()
|
|
|
- await dialog.getByRole('heading', { name: '标准模式' }).waitFor({ timeout: 10_000 })
|
|
|
+ const dialog = await openPresetDetail('标准模式')
|
|
|
const presetRow = dialog.locator('[data-preset-row="fixture/plain-plugin"]')
|
|
|
const rowToggle = presetRow.getByRole('switch', { name: '启用 示例插件' })
|
|
|
await rowToggle.waitFor({ timeout: 10_000 })
|
|
|
@@ -210,7 +216,7 @@ describe('web e2e: plugin manager', () => {
|
|
|
expect(document).toContain('preset/standard:')
|
|
|
expect(document.indexOf('scopes:')).toBeLessThan(document.indexOf('timeoutMs: 12000'))
|
|
|
// On the configuration tab the shared instance still shows the composed default.
|
|
|
- const plugins = await openPluginsTab('插件配置')
|
|
|
+ const plugins = await openPluginsSettings()
|
|
|
await plugins.getByRole('button', { name: '展开设置: 终端' }).click()
|
|
|
await expect.poll(() => plugins.getByLabel('命令超时(毫秒)').inputValue(), { timeout: 5_000 }).toBe('60000')
|
|
|
expect(await plugins.getByText('已覆盖').count()).toBe(0)
|