mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-03 18:33:14 +08:00
fix: 修复退群监控列表错误, 界面左侧滚动条
This commit is contained in:
@@ -1322,7 +1322,11 @@ for (const channel of [
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const window = new BrowserWindow({
|
||||
// Keep the E2E window aligned with createWindow() in src/main/index.ts.
|
||||
/*
|
||||
* E2E 窗口**刻意固定** 1400×800,不再跟随 `createWindow()`
|
||||
* (后者已改为按屏幕工作区 60% 计算初始尺寸):visual 基线是按这个尺寸
|
||||
* 评审入库的,若跟随真实逻辑,截图尺寸会随 runner 屏幕浮动、基线永远对不上。
|
||||
*/
|
||||
width: 1400,
|
||||
height: 800,
|
||||
show: false,
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { existsSync, mkdtempSync, readFileSync, readJsonSync, rmSync, writeJsonSync } from 'fs-extra'
|
||||
import {
|
||||
existsSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
readJsonSync,
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
writeJsonSync
|
||||
} from 'fs-extra'
|
||||
import { tmpdir } from 'os'
|
||||
import { join } from 'path'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
@@ -49,6 +57,37 @@ function installGroupDb(): void {
|
||||
mocks.chat.getChatDb.mockReturnValue({ getWcdb4Client: () => client })
|
||||
}
|
||||
|
||||
/** 写真实形态的 append-only 事件文件:一行一条,文件顺序即传入顺序。 */
|
||||
function writePersistedEvents(detectedAtList: number[]): void {
|
||||
writeFileSync(
|
||||
join(mocks.userData, 'group-exit-monitor-events.jsonl'),
|
||||
detectedAtList
|
||||
.map((detectedAt, index) => `${JSON.stringify(persistedEvent(index, detectedAt))}\n`)
|
||||
.join(''),
|
||||
'utf8'
|
||||
)
|
||||
}
|
||||
|
||||
/** 一条能通过 normalizeEvents 校验的持久化事件(人数必须是净减少)。 */
|
||||
function persistedEvent(index: number, detectedAt: number): Record<string, unknown> {
|
||||
return {
|
||||
id: `room@chatroom:wxid_member_${index}:${detectedAt}:${index}`,
|
||||
contactId: 'room@chatroom-md5',
|
||||
roomId: 'room@chatroom',
|
||||
groupName: '测试群',
|
||||
memberWxid: `wxid_member_${index}`,
|
||||
memberName: `成员${index}`,
|
||||
wechatName: `成员${index}`,
|
||||
groupRemark: '',
|
||||
contactRemark: '',
|
||||
previousCount: 40 + index,
|
||||
currentCount: 39 + index,
|
||||
delta: -1,
|
||||
message: `成员${index}退出了测试群`,
|
||||
detectedAt
|
||||
}
|
||||
}
|
||||
|
||||
function writeBaseline(
|
||||
snapshots: Array<{
|
||||
roomId: string
|
||||
@@ -820,4 +859,47 @@ describe('GroupExitMonitorService', () => {
|
||||
expect(mocks.sender.send).toHaveBeenCalledOnce()
|
||||
expect(service.getState().events).toHaveLength(1)
|
||||
})
|
||||
|
||||
/*
|
||||
* 回归:append-only 文件的物理行序不保证时间有序 —— 迁移写入的一段是倒序
|
||||
* (新 → 旧),之后 append 的新事件是正序。内存必须显式重建「新在前」,
|
||||
* 否则列表顶部恒为最旧的一批(实测:09-20 的事件一直占第一条,
|
||||
* 09-21~09-23 的 46 条被排到第 211 位之后,看起来像「新事件全丢了」)。
|
||||
*/
|
||||
it('returns newest events first even when the append-only file mixes both directions', () => {
|
||||
const base = Date.parse('2026-09-20T19:00:00+08:00')
|
||||
// 迁移段:倒序(新 → 旧)
|
||||
const migrated = [base, base - 60_000, base - 120_000]
|
||||
// 追加段:正序(旧 → 新)
|
||||
const appended = [base + 86_400_000, base + 172_800_000]
|
||||
writeBaseline([])
|
||||
writePersistedEvents([...migrated, ...appended])
|
||||
|
||||
const state = new GroupExitMonitorService().getState()
|
||||
|
||||
expect(state.events.map((event) => event.detectedAt)).toEqual(
|
||||
[...migrated, ...appended].sort((left, right) => right - left)
|
||||
)
|
||||
// 修复前这里会是迁移段的首条(base),正是用户看到的「顶部恒为 09-20」。
|
||||
expect(state.events[0].detectedAt).toBe(base + 172_800_000)
|
||||
})
|
||||
|
||||
/*
|
||||
* 回归:state 回传上限必须截「最新」而不是「最旧」。
|
||||
* 磁盘是 append-only,新事件在文件末尾;未排序时 slice(0, 500) 会恰好把
|
||||
* 最新的事件全部挡在界面之外(按 11 条/天估算约 2026-10-16 触发)。
|
||||
*/
|
||||
it('keeps the newest events when history exceeds the state payload limit', () => {
|
||||
const base = Date.parse('2026-09-20T00:00:00+08:00')
|
||||
const total = 520
|
||||
writeBaseline([])
|
||||
writePersistedEvents(Array.from({ length: total }, (_, index) => base + index * 60_000))
|
||||
|
||||
const state = new GroupExitMonitorService().getState()
|
||||
|
||||
expect(state.totalEventCount).toBe(total)
|
||||
expect(state.events).toHaveLength(500)
|
||||
expect(state.events[0].detectedAt).toBe(base + (total - 1) * 60_000)
|
||||
expect(state.events[state.events.length - 1].detectedAt).toBe(base + (total - 500) * 60_000)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user