test(desktop): define browser access and welcome flow

This commit is contained in:
t4we fan
2026-08-26 14:18:48 +08:00
parent ec0b0e5ebb
commit b59c4022cd
2 changed files with 90 additions and 5 deletions
@@ -60,6 +60,33 @@ describe('Desktop Setup Wizard copy and contract', () => {
expect(english.skipDialogBody).toContain('Desktop settings')
})
it('introduces first-time setup for the current Profile before showing settings', () => {
const english = desktopSetupWizardCopy('en')
const chinese = desktopSetupWizardCopy('zh')
expect(chinese.welcomeTitle).toBeTruthy()
expect(chinese.welcomeBody).toContain('Profile')
expect(chinese.firstProfileSetup).toContain('第一次')
expect(chinese.firstProfileSetup).toContain('桌面模式')
expect(chinese.startSetup).toBe('开始设置')
expect(english.welcomeTitle).toBeTruthy()
expect(english.welcomeBody).toContain('Profile')
expect(english.firstProfileSetup).toMatch(/first(?:-time| time)/iu)
expect(english.firstProfileSetup).toMatch(/Desktop mode/iu)
expect(english.startSetup).toBe('Start setup')
})
it('treats browser opening as permission, not an automatic startup action', () => {
const english = desktopSetupWizardCopy('en')
const chinese = desktopSetupWizardCopy('zh')
expect(chinese.openBrowser).toBe('允许在浏览器中打开')
expect(chinese.openBrowser).not.toMatch(/启动后|自动/u)
expect(chinese.browserCompatibilityNotice).toContain('兼容模式')
expect(chinese.browserCompatibilityNotice).toMatch(/只(?:能|支持|可)/u)
expect(english.openBrowser).toMatch(/allow.+(?:open|opening).+browser/iu)
expect(english.openBrowser).not.toMatch(/after startup|automatically/iu)
expect(english.browserCompatibilityNotice).toMatch(/only.+compatibility mode/iu)
})
it('requires confirmation only when loopback access is changed to LAN', () => {
expect(desktopSetupWizardRequiresLanConfirmation('loopback', 'lan')).toBe(true)
expect(desktopSetupWizardRequiresLanConfirmation('lan', 'loopback')).toBe(false)
@@ -15,6 +15,7 @@ import {
SetupWizardNavigation,
SetupWizardStepPage,
SetupWizardSuccess,
SetupWizardWelcome,
} from '../src/native-ui/setup-wizard/App.tsx'
import { Button } from '../src/native-ui/components/ui/button.tsx'
import { DialogClose } from '../src/native-ui/components/ui/dialog.tsx'
@@ -51,7 +52,7 @@ const selection: DesktopSetupWizardSelection = {
const copy = desktopSetupWizardCopy('zh')
function renderStep(step: Exclude<(typeof DESKTOP_SETUP_WIZARD_STEPS)[number], 'success'>): string {
function renderStep(step: Exclude<(typeof DESKTOP_SETUP_WIZARD_STEPS)[number], 'welcome' | 'success'>): string {
return renderToStaticMarkup(createElement(SetupWizardStepPage, {
copy,
input,
@@ -85,8 +86,9 @@ function elementTree(node: ReactNode): readonly ReactElement[] {
afterEach(() => { vi.unstubAllGlobals() })
describe('Setup Wizard step flow', () => {
it('keeps one setting per page and browser access as the final setting page', () => {
it('starts with an introduction and keeps browser access as the final setting page', () => {
expect(DESKTOP_SETUP_WIZARD_STEPS).toEqual([
'welcome',
'mode',
'material',
'market',
@@ -99,6 +101,7 @@ describe('Setup Wizard step flow', () => {
it('moves only between adjacent pages and stops at both boundaries', () => {
expect(DESKTOP_SETUP_WIZARD_STEPS.map(step => previousDesktopSetupWizardStep(step))).toEqual([
undefined,
'welcome',
'mode',
'material',
'market',
@@ -106,6 +109,7 @@ describe('Setup Wizard step flow', () => {
'browser',
])
expect(DESKTOP_SETUP_WIZARD_STEPS.map(step => nextDesktopSetupWizardStep(step))).toEqual([
'mode',
'material',
'market',
'notifications',
@@ -116,6 +120,48 @@ describe('Setup Wizard step flow', () => {
})
})
describe('Setup Wizard welcome page', () => {
it('identifies the Profile and explains why first-run Desktop setup is shown', () => {
const markup = renderToStaticMarkup(createElement(SetupWizardWelcome, {
copy,
onSkip: () => {},
onStart: () => {},
profileName: input.profileName,
}))
expect(markup).toContain('data-setup-step="welcome"')
expect(markup).toContain(copy.welcomeTitle)
expect(markup).toContain(copy.welcomeBody)
expect(markup).toContain(copy.firstProfileSetup)
expect(markup).toContain(copy.profile)
expect(markup).toContain(input.profileName)
})
it('offers Start setup and confirmed Skip without ordinary arrow navigation', () => {
const markup = renderToStaticMarkup(createElement(SetupWizardWelcome, {
copy,
onSkip: () => {},
onStart: () => {},
profileName: input.profileName,
}))
const navigation = renderToStaticMarkup(createElement(SetupWizardNavigation, {
copy,
onBack: () => {},
onNext: () => {},
onSkip: () => {},
step: 'welcome',
}))
expect(markup).toContain(copy.startSetup)
expect(markup).toContain(copy.skip)
expect(markup).toContain('data-slot="dialog-trigger"')
expect(markup).toContain('aria-haspopup="dialog"')
expect(markup).not.toContain(`aria-label="${copy.back}"`)
expect(markup).not.toContain(`aria-label="${copy.next}"`)
expect(markup).not.toContain('lucide-arrow-left')
expect(markup).not.toContain('lucide-arrow-right')
expect(navigation).toBe('')
})
})
describe('Setup Wizard setting pages', () => {
it.each([
['mode', 'presentationTitle', 'presentationBody'],
@@ -162,6 +208,17 @@ describe('Setup Wizard setting pages', () => {
expect(browser).not.toContain(copy.dshMarket)
})
it('describes browser access as an opt-in capability limited to compatibility mode', () => {
const browser = renderStep('browser')
expect(browser).toContain(copy.openBrowser)
expect(browser).toContain(copy.browserCompatibilityNotice)
expect(copy.openBrowser).toBe('允许在浏览器中打开')
expect(copy.openBrowser).not.toContain('启动后')
expect(copy.openBrowser).not.toContain('自动')
expect(copy.browserCompatibilityNotice).toContain('兼容模式')
expect(copy.browserCompatibilityNotice).toMatch(/只(?:能|支持|可)/u)
})
it('uses the shadcn Switch component for every wizard toggle', () => {
const notifications = renderStep('notifications')
const browser = renderStep('browser')
@@ -174,7 +231,7 @@ describe('Setup Wizard setting pages', () => {
describe('Setup Wizard navigation and completion', () => {
it('shows Skip on the left and back/forward arrow buttons on every setting page', () => {
for (const step of DESKTOP_SETUP_WIZARD_STEPS.slice(0, -1)) {
for (const step of DESKTOP_SETUP_WIZARD_STEPS.slice(1, -1)) {
const markup = renderToStaticMarkup(createElement(SetupWizardNavigation, {
copy,
onBack: () => {},
@@ -191,7 +248,7 @@ describe('Setup Wizard navigation and completion', () => {
}
})
it('keeps the first-page back button visible but disabled', () => {
it('lets the first setting page return to the welcome page', () => {
const markup = renderToStaticMarkup(createElement(SetupWizardNavigation, {
copy,
onBack: () => {},
@@ -199,7 +256,8 @@ describe('Setup Wizard navigation and completion', () => {
onSkip: () => {},
step: 'mode',
}))
expect(markup).toMatch(new RegExp(`<button[^>]+aria-label="${copy.back}"[^>]+disabled`, 'u'))
expect(markup).toContain(`aria-label="${copy.back}"`)
expect(markup).not.toMatch(new RegExp(`<button[^>]+aria-label="${copy.back}"[^>]+disabled`, 'u'))
})
it('uses a dialog trigger for Skip and explains where setup remains available', () => {