fix(web): 登录表单补充隐藏用户名字段以兼容 Safari 密码管理器
Safari/WebKit 在明文 HTTP 下不保存 Secure Cookie(无 localhost 豁免, Chrome/Firefox 有),__Host- 会话被静默丢弃导致登录成功后仍弹回登录页; 本机调试需以 --dev 启动(pn_session 无 Secure,loopback-only 守卫,§7.3-3)。 纯密码表单另令 Safari 保存密码弹窗要求手动输入用户名:补充 sr-only 用户名 输入(autoComplete=username,只读预填 admin),弹窗不再阻断登录。 新增 login.test.tsx 回归测试锁定用户名/密码字段的自动填充标记。
This commit is contained in:
@@ -49,6 +49,18 @@ export default function Login() {
|
|||||||
<h1 className="pn-login-title">管理员登录</h1>
|
<h1 className="pn-login-title">管理员登录</h1>
|
||||||
<p className="pn-login-sub">请输入管理密码以继续</p>
|
<p className="pn-login-sub">请输入管理密码以继续</p>
|
||||||
|
|
||||||
|
<label htmlFor="pn-login-username" className="sr-only">用户名</label>
|
||||||
|
<input
|
||||||
|
id="pn-login-username"
|
||||||
|
className="sr-only"
|
||||||
|
type="text"
|
||||||
|
name="username"
|
||||||
|
autoComplete="username"
|
||||||
|
value="admin"
|
||||||
|
readOnly
|
||||||
|
tabIndex={-1}
|
||||||
|
/>
|
||||||
|
|
||||||
<label htmlFor="pn-login-password" className="sr-only">管理密码</label>
|
<label htmlFor="pn-login-password" className="sr-only">管理密码</label>
|
||||||
<div className="pn-login-field">
|
<div className="pn-login-field">
|
||||||
<Lock size={16} className="pn-login-icon" aria-hidden />
|
<Lock size={16} className="pn-login-icon" aria-hidden />
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import { MemoryRouter } from 'react-router'
|
||||||
|
import Login from '../src/pages/Login'
|
||||||
|
|
||||||
|
vi.mock('../src/lib/auth', () => ({
|
||||||
|
useAuth: () => ({ login: vi.fn(), authenticated: false }),
|
||||||
|
}))
|
||||||
|
vi.mock('../src/features/site', () => ({
|
||||||
|
useDocumentTitle: () => {},
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe('Login 表单密码管理器适配', () => {
|
||||||
|
it('暴露隐藏用户名字段与 current-password 自动填充标记', () => {
|
||||||
|
render(
|
||||||
|
<MemoryRouter>
|
||||||
|
<Login />
|
||||||
|
</MemoryRouter>,
|
||||||
|
)
|
||||||
|
|
||||||
|
const username = screen.getByLabelText('用户名') as HTMLInputElement
|
||||||
|
expect(username.type).toBe('text')
|
||||||
|
expect(username.value).toBe('admin')
|
||||||
|
expect(username.readOnly).toBe(true)
|
||||||
|
expect(username.getAttribute('autocomplete')).toBe('username')
|
||||||
|
|
||||||
|
const password = screen.getByLabelText('管理密码') as HTMLInputElement
|
||||||
|
expect(password.getAttribute('autocomplete')).toBe('current-password')
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user