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 authShellSource = readFileSync(new URL('../components/AuthShell.vue', import.meta.url), 'utf8') const legalSource = readFileSync(new URL('./LegalDocumentView.vue', import.meta.url), 'utf8') const legalDocumentsSource = readFileSync(new URL('../legalDocuments.ts', import.meta.url), 'utf8') const authRoutesSource = readFileSync(new URL('../routes.ts', import.meta.url), 'utf8') const authStoreSource = readFileSync(new URL('../store/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') const appSource = readFileSync(new URL('../../../App.vue', import.meta.url), 'utf8') const routerSource = readFileSync(new URL('../../../router/index.ts', import.meta.url), 'utf8') const stylesSource = readFileSync(new URL('../../../styles/index.css', import.meta.url), 'utf8') test('uses one prototype-inspired responsive shell for login and registration', () => { assert.match(loginSource, / { for (const section of ['账号信息', '企业信息', '联系信息']) assert.match(registerSource, new RegExp(section)) assert.doesNotMatch(loginSource + registerSource, /微信登录|企业微信登录|短信验证码|邮箱验证码|lang-switcher/) }) test('matches the frozen username-or-mobile login contract', () => { assert.match(loginSource, /label="用户名或手机号"/) assert.match(loginSource, /const usernamePattern = \/\^\[A-Za-z\]/) assert.match(loginSource, /const mainlandMobilePattern = \/\^1\[3-9\]\\d\{9\}\$\//) assert.match(authApiSource, /data: \{ loginId, password \}/) assert.doesNotMatch(authApiSource, /data: \{ username, password \}/) assert.match(loginSource, /typeof route\.query\.account === 'string'/) assert.doesNotMatch(loginSource, /developmentHint|demo-tip|merchant_admin|联调账号|本地后端/) assert.match(loginSource, /form\.remember/) assert.match(loginSource, /忘记密码/) }) test('persists rotating tokens independently and only remembers the login id by choice', () => { assert.match(sessionSource, /ACCESS_TOKEN_KEY/) assert.match(sessionSource, /REFRESH_TOKEN_KEY/) assert.match(sessionSource, /localStorage\.setItem\(ACCESS_TOKEN_KEY, tokens\.accessToken\)/) assert.match(sessionSource, /localStorage\.setItem\(REFRESH_TOKEN_KEY, tokens\.refreshToken\)/) assert.match(sessionSource, /persistRememberedLoginId/) assert.match(loginSource, /getRememberedLoginId/) assert.match(loginSource, /记住账号/) assert.match(sessionSource, /clearSessionTokens/) }) test('implements every backend registration field and real submission', () => { for (const field of ['username', 'companyName', 'displayName', 'legalPerson', 'phone', 'contactEmail', 'countryCode', 'address', 'password', 'agreement']) { assert.match(registerSource, new RegExp(`prop="${field}"`)) } assert.match(registerSource, /label="登录手机号" prop="phone"/) assert.doesNotMatch(registerSource, /legalPhone/) assert.match(authApiSource, /phone: string/) assert.doesNotMatch(authApiSource, /legalPhone/) assert.match(registerSource, /注册时同时初始化公司联系电话;注册后两者可分别修改,互不联动/) assert.match(registerSource, /registerMerchant\(/) assert.match(registerSource, /4–64 位/) assert.match(registerSource, /8–72 位/) assert.match(registerSource, /label="法定代表人(选填)" prop="legalPerson"/) assert.doesNotMatch(registerSource, /label="法定代表人[^\"]*" prop="legalPerson" required/) assert.doesNotMatch(registerSource, /required: true, message: '请输入法定代表人'/) assert.match(authApiSource, /legalPerson\?: string/) }) test('shows an explicit registration entry directly below the login button', () => { assert.match(loginSource, /]*class="auth-submit"[\s\S]*?