feat: redesign settings as paper ledger
This commit is contained in:
@@ -115,6 +115,52 @@ describe('mobile navigation styles', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('approved Settings 01 paper ledger', () => {
|
||||
it('moves Settings identity into the body and suppresses the duplicate shell title', () => {
|
||||
expect(app).not.toContain("'settings-main':activeView==='settings'")
|
||||
expect(app).toContain(":class=\"{'settings-topbar':activeView==='settings'}\"")
|
||||
expect(app).toContain("v-if=\"!['today','tasks','habits','settings'].includes(activeView)\" class=\"topbar-title\"")
|
||||
expect(app).toContain('v-if="activeView===\'settings\'" class="icon topbar-refresh settings-refresh"')
|
||||
expect(mvpPanel).toContain('<header class="settings-heading"><h1>设置</h1><p>管理数据、账户与登录设备</p></header>')
|
||||
expect(mvpPanel).toContain('<h2>数据与恢复</h2><small>完整备份</small>')
|
||||
expect(mvpPanel).toContain("<h2>登录设备</h2><small>{{ sessionsState === 'success' ? `${sessions.length} 台` : '状态' }}</small>")
|
||||
expect(mvpPanel).toContain('<h2>活动记录</h2><small>最近操作</small>')
|
||||
})
|
||||
|
||||
it('uses the selected continuous-paper geometry on desktop and mobile', () => {
|
||||
expect(css).toContain('main:has(>.mvp-view .settings-sections){background:#fffdf8}')
|
||||
expect(css).toContain('.settings-sections{width:min(100%,900px);margin:0 auto;display:grid;gap:0;padding:34px 0 64px}')
|
||||
expect(css).toContain('.settings-heading h1{font-size:34px;line-height:1.1;font-weight:700;')
|
||||
expect(css).toContain('.settings-group{min-width:0;background:transparent;border:0;border-radius:0;box-shadow:none;overflow:visible;padding-top:25px}')
|
||||
expect(css).toContain('.settings-group>header{height:44px;padding:0;display:flex;align-items:center;border-bottom:1px solid #e8e0d5}')
|
||||
expect(css).toContain('.settings-row{min-height:62px;padding:6px 0;border-top:0;border-bottom:1px solid #e8e0d5;')
|
||||
expect(css).toContain('.settings-topbar{margin-bottom:0}')
|
||||
expect(css).toContain('.topbar>.settings-refresh{grid-area:filter}')
|
||||
expect(css).toContain('@media(max-width:720px){main:has(>.mvp-view .settings-sections){padding-left:29px;padding-right:29px;padding-bottom:calc(102px + env(safe-area-inset-bottom))}')
|
||||
expect(css).toContain('.settings-sections{width:100%;padding-top:23px;padding-bottom:0}')
|
||||
expect(css).toContain('.settings-heading h1{font-size:24px;line-height:1.1;font-weight:700}')
|
||||
expect(css).toContain('.settings-row{min-height:64px}')
|
||||
})
|
||||
|
||||
it('keeps controls and password errors aligned with the approved form contract', () => {
|
||||
expect(css).toContain('main:has(>.mvp-view .settings-sections) :is(.soft-button,.primary-small,.danger-button,.file-button){min-height:44px;border-radius:9px;border-color:#dcd2c4;background:#fffdf9}')
|
||||
expect(css).toContain('main:has(>.mvp-view .settings-sections) :is(.settings-data>.settings-row:first-of-type .soft-button,.backup-preflight .danger-button,.password-form .primary-small){background:#f15a29;border-color:#f15a29;color:#fff}')
|
||||
expect(css).toContain('main:has(>.mvp-view .settings-sections) .danger-text{min-height:44px;border:0;background:transparent;color:var(--danger)}')
|
||||
expect(css).toContain('.password-form.settings-form{width:100%;display:grid;grid-template-columns:repeat(3,minmax(0,1fr)) auto;gap:10px}')
|
||||
expect(css).toContain('.password-form.settings-form .inline-error{grid-column:1/-1;margin:0}')
|
||||
expect(mvpPanel).toContain("const sessionsState = ref<'loading' | 'success' | 'error'>('loading')")
|
||||
expect(mvpPanel).toContain("const auditState = ref<'loading' | 'success' | 'error'>('loading')")
|
||||
expect(mvpPanel).toContain('await Promise.allSettled([')
|
||||
expect(mvpPanel).toContain("v-if=\"sessionsState==='loading'\"")
|
||||
expect(mvpPanel).toContain("v-else-if=\"sessionsState==='error'\"")
|
||||
expect(mvpPanel).toContain("v-if=\"auditState==='loading'\"")
|
||||
expect(mvpPanel).toContain("v-else-if=\"auditState==='error'\"")
|
||||
expect(mvpPanel).not.toContain("'/sessions').catch(() => [])")
|
||||
expect(css).toContain('@media(max-width:720px){')
|
||||
expect(css).toContain('.password-form.settings-form{grid-template-columns:1fr}')
|
||||
})
|
||||
})
|
||||
|
||||
describe('settings sessions and audit activity', () => {
|
||||
it('renders readable session metadata, local time, and an accessible revoke action', () => {
|
||||
expect(mvpPanel).toContain('class="session-copy"')
|
||||
@@ -798,7 +844,7 @@ describe('task and habit row decoration', () => {
|
||||
expect(app).toContain(':disabled="refreshing || loading"')
|
||||
expect(app).toContain("else if (activeView.value === 'today') await Promise.all([refreshAll(), habitComposer.value?.refreshHabits()])")
|
||||
expect(app).toContain("if (activeView.value === 'habits') await habitComposer.value?.refreshHabits()")
|
||||
expect(mvpPanel).toContain('defineExpose({ openHabitComposer, refreshHabits: loadHabits })')
|
||||
expect(mvpPanel).toContain('defineExpose({ openHabitComposer, refreshHabits: loadHabits, refreshSettings: loadSettings })')
|
||||
expect(app).toContain("readStoredBoolean(window.localStorage, SHOW_COMPLETED_STORAGE_KEY, true)")
|
||||
expect(app).toContain("writeStoredBoolean(window.localStorage, SHOW_COMPLETED_STORAGE_KEY, value)")
|
||||
expect(app).toContain(':show-completed="showCompleted"')
|
||||
@@ -1022,13 +1068,13 @@ describe('approved habit safety and U2 title hierarchy', () => {
|
||||
expect(mvpPanel).not.toContain('<h2>习惯</h2>')
|
||||
expect(mvpPanel).not.toContain('<h2>设置与数据</h2>')
|
||||
expect(mvpPanel).not.toContain('class="view-intro"')
|
||||
for (const title of ['数据', '账户与安全', '登录设备', '活动']) expect(mvpPanel).toContain(`<h2>${title}</h2>`)
|
||||
for (const title of ['数据与恢复', '账户与安全', '登录设备', '活动记录']) expect(mvpPanel).toContain(`<h2>${title}</h2>`)
|
||||
expect(mvpPanel).not.toContain('<h2>危险操作</h2>')
|
||||
expect(mvpPanel).toContain('class="settings-sections"')
|
||||
expect(mvpPanel).not.toContain('class="settings-grid"')
|
||||
expect(mvpPanel).not.toContain('class="tool-card')
|
||||
expect(css).toContain('.settings-sections{width:min(100%,760px);')
|
||||
expect(css).toContain('.settings-row{min-height:56px;')
|
||||
expect(css).toContain('.settings-sections{width:min(100%,900px);')
|
||||
expect(css).toContain('.settings-row{min-height:62px;')
|
||||
expect(css).toContain('.backup-preflight .danger-button{min-height:44px}')
|
||||
expect(css).toContain('.backup-preflight.invalid{background:#fff2ef;')
|
||||
expect(mvpPanel).toContain("v-if=\"restoreMode==='replace'\" class=\"restore-replace-warning\"")
|
||||
@@ -1062,7 +1108,7 @@ describe('approved habit safety and U2 title hierarchy', () => {
|
||||
|
||||
describe('settings data tools', () => {
|
||||
it('uses complete ZIP backup with preflight, restore modes and legacy compatibility', () => {
|
||||
expect(mvpPanel).toContain('<h2>数据</h2>')
|
||||
expect(mvpPanel).toContain('<h2>数据与恢复</h2>')
|
||||
expect(mvpPanel).toContain("downloadFullBackup()")
|
||||
expect(mvpPanel).toContain("'dodo-backup-v2.zip'")
|
||||
expect(mvpPanel).toContain('导出 ZIP')
|
||||
|
||||
Reference in New Issue
Block a user