Add unit tests for channel utilities and configure testing environment
- Created a new test file `channels.test.ts` to cover utilities related to channel configurations and targets. - Implemented tests for normalizing and grouping selected channels by type, as well as building channel targets from account data and cron history. - Mocked necessary dependencies to isolate tests and ensure accurate results. - Updated `vite.config.ts` to set up the testing environment with jsdom and enable global variables for tests.
This commit is contained in:
287
tests/channels.test.ts
Normal file
287
tests/channels.test.ts
Normal file
@@ -0,0 +1,287 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
configGet: vi.fn(),
|
||||
listStoredChannelAccountRecords: vi.fn(),
|
||||
listCronJobs: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@electron/service/config-service', () => ({
|
||||
default: {
|
||||
get: mocks.configGet,
|
||||
},
|
||||
configManager: {
|
||||
get: mocks.configGet,
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../electron/utils/cron-store', () => ({
|
||||
listCronJobs: mocks.listCronJobs,
|
||||
}));
|
||||
|
||||
vi.mock('../electron/utils/channel-config', () => ({
|
||||
listStoredChannelAccountRecords: mocks.listStoredChannelAccountRecords,
|
||||
}));
|
||||
|
||||
import {
|
||||
getSelectedChannelsConfig,
|
||||
listSelectedChannelAccountGroups,
|
||||
listSelectedChannelTargets,
|
||||
} from '../electron/utils/channels';
|
||||
|
||||
describe('channels utilities', () => {
|
||||
beforeEach(() => {
|
||||
mocks.configGet.mockReset();
|
||||
mocks.listStoredChannelAccountRecords.mockReset();
|
||||
mocks.listCronJobs.mockReset();
|
||||
});
|
||||
|
||||
it('normalizes and groups selected channels by inferred channel type', () => {
|
||||
mocks.configGet.mockReturnValue([
|
||||
{
|
||||
id: ' acct-1 ',
|
||||
channelName: ' 抖音 ',
|
||||
channelUrl: ' https://life.douyin.com/live ',
|
||||
},
|
||||
{
|
||||
id: 'acct-1',
|
||||
channelName: '抖音直播',
|
||||
channelUrl: 'https://douyin.com/duplicate',
|
||||
},
|
||||
{
|
||||
id: 'alpha',
|
||||
channelName: 'Alpha Store',
|
||||
channelUrl: 'https://meituan.com/a',
|
||||
},
|
||||
{
|
||||
id: 'beta',
|
||||
channelName: 'Beta Store',
|
||||
channelUrl: 'https://me.meituan.com/b',
|
||||
},
|
||||
]);
|
||||
|
||||
expect(getSelectedChannelsConfig()).toEqual([
|
||||
{
|
||||
id: 'acct-1',
|
||||
channelName: '抖音',
|
||||
channelUrl: 'https://life.douyin.com/live',
|
||||
},
|
||||
{
|
||||
id: 'alpha',
|
||||
channelName: 'Alpha Store',
|
||||
channelUrl: 'https://meituan.com/a',
|
||||
},
|
||||
{
|
||||
id: 'beta',
|
||||
channelName: 'Beta Store',
|
||||
channelUrl: 'https://me.meituan.com/b',
|
||||
},
|
||||
]);
|
||||
|
||||
mocks.listStoredChannelAccountRecords.mockReturnValue([
|
||||
{
|
||||
channelType: 'douyin',
|
||||
channelLabel: '抖音',
|
||||
defaultAccountId: 'acct-1',
|
||||
channelEnabled: true,
|
||||
accountId: 'acct-1',
|
||||
accountName: '抖音',
|
||||
accountEnabled: true,
|
||||
channelUrl: 'https://life.douyin.com/live',
|
||||
config: {},
|
||||
metadata: {},
|
||||
},
|
||||
{
|
||||
channelType: 'meituan',
|
||||
channelLabel: 'Alpha Store',
|
||||
defaultAccountId: 'alpha',
|
||||
channelEnabled: true,
|
||||
accountId: 'alpha',
|
||||
accountName: 'Alpha Store',
|
||||
accountEnabled: true,
|
||||
channelUrl: 'https://meituan.com/a',
|
||||
config: {},
|
||||
metadata: {},
|
||||
},
|
||||
{
|
||||
channelType: 'meituan',
|
||||
channelLabel: 'Alpha Store',
|
||||
defaultAccountId: 'alpha',
|
||||
channelEnabled: true,
|
||||
accountId: 'beta',
|
||||
accountName: 'Beta Store',
|
||||
accountEnabled: true,
|
||||
channelUrl: 'https://me.meituan.com/b',
|
||||
config: {},
|
||||
metadata: {},
|
||||
},
|
||||
]);
|
||||
|
||||
const groups = listSelectedChannelAccountGroups({
|
||||
agents: [
|
||||
{ id: 'Agent-A', name: 'Ada' },
|
||||
{ id: 'agent-b', name: 'Ben' },
|
||||
],
|
||||
channelOwners: {
|
||||
meituan: 'Agent-A',
|
||||
},
|
||||
channelAccountOwners: {
|
||||
'meituan:alpha': 'agent-b',
|
||||
},
|
||||
});
|
||||
|
||||
const byType = Object.fromEntries(groups.map((group) => [group.channelType, group]));
|
||||
|
||||
expect(byType.douyin).toMatchObject({
|
||||
channelType: 'douyin',
|
||||
channelLabel: '抖音',
|
||||
defaultAccountId: 'acct-1',
|
||||
status: 'degraded',
|
||||
accounts: [
|
||||
{
|
||||
accountId: 'acct-1',
|
||||
name: '抖音',
|
||||
configured: true,
|
||||
status: 'degraded',
|
||||
isDefault: true,
|
||||
channelUrl: 'https://life.douyin.com/live',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(byType.meituan).toMatchObject({
|
||||
channelType: 'meituan',
|
||||
channelLabel: 'Alpha Store',
|
||||
defaultAccountId: 'alpha',
|
||||
status: 'connected',
|
||||
accounts: [
|
||||
{
|
||||
accountId: 'alpha',
|
||||
name: 'Alpha Store',
|
||||
configured: true,
|
||||
status: 'connected',
|
||||
isDefault: true,
|
||||
agentId: 'agent-b',
|
||||
bindingScope: 'account',
|
||||
channelUrl: 'https://meituan.com/a',
|
||||
},
|
||||
{
|
||||
accountId: 'beta',
|
||||
name: 'Beta Store',
|
||||
configured: true,
|
||||
status: 'connected',
|
||||
isDefault: false,
|
||||
agentId: 'agent-a',
|
||||
bindingScope: 'channel',
|
||||
channelUrl: 'https://me.meituan.com/b',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('builds channel targets from account data, URL hints, and cron history', () => {
|
||||
mocks.listStoredChannelAccountRecords.mockReturnValue([
|
||||
{
|
||||
id: 'acct-1',
|
||||
channelType: 'douyin',
|
||||
channelLabel: '抖音直播间',
|
||||
defaultAccountId: 'acct-1',
|
||||
channelEnabled: true,
|
||||
accountId: 'acct-1',
|
||||
accountName: '抖音直播间',
|
||||
accountEnabled: true,
|
||||
channelUrl: 'https://webhook.example.com/send?roomId=ROOM-9#panel?threadId=TH-1',
|
||||
config: {},
|
||||
metadata: {},
|
||||
},
|
||||
]);
|
||||
|
||||
mocks.listCronJobs.mockReturnValue([
|
||||
{
|
||||
id: 'cron-1',
|
||||
name: 'nightly announce',
|
||||
message: 'hello',
|
||||
schedule: '* * * * *',
|
||||
enabled: true,
|
||||
createdAt: '2026-04-18T00:00:00.000Z',
|
||||
updatedAt: '2026-04-18T00:00:00.000Z',
|
||||
delivery: {
|
||||
mode: 'announce',
|
||||
channel: 'douyin',
|
||||
accountId: 'acct-1',
|
||||
to: 'https://history.example.com/announce',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'cron-2',
|
||||
name: 'ignored',
|
||||
message: 'skip me',
|
||||
schedule: '* * * * *',
|
||||
enabled: true,
|
||||
createdAt: '2026-04-18T00:00:00.000Z',
|
||||
updatedAt: '2026-04-18T00:00:00.000Z',
|
||||
delivery: {
|
||||
mode: 'announce',
|
||||
channel: 'fliggy',
|
||||
to: 'should-not-appear',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const targets = listSelectedChannelTargets('douyin', 'acct-1');
|
||||
|
||||
expect(targets[0]).toMatchObject({
|
||||
value: '抖音直播间',
|
||||
kind: 'name',
|
||||
source: 'channel-name',
|
||||
});
|
||||
|
||||
expect(targets).toContainEqual(expect.objectContaining({
|
||||
value: 'acct-1',
|
||||
label: '账号 ID · acct-1',
|
||||
kind: 'identifier',
|
||||
source: 'account-id',
|
||||
channelType: 'douyin',
|
||||
accountId: 'acct-1',
|
||||
}));
|
||||
|
||||
expect(targets).toContainEqual(expect.objectContaining({
|
||||
value: 'ROOM-9',
|
||||
label: 'Room ID · ROOM-9',
|
||||
kind: 'identifier',
|
||||
source: 'query-param',
|
||||
channelType: 'douyin',
|
||||
accountId: 'acct-1',
|
||||
}));
|
||||
|
||||
expect(targets).toContainEqual(expect.objectContaining({
|
||||
value: 'TH-1',
|
||||
label: 'Thread ID · TH-1',
|
||||
kind: 'identifier',
|
||||
source: 'hash-param',
|
||||
channelType: 'douyin',
|
||||
accountId: 'acct-1',
|
||||
}));
|
||||
|
||||
expect(targets).toContainEqual(expect.objectContaining({
|
||||
kind: 'webhook',
|
||||
source: 'channel-url',
|
||||
channelType: 'douyin',
|
||||
accountId: 'acct-1',
|
||||
}));
|
||||
|
||||
expect(targets).toContainEqual(expect.objectContaining({
|
||||
value: 'https://history.example.com/announce',
|
||||
kind: 'webhook',
|
||||
source: 'fallback',
|
||||
channelType: 'douyin',
|
||||
accountId: 'acct-1',
|
||||
}));
|
||||
|
||||
const filtered = listSelectedChannelTargets('douyin', 'acct-1', 'history');
|
||||
expect(filtered).toHaveLength(1);
|
||||
expect(filtered[0]).toMatchObject({
|
||||
value: 'https://history.example.com/announce',
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user