feat: 重构 safari — Vue 组件化(内置页/地址栏/历史导航/搜索引擎)+ 6 测试
This commit is contained in:
parent
d210df0133
commit
c577546ada
@ -1 +1,185 @@
|
|||||||
<template><div></div></template>
|
<template>
|
||||||
|
<div class="safari-body">
|
||||||
|
<!-- 工具栏 -->
|
||||||
|
<div class="fb-toolbar sf-toolbar">
|
||||||
|
<button class="fb-btn" title="后退" :disabled="!canBack" @click="back" v-html="'‹'"></button>
|
||||||
|
<button class="fb-btn" title="前进" :disabled="!canFwd" @click="fwd" v-html="'›'"></button>
|
||||||
|
<button class="fb-btn" title="重新载入" @click="reload" v-html="'⟳'"></button>
|
||||||
|
<div class="sf-addr-wrap">
|
||||||
|
<input
|
||||||
|
ref="addrEl"
|
||||||
|
class="text-input sf-addr"
|
||||||
|
type="text"
|
||||||
|
placeholder="搜索或输入网站地址"
|
||||||
|
spellcheck="false"
|
||||||
|
aria-label="地址栏"
|
||||||
|
v-model="addrValue"
|
||||||
|
@keydown.stop="onAddrKeydown"
|
||||||
|
>
|
||||||
|
<div class="sf-progress" :style="{ width: progressW, opacity: progressO, transition: progressT }"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 页面内容区 -->
|
||||||
|
<div class="sf-page">
|
||||||
|
<!-- 起始页 -->
|
||||||
|
<div v-if="currentPage === 'start'" class="sf-start">
|
||||||
|
<h2>个人收藏</h2>
|
||||||
|
<div class="sf-fav-grid">
|
||||||
|
<div v-for="f in favs" :key="f.url" class="sf-fav" @click="navigate(f.url)">
|
||||||
|
<div class="sf-fav-ico">{{ f.ico }}</div>
|
||||||
|
<div class="sf-fav-name">{{ f.name }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="sf-tip">在地址栏输入 macos://weather 等内置地址,或输入网址查看离线回退页。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 内置页 -->
|
||||||
|
<div v-else-if="currentPage === 'builtin'" class="sf-builtin" v-html="builtinHtml"></div>
|
||||||
|
|
||||||
|
<!-- 离线页 -->
|
||||||
|
<div v-else-if="currentPage === 'offline'" class="sf-offline">
|
||||||
|
<div class="sf-offline-globe">🌐</div>
|
||||||
|
<h3>Safari 浏览器无法打开页面</h3>
|
||||||
|
<p>你的 Mac 当前处于离线环境,无法连接到"{{ offlineUrl }}"。</p>
|
||||||
|
<div class="sf-offline-btns">
|
||||||
|
<button class="btn primary" @click="openExternal(offlineUrl)">在新标签页打开</button>
|
||||||
|
<button class="btn" @click="navigate('macos://start')">返回起始页</button>
|
||||||
|
</div>
|
||||||
|
<p class="sf-offline-sub">当前搜索引擎:{{ searchEngineName }}(可在 系统设置 › Safari 中修改)</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, computed, onMounted } from 'vue'
|
||||||
|
import { store } from '../../composables/useStore'
|
||||||
|
import { wm } from '../../composables/useWM'
|
||||||
|
|
||||||
|
const Sys = () => (window as any).Sys
|
||||||
|
const props = defineProps<{ win: any }>()
|
||||||
|
|
||||||
|
// ============ 状态 ============
|
||||||
|
const hist = ref<string[]>([])
|
||||||
|
const hi = ref(-1)
|
||||||
|
const addrValue = ref('')
|
||||||
|
const currentPage = ref<'start' | 'builtin' | 'offline'>('start')
|
||||||
|
const builtinHtml = ref('')
|
||||||
|
const offlineUrl = ref('')
|
||||||
|
const progressW = ref('0')
|
||||||
|
const progressO = ref('0')
|
||||||
|
const progressT = ref('width .2s ease')
|
||||||
|
|
||||||
|
const favs = store.get('safari-favs', [
|
||||||
|
{ name: '天气', url: 'macos://weather', ico: '🌤' },
|
||||||
|
{ name: '科技新闻', url: 'macos://news', ico: '📰' },
|
||||||
|
{ name: '百科', url: 'macos://baike', ico: '📚' },
|
||||||
|
{ name: '代码仓库', url: 'macos://github', ico: '💻' },
|
||||||
|
])
|
||||||
|
|
||||||
|
// ============ 计算属性 ============
|
||||||
|
const canBack = computed(() => hi.value > 0)
|
||||||
|
const canFwd = computed(() => hi.value < hist.value.length - 1)
|
||||||
|
|
||||||
|
const searchEngineName = computed(() => {
|
||||||
|
const S = Sys()
|
||||||
|
return S?.settings?.searchEngine === 'baidu' ? '百度' : 'Bing'
|
||||||
|
})
|
||||||
|
|
||||||
|
// ============ 内置页 ============
|
||||||
|
const builtinPages: Record<string, { title: string; html: string }> = {
|
||||||
|
'macos://weather': {
|
||||||
|
title: '天气 - 内置页',
|
||||||
|
html: '<h2>天气</h2><p>请打开天气应用查看完整天气信息。</p>',
|
||||||
|
},
|
||||||
|
'macos://news': {
|
||||||
|
title: '科技新闻 - 内置页',
|
||||||
|
html: '<h2>科技要闻</h2>'
|
||||||
|
+ '<div class="sf-news-item"><b>macOS 网页版发布</b><p>完全在浏览器中运行的桌面体验。</p></div>'
|
||||||
|
+ '<div class="sf-news-item"><b>本地优先的软件设计兴起</b><p>越来越多的应用选择将数据保存在本地。</p></div>'
|
||||||
|
+ '<div class="sf-news-item"><b>Web 平台能力持续增强</b><p>通知、文件系统访问与硬件加速让网页应用接近原生体验。</p></div>'
|
||||||
|
+ '<div class="sf-news-item"><b>开源社区年度盘点</b><p>来自全球的开发者为桌面模拟器项目贡献了素材。</p></div>',
|
||||||
|
},
|
||||||
|
'macos://baike': {
|
||||||
|
title: '百科 - 内置页',
|
||||||
|
html: '<h2>macOS</h2><p>macOS 是苹果公司为 Mac 系列电脑开发的操作系统。本页面由 Safari 内置百科提供,离线可读。</p><h3>版本沿革</h3><p>Cheetah、Puma、Jaguar … Sequoia。</p>',
|
||||||
|
},
|
||||||
|
'macos://github': {
|
||||||
|
title: '代码仓库 - 内置页',
|
||||||
|
html: '<h2>macos-web</h2><p>一个纯静态的 macOS 桌面模拟器。许可:MIT。</p><pre class="sf-code">macos-web/\n├── index.html\n├── css/\n├── js/\n└── assets/</pre>',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ 导航 ============
|
||||||
|
function setLoading(on: boolean) {
|
||||||
|
progressW.value = on ? '70%' : '100%'
|
||||||
|
progressO.value = on ? '1' : '0'
|
||||||
|
if (on) {
|
||||||
|
setTimeout(() => { progressT.value = 'width 2s ease'; progressW.value = '92%' }, 60)
|
||||||
|
} else {
|
||||||
|
progressT.value = 'width .2s ease'
|
||||||
|
setTimeout(() => { progressW.value = '0' }, 240)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function navigate(url: string, push = true) {
|
||||||
|
url = (url || '').trim()
|
||||||
|
if (!url) { currentPage.value = 'start'; wm.setTitle(props.win, '起始页 — Safari 浏览器'); addrValue.value = ''; return }
|
||||||
|
|
||||||
|
setLoading(true)
|
||||||
|
setTimeout(() => {
|
||||||
|
setLoading(false)
|
||||||
|
if (push) { hist.value = hist.value.slice(0, hi.value + 1); hist.value.push(url); hi.value = hist.value.length - 1 }
|
||||||
|
|
||||||
|
if (url === 'macos://start') { currentPage.value = 'start'; wm.setTitle(props.win, '起始页 — Safari 浏览器'); addrValue.value = ''; return }
|
||||||
|
|
||||||
|
const bp = builtinPages[url]
|
||||||
|
if (bp) {
|
||||||
|
currentPage.value = 'builtin'
|
||||||
|
builtinHtml.value = bp.html
|
||||||
|
addrValue.value = url
|
||||||
|
wm.setTitle(props.win, bp.title)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP URL or search
|
||||||
|
if (/^https?:\/\//i.test(url)) {
|
||||||
|
addrValue.value = url
|
||||||
|
} else {
|
||||||
|
const q = encodeURIComponent(url)
|
||||||
|
const S = Sys()
|
||||||
|
const su = S?.settings?.searchEngine === 'baidu' ? `https://www.baidu.com/s?wd=${q}` : `https://www.bing.com/search?q=${q}`
|
||||||
|
addrValue.value = su
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPage.value = 'offline'
|
||||||
|
offlineUrl.value = url
|
||||||
|
wm.setTitle(props.win, '无法连接 — Safari 浏览器')
|
||||||
|
}, 380)
|
||||||
|
}
|
||||||
|
|
||||||
|
function back() { if (canBack.value) { hi.value--; navigate(hist.value[hi.value], false) } }
|
||||||
|
function fwd() { if (canFwd.value) { hi.value++; navigate(hist.value[hi.value], false) } }
|
||||||
|
function reload() { navigate(hist.value[hi.value] || 'macos://start', false) }
|
||||||
|
|
||||||
|
function onAddrKeydown(e: KeyboardEvent) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
let v = addrValue.value.trim()
|
||||||
|
if (/^[\w-]+(\.[\w-]+)+(\/.*)?$/.test(v) && !v.includes(' ')) v = 'https://' + v
|
||||||
|
navigate(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openExternal(url: string) { window.open(url, '_blank', 'noopener') }
|
||||||
|
|
||||||
|
// 暴露给 menus
|
||||||
|
props.win.appState = { canBack, canFwd, back, fwd, reload }
|
||||||
|
|
||||||
|
// ============ 初始化 ============
|
||||||
|
onMounted(() => {
|
||||||
|
const args = props.win.data || {}
|
||||||
|
navigate(args.url || 'macos://start', true)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
|
// Safari 浏览器 — Vue 组件化
|
||||||
import { h, render } from 'vue'
|
import { h, render } from 'vue'
|
||||||
// Safari 浏览器应用 — 从 js/apps2.js 手动转写
|
|
||||||
import { el } from '../../utils'
|
|
||||||
import { store as Store } from '../../composables/useStore'
|
|
||||||
import { wm as WM } from '../../composables/useWM'
|
|
||||||
import { Apps, stdMenus } from '../../composables/useApps'
|
import { Apps, stdMenus } from '../../composables/useApps'
|
||||||
import SafariComponent from './Safari.vue'
|
import SafariComponent from './Safari.vue'
|
||||||
const Sys = () => (window as any).Sys
|
|
||||||
|
|
||||||
export const SafariApp = {
|
export const SafariApp = {
|
||||||
id: 'safari', name: 'Safari 浏览器', icon: '/assets/icons/safari.png',
|
id: 'safari', name: 'Safari 浏览器', icon: '/assets/icons/safari.png',
|
||||||
@ -18,106 +14,14 @@ export const SafariApp = {
|
|||||||
{ label: '重新载入页面', key: '⌘R', action: () => st?.reload() },
|
{ label: '重新载入页面', key: '⌘R', action: () => st?.reload() },
|
||||||
],
|
],
|
||||||
view: [
|
view: [
|
||||||
{ label: '后退', key: '⌘[', disabled: !st?.canBack(), action: () => st?.back() },
|
{ label: '后退', key: '⌘[', disabled: !st?.canBack?.value, action: () => st?.back() },
|
||||||
{ label: '前进', key: '⌘]', disabled: !st?.canFwd(), action: () => st?.fwd() },
|
{ label: '前进', key: '⌘]', disabled: !st?.canFwd?.value, action: () => st?.fwd() },
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
builtinPages: {
|
render(win: any) {
|
||||||
'macos://weather': {
|
const vnode = h(SafariComponent, { win })
|
||||||
title: '天气 - 内置页',
|
render(vnode, win.body)
|
||||||
build(box: HTMLElement) {
|
|
||||||
box.append(el('h2', { text: '天气' }))
|
|
||||||
box.append(el('p', { text: '请打开天气应用查看完整天气信息。' }))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
'macos://news': {
|
|
||||||
title: '科技新闻 - 内置页',
|
|
||||||
build(box: HTMLElement) {
|
|
||||||
box.append(el('h2', { text: '科技要闻' }));
|
|
||||||
[['macOS 网页版发布', '完全在浏览器中运行的桌面体验。'], ['本地优先的软件设计兴起', '越来越多的应用选择将数据保存在本地。'], ['Web 平台能力持续增强', '通知、文件系统访问与硬件加速让网页应用接近原生体验。'], ['开源社区年度盘点', '来自全球的开发者为桌面模拟器项目贡献了素材。']].forEach(([t, d]) => box.append(el('div', { class: 'sf-news-item' }, el('b', { text: t }), el('p', { text: d }))))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'macos://baike': {
|
|
||||||
title: '百科 - 内置页',
|
|
||||||
build(box: HTMLElement) {
|
|
||||||
box.append(el('h2', { text: 'macOS' }))
|
|
||||||
box.append(el('p', { text: 'macOS 是苹果公司为 Mac 系列电脑开发的操作系统。本页面由 Safari 内置百科提供,离线可读。' }))
|
|
||||||
box.append(el('h3', { text: '版本沿革' }))
|
|
||||||
box.append(el('p', { text: 'Cheetah、Puma、Jaguar … Sequoia。' }))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'macos://github': {
|
|
||||||
title: '代码仓库 - 内置页',
|
|
||||||
build(box: HTMLElement) {
|
|
||||||
box.append(el('h2', { text: 'macos-web' }))
|
|
||||||
box.append(el('p', { text: '一个纯静态的 macOS 桌面模拟器。许可:MIT。' }))
|
|
||||||
box.append(el('pre', { class: 'sf-code', text: 'macos-web/\n├── index.html\n├── css/\n├── js/\n└── assets/' }))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
render(win: any, args: any) {
|
|
||||||
const S = Sys()
|
|
||||||
const st = win.appState = { hist: [] as string[], hi: -1, loading: false }
|
|
||||||
const favs = Store.get('safari-favs', [
|
|
||||||
{ name: '天气', url: 'macos://weather', ico: '🌤' },
|
|
||||||
{ name: '科技新闻', url: 'macos://news', ico: '📰' },
|
|
||||||
{ name: '百科', url: 'macos://baike', ico: '📚' },
|
|
||||||
{ name: '代码仓库', url: 'macos://github', ico: '💻' },
|
|
||||||
])
|
|
||||||
win.body.classList.add('safari-body')
|
|
||||||
const backBtn = el('button', { class: 'fb-btn', html: '‹', title: '后退' }) as HTMLButtonElement
|
|
||||||
const fwdBtn = el('button', { class: 'fb-btn', html: '›', title: '前进' }) as HTMLButtonElement
|
|
||||||
const reloadBtn = el('button', { class: 'fb-btn', html: '⟳', title: '重新载入' })
|
|
||||||
const addr = el('input', { class: 'text-input sf-addr', type: 'text', placeholder: '搜索或输入网站地址', spellcheck: 'false', 'aria-label': '地址栏' }) as HTMLInputElement
|
|
||||||
const progress = el('div', { class: 'sf-progress' })
|
|
||||||
const toolbar = el('div', { class: 'fb-toolbar sf-toolbar' }, backBtn, fwdBtn, reloadBtn, el('div', { class: 'sf-addr-wrap' }, addr, progress))
|
|
||||||
const page = el('div', { class: 'sf-page' })
|
|
||||||
win.body.append(toolbar, page)
|
|
||||||
st.canBack = () => st.hi > 0; st.canFwd = () => st.hi < st.hist.length - 1
|
|
||||||
const syncNav = () => { backBtn.disabled = !st.canBack(); fwdBtn.disabled = !st.canFwd() }
|
|
||||||
const setLoading = (on: boolean) => {
|
|
||||||
st.loading = on; progress.style.width = on ? '70%' : '100%'; progress.style.opacity = on ? '1' : '0'
|
|
||||||
if (on) setTimeout(() => { if (st.loading) { progress.style.transition = 'width 2s ease'; progress.style.width = '92%' } }, 60)
|
|
||||||
else { progress.style.transition = 'width .2s ease'; setTimeout(() => progress.style.width = '0', 240) }
|
|
||||||
}
|
|
||||||
const showStart = () => {
|
|
||||||
page.innerHTML = ''; WM.setTitle(win, '起始页 — Safari 浏览器'); addr.value = ''
|
|
||||||
const wrap = el('div', { class: 'sf-start' }, el('h2', { text: '个人收藏' }))
|
|
||||||
const grid = el('div', { class: 'sf-fav-grid' })
|
|
||||||
favs.forEach((f: any) => { const c = el('div', { class: 'sf-fav' }, el('div', { class: 'sf-fav-ico', text: f.ico }), el('div', { class: 'sf-fav-name', text: f.name })); c.addEventListener('click', () => navigate(f.url)); grid.append(c) })
|
|
||||||
wrap.append(grid, el('p', { class: 'sf-tip', text: '在地址栏输入 macos://weather 等内置地址,或输入网址查看离线回退页。' }))
|
|
||||||
page.append(wrap)
|
|
||||||
}
|
|
||||||
const showOffline = (url: string) => {
|
|
||||||
page.innerHTML = ''; WM.setTitle(win, '无法连接 — Safari 浏览器')
|
|
||||||
const engine = S.settings.searchEngine === 'baidu' ? '百度' : 'Bing'
|
|
||||||
page.append(el('div', { class: 'sf-offline' },
|
|
||||||
el('div', { class: 'sf-offline-globe', text: '🌐' }), el('h3', { text: 'Safari 浏览器无法打开页面' }),
|
|
||||||
el('p', { text: `你的 Mac 当前处于离线环境,无法连接到"${url.replace(/^https?:\/\//, '').slice(0, 60)}"。` }),
|
|
||||||
el('div', { class: 'sf-offline-btns' }, el('button', { class: 'btn primary', text: '在新标签页打开', onclick: () => window.open(url, '_blank', 'noopener') }), el('button', { class: 'btn', text: '返回起始页', onclick: () => navigate('macos://start') })),
|
|
||||||
el('p', { class: 'sf-offline-sub', text: `当前搜索引擎:${engine}(可在 系统设置 › Safari 中修改)` })))
|
|
||||||
}
|
|
||||||
const navigate = (url: string, push = true) => {
|
|
||||||
url = (url || '').trim(); if (!url) return showStart()
|
|
||||||
setLoading(true)
|
|
||||||
setTimeout(() => { setLoading(false)
|
|
||||||
if (push) { st.hist = st.hist.slice(0, st.hi + 1); st.hist.push(url); st.hi++ }
|
|
||||||
syncNav(); if (url === 'macos://start') return showStart()
|
|
||||||
const bp = (SafariApp as any).builtinPages[url]
|
|
||||||
if (bp) { page.innerHTML = ''; addr.value = url; WM.setTitle(win, bp.title); const box = el('div', { class: 'sf-builtin' }); try { bp.build(box) } catch (e) { box.append(el('p', { text: '页面加载失败。' })) }; page.append(box); return }
|
|
||||||
if (/^https?:\/\//i.test(url)) { addr.value = url; return showOffline(url) }
|
|
||||||
const q = encodeURIComponent(url)
|
|
||||||
const su = S.settings.searchEngine === 'baidu' ? `https://www.baidu.com/s?wd=${q}` : `https://www.bing.com/search?q=${q}`
|
|
||||||
addr.value = su; showOffline(su)
|
|
||||||
}, 380)
|
|
||||||
}
|
|
||||||
st.back = () => { if (st.canBack()) { st.hi--; navigate(st.hist[st.hi], false) } }
|
|
||||||
st.fwd = () => { if (st.canFwd()) { st.hi++; navigate(st.hist[st.hi], false) } }
|
|
||||||
st.reload = () => { const u = st.hist[st.hi] || 'macos://start'; navigate(u, false) }
|
|
||||||
backBtn.addEventListener('click', () => st.back()); fwdBtn.addEventListener('click', () => st.fwd()); reloadBtn.addEventListener('click', () => st.reload())
|
|
||||||
addr.addEventListener('keydown', (e: KeyboardEvent) => { e.stopPropagation(); if (e.key === 'Enter') { let v = addr.value.trim(); if (/^[\w-]+(\.[\w-]+)+(\/.*)?$/.test(v) && !v.includes(' ')) v = 'https://' + v; navigate(v) } })
|
|
||||||
navigate(args.url || 'macos://start', true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Apps.register(SafariApp)
|
Apps.register(SafariApp)
|
||||||
|
|||||||
54
tests/cases/27-safari.test.ts
Normal file
54
tests/cases/27-safari.test.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* 27-safari: Safari Vue 组件化测试
|
||||||
|
*/
|
||||||
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
||||||
|
import { h, render, nextTick } from 'vue'
|
||||||
|
import SafariComponent from '../../src/apps/safari/Safari.vue'
|
||||||
|
import { setupDOM } from '../helpers'
|
||||||
|
|
||||||
|
function makeMockWin() {
|
||||||
|
const el = document.createElement('div'); el.className = 'window'
|
||||||
|
document.getElementById('window-layer')?.appendChild(el)
|
||||||
|
const body = document.createElement('div'); body.className = 'win-body'
|
||||||
|
el.appendChild(body)
|
||||||
|
return { id: 'sf', appId: 'safari', el, body, timers: [] as any[], data: {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('27-safari — Safari Vue 组件化', () => {
|
||||||
|
let win: any
|
||||||
|
beforeEach(() => { setupDOM(); win = makeMockWin() })
|
||||||
|
afterEach(() => { render(null, win.body); win.el.remove() })
|
||||||
|
|
||||||
|
function mount() { const v = h(SafariComponent, { win }); render(v, win.body) }
|
||||||
|
|
||||||
|
it('挂载后渲染起始页', async () => {
|
||||||
|
mount(); await nextTick(); await nextTick()
|
||||||
|
// setTimeout 380ms 后导航到 start
|
||||||
|
await new Promise(r => setTimeout(r, 500))
|
||||||
|
expect(win.body.querySelector('.sf-start')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('地址栏存在', () => { mount(); expect(win.body.querySelector('.sf-addr')).toBeTruthy() })
|
||||||
|
|
||||||
|
it('有后退/前进/刷新按钮', () => {
|
||||||
|
mount()
|
||||||
|
expect(win.body.querySelector('.fb-btn[title="后退"]')).toBeTruthy()
|
||||||
|
expect(win.body.querySelector('.fb-btn[title="前进"]')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('导航到内置页后显示内容', async () => {
|
||||||
|
win.data = { url: 'macos://weather' }
|
||||||
|
mount()
|
||||||
|
await new Promise(r => setTimeout(r, 500))
|
||||||
|
expect(win.body.querySelector('.sf-builtin')).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('暴露 back/fwd/reload 给菜单', () => {
|
||||||
|
mount()
|
||||||
|
expect(typeof win.appState.back).toBe('function')
|
||||||
|
expect(typeof win.appState.fwd).toBe('function')
|
||||||
|
expect(typeof win.appState.reload).toBe('function')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('卸载后 DOM 为空', () => { mount(); render(null, win.body); expect(win.body.children.length).toBe(0) })
|
||||||
|
})
|
||||||
Loading…
x
Reference in New Issue
Block a user