ip-operator-production-loop.spec.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. import { expect, Page, test } from '@playwright/test';
  2. import path from 'node:path';
  3. declare global {
  4. interface Window {
  5. __seedIpOperatorScenario?: (scenario: 'ready') => unknown;
  6. }
  7. }
  8. const scenarioSeedPath = path.resolve(__dirname, '../scripts/validation/ip-operator-scenario-seed.js');
  9. const planKey = 'tiktok.ipOperator.plan.ip_plan_e2e_ready.local-guest';
  10. async function seedReadyScenario(page: Page) {
  11. await page.addInitScript({ path: scenarioSeedPath });
  12. await page.addInitScript(() => {
  13. window.__seedIpOperatorScenario?.('ready');
  14. localStorage.setItem('tiktok.currentTab', 'ip-operator');
  15. });
  16. }
  17. test('direction topic can generate script, calendar item, publish package, and review task', async ({ page }) => {
  18. await seedReadyScenario(page);
  19. await page.goto('/');
  20. await expect(page.locator('.account-workbench')).toBeVisible();
  21. await expect(page.locator('.content-direction-grid .direction-topic-card').nth(1)).toBeVisible();
  22. const topicCard = page.locator('.content-direction-grid .direction-topic-card').nth(1);
  23. const topicTitle = (await topicCard.locator('strong').textContent())?.trim() || '';
  24. expect(topicTitle.length).toBeGreaterThan(0);
  25. const scriptAction = topicCard.getByRole('button', { name: /生成分镜脚本|分镜脚本已生成/ });
  26. const scriptLabel = (await scriptAction.textContent()) || '';
  27. await scriptAction.click();
  28. if (!scriptLabel.includes('分镜脚本已生成')) {
  29. await expect(topicCard.getByRole('button', { name: '分镜脚本已生成' })).toBeVisible();
  30. await topicCard.getByRole('button', { name: '分镜脚本已生成' }).click();
  31. }
  32. await expect(page.locator('.shot-script-modal')).toContainText('秒');
  33. await expect(page.locator('.shot-script-modal')).toContainText('开头钩子');
  34. await page.locator('.shot-script-modal__close').click();
  35. await topicCard.getByRole('button', { name: '加入内容日历' }).click();
  36. await expect.poll(async () => page.evaluate(({ key, title }) => {
  37. const plan = JSON.parse(localStorage.getItem(key) || '{}');
  38. return Boolean((plan.contentCalendar || []).some((item: { title?: string }) => item.title === title));
  39. }, { key: planKey, title: topicTitle })).toBe(true);
  40. await topicCard.getByRole('button', { name: '生成发布包' }).click();
  41. await expect(page.getByRole('heading', { name: '发布前人工审核资料包' })).toBeVisible();
  42. await expect(page.getByText('不做自动发布、批量评论、Cookie 池或代理池')).toBeVisible();
  43. await expect.poll(async () => page.evaluate(({ key, title }) => {
  44. const plan = JSON.parse(localStorage.getItem(key) || '{}');
  45. const pkg = (plan.publishPackages || []).find((item: { titleOptions?: string[] }) => (item.titleOptions || []).includes(title));
  46. const calendar = (plan.contentCalendar || []).find((item: { title?: string }) => item.title === title);
  47. return Boolean(pkg && calendar?.publishPackageId === pkg.id && (pkg.riskChecklist || []).some((item: { label?: string }) => item.label?.includes('不自动发布')));
  48. }, { key: planKey, title: topicTitle })).toBe(true);
  49. const packageCard = page.locator('.ip-workbench-card--wide').filter({ hasText: topicTitle }).first();
  50. await packageCard.locator('.publish-binding-panel--inline input').fill('https://www.douyin.com/video/7390000001');
  51. await packageCard.getByRole('button', { name: '手动绑定作品' }).click();
  52. await expect.poll(async () => page.evaluate(() => {
  53. const ids = JSON.parse(localStorage.getItem('tiktok.ipOperator.operationTasks.local-guest') || '[]');
  54. return ids.some((id: string) => {
  55. const raw = localStorage.getItem(`tiktok.ipOperator.operationTask.${id}.local-guest`);
  56. const task = raw ? JSON.parse(raw) : null;
  57. return task?.type === 'review' && String(task?.title || '').includes('复盘');
  58. });
  59. })).toBe(true);
  60. });