import { expect, Page, test } from '@playwright/test'; import path from 'node:path'; declare global { interface Window { __seedIpOperatorScenario?: (scenario: 'empty' | 'ready' | 'needs_input' | 'failed') => unknown; } } const scenarioSeedPath = path.resolve(__dirname, '../scripts/validation/ip-operator-scenario-seed.js'); async function seedScenario(page: Page, scenario: 'empty' | 'ready' | 'needs_input' | 'failed', startTab: 'home' | 'ip-operator' = 'ip-operator') { await page.addInitScript({ path: scenarioSeedPath }); await page.addInitScript(([nextScenario, nextTab]) => { window.__seedIpOperatorScenario?.(nextScenario); localStorage.setItem('tiktok.currentTab', nextTab); }, [scenario, startTab]); } test('sidebar entry opens the account-driven workbench without legacy V1 tools', async ({ page }) => { await seedScenario(page, 'empty', 'home'); await page.goto('/'); await page.getByRole('button', { name: /^IP操盘\s*V1$/ }).click(); await expect(page.locator('.ip-operator-page')).toBeVisible(); await expect(page.locator('.account-workbench')).toBeVisible(); await expect(page.locator('.operator-cockpit')).toContainText('账号运营驾驶舱'); await expect(page.locator('.legacy-plan-archive')).toHaveCount(0); await expect(page.locator('.low-frequency-panel')).toHaveCount(0); await expect(page.getByRole('button', { name: '新建档案' })).toHaveCount(0); await expect(page.getByText('创建 IP 档案')).toHaveCount(0); }); test('ready scenario keeps account evidence, directions and publish review in the current workbench', async ({ page }) => { await seedScenario(page, 'ready'); await page.goto('/'); await expect(page.locator('.account-workbench')).toContainText('账号运营驾驶舱'); await expect(page.locator('.account-monitor-panel')).toContainText('账号内容与数据'); await expect(page.locator('.priority-task-board')).toContainText('今天必须做'); await page.getByRole('button', { name: /方向选题/ }).click(); await expect(page.locator('.direction-workspace')).toBeVisible(); await expect(page.locator('.direction-topic-card').first()).toBeVisible(); const firstTopicCard = page.locator('.direction-topic-card').first(); await firstTopicCard.getByRole('button', { name: /生成分镜脚本|分镜脚本已生成/ }).click(); await expect(page.locator('.shot-script-modal')).toBeVisible(); await expect(page.locator('.shot-script-modal')).toContainText('开头钩子'); await page.locator('.shot-script-modal__close').click(); await firstTopicCard.getByRole('button', { name: /生成并查看发布包|查看发布包/ }).click(); await expect(page.locator('.publish-package-modal')).toBeVisible(); await expect(page.locator('.publish-package-modal')).toContainText('标题候选'); await expect(page.locator('.publish-package-modal')).toContainText('风险检查'); await page.locator('.publish-package-modal__close').click({ force: true }); await expect(page.locator('.publish-binding-panel').first()).toContainText('发布复盘:把真实表现反哺下一轮'); await expect(page.locator('.publish-binding-panel').first()).toContainText('已绑定,待复盘'); }); test('ready scenario exposes the full calibration and retrospective loop without storage internals', async ({ page }) => { await seedScenario(page, 'ready'); await page.goto('/'); await expect(page.locator('.account-lifecycle-card')).toContainText('当前阶段'); await expect(page.locator('.account-lifecycle-card')).toContainText('下一步'); await expect(page.locator('.strategy-report-meta')).toContainText('判断置信度'); await expect(page.locator('.strategy-report-meta')).toContainText('中'); await expect(page.locator('.strategy-report-meta')).toContainText('已采用校准'); await expect(page.locator('.strategy-report-meta')).toContainText('仍缺发布后复盘数据的长期对比'); await expect(page.locator('.account-calibration-panel')).toContainText('减少纯工具合集'); await expect(page.locator('.publish-retrospective-summary')).toContainText('符合预期'); await expect(page.locator('.publish-retrospective-summary')).toContainText('下一步:继续放大'); const pageText = await page.locator('.account-workbench').textContent(); expect(pageText).not.toContain('Parse'); expect(pageText).not.toContain('VideoWorkflowEntity'); expect(pageText).not.toContain('entityType'); expect(pageText).not.toContain('evidenceRefs'); await page.getByLabel('定位/关注理由修正').fill('继续强调老板经营问题,不再泛讲工具清单'); await page.getByLabel('不要再推荐的话题').fill('纯工具合集, 泛 AI 工具盘点'); await page.getByLabel('不要使用的表达').fill('保姆级万能'); await page.getByLabel('老号经验补充').fill('案例拆解更容易带来收藏和有效评论'); await page.getByRole('button', { name: '保存账号校准' }).click(); await expect(page.locator('.calibration-summary')).toContainText('继续强调老板经营问题'); await page.getByRole('button', { name: /方向选题/ }).click(); await expect(page.locator('.direction-workspace')).toBeVisible(); await page.getByRole('button', { name: /总览/ }).click(); await expect(page.locator('.calibration-summary')).toContainText('继续强调老板经营问题'); }); test('failed scenario still renders the account cockpit instead of a blank legacy page', async ({ page }) => { await seedScenario(page, 'failed'); await page.goto('/'); await expect(page.locator('.account-workbench')).toBeVisible(); await expect(page.locator('.operator-cockpit')).toContainText('账号运营驾驶舱'); await expect(page.locator('.legacy-plan-archive')).toHaveCount(0); await expect(page.locator('.account-diagnosis-panel, .operator-cockpit__empty')).toHaveCount(1); });