feat(web): make instance detail the operations workbench
This commit is contained in:
@@ -17,7 +17,39 @@ const instance: InstanceContext = {
|
||||
afterEach(cleanup);
|
||||
|
||||
describe('MessagesModule', () => {
|
||||
it('organizes messages as conversations and starts a new conversation before composing', async () => {
|
||||
const user = userEvent.setup();
|
||||
const dataSource: MessagesDataSource = {
|
||||
load: vi.fn().mockResolvedValue({
|
||||
messages: [
|
||||
{
|
||||
id: '32',
|
||||
direction: 'incoming',
|
||||
phoneNumber: '10086',
|
||||
content: '余额提醒',
|
||||
timestamp: '2026-07-18 09:09:20',
|
||||
status: 'received',
|
||||
},
|
||||
],
|
||||
}),
|
||||
send: vi.fn(),
|
||||
};
|
||||
render(<MessagesModule instance={instance} dataSource={dataSource} />);
|
||||
|
||||
const conversations = await screen.findByRole('region', { name: '短信对话' });
|
||||
expect(within(conversations).getByRole('button', { name: /10086/ })).toBeTruthy();
|
||||
expect(screen.getByRole('region', { name: '当前短信会话' }).textContent).toContain(
|
||||
'选择一个对话',
|
||||
);
|
||||
|
||||
await user.click(within(conversations).getByRole('button', { name: '新建对话' }));
|
||||
const dialog = screen.getByRole('dialog', { name: '新建对话' });
|
||||
expect(within(dialog).getByRole('textbox', { name: '手机号' })).toBeTruthy();
|
||||
expect(screen.queryByRole('textbox', { name: '短信内容' })).toBeNull();
|
||||
});
|
||||
|
||||
it('shows the real message list including content and never exposes PDU', async () => {
|
||||
const user = userEvent.setup();
|
||||
const dataSource: MessagesDataSource = {
|
||||
load: vi.fn().mockResolvedValue({
|
||||
messages: [
|
||||
@@ -35,11 +67,142 @@ describe('MessagesModule', () => {
|
||||
send: vi.fn(),
|
||||
};
|
||||
render(<MessagesModule instance={instance} dataSource={dataSource} />);
|
||||
expect(await screen.findByText('余额提醒')).toBeTruthy();
|
||||
await user.click(await screen.findByRole('button', { name: /10086/ }));
|
||||
expect((await screen.findAllByText('余额提醒')).length).toBeGreaterThanOrEqual(2);
|
||||
expect(screen.getByText(/收到 · 10086/)).toBeTruthy();
|
||||
expect(document.body.textContent).not.toContain('secret-pdu');
|
||||
});
|
||||
|
||||
it('clears recipient-bound drafts when the conversation changes', async () => {
|
||||
const user = userEvent.setup();
|
||||
const dataSource: MessagesDataSource = {
|
||||
load: vi.fn().mockResolvedValue({
|
||||
messages: [
|
||||
{
|
||||
id: 'one',
|
||||
direction: 'incoming',
|
||||
phoneNumber: '10086',
|
||||
content: 'A',
|
||||
timestamp: '2026-07-18 09:09:20',
|
||||
status: 'received',
|
||||
},
|
||||
{
|
||||
id: 'two',
|
||||
direction: 'incoming',
|
||||
phoneNumber: '10010',
|
||||
content: 'B',
|
||||
timestamp: '2026-07-18 09:10:20',
|
||||
status: 'received',
|
||||
},
|
||||
],
|
||||
}),
|
||||
send: vi.fn(),
|
||||
};
|
||||
render(<MessagesModule instance={instance} dataSource={dataSource} />);
|
||||
await user.click(await screen.findByRole('button', { name: /10086/ }));
|
||||
await user.type(screen.getByRole('textbox', { name: '短信内容' }), 'draft for 10086');
|
||||
await user.click(screen.getByRole('button', { name: /10010/ }));
|
||||
expect((screen.getByRole('textbox', { name: '短信内容' }) as HTMLTextAreaElement).value).toBe(
|
||||
'',
|
||||
);
|
||||
});
|
||||
|
||||
it('discards a pending confirmation when the instance owner changes', async () => {
|
||||
const user = userEvent.setup();
|
||||
const send = vi.fn().mockResolvedValue(undefined);
|
||||
const dataSource: MessagesDataSource = {
|
||||
load: vi.fn().mockResolvedValue({ messages: [] }),
|
||||
send,
|
||||
};
|
||||
const second = { ...instance, id: 'beta', name: 'Beta' };
|
||||
const { rerender } = render(<MessagesModule instance={instance} dataSource={dataSource} />);
|
||||
await user.click(await screen.findByRole('button', { name: '新建对话' }));
|
||||
const dialog = screen.getByRole('dialog', { name: '新建对话' });
|
||||
await user.type(within(dialog).getByRole('textbox', { name: '手机号' }), '10086');
|
||||
await user.click(within(dialog).getByRole('button', { name: '开始对话' }));
|
||||
await user.type(screen.getByRole('textbox', { name: '短信内容' }), 'owner-bound draft');
|
||||
await user.click(screen.getByRole('button', { name: '发送短信' }));
|
||||
expect(screen.getByRole('region', { name: '确认发送短信' })).toBeTruthy();
|
||||
|
||||
rerender(<MessagesModule instance={second} dataSource={dataSource} />);
|
||||
expect(screen.queryByRole('region', { name: '确认发送短信' })).toBeNull();
|
||||
expect(send).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('drops an old confirmation when starting another recipient conversation', async () => {
|
||||
const user = userEvent.setup();
|
||||
const dataSource: MessagesDataSource = {
|
||||
load: vi.fn().mockResolvedValue({
|
||||
messages: [
|
||||
{
|
||||
id: 'one',
|
||||
direction: 'incoming',
|
||||
phoneNumber: '10086',
|
||||
content: 'A',
|
||||
timestamp: '2026-07-18 09:09:20',
|
||||
status: 'received',
|
||||
},
|
||||
],
|
||||
}),
|
||||
send: vi.fn(),
|
||||
};
|
||||
render(<MessagesModule instance={instance} dataSource={dataSource} />);
|
||||
await user.click(await screen.findByRole('button', { name: /10086/ }));
|
||||
await user.type(screen.getByRole('textbox', { name: '短信内容' }), 'old confirmation');
|
||||
await user.click(screen.getByRole('button', { name: '发送短信' }));
|
||||
expect(screen.getByRole('region', { name: '确认发送短信' })).toBeTruthy();
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '新建对话' }));
|
||||
const dialog = screen.getByRole('dialog', { name: '新建对话' });
|
||||
await user.type(within(dialog).getByRole('textbox', { name: '手机号' }), '10010');
|
||||
await user.click(within(dialog).getByRole('button', { name: '开始对话' }));
|
||||
expect(screen.queryByRole('region', { name: '确认发送短信' })).toBeNull();
|
||||
});
|
||||
|
||||
it('preserves a new recipient draft when an earlier send completes late', async () => {
|
||||
const user = userEvent.setup();
|
||||
let resolveSend: (() => void) | undefined;
|
||||
const send = vi.fn(
|
||||
() =>
|
||||
new Promise<void>((resolve) => {
|
||||
resolveSend = resolve;
|
||||
}),
|
||||
);
|
||||
const dataSource: MessagesDataSource = {
|
||||
load: vi.fn().mockResolvedValue({
|
||||
messages: [
|
||||
{
|
||||
id: 'one',
|
||||
direction: 'incoming',
|
||||
phoneNumber: '10086',
|
||||
content: 'A',
|
||||
timestamp: '2026-07-18 09:09:20',
|
||||
status: 'received',
|
||||
},
|
||||
{
|
||||
id: 'two',
|
||||
direction: 'incoming',
|
||||
phoneNumber: '10010',
|
||||
content: 'B',
|
||||
timestamp: '2026-07-18 09:10:20',
|
||||
status: 'received',
|
||||
},
|
||||
],
|
||||
}),
|
||||
send,
|
||||
};
|
||||
render(<MessagesModule instance={instance} dataSource={dataSource} />);
|
||||
await user.click(await screen.findByRole('button', { name: /10086/ }));
|
||||
await user.type(screen.getByRole('textbox', { name: '短信内容' }), 'send to 10086');
|
||||
await user.click(screen.getByRole('button', { name: '发送短信' }));
|
||||
await user.click(screen.getByRole('button', { name: '确认并发送' }));
|
||||
await user.click(screen.getByRole('button', { name: /10010/ }));
|
||||
await user.type(screen.getByRole('textbox', { name: '短信内容' }), 'draft for 10010');
|
||||
|
||||
resolveSend?.();
|
||||
expect(await screen.findByDisplayValue('draft for 10010')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('requires an explicit review step before it submits exactly once', async () => {
|
||||
const user = userEvent.setup();
|
||||
const send = vi.fn().mockResolvedValue(undefined);
|
||||
@@ -48,7 +211,10 @@ describe('MessagesModule', () => {
|
||||
send,
|
||||
};
|
||||
render(<MessagesModule instance={instance} dataSource={dataSource} />);
|
||||
await user.type(screen.getByRole('textbox', { name: '手机号' }), '10086');
|
||||
await user.click(await screen.findByRole('button', { name: '新建对话' }));
|
||||
const dialog = screen.getByRole('dialog', { name: '新建对话' });
|
||||
await user.type(within(dialog).getByRole('textbox', { name: '手机号' }), '10086');
|
||||
await user.click(within(dialog).getByRole('button', { name: '开始对话' }));
|
||||
await user.type(screen.getByRole('textbox', { name: '短信内容' }), 'CXLL');
|
||||
await user.click(screen.getByRole('button', { name: '发送短信' }));
|
||||
expect(send).not.toHaveBeenCalled();
|
||||
|
||||
Reference in New Issue
Block a user