fix(e2e): strict squad terminal and doc-free upload selectors
Only completed/pending_author_confirmation pass waitForSquadCompletion; category trigger/data-category for private open; UI-11/16 pin exact doc-free id without global attachment text clicks. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -269,12 +269,31 @@ async function waitForSquadCompletion(
|
||||
);
|
||||
|
||||
const terminal = (await assistant.getAttribute('data-chat-status')) || '';
|
||||
if (terminal === 'failed') {
|
||||
fail('squad terminal status is failed');
|
||||
if (
|
||||
terminal === 'failed' ||
|
||||
terminal === 'cancelled' ||
|
||||
terminal === 'quality_failed'
|
||||
) {
|
||||
fail(`squad terminal status is ${terminal}`);
|
||||
}
|
||||
if (terminal !== 'completed' && terminal !== 'pending_author_confirmation') {
|
||||
fail(`squad terminal status unexpected: ${terminal}`);
|
||||
}
|
||||
return Date.now() - startedAt;
|
||||
}
|
||||
|
||||
async function selectDeliverableCategory(
|
||||
page: Page,
|
||||
category: 'workflow' | 'shared' | 'private',
|
||||
) {
|
||||
const trigger = page.getByTestId('doc-category-trigger');
|
||||
await expect(trigger).toBeVisible({ timeout: 15_000 });
|
||||
await trigger.click();
|
||||
const item = page.locator(`.dropdown-menu button[data-category="${category}"]`).first();
|
||||
if ((await item.count()) === 0) fail(`category menu missing data-category=${category}`);
|
||||
await item.click();
|
||||
}
|
||||
|
||||
async function openWorkflowDocByLabel(
|
||||
page: Page,
|
||||
label: RegExp,
|
||||
@@ -408,6 +427,7 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
let versionBeforePending = '';
|
||||
let secondDocTitle = '';
|
||||
let secondDocId = '';
|
||||
let uploadedDocId = '';
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
context = await browser.newContext();
|
||||
@@ -850,21 +870,27 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
const { v, r } = await readVersionBadge(page);
|
||||
states.push(`${target.title}:${v}/${r}`);
|
||||
}
|
||||
// Page upload attachment
|
||||
// Page upload attachment — assert registered doc-free id on pane (ignore any chat assistant side effects).
|
||||
const fileInput = page.locator('input[type="file"]');
|
||||
if ((await fileInput.count()) === 0) fail('no file input for page upload');
|
||||
const tmp = path.join(ASSETS, `upload-${suffix}.txt`);
|
||||
fs.writeFileSync(tmp, `attachment ${suffix}\n`, 'utf-8');
|
||||
await fileInput.first().setInputFiles(tmp);
|
||||
await expect(page.getByText(`upload-${suffix}`).or(page.getByText('attachment'))).toBeVisible({
|
||||
timeout: 30_000,
|
||||
});
|
||||
const pane = page.getByTestId('active-document-pane');
|
||||
await expect(pane).toHaveAttribute('data-doc-id', /^doc-free-/, { timeout: 45_000 });
|
||||
uploadedDocId = (await pane.getAttribute('data-doc-id')) || '';
|
||||
if (!uploadedDocId.startsWith('doc-free-')) {
|
||||
fail(`UI-11 upload must activate doc-free-* pane, got ${uploadedDocId}`);
|
||||
}
|
||||
if (uploadedDocId.startsWith('doc-upload')) {
|
||||
fail(`UI-11 must not leave doc-upload orphan active: ${uploadedDocId}`);
|
||||
}
|
||||
await shot(page, 'UI-11-multi-doc.png');
|
||||
recordPass({
|
||||
id: 'UI-11',
|
||||
name: 'multi doc + upload',
|
||||
input: 'chat second doc; upload file',
|
||||
actual: states.join('; '),
|
||||
actual: `${states.join('; ')}; uploadedDocId=${uploadedDocId}`,
|
||||
networkLogs: logs,
|
||||
durationMs: Date.now() - t0,
|
||||
verdict: 'PASS',
|
||||
@@ -1006,11 +1032,19 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
test('UI-16 registered upload uses server doc-free ledger (no doc-upload orphan)', async () => {
|
||||
const t0 = Date.now();
|
||||
logs = [];
|
||||
const uploadTab = page.getByText(`upload-${suffix}`).or(page.getByText(/attachment/));
|
||||
if ((await uploadTab.count()) === 0) fail('upload tab missing (UI-11 must have uploaded via page)');
|
||||
await uploadTab.first().click();
|
||||
if (!uploadedDocId || !uploadedDocId.startsWith('doc-free-')) {
|
||||
fail(`UI-16 requires uploadedDocId from UI-11 (doc-free-*), got ${uploadedDocId || '(empty)'}`);
|
||||
}
|
||||
await selectDeliverableCategory(page, 'private');
|
||||
await openWorkflowDocById(page, uploadedDocId);
|
||||
const pane = page.getByTestId('active-document-pane');
|
||||
await expect(pane).toHaveAttribute('data-doc-id', uploadedDocId, { timeout: 15_000 });
|
||||
await expect(pane).toHaveAttribute('data-doc-id', /^doc-free-/);
|
||||
const paneId = (await pane.getAttribute('data-doc-id')) || '';
|
||||
if (paneId.startsWith('doc-upload')) {
|
||||
fail(`UI-16 must not open doc-upload orphan: ${paneId}`);
|
||||
}
|
||||
await expect(page.locator('text=版本账本未就绪')).toHaveCount(0);
|
||||
// Registered upload must mount real toolbar (doc-free), not orphan hint.
|
||||
await expect(page.locator('[data-testid="doc-rev-version"]')).toBeVisible({ timeout: 30_000 });
|
||||
const orphanHits = logs.filter(
|
||||
(l) => l.path.includes('/revision-state') && /\/documents\/doc-upload-/.test(l.path),
|
||||
@@ -1022,8 +1056,8 @@ test.describe.serial('document-revision-runtime UI acceptance', () => {
|
||||
recordPass({
|
||||
id: 'UI-16',
|
||||
name: 'registered upload server ledger',
|
||||
input: 'open page-uploaded attachment after createFreeform registration',
|
||||
actual: 'toolbar V visible; no doc-upload revision-state',
|
||||
input: `category=private; open exact ${uploadedDocId}`,
|
||||
actual: `pane=${paneId}; toolbar V visible; no doc-upload revision-state`,
|
||||
networkLogs: logs,
|
||||
durationMs: Date.now() - t0,
|
||||
verdict: 'PASS',
|
||||
|
||||
@@ -177,6 +177,7 @@ export const SlimTabBar: React.FC<SlimTabBarProps> = ({
|
||||
<div style={{ position: 'relative', flexShrink: 0 }}>
|
||||
<button
|
||||
className="dropdown-trigger"
|
||||
data-testid="doc-category-trigger"
|
||||
onClick={() => {
|
||||
setShowCategoryMenu(!showCategoryMenu);
|
||||
setShowDocMenu(false);
|
||||
@@ -239,6 +240,7 @@ export const SlimTabBar: React.FC<SlimTabBarProps> = ({
|
||||
return (
|
||||
<button
|
||||
key={cat}
|
||||
data-category={cat}
|
||||
onClick={() => {
|
||||
handleCategoryClick(cat);
|
||||
setShowCategoryMenu(false);
|
||||
|
||||
Reference in New Issue
Block a user