From 3f78115277acd328f42890df96e71ba3252732bf Mon Sep 17 00:00:00 2001 From: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:09:43 +0800 Subject: [PATCH] test(web): align e2e with redesigned experience Update real-service E2E assertions for the current landing and dashboard flows, and make settings card headings distinct from their page headings. Made-with: Proma Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> --- web/e2e/dashboard-shell.spec.ts | 11 +-- web/e2e/landing-navigation.spec.ts | 4 +- web/e2e/landing-quick-start-cli.spec.ts | 79 ++++++++----------- web/e2e/route-guard.spec.ts | 3 +- web/e2e/settings-pages.spec.ts | 6 +- web/e2e/theme-toggle.spec.ts | 2 +- web/e2e/user-id-display.spec.ts | 14 +--- web/package.json | 2 +- .../notification-preference-form.tsx | 2 +- web/src/i18n/locales/en.json | 9 ++- web/src/i18n/locales/ru.json | 9 ++- web/src/i18n/locales/zh.json | 11 ++- web/src/pages/settings/profile.tsx | 2 +- web/src/pages/settings/security.tsx | 2 +- 14 files changed, 75 insertions(+), 81 deletions(-) diff --git a/web/e2e/dashboard-shell.spec.ts b/web/e2e/dashboard-shell.spec.ts index edf16833..440cb86a 100644 --- a/web/e2e/dashboard-shell.spec.ts +++ b/web/e2e/dashboard-shell.spec.ts @@ -8,12 +8,13 @@ test.describe('Dashboard Shell (Real API)', () => { await registerSession(page, testInfo) }) - test('renders account summary and quick links', async ({ page }) => { + test('renders account navigation and overview links', async ({ page }) => { await page.goto('/dashboard') - await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible() - await expect(page.getByText('Account Information')).toBeVisible() - await expect(page.getByRole('link', { name: 'View API Tokens' })).toBeVisible() - await expect(page.getByRole('link', { name: 'View My Skills' }).first()).toBeVisible() + const sidebar = page.getByRole('complementary') + await expect(sidebar.getByRole('link', { name: 'Profile', exact: true })).toBeVisible() + await expect(sidebar.getByRole('link', { name: 'My Skills', exact: true })).toBeVisible() + await expect(sidebar.getByRole('link', { name: 'API Tokens', exact: true })).toBeVisible() + await expect(page.getByText('View and manage all your published skills')).toBeVisible() }) }) diff --git a/web/e2e/landing-navigation.spec.ts b/web/e2e/landing-navigation.spec.ts index f3368b29..97b10b8c 100644 --- a/web/e2e/landing-navigation.spec.ts +++ b/web/e2e/landing-navigation.spec.ts @@ -9,9 +9,9 @@ test.describe('Landing Navigation (Real API)', () => { test('submits the hero search to the search page', async ({ page }) => { await page.goto('/') - await expect(page.getByRole('heading', { name: 'Discover & Share AI Skills' })).toBeVisible() + await expect(page.getByRole('heading', { name: '把团队的专业能力,沉淀成 Agent 可用的技能' })).toBeVisible() - const searchInput = page.getByPlaceholder('Search skills...') + const searchInput = page.getByPlaceholder('搜索技能...') await searchInput.fill('agent ops') await searchInput.press('Enter') diff --git a/web/e2e/landing-quick-start-cli.spec.ts b/web/e2e/landing-quick-start-cli.spec.ts index 624fd3c6..7a62f7a9 100644 --- a/web/e2e/landing-quick-start-cli.spec.ts +++ b/web/e2e/landing-quick-start-cli.spec.ts @@ -1,80 +1,69 @@ import { expect, test } from '@playwright/test' import { setEnglishLocale } from './helpers/auth-fixtures' -test.describe('Landing Quick Start CLI Tab (Real API)', () => { +test.describe('Landing access methods (Real API)', () => { test.beforeEach(async ({ page }) => { await setEnglishLocale(page) }) - test('renders three peer tabs and exposes the CLI install command', async ({ page }) => { + test('renders three access methods and exposes current CLI commands', async ({ page }) => { await page.goto('/') - const agentTab = page.getByRole('button', { name: 'I am Agent', exact: true }) - const humanTab = page.getByRole('button', { name: 'I am Human', exact: true }) - const cliTab = page.getByRole('button', { name: 'CLI', exact: true }) + const agentMode = page.getByRole('button', { name: /Agent 自动接入/ }) + const cliMode = page.getByRole('button', { name: /CLI 命令行/ }) + const webMode = page.getByRole('button', { name: /Web 界面/ }) - await expect(agentTab).toBeVisible() - await expect(humanTab).toBeVisible() - await expect(cliTab).toBeVisible() + await expect(agentMode).toBeVisible() + await expect(cliMode).toBeVisible() + await expect(webMode).toBeVisible() + await expect(agentMode).toHaveAttribute('aria-pressed', 'true') - await expect(agentTab).toHaveAttribute('aria-pressed', 'true') + await cliMode.click() + await expect(cliMode).toHaveAttribute('aria-pressed', 'true') + await expect(agentMode).toHaveAttribute('aria-pressed', 'false') + await expect(webMode).toHaveAttribute('aria-pressed', 'false') - await cliTab.click() - await expect(cliTab).toHaveAttribute('aria-pressed', 'true') - await expect(agentTab).toHaveAttribute('aria-pressed', 'false') - await expect(humanTab).toHaveAttribute('aria-pressed', 'false') - - await expect( - page.getByText('Install the SkillHub CLI locally to run skillhub install for skills.'), - ).toBeVisible() - await expect(page.getByText('npm i -g @astron-team/skillhub', { exact: true })).toBeVisible() + await expect(page.getByText('npx -y @astron-team/skillhub@0.1.11 --version', { exact: true })).toBeVisible() + await expect(page.getByText(/npx -y @astron-team\/skillhub@0\.1\.11 search weather/)).toBeVisible() + await expect(page.getByText(/npx -y @astron-team\/skillhub@0\.1\.11 install @global\/weather/)).toBeVisible() + await expect(page.getByRole('link', { name: 'CLI 文档' })).toHaveAttribute( + 'href', + 'https://github.com/iflytek/skillhub/tree/main/cli', + ) }) - test('agent and human tabs expose the current SkillHub guidance', async ({ page }) => { + test('agent views expose Registry configuration and implicit discovery', async ({ page }) => { await page.goto('/') - const agentTab = page.getByRole('button', { name: 'I am Agent', exact: true }) - const humanTab = page.getByRole('button', { name: 'I am Human', exact: true }) + const registryTab = page.getByRole('tab', { name: 'Registry 配置' }) + const discoveryTab = page.getByRole('tab', { name: '隐式发现' }) + + await expect(registryTab).toHaveAttribute('aria-selected', 'true') + await expect(page.getByText(/registry\/skill\.md/).first()).toBeVisible() + + await discoveryTab.click() + await expect(discoveryTab).toHaveAttribute('aria-selected', 'true') + await expect(page.getByText('检索 SkillHub Registry')).toBeVisible() + await expect(page.getByText('匹配 @global/weather · v1.3.0')).toBeVisible() - await expect( - page.getByText( - 'Connect SkillHub using http://127.0.0.1:3000/install/skillhub.md', - { exact: true }, - ), - ).toBeVisible() const guideResponse = await page.request.get('/install/skillhub.md') expect(guideResponse.status()).toBe(200) const guide = await guideResponse.text() expect(guide).toContain('http://127.0.0.1:3000') expect(guideResponse.headers()['cache-control']).toContain('no-cache') + const legacyGuideResponse = await page.request.get('/registry/skill.md') expect(legacyGuideResponse.status()).toBe(200) expect(await legacyGuideResponse.text()).toBe(guide) + const hostileHostResponse = await page.request.get('/install/skillhub.md', { headers: { Host: 'attacker.example' }, }) expect(hostileHostResponse.status()).toBe(403) + const extensionHostResponse = await page.request.get('/install/skillhub.md', { headers: { Host: 'chrome-extension:evil;echo_injected' }, }) expect(extensionHostResponse.status()).toBe(400) - - await humanTab.click() - await expect(humanTab).toHaveAttribute('aria-pressed', 'true') - await expect( - page.getByText( - 'npx @astron-team/skillhub@latest search --registry http://127.0.0.1:3000', - { exact: true }, - ), - ).toBeVisible() - - await agentTab.click() - await expect(agentTab).toHaveAttribute('aria-pressed', 'true') - await expect( - page.getByText( - 'Connect SkillHub using http://127.0.0.1:3000/install/skillhub.md', - { exact: true }, - ), - ).toBeVisible() }) }) diff --git a/web/e2e/route-guard.spec.ts b/web/e2e/route-guard.spec.ts index 0a6d7254..d99e6566 100644 --- a/web/e2e/route-guard.spec.ts +++ b/web/e2e/route-guard.spec.ts @@ -20,6 +20,7 @@ test.describe('Route Guards (Real API)', () => { await page.goto('/dashboard') await expect(page).toHaveURL('/dashboard') - await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible() + await expect(page.getByRole('link', { name: 'My Skills', exact: true })).toBeVisible() + await expect(page.getByRole('link', { name: 'Profile', exact: true })).toBeVisible() }) }) diff --git a/web/e2e/settings-pages.spec.ts b/web/e2e/settings-pages.spec.ts index f41f808b..f3a28dde 100644 --- a/web/e2e/settings-pages.spec.ts +++ b/web/e2e/settings-pages.spec.ts @@ -15,11 +15,11 @@ test.describe('Settings Pages (Real API)', () => { await expect(page.getByRole('heading', { name: 'Profile Settings' })).toBeVisible() }) - test('navigates to reset-password page from profile settings', async ({ page }) => { + test('navigates to security settings from profile settings', async ({ page }) => { await page.goto('/settings/profile') await page.getByRole('button', { name: 'Reset Password' }).click() - await expect(page).toHaveURL('/reset-password') - await expect(page.getByRole('heading', { name: 'Reset Password' })).toBeVisible() + await expect(page).toHaveURL('/settings/security') + await expect(page.getByRole('heading', { name: 'Security Settings' })).toBeVisible() }) test('shows validation when current password is missing', async ({ page }) => { diff --git a/web/e2e/theme-toggle.spec.ts b/web/e2e/theme-toggle.spec.ts index 84628c7f..6e2271a9 100644 --- a/web/e2e/theme-toggle.spec.ts +++ b/web/e2e/theme-toggle.spec.ts @@ -168,7 +168,7 @@ test.describe('Light and dark theme', () => { await page.reload() await expect(page.locator('html')).toHaveClass(/dark/) await expect(page.getByRole('switch', { name: 'Dark theme' })).toHaveAttribute('aria-checked', 'true') - await expect(page.getByRole('heading', { name: 'SkillHub', exact: true })).toBeVisible() + await expect(page.getByRole('heading', { name: '把团队的专业能力,沉淀成 Agent 可用的技能' })).toBeVisible() await expect.poll(() => page.evaluate(() => ( window as Window & { __themeAtFirstReactContent?: boolean } ).__themeAtFirstReactContent)).toBe(true) diff --git a/web/e2e/user-id-display.spec.ts b/web/e2e/user-id-display.spec.ts index 0f3f0485..58781eda 100644 --- a/web/e2e/user-id-display.spec.ts +++ b/web/e2e/user-id-display.spec.ts @@ -8,18 +8,12 @@ test.describe('User ID Display', () => { await registerSession(page, testInfo) }) - test('shows user ID in dashboard account card', async ({ page }) => { + test('shows the signed-in account identity in the dashboard sidebar', async ({ page }) => { await page.goto('/dashboard') - await expect(page.getByText('Account Information')).toBeVisible() - const userIdText = page.getByText('User ID', { exact: false }) - await expect(userIdText).toBeVisible() - - // The dashboard renders "User ID: " in a single element. - // Verify the value is not empty by checking the text content is longer than just the label. - const content = await userIdText.textContent() - const valueAfterLabel = content?.replace(/^.*User ID[:\s]*/i, '').trim() ?? '' - expect(valueAfterLabel.length).toBeGreaterThan(0) + const accountSummary = page.locator('aside').locator('.mb-4').first() + await expect(accountSummary).toBeVisible() + await expect(accountSummary).not.toHaveText(/^\s*$/) }) test('shows user ID on profile settings page', async ({ page }) => { diff --git a/web/package.json b/web/package.json index 8ca3ed7a..687aab6f 100644 --- a/web/package.json +++ b/web/package.json @@ -1,7 +1,7 @@ { "name": "skillhub-web", "private": true, - "version": "0.1.14", + "version": "0.1.15", "type": "module", "packageManager": "pnpm@10.33.0", "pnpm": { diff --git a/web/src/features/notification/notification-preference-form.tsx b/web/src/features/notification/notification-preference-form.tsx index 79818587..84a8e18c 100644 --- a/web/src/features/notification/notification-preference-form.tsx +++ b/web/src/features/notification/notification-preference-form.tsx @@ -50,7 +50,7 @@ export function NotificationPreferenceForm() { return ( - {t('notification.preferences.title')} + {t('notification.preferences.channelsTitle')} {t('notification.preferences.description')} diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 78887441..d4cbccef 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -820,7 +820,8 @@ "partiallyAppliedTitle": "Partially Applied", "partiallyAppliedDescription": "Some changes were applied immediately. Others are pending admin review.", "noChanges": "No changes to save.", - "userId": "User ID" + "userId": "User ID", + "detailsTitle": "Profile Details" }, "security": { "title": "Security Settings", @@ -837,7 +838,8 @@ "unavailableTitle": "Password changes are unavailable for this account.", "unavailableDescription": "This account signs in through an external identity provider or has no local password credential.", "submitting": "Submitting...", - "submit": "Update Password" + "submit": "Update Password", + "passwordTitle": "Password" }, "accounts": { "initiateTitle": "Initiate Account Merge", @@ -1702,7 +1704,8 @@ "promotion": "Promotion Notifications", "promotionDesc": "Notify on promotion request, approval, or rejection", "report": "Report Notifications", - "reportDesc": "Notify on report submission or resolution" + "reportDesc": "Notify on report submission or resolution", + "channelsTitle": "Notification Channels" } }, "adminNamespaces": { diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index 8780c618..cd699476 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -885,7 +885,8 @@ "partiallyAppliedTitle": "Применено частично", "partiallyAppliedDescription": "Часть изменений применена сразу. Остальные ожидают проверки администратором.", "noChanges": "Нет изменений для сохранения.", - "userId": "ID пользователя" + "userId": "ID пользователя", + "detailsTitle": "Данные профиля" }, "security": { "title": "Настройки безопасности", @@ -902,7 +903,8 @@ "unavailableTitle": "Смена пароля недоступна для этой учётной записи.", "unavailableDescription": "Эта учётная запись входит через внешнего поставщика идентификации или не имеет локальных учётных данных пароля.", "submitting": "Отправка...", - "submit": "Обновить пароль" + "submit": "Обновить пароль", + "passwordTitle": "Пароль" }, "accounts": { "initiateTitle": "Начать объединение учётных записей", @@ -1477,7 +1479,8 @@ "promotion": "Уведомления о продвижении", "promotionDesc": "Уведомлять о заявке, одобрении или отклонении продвижения", "report": "Уведомления о жалобах", - "reportDesc": "Уведомлять о подаче или разрешении жалобы" + "reportDesc": "Уведомлять о подаче или разрешении жалобы", + "channelsTitle": "Каналы уведомлений" } }, "nsReviews": { diff --git a/web/src/i18n/locales/zh.json b/web/src/i18n/locales/zh.json index 8fed79c6..6f335238 100644 --- a/web/src/i18n/locales/zh.json +++ b/web/src/i18n/locales/zh.json @@ -336,7 +336,7 @@ "viewSubscriptions": "查看我的订阅", "mySkillsTitle": "我的技能", "openMySkills": "查看我的技能", - "mySkillsPreviewDescription": "展示最近的 5 个技能,可进入详情或前往\u201C我的技能\u201D查看全部。", + "mySkillsPreviewDescription": "展示最近的 5 个技能,可进入详情或前往“我的技能”查看全部。", "mySkillsPreviewEmpty": "你还没有发布任何技能", "credentials": "访问凭证", "openTokens": "查看 API Tokens", @@ -820,7 +820,8 @@ "partiallyAppliedTitle": "部分已生效", "partiallyAppliedDescription": "部分修改已立即生效,其余修改正在等待管理员审核。", "noChanges": "没有需要保存的修改。", - "userId": "用户 ID" + "userId": "用户 ID", + "detailsTitle": "个人资料" }, "security": { "title": "安全设置", @@ -837,7 +838,8 @@ "unavailableTitle": "此账号暂不可修改密码。", "unavailableDescription": "此账号通过外部身份提供方登录,或尚未配置本地密码凭据。", "submitting": "提交中...", - "submit": "更新密码" + "submit": "更新密码", + "passwordTitle": "密码" }, "accounts": { "initiateTitle": "发起账号合并", @@ -1701,7 +1703,8 @@ "promotion": "提升通知", "promotionDesc": "提升申请提交、通过或拒绝时通知", "report": "举报通知", - "reportDesc": "举报提交或处理完成时通知" + "reportDesc": "举报提交或处理完成时通知", + "channelsTitle": "通知类型" } }, "adminNamespaces": { diff --git a/web/src/pages/settings/profile.tsx b/web/src/pages/settings/profile.tsx index 166282c8..01e0c107 100644 --- a/web/src/pages/settings/profile.tsx +++ b/web/src/pages/settings/profile.tsx @@ -181,7 +181,7 @@ export function ProfileSettingsPage() {
- {t('profile.title')} + {t('profile.detailsTitle')} {t('profile.subtitle')}
{!isEditing ? ( diff --git a/web/src/pages/settings/security.tsx b/web/src/pages/settings/security.tsx index 7a8248c6..43a87036 100644 --- a/web/src/pages/settings/security.tsx +++ b/web/src/pages/settings/security.tsx @@ -92,7 +92,7 @@ export function SecuritySettingsPage() { - {t('security.title')} + {t('security.passwordTitle')} {t('security.subtitle')}