mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-04 03:01:42 +08:00
feat: 定时日报支持Windows
This commit is contained in:
@@ -647,7 +647,7 @@ describe('daily report controls', () => {
|
||||
|
||||
const button = screen.getByRole('button', { name: '发送到当前群聊' })
|
||||
expect(button).toBeDisabled()
|
||||
expect(button.parentElement).toHaveAttribute('title', '仅支持 macOS')
|
||||
expect(button.parentElement).toHaveAttribute('title', '仅支持 macOS 和 Windows')
|
||||
})
|
||||
|
||||
it('switches templates from the top toolbar using the saved report snapshot', async () => {
|
||||
|
||||
@@ -81,7 +81,9 @@ describe('ScheduledReportsWorkspace', () => {
|
||||
onOpenWechatSettings={onOpenWechatSettings}
|
||||
/>
|
||||
)
|
||||
await waitFor(() => expect(screen.getByText('微信消息发送目前仅支持 macOS')).toBeVisible())
|
||||
await waitFor(() =>
|
||||
expect(screen.getByText('微信消息发送目前仅支持 macOS 和 Windows')).toBeVisible()
|
||||
)
|
||||
expect(screen.getByRole('button', { name: /新建定时日报/ })).toBeDisabled()
|
||||
})
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ import { expect, test } from '@playwright/test'
|
||||
import { launchTestApp } from './support/electron'
|
||||
|
||||
test('SCHEDULED-REPORT-UI-01 opens the scheduled report dialog without viewport overflow', async () => {
|
||||
test.skip(process.platform !== 'darwin', 'The scheduled report send capability is macOS-only')
|
||||
test.skip(
|
||||
process.platform !== 'darwin' && process.platform !== 'win32',
|
||||
'The scheduled report send capability requires macOS or Windows'
|
||||
)
|
||||
const fixture = await launchTestApp({ now: Date.parse('2026-08-27T08:30:00+08:00') })
|
||||
const pageErrors: Error[] = []
|
||||
fixture.page.on('pageerror', (error) => pageErrors.push(error))
|
||||
|
||||
@@ -16,6 +16,7 @@ const imageData = `data:image/png;base64,${fs.readFileSync(path.join(root, 'reso
|
||||
const voiceData = 'UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQAAAAA='
|
||||
const configuredNow = Number(process.env.WXE_E2E_NOW_MS)
|
||||
const updateSimulation = process.env.WXE_E2E_UPDATE_SIMULATION === '1'
|
||||
const personalWechatSupported = process.platform === 'darwin' || process.platform === 'win32'
|
||||
const unsignedMacUpdate = process.env.WXE_E2E_UNSIGNED_MAC_UPDATE === '1'
|
||||
const fixtureNowMs =
|
||||
Number.isFinite(configuredNow) && configuredNow > 0 ? configuredNow : Date.now()
|
||||
@@ -378,8 +379,8 @@ handle('wechat-personal:getRuntimeStatus', () => ({
|
||||
}))
|
||||
handle('wechat-personal:getStatus', () => ({
|
||||
state: 'online',
|
||||
platform: 'darwin',
|
||||
arch: 'arm64',
|
||||
platform: process.platform,
|
||||
arch: process.arch,
|
||||
sipDisabled: true,
|
||||
wechatRunning: true,
|
||||
wechatPid: 4668,
|
||||
@@ -404,16 +405,16 @@ handle('wechat-personal:getStatus', () => ({
|
||||
message: '个人微信已绑定'
|
||||
}))
|
||||
handle('wechat-personal:getSendCapability', () => ({
|
||||
supported: process.platform === 'darwin',
|
||||
ready: process.platform === 'darwin',
|
||||
status: process.platform === 'darwin' ? 'ready' : 'unsupported',
|
||||
supported: personalWechatSupported,
|
||||
ready: personalWechatSupported,
|
||||
status: personalWechatSupported ? 'ready' : 'unsupported',
|
||||
capabilities: {
|
||||
text: process.platform === 'darwin',
|
||||
image: process.platform === 'darwin',
|
||||
voice: process.platform === 'darwin'
|
||||
text: personalWechatSupported,
|
||||
image: personalWechatSupported,
|
||||
voice: personalWechatSupported
|
||||
},
|
||||
senderStatus: {
|
||||
state: process.platform === 'darwin' ? 'online' : 'unsupported_platform',
|
||||
state: personalWechatSupported ? 'online' : 'unsupported_platform',
|
||||
platform: process.platform,
|
||||
arch: process.arch,
|
||||
sipDisabled: true,
|
||||
@@ -429,14 +430,16 @@ handle('wechat-personal:getSendCapability', () => ({
|
||||
imageHookInstalled: true,
|
||||
imageHookReady: true,
|
||||
messageListenerReady: true,
|
||||
canSend: process.platform === 'darwin',
|
||||
canSendText: process.platform === 'darwin',
|
||||
canSendImage: process.platform === 'darwin',
|
||||
canSendVoice: process.platform === 'darwin',
|
||||
canSend: personalWechatSupported,
|
||||
canSendText: personalWechatSupported,
|
||||
canSendImage: personalWechatSupported,
|
||||
canSendVoice: personalWechatSupported,
|
||||
message: '个人微信已绑定'
|
||||
},
|
||||
message:
|
||||
process.platform === 'darwin' ? '个人微信已准备好发送日报' : '微信消息发送目前仅支持 macOS'
|
||||
personalWechatSupported
|
||||
? '个人微信已准备好发送日报'
|
||||
: '微信消息发送目前仅支持 macOS 和 Windows'
|
||||
}))
|
||||
handle('scheduled-report:list', () => [...scheduledReportTasks])
|
||||
handle('scheduled-report:listExecutions', (taskId) =>
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { mkdirSync, mkdtempSync, rmSync, utimesSync, writeFileSync } from 'fs'
|
||||
import {
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
unlinkSync,
|
||||
utimesSync,
|
||||
writeFileSync
|
||||
} from 'fs'
|
||||
import { tmpdir } from 'os'
|
||||
import { join, sep } from 'path'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
@@ -20,7 +28,9 @@ import {
|
||||
normalizeWindowsWechatPort,
|
||||
parseWindowsLoginStatus,
|
||||
parseWindowsHookResponse,
|
||||
parsePersonalWechatHookLog
|
||||
parsePersonalWechatHookLog,
|
||||
prepareWindowsImageFile,
|
||||
WindowsHookHttpError
|
||||
} from '../../src/main/services/personal-wechat-send-service'
|
||||
|
||||
const temporaryDirectories: string[] = []
|
||||
@@ -158,6 +168,14 @@ describe('Windows request', () => {
|
||||
expect(() => parseWindowsHookResponse('', true)).toThrow('空响应')
|
||||
})
|
||||
|
||||
it('preserves the Windows Hook HTTP status and response body', () => {
|
||||
const error = new WindowsHookHttpError(500, '{"ret":-9,"retmsg":"image failed"}')
|
||||
expect(error.message).toBe('HTTP 500: {"ret":-9,"retmsg":"image failed"}')
|
||||
expect(error.status).toBe(500)
|
||||
expect(error.responseBody).toContain('image failed')
|
||||
expect(new WindowsHookHttpError(500, '').message).toBe('HTTP 500')
|
||||
})
|
||||
|
||||
it('allows Windows sending only for a strict logged-in status', () => {
|
||||
expect(parseWindowsLoginStatus({ status: true })).toBe(true)
|
||||
expect(parseWindowsLoginStatus({ status: false })).toBe(false)
|
||||
@@ -217,6 +235,31 @@ describe('Windows request', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('copies adjacent non-ASCII image paths to ASCII temporary files', () => {
|
||||
const root = temporaryDirectory()
|
||||
const content = Buffer.from('fixture-image')
|
||||
for (const fileName of ['测试图片_2026-08-31_经典.png', '测试图片_2026-08-31_经典版.png']) {
|
||||
const source = join(root, fileName)
|
||||
writeFileSync(source, content)
|
||||
|
||||
const prepared = prepareWindowsImageFile(source)
|
||||
|
||||
expect(prepared.temporary).toBe(true)
|
||||
expect(prepared.filePath).not.toContain(fileName)
|
||||
expect(prepared.filePath).toMatch(/^[\x20-\x7e]+$/)
|
||||
expect(readFileSync(prepared.filePath)).toEqual(content)
|
||||
unlinkSync(prepared.filePath)
|
||||
}
|
||||
})
|
||||
|
||||
it('keeps an ASCII image path unchanged', () => {
|
||||
const root = temporaryDirectory()
|
||||
const source = join(root, 'report.png')
|
||||
writeFileSync(source, 'fixture-image')
|
||||
|
||||
expect(prepareWindowsImageFile(source)).toEqual({ filePath: source, temporary: false })
|
||||
})
|
||||
})
|
||||
|
||||
describe('personal WeChat runtime discovery', () => {
|
||||
|
||||
@@ -136,7 +136,7 @@ describe('ScheduledReportApiService', () => {
|
||||
}
|
||||
)
|
||||
|
||||
it('enforces the macOS platform gate even if a provider reports ready', async () => {
|
||||
it('allows Windows when the image send capability is ready', async () => {
|
||||
const { service } = makeApi()
|
||||
const api = new ScheduledReportApiService({
|
||||
service,
|
||||
@@ -145,6 +145,23 @@ describe('ScheduledReportApiService', () => {
|
||||
isDatabaseReady: () => true,
|
||||
platform: 'win32'
|
||||
})
|
||||
const created = await api.create({
|
||||
group: '技术交流群',
|
||||
schedule: { type: 'daily', time: '09:00' }
|
||||
})
|
||||
expect(created.group).toMatchObject({ talker: 'tech@chatroom' })
|
||||
expect(service.createTask).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('keeps unsupported platforms blocked even if a provider reports ready', async () => {
|
||||
const { service } = makeApi()
|
||||
const api = new ScheduledReportApiService({
|
||||
service,
|
||||
getCapability: async () => capability('ready'),
|
||||
listContacts: () => contacts,
|
||||
isDatabaseReady: () => true,
|
||||
platform: 'linux'
|
||||
})
|
||||
await expect(
|
||||
api.create({ group: '技术交流群', schedule: { type: 'daily', time: '09:00' } })
|
||||
).rejects.toMatchObject({ code: 'wechat_not_ready', status: 409 })
|
||||
|
||||
Reference in New Issue
Block a user