import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' const loginSource = readFileSync(new URL('./LoginView.vue', import.meta.url), 'utf8') const registerSource = readFileSync(new URL('./RegisterView.vue', import.meta.url), 'utf8') const authStoreSource = readFileSync(new URL('../stores/auth.ts', import.meta.url), 'utf8') const httpSource = readFileSync(new URL('../api/http.ts', import.meta.url), 'utf8') const sessionSource = readFileSync(new URL('../api/session.ts', import.meta.url), 'utf8') const authApiSource = readFileSync(new URL('../api/auth.ts', import.meta.url), 'utf8') const mainSource = readFileSync(new URL('../main.ts', import.meta.url), 'utf8') test('matches the backend username login contract', () => { assert.match(loginSource, /label="用户名"/) assert.match(loginSource, /\^\[A-Za-z0-9_\]/) assert.match(loginSource, /const isDevelopment = import\.meta\.env\.DEV/) assert.match(loginSource, /v-if="developmentHint" class="demo-tip"/) assert.match(loginSource, /merchant_admin/) assert.match(loginSource, /form\.remember/) assert.match(loginSource, /忘记密码/) }) test('persists both rotating tokens according to the remember choice', () => { assert.match(sessionSource, /ACCESS_TOKEN_KEY/) assert.match(sessionSource, /REFRESH_TOKEN_KEY/) assert.match(sessionSource, /remember \? localStorage : sessionStorage/) assert.match(sessionSource, /clearSessionTokens/) }) test('implements every backend registration field and real submission', () => { for (const field of ['username', 'companyName', 'displayName', 'legalPerson', 'legalPhone', 'contactEmail', 'countryCode', 'address', 'password', 'agreement']) { assert.match(registerSource, new RegExp(`prop="${field}"`)) } assert.match(registerSource, /registerMerchant\(/) assert.match(registerSource, /4–64 位/) assert.match(registerSource, /8–72 位/) }) test('shows immediate login after registration and separates company review from account activation', () => { assert.match(registerSource, /企业账号注册成功/) assert.match(registerSource, /无需等待注册审核/) assert.match(registerSource, /提交订单前还需完成一步/) assert.match(registerSource, /立即登录/) }) test('uses real auth endpoints and single-flight refresh handling', () => { assert.match(authApiSource, /\/auth\/login/) assert.match(authApiSource, /\/auth\/register/) assert.match(authApiSource, /\/auth\/logout/) assert.match(httpSource, /refreshPromise/) assert.match(httpSource, /\/auth\/refresh/) assert.match(httpSource, /envelope\.code !== '0'/) assert.match(authStoreSource, /decodeMerchantToken/) assert.doesNotMatch(authStoreSource, /mock-client:/) }) test('keeps a valid local session during temporary context and refresh outages', () => { assert.match(authStoreSource, /function isExpiredSessionError/) assert.match(authStoreSource, /if \(isExpiredSessionError\(error\)\)/) assert.match(authStoreSource, /sessionRestored\.value = true\s+return/) assert.match(httpSource, /if \(mappedError\.status === 401 \|\| mappedError\.code === 'A0401'\)/) assert.doesNotMatch(httpSource, /catch \(refreshError\) \{\s+clearSessionTokens\(\)/) }) test('loads styles for programmatic Element Plus messages and dialogs', () => { assert.match(mainSource, /element-plus\/es\/components\/message\/style\/css/) assert.match(mainSource, /element-plus\/es\/components\/message-box\/style\/css/) })