mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-03 18:33:14 +08:00
feat: 修改社区模板市场入口 放到日报二级菜单下, 增加跳转github
This commit is contained in:
@@ -911,10 +911,23 @@ describe('daily report controls', () => {
|
||||
expect(onChange).toHaveBeenCalledWith('mobile-magazine')
|
||||
})
|
||||
|
||||
it('does not select a market template before it is installed', async () => {
|
||||
it('keeps market browsing on the community page instead of the generation form', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onChange = vi.fn()
|
||||
Object.assign(window.api, {
|
||||
listReportTemplates: vi.fn(async () => [
|
||||
{
|
||||
id: 'community.github.tracememo.quickread',
|
||||
version: '1.0.0',
|
||||
interfaceVersion: '1',
|
||||
source: 'installed' as const,
|
||||
name: '极简速读',
|
||||
author: 'fixture',
|
||||
entryPath: '/tmp/entry.html',
|
||||
capture: { width: 414, maxWidth: 414, maxHeight: 2000 },
|
||||
license: { spdx: 'MIT' }
|
||||
}
|
||||
]),
|
||||
listReportTemplateCatalog: vi.fn(async () => ({
|
||||
success: true,
|
||||
catalog: {
|
||||
@@ -931,11 +944,10 @@ describe('daily report controls', () => {
|
||||
tags: [],
|
||||
license: 'MIT',
|
||||
minAppVersion: null,
|
||||
download: 'https://raw.githubusercontent.com/Wxw-Gu/TraceMemo/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/packages/community.github.example.market/1.0.0/template.zip',
|
||||
download: 'https://example.com/template.zip',
|
||||
sizeBytes: 1,
|
||||
sha256: 'a'.repeat(64),
|
||||
preview: 'https://raw.githubusercontent.com/Wxw-Gu/TraceMemo/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/previews/community.github.example.market.png',
|
||||
status: 'published'
|
||||
status: 'published' as const
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -943,12 +955,18 @@ describe('daily report controls', () => {
|
||||
})
|
||||
|
||||
render(<ReportTemplateSelector value="v1" onChange={onChange} />)
|
||||
const marketItem = (await screen.findByText('市场测试模板')).closest('.report-template-item')
|
||||
expect(marketItem).not.toBeNull()
|
||||
await user.click(within(marketItem!).getByRole('button', { name: '查看版式' }))
|
||||
const dialog = screen.getByRole('dialog', { name: '市场测试模板' })
|
||||
expect(within(dialog).getByRole('button', { name: '请先安装' })).toBeDisabled()
|
||||
expect(within(dialog).queryByRole('button', { name: '选择此模板' })).not.toBeInTheDocument()
|
||||
expect(onChange).not.toHaveBeenCalled()
|
||||
|
||||
// 已安装的市场模板仍可在生成页选用,但不再提供卸载入口。
|
||||
expect(await screen.findByText('极简速读')).toBeVisible()
|
||||
expect(screen.getByText('已安装市场模板')).toBeVisible()
|
||||
expect(screen.queryByRole('button', { name: '卸载' })).not.toBeInTheDocument()
|
||||
// 未安装的远端模板只出现在社区模板市场页。
|
||||
expect(screen.queryByText('市场测试模板')).not.toBeInTheDocument()
|
||||
expect(screen.queryByText('安装')).not.toBeInTheDocument()
|
||||
|
||||
await user.click(screen.getByRole('radio', { name: /极简速读/ }))
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
'external:community.github.tracememo.quickread@1.0.0'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
import { render, screen, waitFor, within } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { ReportTemplateMarketWorkspace } from '../../src/renderer/src/components/reports/ReportTemplateMarketWorkspace'
|
||||
|
||||
const installed = {
|
||||
id: 'community.github.tracememo.quickread',
|
||||
version: '1.0.0',
|
||||
interfaceVersion: '1',
|
||||
source: 'installed' as const,
|
||||
name: '极简速读',
|
||||
author: 'fixture',
|
||||
entryPath: '/tmp/entry.html',
|
||||
capture: { width: 414, maxWidth: 414, maxHeight: 2000 },
|
||||
license: { spdx: 'MIT' }
|
||||
}
|
||||
|
||||
const availableEntry = {
|
||||
id: 'community.github.tracememo.paperdaily',
|
||||
version: '1.0.2',
|
||||
interfaceVersion: '1',
|
||||
name: '霓光指挥日报',
|
||||
description: '霓光主题的桌面指挥日报。',
|
||||
author: 'fixture',
|
||||
platform: 'desktop' as const,
|
||||
tags: [],
|
||||
license: 'MIT',
|
||||
minAppVersion: null,
|
||||
download: 'https://example.com/template.zip',
|
||||
sizeBytes: 1,
|
||||
sha256: 'a'.repeat(64),
|
||||
preview: 'https://example.com/preview.png',
|
||||
status: 'published' as const
|
||||
}
|
||||
|
||||
const listReportTemplates = vi.fn()
|
||||
const listReportTemplateCatalog = vi.fn()
|
||||
const installReportTemplateFromCatalog = vi.fn()
|
||||
const uninstallReportTemplate = vi.fn()
|
||||
|
||||
describe('ReportTemplateMarketWorkspace', () => {
|
||||
beforeEach(() => {
|
||||
listReportTemplates.mockReset().mockResolvedValue([installed])
|
||||
listReportTemplateCatalog.mockReset().mockResolvedValue({
|
||||
success: true,
|
||||
catalog: { schemaVersion: '1', status: 'published', templates: [availableEntry] }
|
||||
})
|
||||
installReportTemplateFromCatalog.mockReset().mockResolvedValue({ success: true })
|
||||
uninstallReportTemplate.mockReset().mockResolvedValue({ success: true })
|
||||
Object.defineProperty(window, 'api', {
|
||||
configurable: true,
|
||||
value: {
|
||||
listReportTemplates,
|
||||
listReportTemplateCatalog,
|
||||
installReportTemplateFromCatalog,
|
||||
uninstallReportTemplate
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('lists builtin, installed and installable market templates with all market actions', async () => {
|
||||
render(<ReportTemplateMarketWorkspace value="v1" onChange={vi.fn()} />)
|
||||
|
||||
expect(await screen.findByText('极简速读')).toBeVisible()
|
||||
expect(screen.getByText('霓光指挥日报')).toBeVisible()
|
||||
expect(screen.getByText('经典日报')).toBeVisible()
|
||||
expect(screen.getByText('已安装市场模板')).toBeVisible()
|
||||
expect(screen.getByText('已安装 1 个市场模板,另有 1 个可以安装。')).toBeVisible()
|
||||
expect(screen.getByRole('button', { name: '安装' })).toBeEnabled()
|
||||
expect(screen.getByRole('button', { name: '卸载' })).toBeEnabled()
|
||||
expect(screen.queryByRole('button', { name: '当前使用' })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('links to the community template repository for submitting new templates', async () => {
|
||||
render(<ReportTemplateMarketWorkspace value="v1" onChange={vi.fn()} />)
|
||||
|
||||
const link = await screen.findByRole('link', { name: '前往提交模板' })
|
||||
expect(link).toHaveAttribute('href', 'https://github.com/Wxw-Gu/TraceMemo-Templates')
|
||||
expect(link).toHaveAttribute('target', '_blank')
|
||||
expect(
|
||||
screen.getByText(
|
||||
'如果想提供给其他人模板样式,可以来 TraceMemo-Templates 项目里提交 PR,方便给其他用户使用。'
|
||||
)
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
it('uninstalls a market template and refreshes the list', async () => {
|
||||
const user = userEvent.setup()
|
||||
uninstallReportTemplate.mockImplementation(async () => {
|
||||
listReportTemplates.mockResolvedValue([])
|
||||
return { success: true }
|
||||
})
|
||||
render(<ReportTemplateMarketWorkspace value="v1" onChange={vi.fn()} />)
|
||||
|
||||
await user.click(await screen.findByRole('button', { name: '卸载' }))
|
||||
|
||||
await waitFor(() =>
|
||||
expect(uninstallReportTemplate).toHaveBeenCalledWith(installed.id, installed.version)
|
||||
)
|
||||
expect(await screen.findByText('已安装 0 个市场模板,另有 1 个可以安装。')).toBeVisible()
|
||||
})
|
||||
|
||||
it('falls back to the default template when the active market template is uninstalled', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onChange = vi.fn()
|
||||
uninstallReportTemplate.mockResolvedValue({ success: true })
|
||||
render(
|
||||
<ReportTemplateMarketWorkspace
|
||||
value={`external:${installed.id}@${installed.version}`}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)
|
||||
|
||||
await user.click(await screen.findByRole('button', { name: '卸载' }))
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith('v1')
|
||||
})
|
||||
|
||||
it('reports an uninstall failure without dropping the installed template', async () => {
|
||||
const user = userEvent.setup()
|
||||
uninstallReportTemplate.mockResolvedValue({ success: false, error: '模板正在使用中' })
|
||||
render(<ReportTemplateMarketWorkspace value="v1" onChange={vi.fn()} />)
|
||||
|
||||
await user.click(await screen.findByRole('button', { name: '卸载' }))
|
||||
|
||||
expect(await screen.findByText('模板市场:模板正在使用中')).toBeVisible()
|
||||
expect(screen.getByText('极简速读')).toBeVisible()
|
||||
})
|
||||
|
||||
it('installs a market template and refreshes the list', async () => {
|
||||
const user = userEvent.setup()
|
||||
installReportTemplateFromCatalog.mockImplementation(async () => {
|
||||
listReportTemplates.mockResolvedValue([installed, { ...availableEntry, source: 'installed' }])
|
||||
listReportTemplateCatalog.mockResolvedValue({
|
||||
success: true,
|
||||
catalog: { schemaVersion: '1', status: 'published', templates: [availableEntry] }
|
||||
})
|
||||
return { success: true }
|
||||
})
|
||||
render(<ReportTemplateMarketWorkspace value="v1" onChange={vi.fn()} />)
|
||||
|
||||
await user.click(await screen.findByRole('button', { name: '安装' }))
|
||||
|
||||
await waitFor(() =>
|
||||
expect(installReportTemplateFromCatalog).toHaveBeenCalledWith(
|
||||
availableEntry.id,
|
||||
availableEntry.version
|
||||
)
|
||||
)
|
||||
expect(await screen.findByText('已安装 2 个市场模板,另有 0 个可以安装。')).toBeVisible()
|
||||
})
|
||||
|
||||
it('reports an install failure without losing the catalog', async () => {
|
||||
const user = userEvent.setup()
|
||||
installReportTemplateFromCatalog.mockResolvedValue({ success: false, error: '下载校验失败' })
|
||||
render(<ReportTemplateMarketWorkspace value="v1" onChange={vi.fn()} />)
|
||||
|
||||
await user.click(await screen.findByRole('button', { name: '安装' }))
|
||||
|
||||
expect(await screen.findByText('模板市场:下载校验失败')).toBeVisible()
|
||||
expect(screen.getByText('霓光指挥日报')).toBeVisible()
|
||||
})
|
||||
|
||||
it('previews a market template and can switch to it once installed', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onChange = vi.fn()
|
||||
render(<ReportTemplateMarketWorkspace value="v1" onChange={onChange} />)
|
||||
|
||||
const installedRow = (await screen.findByText('极简速读')).closest('.report-template-item')!
|
||||
await user.click(within(installedRow as HTMLElement).getByRole('button', { name: '查看版式' }))
|
||||
const dialog = await screen.findByRole('dialog', { name: '极简速读' })
|
||||
await user.click(within(dialog).getByRole('button', { name: '选择此模板' }))
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(`external:${installed.id}@${installed.version}`)
|
||||
})
|
||||
|
||||
it('does not let an uninstalled market template be selected from the preview', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onChange = vi.fn()
|
||||
render(<ReportTemplateMarketWorkspace value="v1" onChange={onChange} />)
|
||||
|
||||
const availableRow = (await screen.findByText('霓光指挥日报')).closest('.report-template-item')!
|
||||
await user.click(within(availableRow as HTMLElement).getByRole('button', { name: '查看版式' }))
|
||||
const dialog = await screen.findByRole('dialog', { name: '霓光指挥日报' })
|
||||
|
||||
expect(within(dialog).getByRole('button', { name: '请先安装' })).toBeDisabled()
|
||||
expect(within(dialog).queryByRole('button', { name: '使用此模板' })).not.toBeInTheDocument()
|
||||
expect(onChange).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('marks the active market template and hides the switch action', async () => {
|
||||
render(
|
||||
<ReportTemplateMarketWorkspace
|
||||
value={`external:${installed.id}@${installed.version}`}
|
||||
onChange={vi.fn()}
|
||||
/>
|
||||
)
|
||||
|
||||
const installedRow = (await screen.findByText('极简速读')).closest('.report-template-item')!
|
||||
expect(within(installedRow as HTMLElement).getByRole('button', { name: '当前使用' })).toBeDisabled()
|
||||
expect(within(installedRow as HTMLElement).queryByRole('button', { name: '使用' })).toBeNull()
|
||||
expect(within(installedRow as HTMLElement).getByRole('button', { name: '卸载' })).toBeEnabled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,63 @@
|
||||
import { expect, test } from '@playwright/test'
|
||||
import { launchTestApp } from './support/electron'
|
||||
|
||||
/**
|
||||
* 社区模板市场现在是日报工作区顶部的第三个标签页,也是模板市场的主入口:
|
||||
* 浏览、预览、安装、选用与卸载都在这里;生成页只保留选用与预览。
|
||||
*/
|
||||
test('REPORT-TEMPLATE-MARKET-TAB-01 browses and installs community templates from the report tabs', async () => {
|
||||
const fixture = await launchTestApp({ templateMarket: 'fixture' })
|
||||
const page = fixture.page
|
||||
const pageErrors: Error[] = []
|
||||
page.on('pageerror', (error) => pageErrors.push(error))
|
||||
try {
|
||||
await page.getByRole('button', { name: '日报' }).click()
|
||||
await page.getByRole('button', { name: '开始生成日报' }).click()
|
||||
|
||||
await page.getByRole('tab', { name: '社区模板市场' }).click()
|
||||
await expect(page.getByRole('heading', { name: '社区模板市场' })).toBeVisible()
|
||||
await expect(page.getByText('经典日报')).toBeVisible()
|
||||
await expect(page.getByText('微信信息流')).toBeVisible()
|
||||
await expect(page.getByText('已安装 0 个市场模板,另有 1 个可以安装。')).toBeVisible()
|
||||
|
||||
// 投稿入口紧跟在标题下方,不需要滚到底部就能看到。
|
||||
const contributeLink = page.getByRole('link', { name: '前往提交模板' })
|
||||
await expect(contributeLink).toBeInViewport()
|
||||
await expect(contributeLink).toHaveAttribute(
|
||||
'href',
|
||||
'https://github.com/Wxw-Gu/TraceMemo-Templates'
|
||||
)
|
||||
await expect(page.getByText('想把你做的模板分享给其他人?')).toBeVisible()
|
||||
|
||||
const marketRow = page.locator('.report-template-item').filter({ hasText: '霓光指挥日报' })
|
||||
await expect(marketRow).toBeVisible()
|
||||
await expect(marketRow.getByRole('button', { name: '安装' })).toBeEnabled()
|
||||
|
||||
await marketRow.getByRole('button', { name: '安装' }).click()
|
||||
await expect(page.getByText('已安装 1 个市场模板,另有 0 个可以安装。')).toBeVisible()
|
||||
await expect(page.getByText('已安装市场模板')).toBeVisible()
|
||||
|
||||
// 已安装后市场页提供卸载入口,未安装时只有安装。
|
||||
await expect(
|
||||
marketRow.getByRole('button', { name: '卸载' })
|
||||
).toBeEnabled()
|
||||
|
||||
await page.getByRole('tab', { name: '今日日报' }).click()
|
||||
await expect(page.getByRole('heading', { name: '生成群聊日报' })).toBeVisible()
|
||||
|
||||
const templateSection = page.getByRole('heading', { name: '日报模板' }).locator('..')
|
||||
// 生成页只保留选用与预览:没有卸载,也不重复展示市场列表。
|
||||
await expect(templateSection.getByText('霓光指挥日报')).toBeVisible()
|
||||
await expect(templateSection.getByRole('button', { name: '卸载' })).toHaveCount(0)
|
||||
await expect(templateSection.getByRole('button', { name: '安装' })).toHaveCount(0)
|
||||
|
||||
await page.getByRole('tab', { name: '社区模板市场' }).click()
|
||||
const roundTripRow = page.locator('.report-template-item').filter({ hasText: '霓光指挥日报' })
|
||||
await roundTripRow.getByRole('button', { name: '卸载' }).click()
|
||||
await expect(page.getByText('已安装 0 个市场模板,另有 1 个可以安装。')).toBeVisible()
|
||||
|
||||
expect(pageErrors).toEqual([])
|
||||
} finally {
|
||||
await fixture.close()
|
||||
}
|
||||
})
|
||||
@@ -991,6 +991,72 @@ handle('report:updateGeneratedTemplate', (request) => ({
|
||||
handle('report:deleteGenerated', () => ({ success: true }))
|
||||
handle('report:reveal', () => ({ success: true }))
|
||||
handle('copy-image', () => ({ success: true }))
|
||||
// 社区模板市场:默认关闭(返回空目录),测试用例可通过 WXE_E2E_TEMPLATE_MARKET=fixture 打开合成目录。
|
||||
const templateMarketEnabled = process.env.WXE_E2E_TEMPLATE_MARKET === 'fixture'
|
||||
const templateMarketEntry = {
|
||||
id: 'community.github.example.neonboard',
|
||||
version: '1.0.2',
|
||||
interfaceVersion: '1',
|
||||
name: '霓光指挥日报',
|
||||
description: '霓光主题的桌面指挥日报,突出实时讨论、重点消息与行动项。',
|
||||
author: 'TraceMemo 社区',
|
||||
platform: 'desktop',
|
||||
tags: ['desktop'],
|
||||
license: 'MIT',
|
||||
minAppVersion: null,
|
||||
download: 'https://example.com/packages/community.github.example.neonboard/1.0.2/template.zip',
|
||||
sizeBytes: 2048,
|
||||
sha256: 'b'.repeat(64),
|
||||
status: 'published'
|
||||
}
|
||||
const templateMarketInstalled = []
|
||||
handle('report-template:list', () =>
|
||||
templateMarketInstalled.map((entry) => ({
|
||||
id: entry.id,
|
||||
version: entry.version,
|
||||
interfaceVersion: entry.interfaceVersion,
|
||||
source: 'installed',
|
||||
name: entry.name,
|
||||
author: entry.author,
|
||||
entryPath: path.join(userData, 'report-templates', entry.id, 'template.html'),
|
||||
capture: { width: 1280, maxWidth: 1280, maxHeight: 4000 },
|
||||
license: { spdx: entry.license },
|
||||
installedAt: '2026-09-15T00:00:00.000Z'
|
||||
}))
|
||||
)
|
||||
handle('report-template-market:list', () =>
|
||||
templateMarketEnabled
|
||||
? {
|
||||
success: true,
|
||||
catalog: {
|
||||
schemaVersion: '1',
|
||||
generatedAt: '2026-09-15T00:00:00.000Z',
|
||||
status: 'published',
|
||||
templates: [templateMarketEntry]
|
||||
}
|
||||
}
|
||||
: { success: true, catalog: { schemaVersion: '1', status: 'published', templates: [] } }
|
||||
)
|
||||
handle('report-template-market:install', (id, version) => {
|
||||
if (
|
||||
!templateMarketEnabled ||
|
||||
String(id) !== templateMarketEntry.id ||
|
||||
String(version) !== templateMarketEntry.version
|
||||
) {
|
||||
return { success: false, error: '模板不存在', code: 'unknown_template' }
|
||||
}
|
||||
if (!templateMarketInstalled.some((entry) => entry.id === id && entry.version === version)) {
|
||||
templateMarketInstalled.push(templateMarketEntry)
|
||||
}
|
||||
return { success: true, catalogEntry: templateMarketEntry }
|
||||
})
|
||||
handle('report-template:uninstall', (id, version) => {
|
||||
const index = templateMarketInstalled.findIndex(
|
||||
(entry) => entry.id === String(id) && entry.version === String(version)
|
||||
)
|
||||
if (index >= 0) templateMarketInstalled.splice(index, 1)
|
||||
return { success: true }
|
||||
})
|
||||
handle('api:copyText', () => ({ success: true }))
|
||||
handle('app-log:write', (entry) => {
|
||||
const safe = JSON.stringify(entry)
|
||||
|
||||
@@ -16,6 +16,8 @@ export interface TestApplication {
|
||||
export async function launchTestApp(
|
||||
options: {
|
||||
mode?: 'connected' | 'disconnected'
|
||||
/** 让模板市场 IPC 返回确定性的合成目录,供社区模板市场页视觉/行为用例使用。 */
|
||||
templateMarket?: 'off' | 'fixture'
|
||||
userData?: string
|
||||
largeContacts?: number
|
||||
corruptCache?: boolean
|
||||
@@ -55,7 +57,8 @@ export async function launchTestApp(
|
||||
WXE_E2E_UNSIGNED_MAC_UPDATE: options.unsignedMacUpdate ? '1' : '0',
|
||||
WXE_E2E_REALTIME_REORDER: options.realtimeReorder ? '1' : '0',
|
||||
WXE_E2E_NOW_MS: options.now ? String(options.now) : '',
|
||||
WXE_E2E_APPEARANCE_THEME: options.appearanceTheme || 'light'
|
||||
WXE_E2E_APPEARANCE_THEME: options.appearanceTheme || 'light',
|
||||
WXE_E2E_TEMPLATE_MARKET: options.templateMarket || 'off'
|
||||
}
|
||||
})
|
||||
const page = await app.firstWindow()
|
||||
|
||||
Reference in New Issue
Block a user