105 lines
3.3 KiB
JavaScript
105 lines
3.3 KiB
JavaScript
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||
import * as fc from 'fast-check'
|
||
import { TOKEN_KEY } from '../utils/storage.js'
|
||
|
||
// 模拟uni全局对象和请求
|
||
let mockStorage = {}
|
||
let requestSuccessCallback = null
|
||
|
||
beforeEach(() => {
|
||
mockStorage = {}
|
||
requestSuccessCallback = null
|
||
|
||
globalThis.uni = {
|
||
setStorageSync: (key, value) => { mockStorage[key] = value },
|
||
getStorageSync: (key) => mockStorage[key] || '',
|
||
removeStorageSync: (key) => { delete mockStorage[key] },
|
||
showToast: vi.fn(),
|
||
reLaunch: vi.fn(),
|
||
request: (options) => {
|
||
requestSuccessCallback = options.success
|
||
}
|
||
}
|
||
})
|
||
|
||
/**
|
||
* 动态导入模块
|
||
*/
|
||
async function loadModules() {
|
||
vi.resetModules()
|
||
const { createPinia, setActivePinia } = await import('pinia')
|
||
setActivePinia(createPinia())
|
||
const { useUserStore } = await import('../stores/user.js')
|
||
return { useUserStore }
|
||
}
|
||
|
||
describe('Property 4: 未勾选协议阻止登录', () => {
|
||
/**
|
||
* **Feature: mobile-app, Property 4: 未勾选协议阻止登录**
|
||
* **Validates: Requirements 2.3**
|
||
*
|
||
* 对于任意手机号和验证码输入,当协议勾选状态为false时,
|
||
* 调用登录操作应被阻止且不发出API请求。
|
||
*/
|
||
it('协议未勾选时登录应被阻止,不发出API请求', async () => {
|
||
const phoneArb = fc.stringMatching(/^1[3-9]\d{9}$/)
|
||
const codeArb = fc.stringMatching(/^\d{4,6}$/)
|
||
const areaCodeArb = fc.constantFrom('+86', '+852', '+886', '+1')
|
||
|
||
await fc.assert(
|
||
fc.asyncProperty(phoneArb, codeArb, areaCodeArb, async (phone, code, areaCode) => {
|
||
const { useUserStore } = await loadModules()
|
||
const store = useUserStore()
|
||
|
||
// agreedToTerms = false
|
||
await expect(store.login(phone, code, areaCode, false)).rejects.toThrow('未同意协议')
|
||
|
||
// 不应发出API请求
|
||
expect(requestSuccessCallback).toBeNull()
|
||
// 应弹出提示
|
||
expect(uni.showToast).toHaveBeenCalledWith({ title: '请阅读并同意协议', icon: 'none' })
|
||
}),
|
||
{ numRuns: 100 }
|
||
)
|
||
})
|
||
})
|
||
|
||
|
||
describe('Property 5: 登录Token存储', () => {
|
||
/**
|
||
* **Feature: mobile-app, Property 5: 登录Token存储**
|
||
* **Validates: Requirements 2.5**
|
||
*
|
||
* 对于任意成功的登录API响应(包含token字段),
|
||
* 登录操作完成后,本地存储中应包含该token值,且用户状态应为已登录。
|
||
*/
|
||
it('登录成功后Token应存储到本地且状态为已登录', async () => {
|
||
const tokenArb = fc.string({ minLength: 10, maxLength: 200 }).filter(s => s.trim().length > 0)
|
||
|
||
await fc.assert(
|
||
fc.asyncProperty(tokenArb, async (token) => {
|
||
const { useUserStore } = await loadModules()
|
||
const store = useUserStore()
|
||
|
||
// 发起登录请求(agreedToTerms = true)
|
||
const loginPromise = store.login('13800138000', '123456', '+86', true)
|
||
|
||
// 模拟成功响应
|
||
requestSuccessCallback({
|
||
statusCode: 200,
|
||
data: { success: true, data: { token }, message: 'ok' }
|
||
})
|
||
|
||
await loginPromise
|
||
|
||
// 验证Token已存储到本地
|
||
expect(mockStorage[TOKEN_KEY]).toBe(token)
|
||
// 验证用户状态为已登录
|
||
expect(store.isLoggedIn).toBe(true)
|
||
expect(store.token).toBe(token)
|
||
}),
|
||
{ numRuns: 100 }
|
||
)
|
||
})
|
||
})
|