Record project 6275 PI 409 DOC_BUSY plus Docker screenshots, mark CHAT-INTENT-STATE PASS, and archive the short-term so CURRENT has no in_progress rows. Co-authored-by: Cursor <cursoragent@cursor.com>
100 lines
4.4 KiB
TypeScript
100 lines
4.4 KiB
TypeScript
import { test, expect, type Page } from '@playwright/test';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const runDualTab = process.env.INTENT_DUAL_TAB === '1';
|
|
const ASSETS = process.env.INTENT_ASSETS
|
|
? path.resolve(process.env.INTENT_ASSETS)
|
|
: path.resolve(__dirname, '../../.doc/test-reports/assets/2026-09-15-conversation-intent-dual-tab');
|
|
const SCREENSHOTS = path.join(ASSETS, 'screenshots');
|
|
fs.mkdirSync(SCREENSHOTS, { recursive: true });
|
|
|
|
const OWNER_USER = process.env.OWNER_USERNAME || process.env.ADMIN_USERNAME || '';
|
|
const OWNER_PASS = process.env.OWNER_PASSWORD || process.env.ADMIN_PASSWORD || '';
|
|
const PROJECT_NAME = (process.env.INTENT_PROJECT_NAME || '').trim();
|
|
|
|
test.skip(!runDualTab, 'set INTENT_DUAL_TAB=1 against rebuilt dev; do not use project 143');
|
|
|
|
async function login(page: Page, username: string, password: string) {
|
|
await page.goto('/');
|
|
await page.fill('input[placeholder*="系统用户名"]', username);
|
|
await page.fill('input[placeholder*="登录密码"]', password);
|
|
await page.click('button:has-text("进入医疗协同工作台")');
|
|
await expect(page.getByText('新建立项会话')).toBeVisible({ timeout: 30_000 });
|
|
}
|
|
|
|
async function openProject(page: Page, name: string) {
|
|
const entry = page.getByTitle(name, { exact: true });
|
|
await expect(entry.first()).toBeVisible({ timeout: 30_000 });
|
|
await entry.first().click();
|
|
await expect(page.getByRole('banner').getByText(name, { exact: true })).toBeVisible({ timeout: 30_000 });
|
|
}
|
|
|
|
test.describe.serial('conversation intent dual-tab busy', () => {
|
|
test('tab B shows busy without reload and keeps empty actions', async ({ browser }) => {
|
|
if (!OWNER_USER || !OWNER_PASS) {
|
|
throw new Error('OWNER_USERNAME and OWNER_PASSWORD are required');
|
|
}
|
|
if (!PROJECT_NAME) {
|
|
throw new Error('INTENT_PROJECT_NAME is required');
|
|
}
|
|
if (/143/.test(PROJECT_NAME) || process.env.INTENT_PROJECT_ID === '143') {
|
|
throw new Error('project 143 is read-only');
|
|
}
|
|
|
|
const context = await browser.newContext();
|
|
const tabA = await context.newPage();
|
|
const posts: { status: number; body: string }[] = [];
|
|
const attachRunProbe = (page: Page) => {
|
|
page.on('response', async (res) => {
|
|
if (res.url().includes('/api/v1/tasks/runs') && res.request().method() === 'POST') {
|
|
posts.push({ status: res.status(), body: await res.text().catch(() => '') });
|
|
}
|
|
});
|
|
};
|
|
attachRunProbe(tabA);
|
|
await login(tabA, OWNER_USER, OWNER_PASS);
|
|
await openProject(tabA, PROJECT_NAME);
|
|
|
|
const tabB = await context.newPage();
|
|
attachRunProbe(tabB);
|
|
await tabB.goto('/');
|
|
await openProject(tabB, PROJECT_NAME);
|
|
|
|
const busyBanner = tabB.getByText(/本文档正由【(项目助手|其他用户)】编辑/)
|
|
const busyBadge = tabB.getByText('编辑中', { exact: true })
|
|
await expect(busyBanner.or(busyBadge).first()).toBeVisible({ timeout: 20_000 })
|
|
await tabA.screenshot({ path: path.join(SCREENSHOTS, 'tab-a-running.png'), fullPage: true })
|
|
await tabB.screenshot({ path: path.join(SCREENSHOTS, 'tab-b-busy.png'), fullPage: true })
|
|
|
|
await expect(tabB.getByRole('button', { name: /发送/ })).toBeDisabled()
|
|
await expect(tabB.getByText('DOC_BUSY')).toBeVisible()
|
|
|
|
const box = tabB.getByPlaceholder(/向项目助手提问/)
|
|
await expect(box).toBeVisible({ timeout: 15_000 })
|
|
await box.fill('请开始编写用户初始需求表')
|
|
const send = tabB.getByRole('button', { name: /发送/ })
|
|
if (await send.isEnabled()) {
|
|
await send.click()
|
|
}
|
|
await tabB.waitForTimeout(2000)
|
|
await tabB.screenshot({ path: path.join(SCREENSHOTS, 'tab-b-after-write.png'), fullPage: true })
|
|
|
|
await expect(tabB.getByRole('button', { name: /重试|重新开始/ })).toHaveCount(0)
|
|
|
|
fs.writeFileSync(
|
|
path.join(ASSETS, 'playwright-network.json'),
|
|
JSON.stringify({ posts, project: PROJECT_NAME }, null, 2),
|
|
)
|
|
|
|
await tabB.reload()
|
|
await expect(tabB.getByText(/本文档正由【(项目助手|其他用户)】编辑/).or(tabB.getByText('DOC_BUSY')).first()).toBeVisible({ timeout: 20_000 })
|
|
await expect(tabB.getByRole('button', { name: /重试|重新开始/ })).toHaveCount(0)
|
|
await tabB.screenshot({ path: path.join(SCREENSHOTS, 'tab-b-after-reload.png'), fullPage: true })
|
|
|
|
await context.close();
|
|
});
|
|
});
|