0238f505c4
P4b Task 5:/system/embed-slots 管理页(Task 8/9 页面模式:useCursorList
游标累积 + 对话框治理)。列表(名称/绑定相册/启停/configVersion/更新时间,
启停开关行内 PATCH → configVersion+1 toast 提示)+ 新建/编辑对话框
(设置表单位置/尺寸/间隔/语言/默认收起,intervalMs 契约区间夹取;编辑按
整体替换语义提交完整 settings)+ 绑定相册对话框(相册标题/ID 搜索选中 →
确认框携带 A22 文案「configVersion 将递增,官网无需改动」;清空即解绑)+
嵌入代码卡({EMBED_ORIGIN}/embed/v1/loader.js + data-slot 令牌,一键复制;
EMBED_ORIGIN=VITE_EMBED_ORIGIN 缺省本机 viewer dev,.env.example 登记)。
- API 层 src/api/embedSlots.ts + types/api/embedSlot.d.ts(契约镜像)
- vitest 9 例:表单→载荷(缺省+夹取)、编辑载荷收敛、snippet 拼接、
环境基址兜底、绑定变更分类、zh/en 键一致 + A22 短语断言
- Playwright e2e(真实 cn+ov 后端):新建→绑定(gov-photos 直种 +
seed-fixture 新增 sync-flush 模式经 bootstrap/真实发送管线投影 photos_ov)
→嵌入代码卡含 token→ov 公开 feed 返回相册照片→换绑 configVersion+1 +
feed 切换(A22)→停用 feed 404;admin playwright 配置补 ov webServer
- i18n zh/en(embedSlotsManage.* + menus.system.embedSlots)
Co-Authored-By: Claude Code <noreply@anthropic.com>
81 lines
3.2 KiB
TypeScript
81 lines
3.2 KiB
TypeScript
// e2e 辅助:根 .env 凭据/环境读取、UI 登录、DB 直种夹具(seed-fixture.cjs)。
|
||
import { execSync } from 'node:child_process'
|
||
import { existsSync, readFileSync } from 'node:fs'
|
||
import { join } from 'node:path'
|
||
import { fileURLToPath } from 'node:url'
|
||
import type { Page } from '@playwright/test'
|
||
import { expect } from '@playwright/test'
|
||
|
||
const repoRoot = fileURLToPath(new URL('../../..', import.meta.url))
|
||
|
||
export interface AdminCredentials {
|
||
account: string
|
||
password: string
|
||
}
|
||
|
||
function rootEnv(): Map<string, string> {
|
||
const envPath = join(repoRoot, '.env')
|
||
if (!existsSync(envPath)) {
|
||
throw new Error(`[admin e2e] 仓库根 .env 不存在(${envPath})`)
|
||
}
|
||
const vars = new Map<string, string>()
|
||
for (const line of readFileSync(envPath, 'utf-8').split(/\r?\n/)) {
|
||
const match = /^([A-Z0-9_]+)=(.*)$/.exec(line.trim())
|
||
if (match) vars.set(match[1], match[2])
|
||
}
|
||
return vars
|
||
}
|
||
|
||
export function adminCredentials(): AdminCredentials {
|
||
const vars = rootEnv()
|
||
const account = vars.get('ADMIN_ACCOUNT') ?? 'admin'
|
||
const password = vars.get('ADMIN_PASSWORD') ?? 'admin-secret-change-me'
|
||
return { account, password }
|
||
}
|
||
|
||
/** ov 入口基址(公开嵌入 feed 断言;缺省同根 .env.example PORT_OV=8081) */
|
||
export function ovBaseUrl(): string {
|
||
return (rootEnv().get('OV_API_URL') ?? 'http://127.0.0.1:8081').replace(/\/+$/, '')
|
||
}
|
||
|
||
/** UI 登录(真实后端):填账号密码 → 落到 /albums/manage */
|
||
export async function loginAsAdmin(page: Page): Promise<void> {
|
||
const { account, password } = adminCredentials()
|
||
await page.goto('/#/auth/login')
|
||
await page.getByPlaceholder('请输入账号').fill(account)
|
||
await page.getByPlaceholder('请输入密码', { exact: false }).fill(password)
|
||
await page.getByRole('button', { name: '登录' }).click()
|
||
await expect(page).toHaveURL(/\/albums\/manage/, { timeout: 20_000 })
|
||
}
|
||
|
||
/** 直种夹具(seed-fixture.cjs):staff 账号 / 相册种子照片 / 治理批量照片 /
|
||
* 水印旅程真实对象照片 / 水印任务直驱(详见 seed-fixture.cjs 文件头) */
|
||
export function seedFixture(
|
||
mode: 'staff' | 'photos' | 'gov-photos' | 'wm-photos' | 'watermark-run',
|
||
...args: string[]
|
||
): void {
|
||
const vars = rootEnv()
|
||
const databaseUrl = vars.get('DATABASE_URL_CN')
|
||
if (!databaseUrl) {
|
||
throw new Error('[admin e2e] 根 .env 缺少 DATABASE_URL_CN')
|
||
}
|
||
// @photo/db-cn / @photo/storage 绝对路径:apps/admin 不依赖这些包,借
|
||
// services/api 的 node_modules(node 从 e2e 目录解析不到 workspace 链接)
|
||
const dbCn = join(repoRoot, 'services', 'api', 'node_modules', '@photo', 'db-cn')
|
||
const storage = join(repoRoot, 'services', 'api', 'node_modules', '@photo', 'storage')
|
||
// 全量根 .env 注入(S3_* 供水印对象直写与 worker 直驱;process.env 覆盖
|
||
// 优先级更高——playwright 进程自身的环境变量优先于文件值)
|
||
execSync(`node seed-fixture.cjs ${mode} ${args.join(' ')}`, {
|
||
cwd: fileURLToPath(new URL('.', import.meta.url)),
|
||
stdio: 'inherit',
|
||
shell: true,
|
||
env: {
|
||
...Object.fromEntries(vars),
|
||
...process.env,
|
||
PHOTO_DB_CN: dbCn,
|
||
PHOTO_STORAGE: storage,
|
||
DATABASE_URL_CN: databaseUrl
|
||
}
|
||
})
|
||
}
|