mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-03 18:33:14 +08:00
feat: 精确会话自动化规则
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { RuleEditorPanel } from '../../src/renderer/src/features/automation/RuleEditorPanel'
|
||||
import {
|
||||
createDefaultDailyReportRule,
|
||||
type AutomationRule,
|
||||
type AutomationRuleDraft
|
||||
} from '../../src/shared/automation'
|
||||
|
||||
/**
|
||||
* 「回复前等待」是**规则自己的一项执行参数**,配置位置就是
|
||||
* 「编辑自动化 → 3 · 触发后执行(命中条件后,TraceMemo 会按下面的顺序依次执行)」。
|
||||
*
|
||||
* 这一层要锁的是**接线**:初值必须来自规则本身、保存必须带回去。
|
||||
* 因为 `draftFromRule` 是逐字段搬运的,漏一个字段就会被 `normalizeRuleDraft`
|
||||
* 的默认值悄悄覆盖(把用户设成 0 的等待重置回 2 秒),这种 bug 只有真渲染才发现。
|
||||
*/
|
||||
const GROUPS = [{ id: '12345678@chatroom', name: '测试群' }]
|
||||
|
||||
function renderEditor(
|
||||
rule: AutomationRule,
|
||||
onSave: (draft: AutomationRuleDraft) => void = () => {}
|
||||
): void {
|
||||
render(
|
||||
<RuleEditorPanel
|
||||
mode="edit"
|
||||
rule={rule}
|
||||
groups={GROUPS}
|
||||
saving={false}
|
||||
onCancel={() => {}}
|
||||
onSave={onSave}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const delayField = (): HTMLInputElement =>
|
||||
screen.getByLabelText('回复前等待秒数') as HTMLInputElement
|
||||
|
||||
describe('RuleEditorPanel · 回复前等待', () => {
|
||||
it('它就在「3 · 触发后执行」里,和动作顺序在同一段', () => {
|
||||
renderEditor(createDefaultDailyReportRule(0))
|
||||
|
||||
expect(screen.getByText('命中条件后,TraceMemo 会按下面的顺序依次执行。')).toBeInTheDocument()
|
||||
expect(delayField()).toBeInTheDocument()
|
||||
expect(screen.getByText('回复前等待(秒)')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('初值来自规则本身,不是硬编码的默认值', () => {
|
||||
const rule = createDefaultDailyReportRule(0)
|
||||
rule.replyDelaySeconds = 7
|
||||
renderEditor(rule)
|
||||
|
||||
expect(delayField()).toHaveValue(7)
|
||||
})
|
||||
|
||||
it('保存时把改动带回去', () => {
|
||||
const saved: AutomationRuleDraft[] = []
|
||||
renderEditor(createDefaultDailyReportRule(0), (draft) => saved.push(draft))
|
||||
|
||||
fireEvent.change(delayField(), { target: { value: '0' } })
|
||||
fireEvent.click(screen.getByRole('button', { name: '保存' }))
|
||||
|
||||
expect(saved).toHaveLength(1)
|
||||
expect(saved[0].replyDelaySeconds).toBe(0)
|
||||
})
|
||||
|
||||
it('用户设成 0(立刻回复)时,保存不会把它顶回默认 2 秒', () => {
|
||||
const saved: AutomationRuleDraft[] = []
|
||||
const rule = createDefaultDailyReportRule(0)
|
||||
rule.replyDelaySeconds = 0
|
||||
renderEditor(rule, (draft) => saved.push(draft))
|
||||
|
||||
// 什么都不改,直接保存:0 必须原样保留(而不是被默认的 2 顶掉)。
|
||||
fireEvent.click(screen.getByRole('button', { name: '保存' }))
|
||||
|
||||
expect(saved[0].replyDelaySeconds).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* 「所有群聊」的边界提示。
|
||||
*
|
||||
* 真实能力是:同一时刻多个会话同时来消息时可能漏掉少量触发。
|
||||
* 这条限制**只挂在「所有群聊」这个上下文里** —— 首页不挂、指定群聊时不挂,
|
||||
* 而且只讲用户能理解的结果,不解释表事件 / 会话回读这类实现细节。
|
||||
*/
|
||||
const LIMITATION_TEXT = '当前版本在多个会话同时收到消息时,极少数自动化触发可能遗漏。'
|
||||
|
||||
function renderScopeEditor(conversationIds: string[]): void {
|
||||
const rule = createDefaultDailyReportRule(0)
|
||||
rule.conditions.conversationIds = conversationIds
|
||||
renderEditor(rule)
|
||||
}
|
||||
|
||||
describe('RuleEditorPanel · 生效范围的边界提示', () => {
|
||||
it('一个群都没选(所有群聊)时显示提示', () => {
|
||||
renderScopeEditor([])
|
||||
|
||||
// 编辑页与右侧效果预览都会显示范围描述,所以这里用 getAllByText。
|
||||
expect(screen.getAllByText('所有群聊').length).toBeGreaterThan(0)
|
||||
expect(screen.getAllByText(LIMITATION_TEXT)).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('选了具体群聊时不显示提示', () => {
|
||||
renderScopeEditor([GROUPS[0].id])
|
||||
|
||||
expect(screen.getAllByText('已选 1 个群').length).toBeGreaterThan(0)
|
||||
expect(screen.queryAllByText(LIMITATION_TEXT)).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('提示里不出现底层实现词,也不做绝对承诺', () => {
|
||||
renderScopeEditor([])
|
||||
|
||||
expect(LIMITATION_TEXT).not.toMatch(/WCDB|SessionTable|表变化|native|回读/)
|
||||
expect(document.body.textContent).not.toMatch(/覆盖所有群聊|100%|不会遗漏/)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,44 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { ToastProvider } from '../../src/renderer/src/components/ui'
|
||||
import { AutomationWorkspace } from '../../src/renderer/src/features/automation/AutomationWorkspace'
|
||||
|
||||
/**
|
||||
* 首页状态卡只讲用户能理解的状态。
|
||||
*
|
||||
* 以前这里挂着一句「当前仅能捕获最近活跃的会话」,还用 Tooltip 解释了
|
||||
* 「微信底层只会上报有表发生了变化」——那是开发报告的内容,不该出现在普通用户界面。
|
||||
* 真实边界改挂在「规则编辑页 → 所有群聊」那个具体上下文里。
|
||||
*/
|
||||
describe('Automation 首页状态卡', () => {
|
||||
function renderWorkspace(): void {
|
||||
render(
|
||||
<ToastProvider>
|
||||
<AutomationWorkspace dbReady={false} />
|
||||
</ToastProvider>
|
||||
)
|
||||
}
|
||||
|
||||
it('仍然如实展示「消息监听」与发送能力两项', () => {
|
||||
renderWorkspace()
|
||||
|
||||
expect(screen.getByText('消息监听')).toBeInTheDocument()
|
||||
expect(screen.getByText('发送能力')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('不再出现「最近活跃的会话」这类实现说明', () => {
|
||||
renderWorkspace()
|
||||
|
||||
expect(screen.queryByText(/最近活跃的会话/)).toBeNull()
|
||||
expect(screen.queryByText(/有表发生了变化/)).toBeNull()
|
||||
})
|
||||
|
||||
it('首页不出现底层实现词', () => {
|
||||
renderWorkspace()
|
||||
|
||||
expect(document.body.textContent).not.toMatch(
|
||||
/WCDB|SessionTable|表变化|native event|回读机制|coalesce/
|
||||
)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user