feat: support DeepSeek Harness Desktop and refine management UI
- Detect and launch DeepSeek Harness Desktop on Windows - Improve agent client selection and responsive layouts - Unify dashboard runtime and connection card styling - Streamline plugin navigation, cards, and installation dialogs - Update translations and regression tests
This commit is contained in:
1 parent
3b11361f1b
commit
2649df000c
24 files changed
+587
-180
No files matched your search
@@ -21,15 +21,13 @@ const screenshots = path.join(os.tmpdir(), 'easycliproxy-agent-clients');
|
||||
await page.route('**/*', route => route.request().url().startsWith(base + '/') ? route.continue() : route.abort());
|
||||
fs.mkdirSync(screenshots, { recursive: true });
|
||||
|
||||
const clients = () => page.locator('.agent-list-items button strong');
|
||||
const visibleClients = () => clients().allTextContents();
|
||||
const activeClient = () => page.locator('.agent-list-items button.active strong').textContent();
|
||||
const stored = () => page.evaluate(key => localStorage.getItem(key), storageKey);
|
||||
const manage = () => page.getByRole('button', { name: '管理客户端', exact: true });
|
||||
const dialog = () => page.getByRole('dialog', { name: '管理客户端', exact: true });
|
||||
const manage = () => page.getByRole('button', { name: /^(管理客户端|Manage Clients)$/, exact: true });
|
||||
const dialog = () => page.getByRole('dialog', { name: /^(管理客户端|Manage Clients)$/, exact: true });
|
||||
const checkbox = name => dialog().getByRole('checkbox', { name, exact: true });
|
||||
const save = () => dialog().getByRole('button', { name: '保存', exact: true });
|
||||
const cancel = () => dialog().getByRole('button', { name: '取消', exact: true });
|
||||
const cancel = () => dialog().getByRole('button', { name: /^(取消|Cancel)$/, exact: true });
|
||||
const search = () => dialog().getByRole('searchbox', { name: '搜索客户端', exact: true });
|
||||
const automatic = () => dialog().getByRole('button', { name: '恢复自动显示', exact: true });
|
||||
const ready = () => page.waitForFunction(() => window.fixtureCalls?.some(call => call.cmd === 'get_agent_config_statuses')
|
||||
@@ -37,10 +35,15 @@ const screenshots = path.join(os.tmpdir(), 'easycliproxy-agent-clients');
|
||||
&& !document.querySelector('.agent-client-list-heading button').disabled);
|
||||
const waitClients = async names => {
|
||||
await page.waitForFunction(expected => {
|
||||
const actual = Array.from(document.querySelectorAll('.agent-list-items button strong'), node => node.textContent);
|
||||
return JSON.stringify(actual) === JSON.stringify(expected);
|
||||
const list = document.querySelector('.agent-list-items');
|
||||
const actual = Array.from(list.querySelectorAll('button strong'), node => node.textContent);
|
||||
const selected = list.querySelector('button.active strong')?.textContent;
|
||||
return actual.length > 0 && actual.length <= 6 && actual.every(name => expected.includes(name))
|
||||
&& expected.includes(selected) && list.scrollHeight <= list.clientHeight + 1;
|
||||
}, names);
|
||||
assert.deepEqual(await visibleClients(), names);
|
||||
await manage().click();
|
||||
assert.deepEqual(await dialog().locator('label:has(input:checked) strong').allTextContents(), names);
|
||||
await cancel().click();
|
||||
};
|
||||
const open = async (extra = 'mixed-clients', saved = null) => {
|
||||
if (page.url() !== 'about:blank') {
|
||||
@@ -172,6 +175,48 @@ const screenshots = path.join(os.tmpdir(), 'easycliproxy-agent-clients');
|
||||
await page.screenshot({ path: path.join(screenshots, 'manager-desktop.png'), fullPage: true });
|
||||
await closeAndFocus(() => cancel().click());
|
||||
|
||||
// Reproduce the reported desktop size with twelve previously saved clients.
|
||||
await page.setViewportSize({ width: 1368, height: 912 });
|
||||
const twelve = ['claude-code', 'claude-desktop', 'codex', 'deepseek-harness', 'opencode', 'pi',
|
||||
'grok-build', 'antigravity-cli', 'workbuddy', 'zcode', 'kimi-code', 'openclaw'];
|
||||
await page.evaluate(({ key, ids }) => localStorage.setItem(key, JSON.stringify(ids)), { key: storageKey, ids: twelve });
|
||||
await page.goto(`${base}/tests/fixtures/agent-backups.html?shell&client=claude-code`, { waitUntil: 'domcontentloaded' });
|
||||
await ready();
|
||||
await page.waitForFunction(() => document.querySelectorAll('.agent-list-items button').length === 6);
|
||||
const assertFits = async () => {
|
||||
const metrics = await page.locator('.agent-list-items').evaluate(list => {
|
||||
const rect = list.getBoundingClientRect();
|
||||
return { height: list.clientHeight, scrollHeight: list.scrollHeight, scrollWidth: list.scrollWidth,
|
||||
width: list.clientWidth, clipped: Array.from(list.children).some(child => {
|
||||
const r = child.getBoundingClientRect();
|
||||
return r.top < rect.top - 1 || r.bottom > rect.bottom + 1;
|
||||
}) };
|
||||
});
|
||||
assert.ok(metrics.scrollHeight <= metrics.height + 1 && metrics.scrollWidth <= metrics.width + 1);
|
||||
assert.equal(metrics.clipped, false, 'all shortcuts are fully visible without hiding overflowing rows');
|
||||
};
|
||||
await assertFits();
|
||||
assert.deepEqual(JSON.parse(await stored()), twelve, 'limiting shortcuts preserves saved preferences');
|
||||
const right = await page.locator('.agent-config-scroll-region').evaluate(node => [node.clientHeight, node.scrollHeight]);
|
||||
assert.ok(right[1] <= right[0] + 1, 'Claude Code settings fit at the reported desktop size');
|
||||
const switchClient = () => page.getByRole('button', { name: '切换客户端', exact: true });
|
||||
await switchClient().click();
|
||||
const picker = page.getByRole('dialog', { name: '切换客户端', exact: true });
|
||||
await picker.getByRole('searchbox').fill('herMES');
|
||||
await picker.getByRole('button', { name: 'Hermes Agent', exact: true }).click();
|
||||
assert.equal(await activeClient(), 'Hermes Agent', 'a client outside the first six remains in the shortcuts after selection');
|
||||
assert.ok(JSON.parse(await stored()).includes('hermes'), 'switching to a hidden client brings it back');
|
||||
assert.equal(await switchClient().evaluate(node => node === document.activeElement), true);
|
||||
await assertFits();
|
||||
await page.setViewportSize({ width: 360, height: 600 });
|
||||
await page.waitForFunction(() => document.querySelectorAll('.agent-list-items button').length < 6);
|
||||
await assertFits();
|
||||
assert.equal(await activeClient(), 'Hermes Agent', 'resizing always retains the active client');
|
||||
await page.setViewportSize({ width: 1368, height: 912 });
|
||||
await switchClient().click();
|
||||
await picker.getByRole('button', { name: 'Claude Code', exact: true }).click();
|
||||
await page.locator('.agent-workbench').screenshot({ path: path.join(screenshots, 'shortcuts-desktop.png') });
|
||||
|
||||
// Both entry points remain usable with narrow windows and English text in dark mode.
|
||||
for (const embedded of [false, true]) {
|
||||
for (const english of [false, true]) {
|
||||
|
||||
@@ -130,48 +130,23 @@ const desktopViewports = [
|
||||
return metrics;
|
||||
};
|
||||
|
||||
const assertClientListScroll = async (label, { requireOverflow = false } = {}) => {
|
||||
const assertClientShortcuts = async label => {
|
||||
const metrics = await page.locator('.agent-list-items').evaluate(list => {
|
||||
const style = getComputedStyle(list);
|
||||
const overflowX = list.scrollWidth > list.clientWidth + 1;
|
||||
const overflowY = list.scrollHeight > list.clientHeight + 1;
|
||||
const original = {
|
||||
left: list.scrollLeft,
|
||||
top: list.scrollTop,
|
||||
snap: list.style.scrollSnapType,
|
||||
behavior: list.style.scrollBehavior,
|
||||
};
|
||||
list.style.scrollSnapType = 'none';
|
||||
list.style.scrollBehavior = 'auto';
|
||||
const listRect = list.getBoundingClientRect();
|
||||
const unreachable = Array.from(list.querySelectorAll('button')).filter(button => {
|
||||
const before = button.getBoundingClientRect();
|
||||
list.scrollLeft += before.left - listRect.left;
|
||||
list.scrollTop += before.top - listRect.top;
|
||||
const after = button.getBoundingClientRect();
|
||||
return after.left < listRect.left - 1 || after.right > listRect.right + 1
|
||||
|| after.top < listRect.top - 1 || after.bottom > listRect.bottom + 1;
|
||||
}).map(button => button.textContent.trim());
|
||||
const scrollable = (!overflowX || ['auto', 'scroll'].includes(style.overflowX))
|
||||
&& (!overflowY || ['auto', 'scroll'].includes(style.overflowY));
|
||||
list.scrollLeft = original.left;
|
||||
list.scrollTop = original.top;
|
||||
list.style.scrollSnapType = original.snap;
|
||||
list.style.scrollBehavior = original.behavior;
|
||||
return {
|
||||
overflows: overflowX || overflowY,
|
||||
scrollable,
|
||||
unreachable,
|
||||
};
|
||||
const rect = list.getBoundingClientRect();
|
||||
const buttons = Array.from(list.querySelectorAll('button'));
|
||||
return { count: buttons.length,
|
||||
overflow: list.scrollHeight > list.clientHeight + 1 || list.scrollWidth > list.clientWidth + 1,
|
||||
clipped: buttons.filter(button => {
|
||||
const r = button.getBoundingClientRect();
|
||||
return r.top < rect.top - 1 || r.bottom > rect.bottom + 1 || r.left < rect.left - 1 || r.right > rect.right + 1;
|
||||
}).map(button => button.textContent) };
|
||||
});
|
||||
assert.equal(metrics.scrollable, true, `${label}: overflowing client options must remain scrollable`);
|
||||
if (requireOverflow) {
|
||||
assert.equal(metrics.overflows, true, `${label}: the fixture must exercise scrolling through the client list`);
|
||||
}
|
||||
assert.deepEqual(metrics.unreachable, [], `${label}: scrolling must reveal every client option`);
|
||||
assert.ok(metrics.count >= 1 && metrics.count <= 6, `${label}: show at most six shortcuts`);
|
||||
assert.equal(metrics.overflow, false, `${label}: shortcuts must fit without scrolling`);
|
||||
assert.deepEqual(metrics.clipped, [], `${label}: no shortcut may be clipped`);
|
||||
};
|
||||
|
||||
const assertDesktopLayout = async (label, { requireClientOverflow = false } = {}) => {
|
||||
const assertDesktopLayout = async label => {
|
||||
const [clientList, configuration] = await page.locator('.agent-client-list, .agent-config-panel')
|
||||
.evaluateAll(nodes => nodes.map(node => {
|
||||
const rect = node.getBoundingClientRect();
|
||||
@@ -184,7 +159,7 @@ const desktopViewports = [
|
||||
assert.ok(Math.abs(clientList.y - configuration.y) <= 1,
|
||||
`${label}: the desktop panels must share a top edge`);
|
||||
await assertNaturalPanel(label);
|
||||
await assertClientListScroll(label, { requireOverflow: requireClientOverflow });
|
||||
await assertClientShortcuts(label);
|
||||
return { clientList, configuration };
|
||||
};
|
||||
|
||||
@@ -200,7 +175,7 @@ const desktopViewports = [
|
||||
assert.ok(clientList.y + clientList.height <= configuration.y + 1,
|
||||
`${label}: the client list must stay above the configuration panel`);
|
||||
await assertNaturalPanel(label, { checkControls: false });
|
||||
await assertClientListScroll(label);
|
||||
await assertClientShortcuts(label);
|
||||
const pageWidth = await page.evaluate(() => ({
|
||||
clientWidth: document.documentElement.clientWidth,
|
||||
scrollWidth: document.documentElement.scrollWidth,
|
||||
@@ -335,7 +310,7 @@ const desktopViewports = [
|
||||
};
|
||||
});
|
||||
|
||||
const assertFixedShellDesktop = async (label, { requireRightOverflow = false, requireClientOverflow = false } = {}) => {
|
||||
const assertFixedShellDesktop = async (label, { requireRightOverflow = false } = {}) => {
|
||||
const metrics = await shellLayoutMetrics();
|
||||
assert.ok(Math.abs(metrics.client.height - metrics.configuration.height) <= 1,
|
||||
`${label}: fixed-height desktop panels must remain equal in height`);
|
||||
@@ -349,7 +324,7 @@ const desktopViewports = [
|
||||
`${label}: fixed-height desktop layout must not create a far-right page scrollbar`);
|
||||
assert.ok(metrics.documentScrollWidth <= metrics.documentClientWidth + 1,
|
||||
`${label}: fixed-height desktop layout must not create horizontal page scrolling`);
|
||||
await assertClientListScroll(label, { requireOverflow: requireClientOverflow });
|
||||
await assertClientShortcuts(label);
|
||||
await assertShellScrollRegion(label, { requireOverflow: requireRightOverflow });
|
||||
return metrics;
|
||||
};
|
||||
@@ -369,10 +344,10 @@ const desktopViewports = [
|
||||
const label = `${viewport.width}x${viewport.height}/${embedded ? 'embedded' : 'full'}/${client}/core`;
|
||||
await assertDesktopLayout(label);
|
||||
if (client === 'zcode' && viewport.height === 941) {
|
||||
const status = page.locator('.agent-list-items button').filter({ hasText: 'Antigravity CLI' }).locator('small');
|
||||
const status = page.locator('.agent-list-items button').filter({ hasText: 'Claude Code' }).locator('small');
|
||||
const longModel = 'gemini-3.8-flash-high-with-a-long-client-status-for-layout-verification';
|
||||
await page.evaluate(model => {
|
||||
window.fixtureClientStatusesOverride = { 'antigravity-cli': { appliedModel: model } };
|
||||
window.fixtureClientStatusesOverride = { 'claude-code': { appliedModel: model } };
|
||||
}, longModel);
|
||||
await page.locator('.agent-client-list-heading button').click();
|
||||
await page.waitForFunction(model => Array.from(document.querySelectorAll('.agent-list-items small'))
|
||||
@@ -429,7 +404,6 @@ const desktopViewports = [
|
||||
const label = `${viewport.width}x${viewport.height}/shell/claude-desktop/core`;
|
||||
const metrics = await assertFixedShellDesktop(label, {
|
||||
requireRightOverflow: height === 700,
|
||||
requireClientOverflow: height === 700,
|
||||
});
|
||||
fixedShellByHeight.set(height, metrics);
|
||||
}
|
||||
@@ -466,7 +440,7 @@ const desktopViewports = [
|
||||
`${shellNarrowLabel}: the stacked workbench must preserve the fixed window-edge gap`);
|
||||
assert.ok(shellNarrow.documentScrollHeight <= shellNarrow.documentClientHeight + 1,
|
||||
`${shellNarrowLabel}: the bounded one-column shell must not leak scrolling to the page`);
|
||||
await assertClientListScroll(shellNarrowLabel);
|
||||
await assertClientShortcuts(shellNarrowLabel);
|
||||
await assertShellScrollRegion(shellNarrowLabel);
|
||||
|
||||
// At phone width the shell follows Usage records and returns to natural document scrolling.
|
||||
@@ -482,7 +456,7 @@ const desktopViewports = [
|
||||
`${shellPhoneLabel}: phone-width client list must remain above configuration content`);
|
||||
assert.ok(shellPhone.documentScrollWidth <= shellPhone.documentClientWidth + 1,
|
||||
`${shellPhoneLabel}: natural document flow must not create horizontal scrolling`);
|
||||
await assertClientListScroll(shellPhoneLabel);
|
||||
await assertClientShortcuts(shellPhoneLabel);
|
||||
await assertShellScrollRegion(shellPhoneLabel, { natural: true });
|
||||
|
||||
for (const viewport of [{ width: 640, height: 700 }, { width: 360, height: 941 }]) {
|
||||
@@ -511,7 +485,7 @@ const desktopViewports = [
|
||||
await tab('会话管理').click();
|
||||
await page.locator('.codex-session-row').first().waitFor();
|
||||
const label = `${viewport.width}x${viewport.height}/full/codex/sessions`;
|
||||
await assertDesktopLayout(label, { requireClientOverflow: false });
|
||||
await assertDesktopLayout(label);
|
||||
const sessions = await page.locator('.codex-session-list').evaluate(list => {
|
||||
const style = getComputedStyle(list);
|
||||
const last = list.querySelector('.codex-session-row:last-child');
|
||||
|
||||
@@ -12,7 +12,14 @@ const assert = require('node:assert/strict');
|
||||
page.on('pageerror', error => errors.push(String(error)));
|
||||
const tab = name => page.getByRole('tab', { name, exact: true });
|
||||
const button = name => page.getByRole('button', { name, exact: true });
|
||||
const client = name => page.locator('.agent-list-items button').filter({ has: page.getByText(name, { exact: true }) });
|
||||
const client = name => ({ click: async () => {
|
||||
const shortcut = page.locator('.agent-list-items button').filter({ has: page.getByText(name, { exact: true }) });
|
||||
if (await shortcut.count()) await shortcut.click();
|
||||
else {
|
||||
await button('切换客户端').click();
|
||||
await page.getByRole('dialog', { name: '切换客户端', exact: true }).getByRole('button', { name, exact: true }).click();
|
||||
}
|
||||
} });
|
||||
const active = async name => {
|
||||
await page.waitForFunction(name => Array.from(document.querySelectorAll('[role=tab]'))
|
||||
.some(el => el.textContent === name && el.getAttribute('aria-selected') === 'true'), name);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Run Vite on port 1421 before this test.
|
||||
const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({ channel: 'msedge', headless: true });
|
||||
try {
|
||||
for (const scenario of ['desktop', 'both', 'running', 'cli']) {
|
||||
const page = await browser.newPage();
|
||||
await page.addInitScript(scenario => {
|
||||
window.fixtureClientStatusesOverride = {
|
||||
'deepseek-harness': {
|
||||
cliVersion: scenario === 'desktop' ? null : '0.1.9',
|
||||
appVersion: scenario === 'cli' ? null : '0.2.0-rc.2',
|
||||
launchTargets: [
|
||||
...(scenario === 'desktop' ? [] : [{ id: 'cli', label: 'DeepSeek Harness Web', detail: 'dsh web' }]),
|
||||
...(scenario === 'cli' ? [] : [{ id: 'app', label: 'DeepSeek Harness Desktop', detail: 'DeepSeek Harness.exe' }]),
|
||||
],
|
||||
},
|
||||
};
|
||||
}, scenario);
|
||||
await page.goto(`http://localhost:1421/tests/fixtures/agent-backups.html?client=deepseek-harness&reset-selections${scenario === 'running' ? '&running' : ''}`);
|
||||
const controls = page.locator('.agent-run-controls');
|
||||
const app = controls.getByRole('button', { name: '启动 App', exact: true });
|
||||
await app.waitFor();
|
||||
if (scenario === 'cli') {
|
||||
assert.ok(await app.isDisabled());
|
||||
} else {
|
||||
await page.locator('.agent-status-grid').getByText('0.2.0-rc.2', { exact: true }).waitFor();
|
||||
await app.click();
|
||||
await page.waitForFunction(() => window.fixtureCalls.some(call => call.cmd === 'launch_agent'));
|
||||
const calls = await page.evaluate(() => window.fixtureCalls.filter(call => call.cmd === 'launch_agent'));
|
||||
assert.equal(calls[0].args.target, 'app');
|
||||
assert.equal(calls[0].args.deepseekHarnessOptions, null);
|
||||
}
|
||||
if (scenario === 'running') {
|
||||
await controls.getByRole('button', { name: '重启 Web', exact: true }).waitFor();
|
||||
await controls.getByRole('button', { name: '关闭 DeepSeek Harness', exact: true }).click();
|
||||
await controls.getByRole('button', { name: '启动 CLI', exact: true }).waitFor();
|
||||
} else {
|
||||
assert.equal(await controls.getByRole('button', { name: '启动 CLI', exact: true }).isDisabled(), scenario === 'desktop');
|
||||
}
|
||||
assert.equal(await controls.getByRole('button', { name: '重启 App', exact: true }).count(), 0);
|
||||
await page.close();
|
||||
}
|
||||
console.log('DeepSeek Desktop UI: desktop-only, CLI-only, both, and running Web passed');
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
})().catch(error => { console.error(error); process.exitCode = 1; });
|
||||
+23
-10
@@ -107,14 +107,6 @@ const fs = require('node:fs');
|
||||
await dialog().getByLabel('Version tag (blank for latest)').fill('../unsafe');
|
||||
assert.equal(await dialog().getByRole('button', { name: 'Install', exact: true }).isDisabled(), true);
|
||||
await dialog().getByLabel('Version tag (blank for latest)').fill('v1.2.3');
|
||||
await dialog().getByRole('button', { name: 'Install', exact: true }).click();
|
||||
assert.equal((await installs()).length, 0, 'First third-party confirmation cannot install');
|
||||
await dialog().getByText(/has not been reviewed by the official project/).waitFor();
|
||||
await dialog().getByRole('button', { name: 'Understood, review source' }).click();
|
||||
const token = dialog().getByLabel(/Type the following/);
|
||||
await token.fill('wrong/source');
|
||||
assert.equal(await dialog().getByRole('button', { name: 'Install', exact: true }).isDisabled(), true);
|
||||
await token.fill('router-for-me/analytics');
|
||||
await page.evaluate(() => { window.pluginsFixture.holdInstall = true; });
|
||||
await dialog().getByRole('button', { name: 'Install', exact: true }).click();
|
||||
await page.waitForFunction(() => window.pluginsFixture.installs.length === 1);
|
||||
@@ -166,7 +158,7 @@ const fs = require('node:fs');
|
||||
await page.getByRole('alert').filter({ hasText: 'Fixture list temporarily unavailable' }).waitFor({ state: 'detached' });
|
||||
|
||||
for (const locale of ['en', 'zh-CN']) {
|
||||
for (const width of [640, 1024]) {
|
||||
for (const width of [375, 640, 1070, 1368]) {
|
||||
await page.setViewportSize({ width, height: 800 });
|
||||
await open(`locale=${locale}&theme=${locale === 'en' ? 'light' : 'dark'}`);
|
||||
await page.locator('.plugin-card').first().waitFor();
|
||||
@@ -186,6 +178,27 @@ const fs = require('node:fs');
|
||||
fs.mkdirSync(process.env.PLUGIN_SCREENSHOT_DIR, { recursive: true });
|
||||
await page.screenshot({ path: path.join(process.env.PLUGIN_SCREENSHOT_DIR, `plugins-${locale}-${width}.png`), fullPage: true });
|
||||
}
|
||||
await card('Community analytics').getByRole('button', { name: locale === 'en' ? 'Install' : '安装', exact: true }).click();
|
||||
await dialog().waitFor();
|
||||
// Exercise wrapping and scrolling with metadata longer than a typical registry entry.
|
||||
await dialog().locator('h2').evaluate(el => { el.textContent += ' · model-fallback-router-compatibility'; });
|
||||
await dialog().locator('.plugin-install-origin small').evaluate(el => { el.textContent = 'https://raw.githubusercontent.com/example/plugins-store/main/' + 'long-registry-path-'.repeat(10) + 'registry.json'; });
|
||||
await page.setViewportSize({ width, height: 480 });
|
||||
const layout = await dialog().evaluate(el => {
|
||||
const heading = el.querySelector('.config-dialog-heading').getBoundingClientRect();
|
||||
const body = el.querySelector('.plugin-install-body').getBoundingClientRect();
|
||||
const footer = el.querySelector('.plugin-dialog-actions').getBoundingClientRect();
|
||||
const bounds = el.getBoundingClientRect();
|
||||
return { ordered: heading.bottom <= body.top + 1 && body.bottom <= footer.top + 1,
|
||||
fits: bounds.top >= 0 && bounds.bottom <= innerHeight && el.scrollWidth <= el.clientWidth + 1 };
|
||||
});
|
||||
assert.deepEqual(layout, { ordered: true, fits: true }, 'Dialog sections must not overlap or overflow');
|
||||
await noOverflow();
|
||||
if (process.env.PLUGIN_SCREENSHOT_DIR) {
|
||||
await page.screenshot({ path: path.join(process.env.PLUGIN_SCREENSHOT_DIR, `plugins-install-${locale}-${width}.png`) });
|
||||
}
|
||||
await page.keyboard.press('Escape');
|
||||
await dialog().waitFor({ state: 'detached' });
|
||||
}
|
||||
}
|
||||
await open('unsupported');
|
||||
@@ -193,7 +206,7 @@ const fs = require('node:fs');
|
||||
assert.equal(await page.locator('.plugin-tabs').count(), 0);
|
||||
assert.equal(await page.evaluate(() => window.pluginsFixture.requests.length), 0, 'Unsupported cores do not receive plugin mutations or list requests');
|
||||
assert.deepEqual(errors, []);
|
||||
console.log('Plugin UI passed: configured-only and unloaded states, installed counts, store lookup, configuration removal, resources, global/instance toggle, store filters, source trust gate, versions, retry, settings, uninstall, unsupported cores, responsive English/Chinese.');
|
||||
console.log('Plugin UI passed: configured-only and unloaded states, installed counts, store lookup, configuration removal, resources, global/instance toggle, store filters, single-step installation, versions, retry, settings, uninstall, unsupported cores, responsive English/Chinese.');
|
||||
} finally {
|
||||
await browser?.close();
|
||||
await server.close();
|
||||
|
||||
Reference in new issue
Block a user