feat(history): add multi-account and WhatsApp foundation
This commit is contained in:
+82
-15
@@ -11,6 +11,27 @@ async function login(page: Page, loginAccount = account, loginPassword = passwor
|
||||
await expect(page).toHaveURL(/\/dashboard$/)
|
||||
}
|
||||
|
||||
test('过期登录状态会回到登录页而不是显示网络错误', async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
window.localStorage.setItem('lx-auth', JSON.stringify({
|
||||
state: {
|
||||
token: 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjF9.expired',
|
||||
user: { id: 'expired-user', name: 'Expired User', role: 'admin' },
|
||||
tenant: { id: 'expired-tenant', name: 'Expired Tenant', plan: 'pro' },
|
||||
onboarded: true,
|
||||
},
|
||||
version: 0,
|
||||
}))
|
||||
})
|
||||
await page.goto('/dashboard')
|
||||
await expect(page).toHaveURL(/\/login(?:\?|$)/)
|
||||
await expect(page.getByText('登录状态已过期,请重新登录')).toBeVisible()
|
||||
await expect.poll(() => page.evaluate(() => {
|
||||
const persisted = JSON.parse(window.localStorage.getItem('lx-auth') || '{}')
|
||||
return persisted.state?.token ?? null
|
||||
})).toBeNull()
|
||||
})
|
||||
|
||||
test('Telegram 手机号验证码和二步密码登录向导', async ({ page, request }, testInfo) => {
|
||||
const suffix = `${Date.now()}-${testInfo.project.name}`
|
||||
const loginAccount = `channel-e2e-${suffix}@local.test`
|
||||
@@ -20,8 +41,11 @@ test('Telegram 手机号验证码和二步密码登录向导', async ({ page, re
|
||||
})
|
||||
expect(response.ok()).toBeTruthy()
|
||||
await login(page, loginAccount, loginPassword)
|
||||
await page.goto('/channels?setup=telegram')
|
||||
await expect(page.getByTestId('telegram-onboarding')).toContainText('先绑定 Telegram')
|
||||
await page.goto('/channels?setup=choose')
|
||||
await expect(page.getByTestId('channel-onboarding')).toContainText('不强制绑定 Telegram')
|
||||
await expect(page.getByTestId('choose-whatsapp')).toBeVisible()
|
||||
await expect(page.getByTestId('choose-email')).toBeVisible()
|
||||
await page.getByTestId('choose-telegram').click()
|
||||
await expect(page.getByPlaceholder('+8613800138000')).toBeVisible()
|
||||
await expect(page.getByLabel('Telegram 验证码', { exact: true })).toHaveCount(0)
|
||||
const sendTelegramCode = page.getByRole('button', { name: '发送 Telegram 验证码' })
|
||||
@@ -29,10 +53,29 @@ test('Telegram 手机号验证码和二步密码登录向导', async ({ page, re
|
||||
await sendTelegramCode.click()
|
||||
await expect(page.getByText('请输入国际格式手机号,例如 +8613800138000')).toBeVisible()
|
||||
await expect(page.getByPlaceholder('+8613800138000')).toBeVisible()
|
||||
await expect(page.getByText('每个企业独立绑定账号')).toBeVisible()
|
||||
await expect(page.getByText('可以继续添加其他工作账号', { exact: false })).toBeVisible()
|
||||
await page.screenshot({ path: testInfo.outputPath('telegram-login-wizard.png'), fullPage: true })
|
||||
})
|
||||
|
||||
test('WhatsApp 普通账号关联设备向导', async ({ page, request }, testInfo) => {
|
||||
const suffix = `${Date.now()}-${testInfo.project.name}`
|
||||
const loginAccount = `whatsapp-e2e-${suffix}@local.test`
|
||||
const loginPassword = 'Lingxi-WhatsApp-2026!'
|
||||
const response = await request.post('/api/auth/register', {
|
||||
data: { name: 'WhatsApp E2E', account: loginAccount, password: loginPassword, company: 'WhatsApp E2E', industry: '跨境电商', teamSize: '1-10人' },
|
||||
})
|
||||
expect(response.ok()).toBeTruthy()
|
||||
await login(page, loginAccount, loginPassword)
|
||||
await page.goto('/channels?setup=choose')
|
||||
await page.getByTestId('choose-whatsapp').click()
|
||||
await expect(page.getByText('普通 WhatsApp 关联设备接入', { exact: true })).toBeVisible()
|
||||
await expect(page.getByText('并非 Meta 官方 Business Cloud API', { exact: false })).toBeVisible()
|
||||
await expect(page.getByRole('button', { name: '生成 WhatsApp 配对码' })).toBeVisible()
|
||||
await page.getByRole('button', { name: '生成 WhatsApp 配对码' }).click()
|
||||
await expect(page.getByText('请输入包含国家/地区代码的 WhatsApp 手机号')).toBeVisible()
|
||||
await page.screenshot({ path: testInfo.outputPath('whatsapp-link-device-wizard.png'), fullPage: true })
|
||||
})
|
||||
|
||||
test('企业邮箱 IMAP 和 SMTP 连接向导', async ({ page, request }, testInfo) => {
|
||||
const suffix = `${Date.now()}-${testInfo.project.name}`
|
||||
const loginAccount = `email-e2e-${suffix}@local.test`
|
||||
@@ -47,11 +90,30 @@ test('企业邮箱 IMAP 和 SMTP 连接向导', async ({ page, request }, testIn
|
||||
await emailCard.getByRole('button', { name: '连接', exact: true }).click()
|
||||
await expect(page.getByText('IMAP 收信 + SMTP 发信', { exact: true })).toBeVisible()
|
||||
await expect(page.getByPlaceholder('sales@company.com')).toBeVisible()
|
||||
await expect(page.getByPlaceholder('仅提交到本地服务端验证')).toHaveAttribute('type', 'password')
|
||||
await expect(page.getByPlaceholder('邮箱密码或应用专用密码')).toHaveAttribute('type', 'password')
|
||||
await expect(page.getByRole('button', { name: '验证并连接邮箱' })).toBeVisible()
|
||||
await page.screenshot({ path: testInfo.outputPath('email-connection-wizard.png'), fullPage: true })
|
||||
})
|
||||
|
||||
test('渠道真实消息统计与多 Telegram 账号入口', async ({ page }) => {
|
||||
await login(page)
|
||||
const responsePromise = page.waitForResponse((response) =>
|
||||
response.url().endsWith('/api/channels') && response.request().method() === 'GET',
|
||||
)
|
||||
await page.goto('/channels')
|
||||
const response = await responsePromise
|
||||
expect(response.ok()).toBeTruthy()
|
||||
const payload = await response.json()
|
||||
for (const channel of payload.data) {
|
||||
expect(Number.isInteger(channel.todayMessages)).toBeTruthy()
|
||||
expect(channel.todayMessages).toBeGreaterThanOrEqual(0)
|
||||
}
|
||||
await page.getByRole('button', { name: 'Telegram', exact: true }).click()
|
||||
await expect(page.getByText('连接 Telegram', { exact: true })).toBeVisible()
|
||||
await expect(page.getByPlaceholder('+8613800138000')).toBeVisible()
|
||||
await expect(page.getByText('可以继续添加其他工作账号', { exact: false })).toBeVisible()
|
||||
})
|
||||
|
||||
test('登录与核心工作台视觉链路', async ({ page }, testInfo) => {
|
||||
await login(page)
|
||||
await expect(page).toHaveURL(/\/dashboard$/)
|
||||
@@ -62,8 +124,8 @@ test('登录与核心工作台视觉链路', async ({ page }, testInfo) => {
|
||||
await page.screenshot({ path: testInfo.outputPath('channels.png'), fullPage: true })
|
||||
|
||||
await page.goto('/inbox')
|
||||
await page.getByRole('button', { name: '添加 Telegram 用户或群组' }).click()
|
||||
await expect(page.getByRole('dialog')).toContainText('添加 Telegram 会话')
|
||||
await page.getByRole('button', { name: '添加联系人或群组' }).click()
|
||||
await expect(page.getByRole('dialog')).toContainText('添加联系人或群组')
|
||||
await expect(page.getByLabel('用户名、手机号或用户 ID')).toBeVisible()
|
||||
await page.getByRole('button', { name: '群组/频道', exact: true }).click()
|
||||
await expect(page.getByLabel('群组/频道用户名或 Chat ID')).toBeVisible()
|
||||
@@ -71,8 +133,9 @@ test('登录与核心工作台视觉链路', async ({ page }, testInfo) => {
|
||||
await expect(page.getByLabel('Telegram 邀请链接')).toBeVisible()
|
||||
await page.getByRole('button', { name: '关闭', exact: true }).click()
|
||||
const privateConversation = page.locator(
|
||||
'[data-testid="conversation-item"][data-channel="telegram"]:not([data-telegram-chat-type="channel"])',
|
||||
'[data-testid="conversation-item"][data-channel="telegram"][data-last-sender="agent"]:not([data-telegram-chat-type="channel"])',
|
||||
).first()
|
||||
await expect(privateConversation).toBeVisible()
|
||||
await privateConversation.click()
|
||||
await expect(page.getByTestId('message-content')).toHaveClass(/justify-end/)
|
||||
const languageSwitcher = page.getByRole('radiogroup', { name: '消息显示语言' })
|
||||
@@ -88,11 +151,15 @@ test('登录与核心工作台视觉链路', async ({ page }, testInfo) => {
|
||||
await expect(outgoingSurface.first()).toHaveClass(/telegram-bubble-out/)
|
||||
const incomingSurface = page.locator('[data-testid="message-surface"][data-direction="incoming"]')
|
||||
if (await incomingSurface.count()) await expect(incomingSurface.first()).toHaveClass(/telegram-bubble-in/)
|
||||
await expect(privateConversation).toHaveClass(/conversation-selected/)
|
||||
await expect(page.getByTestId('conversation-list-panel')).toHaveClass(/border-slate-300/)
|
||||
await expect(page.getByTestId('chat-panel')).toHaveClass(/bg-\[#eaf2f9\]/)
|
||||
if (testInfo.project.name !== 'mobile-chromium') {
|
||||
await expect(privateConversation).toHaveClass(/conversation-selected/)
|
||||
await expect(page.getByTestId('conversation-list-panel')).toHaveClass(/border-slate-300/)
|
||||
await expect(page.getByTestId('chat-panel')).toHaveClass(/bg-\[#eaf2f9\]/)
|
||||
} else {
|
||||
await expect(page.getByRole('button', { name: '返回会话列表' })).toBeVisible()
|
||||
}
|
||||
await expect(page.getByTestId('conversation-insight-bar').locator('img')).toHaveCount(0)
|
||||
await expect(page.getByTestId('conversation-insight-bar')).toContainText('AI 客户洞察')
|
||||
await expect(page.getByTestId('conversation-insight-bar')).toContainText('客户洞察')
|
||||
await page.getByRole('button', { name: 'Telegram 会话操作' }).click()
|
||||
await expect(page.getByText(/置顶会话|取消置顶/)).toBeVisible()
|
||||
await expect(page.getByText(/归档会话|移出归档/)).toBeVisible()
|
||||
@@ -192,12 +259,12 @@ test('客户画像雷达与采购情报可视化', async ({ page }, testInfo) =>
|
||||
await expect(rows.first()).toBeVisible()
|
||||
await rows.first().getByRole('cell').nth(1).click()
|
||||
await expect(page.getByText('客户价值雷达', { exact: true })).toBeVisible()
|
||||
await expect(page.getByText('AI 客户洞察', { exact: true })).toBeVisible()
|
||||
await expect(page.getByText('采购情报', { exact: true })).toBeVisible()
|
||||
await expect(page.getByText('客户洞察', { exact: true })).toBeVisible()
|
||||
await expect(page.getByText('采购信息', { exact: true })).toBeVisible()
|
||||
await expect(page.getByText('标签画像', { exact: true })).toBeVisible()
|
||||
const semanticTags = page.locator('[data-tag-category]')
|
||||
if (await semanticTags.count()) await expect(semanticTags.first()).toBeVisible()
|
||||
else await expect(page.getByText('暂无标签,AI 将在收到有效客户消息后自动补全。')).toBeVisible()
|
||||
else await expect(page.getByText('暂无标签,收到更多客户消息后将自动补充。')).toBeVisible()
|
||||
await page.screenshot({ path: testInfo.outputPath('customer-profile-radar.png'), fullPage: true })
|
||||
})
|
||||
|
||||
@@ -208,6 +275,6 @@ test('团队邀请和支付页不显示虚假成功', async ({ page }, testInfo)
|
||||
await page.screenshot({ path: testInfo.outputPath('team.png'), fullPage: true })
|
||||
|
||||
await page.goto('/billing')
|
||||
await expect(page.getByText('Stripe Checkout', { exact: false })).toBeVisible()
|
||||
await expect(page.getByRole('heading', { name: '套餐与账单' })).toBeVisible()
|
||||
await page.screenshot({ path: testInfo.outputPath('billing.png'), fullPage: true })
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user