| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- import { expect, Page, test } from '@playwright/test';
- import path from 'node:path';
- declare global {
- interface Window {
- __seedIpOperatorScenario?: (scenario: 'ready') => unknown;
- }
- }
- const scenarioSeedPath = path.resolve(__dirname, '../scripts/validation/ip-operator-scenario-seed.js');
- async function seedEvidenceLoop(page: Page) {
- await page.addInitScript({ path: scenarioSeedPath });
- await page.addInitScript(() => {
- window.__seedIpOperatorScenario?.('ready');
- const scope = 'local-guest';
- const planId = 'ip_plan_e2e_ready';
- const accountId = 'ip_account_e2e_ready';
- const now = new Date().toISOString();
- const ownedWorkId = 'evidence_owned_work';
- const commentId = 'evidence_owned_comment';
- const benchmarkId = 'evidence_benchmark_work';
- const painId = 'pain_evidence_loop';
- const planKey = `tiktok.ipOperator.plan.${planId}.${scope}`;
- const plan = JSON.parse(localStorage.getItem(planKey) || '{}');
- plan.evidenceItems = [
- {
- id: ownedWorkId,
- platform: 'douyin',
- kind: 'owned_post',
- bucket: 'accounts',
- accountId,
- sourceRole: 'owned',
- title: '自有账号作品:老板 IP 如何避免成为广告号',
- rawText: '先讲客户判断,再讲产品。',
- summary: '播放 50000,评论 120,收藏 380。',
- tags: ['自有账号', '账号作品'],
- relatedTopicIds: [],
- relatedScriptIds: [],
- sourceId: 'work-owned-1',
- evidenceRefs: ['https://www.douyin.com/video/7304943423281171749'],
- selectedForGeneration: true,
- confidence: 'high',
- createdAt: now,
- updatedAt: now,
- },
- {
- id: commentId,
- platform: 'douyin',
- kind: 'comment',
- bucket: 'topics',
- accountId,
- sourceRole: 'owned',
- title: '自有账号评论:老板 IP 如何避免成为广告号',
- rawText: '传统行业老板怎么讲案例才不像广告?',
- summary: '传统行业老板怎么讲案例才不像广告?',
- tags: ['自有账号', '评论痛点'],
- relatedTopicIds: [],
- relatedScriptIds: [],
- sourceId: 'comment-owned-1',
- evidenceRefs: [],
- selectedForGeneration: true,
- confidence: 'high',
- createdAt: now,
- updatedAt: now,
- },
- {
- id: benchmarkId,
- platform: 'douyin',
- kind: 'competitor_post',
- bucket: 'accounts',
- sourceRole: 'benchmark',
- title: '对标作品:先讲判断标准再讲方法',
- rawText: '反常识开头,案例证明。',
- summary: '可借鉴问题钩子和案例结构。',
- tags: ['对标账号'],
- relatedTopicIds: [],
- relatedScriptIds: [],
- sourceId: 'benchmark-work-1',
- evidenceRefs: [],
- selectedForGeneration: true,
- confidence: 'medium',
- createdAt: now,
- updatedAt: now,
- },
- ];
- plan.commentPainInsights = [{
- id: painId,
- title: '用户追问:怎么讲案例',
- category: 'question',
- severity: 'high',
- summary: '评论集中追问老板如何讲案例而不显得像广告。',
- commentCount: 8,
- keywordFrequency: [{ keyword: '怎么', count: 8 }],
- representativeComments: ['传统行业老板怎么讲案例才不像广告?'],
- evidenceItemIds: [commentId],
- suggestedTopics: ['案例表达三步法'],
- scriptAngles: ['用评论原话开头'],
- riskBoundaries: ['不承诺结果'],
- createdAt: now,
- updatedAt: now,
- }];
- plan.accountStrategyReports = [{
- id: 'strategy_evidence_loop',
- accountId,
- snapshotId: 'ip_snapshot_e2e_ready',
- positioningProposalId: 'ip_positioning_proposal_e2e_ready',
- sourceMode: 'llm',
- sourceLabel: 'LLM 证据诊断',
- diagnosisSummary: {},
- evidenceRefs: [],
- directions: [],
- taskSuggestions: [],
- createdAt: now,
- updatedAt: now,
- }];
- plan.topics = (plan.topics || []).map((topic: Record<string, unknown>, index: number) => ({
- ...topic,
- evidenceRefs: index === 0 ? [ownedWorkId, commentId] : [ownedWorkId],
- }));
- localStorage.setItem(planKey, JSON.stringify(plan));
- const directionIndexKey = `tiktok.ipOperator.contentDirections.${scope}`;
- for (const directionId of JSON.parse(localStorage.getItem(directionIndexKey) || '[]')) {
- const key = `tiktok.ipOperator.contentDirection.${directionId}.${scope}`;
- const direction = JSON.parse(localStorage.getItem(key) || '{}');
- direction.sourceEvidenceIds = [ownedWorkId, benchmarkId];
- direction.commentPainRefs = [painId];
- localStorage.setItem(key, JSON.stringify(direction));
- }
- const taskIndexKey = `tiktok.ipOperator.operationTasks.${scope}`;
- for (const taskId of JSON.parse(localStorage.getItem(taskIndexKey) || '[]')) {
- const key = `tiktok.ipOperator.operationTask.${taskId}.${scope}`;
- const task = JSON.parse(localStorage.getItem(key) || '{}');
- task.relatedEvidenceIds = [ownedWorkId, commentId];
- localStorage.setItem(key, JSON.stringify(task));
- }
- const proposalKey = `tiktok.ipOperator.positioningProposal.ip_positioning_proposal_e2e_ready.${scope}`;
- const proposal = JSON.parse(localStorage.getItem(proposalKey) || '{}');
- proposal.evidenceItemIds = [ownedWorkId, commentId];
- localStorage.setItem(proposalKey, JSON.stringify(proposal));
- localStorage.setItem('tiktok.currentTab', 'ip-operator');
- });
- }
- test('IP operator shows traceable evidence drawer for direction conclusions', async ({ page }) => {
- await seedEvidenceLoop(page);
- await page.goto('/');
- await expect(page.getByRole('heading', { name: 'IP操盘工作台' })).toBeVisible();
- await page.getByRole('button', { name: /方向选题/ }).click();
- await page.getByRole('button', { name: '查看方向证据' }).first().click();
- const drawer = page.getByRole('complementary', { name: '证据来源' });
- await expect(drawer).toBeVisible();
- await expect(drawer.getByText('自有账号作品').first()).toBeVisible();
- await expect(drawer.getByText('评论痛点').first()).toBeVisible();
- await expect(drawer.getByText('对标作品').first()).toBeVisible();
- });
|