@nextclaw/ui 0.12.9 → 0.12.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -0
- package/dist/assets/ChannelsList-M9FTK1Ak.js +8 -0
- package/dist/assets/DocBrowser-CH7-GxlL.js +1 -0
- package/dist/assets/{DocBrowser-6ReNjvzF.js → DocBrowser-DMfr0Oow.js} +1 -1
- package/dist/assets/{DocBrowserContext-B6SpA7Qs.js → DocBrowserContext-BXydqby-.js} +1 -1
- package/dist/assets/{LogoBadge-ByNLYg65.js → LogoBadge-hO7tY7hE.js} +1 -1
- package/dist/assets/ModelConfig-CNIgLf0e.js +1 -0
- package/dist/assets/{ProviderScopedModelInput-Da7khnBA.js → ProviderScopedModelInput-B3HWP4oz.js} +1 -1
- package/dist/assets/ProvidersList-CHjMnRhX.js +1 -0
- package/dist/assets/RuntimeConfig-psp8nMSG.js +1 -0
- package/dist/assets/SearchConfig-CSoKip1f.js +1 -0
- package/dist/assets/{SecretsConfig-D281Rotl.js → SecretsConfig-MEt6MjuD.js} +2 -2
- package/dist/assets/SessionsConfig-DifCiXwR.js +2 -0
- package/dist/assets/{app-query-client-VnFElj4E.js → app-query-client-9jNewezV.js} +1 -1
- package/dist/assets/{book-open-BdcxxoQu.js → book-open-DzdUViDm.js} +1 -1
- package/dist/assets/chat-page-CLp0UV0Y.js +58 -0
- package/dist/assets/chat-session-display-DsYHx0RZ.js +1 -0
- package/dist/assets/{chunk-JZWAC4HX-DK5HPmIK.js → chunk-JZWAC4HX-C5dEc8hV.js} +1 -1
- package/dist/assets/{client-_i4MU2bB.js → client-C-8fH7-c.js} +1 -1
- package/dist/assets/{config-DtIQwrHF.js → config-CBScxsdV.js} +1 -1
- package/dist/assets/config-split-page-BUout_Ak.js +1 -0
- package/dist/assets/{createLucideIcon-BSeTgkZW.js → createLucideIcon-dy5ie7Ox.js} +1 -1
- package/dist/assets/desktop-update-config-2BS6BMkW.js +1 -0
- package/dist/assets/{dist-ccBFUi-o.js → dist-BruyLa92.js} +1 -1
- package/dist/assets/{dist-6TrrnPCR.js → dist-Cy7_j6hA.js} +1 -1
- package/dist/assets/{download-BhDxnyvU.js → download-BD0ETkB-.js} +1 -1
- package/dist/assets/{external-link-BgErLCNT.js → external-link-kZSAO8nT.js} +1 -1
- package/dist/assets/{hash-Bl7dr_UG.js → hash-BHJC2Ovu.js} +1 -1
- package/dist/assets/{i18n-eDHeDY0n.js → i18n-CpTZLchQ.js} +1 -1
- package/dist/assets/index-mW8W2FUu.css +1 -0
- package/dist/assets/index-zDZfXoI4.js +6 -0
- package/dist/assets/{infiniteQueryBehavior-ZDS92Qpp.js → infiniteQueryBehavior-CyER9hv0.js} +1 -1
- package/dist/assets/loader-circle-Bc2gCU33.js +1 -0
- package/dist/assets/{logos-x89HbrZ4.js → logos-B7gRObP8.js} +1 -1
- package/dist/assets/marketplace-page-3qVMnF3d.js +1 -0
- package/dist/assets/marketplace-page-BhFIeQzI.js +49 -0
- package/dist/assets/mcp-marketplace-page-DYfteJ1D.js +40 -0
- package/dist/assets/{page-layout-vZnghcFy.js → page-layout-0UcO9H9Z.js} +1 -1
- package/dist/assets/play-CKDjSQFL.js +1 -0
- package/dist/assets/plus-CG0QrVY_.js +1 -0
- package/dist/assets/{refresh-ccw-DT98i__E.js → refresh-ccw-COVhNHtN.js} +1 -1
- package/dist/assets/{refresh-cw-C47QSEwg.js → refresh-cw-Bcv40SXy.js} +1 -1
- package/dist/assets/remote-access-page-CWHG-sug.js +1 -0
- package/dist/assets/{rotate-cw-JtFzpNn6.js → rotate-cw-oHMKJMC8.js} +1 -1
- package/dist/assets/{save-3S6-H3Xw.js → save-EqJPOF0G.js} +1 -1
- package/dist/assets/search-BCAlB8nz.js +1 -0
- package/dist/assets/security-config-Slh0Mayz.js +1 -0
- package/dist/assets/select-CVz0t7MF.js +41 -0
- package/dist/assets/setting-row-CbVHAuQt.js +1 -0
- package/dist/assets/skeleton-D5rdKvzy.js +1 -0
- package/dist/assets/{status-dot-vbanNPFU.js → status-dot-DpPtVzQT.js} +1 -1
- package/dist/assets/{switch-BsLtHOH-.js → switch-CM29eCAR.js} +1 -1
- package/dist/assets/{tabs-custom-D3HYMt6k.js → tabs-custom-YcZUWn3o.js} +1 -1
- package/dist/assets/tag-chip-DMXdnLcj.js +1 -0
- package/dist/assets/{trash-2-G48scll7.js → trash-2-mJT6oWa2.js} +1 -1
- package/dist/assets/{use-infinite-scroll-loader-DkNhD-42.js → use-infinite-scroll-loader-DJ1L81Dz.js} +1 -1
- package/dist/assets/{useConfirmDialog-BkvTN-vd.js → useConfirmDialog-BsVuqu1x.js} +1 -1
- package/dist/assets/{useMutation-CBWjE2uj.js → useMutation-CNcz2fgt.js} +1 -1
- package/dist/assets/x-Czwxm82I.js +1 -0
- package/dist/index.html +22 -22
- package/dist/runtime-icons/claude.ico +0 -0
- package/dist/runtime-icons/codex-openai.svg +6 -0
- package/dist/runtime-icons/hermes-agent.png +0 -0
- package/package.json +6 -6
- package/public/runtime-icons/claude.ico +0 -0
- package/public/runtime-icons/codex-openai.svg +6 -0
- package/public/runtime-icons/hermes-agent.png +0 -0
- package/src/account/components/account-panel.tsx +217 -97
- package/src/account/managers/account.manager.ts +3 -2
- package/src/api/chat-session-type.types.ts +7 -0
- package/src/api/runtime-control.types.ts +8 -0
- package/src/api/types.ts +8 -0
- package/src/app.tsx +221 -57
- package/src/components/agents/agent-dialogs.tsx +499 -0
- package/src/components/agents/agents-page.test.tsx +238 -0
- package/src/components/agents/agents-page.tsx +435 -0
- package/src/components/chat/ChatSidebar.tsx +11 -35
- package/src/components/chat/chat-conversation-panel.test.tsx +20 -0
- package/src/components/chat/chat-conversation-panel.tsx +83 -13
- package/src/components/chat/chat-page-shell.tsx +19 -13
- package/src/components/chat/chat-session-type-option-item.test.tsx +46 -0
- package/src/components/chat/chat-session-type-option-item.tsx +68 -0
- package/src/components/chat/chat-session-workspace-file-preview.test.tsx +87 -0
- package/src/components/chat/chat-session-workspace-file-preview.tsx +14 -43
- package/src/components/chat/chat-session-workspace-panel-nav.tsx +8 -2
- package/src/components/chat/chat-sidebar-project-groups.tsx +11 -36
- package/src/components/chat/ncp/__tests__/ncp-session-adapter.cancelled-tool.test.ts +77 -0
- package/src/components/chat/ncp/ncp-chat-page.tsx +2 -0
- package/src/components/chat/ncp/ncp-session-adapter.test.ts +1 -0
- package/src/components/chat/ncp/ncp-session-adapter.ts +3 -0
- package/src/components/chat/ncp/page/ncp-chat-derived-state.ts +10 -4
- package/src/components/chat/stores/chat-input.store.ts +2 -1
- package/src/components/chat/stores/chat-thread.store.ts +3 -1
- package/src/components/chat/useChatSessionTypeState.ts +10 -1
- package/src/components/chat/workspace/chat-session-workspace-file-breadcrumbs.tsx +86 -0
- package/src/components/common/BrandHeader.tsx +3 -1
- package/src/components/common/session-context-icon.tsx +15 -2
- package/src/components/common/{TagInput.tsx → tag-input.tsx} +25 -17
- package/src/components/config/ChannelForm.test.tsx +89 -3
- package/src/components/config/ChannelForm.tsx +157 -188
- package/src/components/config/ChannelsList.test.tsx +163 -119
- package/src/components/config/ChannelsList.tsx +90 -101
- package/src/components/config/ProviderForm.tsx +108 -146
- package/src/components/config/ProvidersList.tsx +100 -123
- package/src/components/config/SearchConfig.tsx +423 -393
- package/src/components/config/channel-form-fields-section.tsx +70 -37
- package/src/components/config/config-split-page.tsx +109 -0
- package/src/components/config/provider-enabled-field.tsx +17 -10
- package/src/components/config/runtime-control-card.test.tsx +56 -0
- package/src/components/config/runtime-control-card.tsx +25 -0
- package/src/components/config/runtime-presence-card.tsx +93 -79
- package/src/components/layout/AppLayout.tsx +25 -37
- package/src/components/layout/app-layout.test.tsx +46 -14
- package/src/components/layout/runtime-status-entry.test.tsx +157 -0
- package/src/components/layout/runtime-status-entry.tsx +143 -0
- package/src/components/marketplace/marketplace-detail-doc.ts +93 -0
- package/src/components/marketplace/marketplace-list-card.tsx +288 -0
- package/src/components/marketplace/marketplace-page-data.ts +129 -0
- package/src/components/marketplace/marketplace-page.test.tsx +339 -0
- package/src/components/marketplace/marketplace-page.tsx +596 -0
- package/src/components/marketplace/mcp/mcp-marketplace-card.tsx +128 -0
- package/src/components/marketplace/mcp/mcp-marketplace-dialogs.tsx +191 -0
- package/src/components/marketplace/mcp/mcp-marketplace-doc.ts +152 -0
- package/src/components/marketplace/mcp/mcp-marketplace-page.test.tsx +223 -0
- package/src/components/marketplace/mcp/mcp-marketplace-page.tsx +414 -0
- package/src/components/remote/remote-access-page.test.tsx +105 -0
- package/src/components/remote/remote-access-page.tsx +248 -0
- package/src/components/ui/notice-card.tsx +129 -0
- package/src/components/ui/setting-row.tsx +51 -0
- package/src/components/ui/tag-chip.tsx +39 -0
- package/src/components/ui/textarea.tsx +19 -0
- package/src/hooks/useConfig.ts +2 -1
- package/src/index.css +24 -0
- package/src/lib/app-resource-uri.test.ts +20 -0
- package/src/lib/app-resource-uri.ts +29 -0
- package/src/lib/i18n.remote.ts +1 -1
- package/src/lib/i18n.runtime-control.ts +31 -0
- package/src/lib/i18n.ts +5 -8
- package/src/lib/session-context.utils.test.ts +71 -0
- package/src/lib/session-context.utils.ts +28 -3
- package/src/lib/session-project/workspace-file-breadcrumb.test.ts +83 -0
- package/src/lib/session-project/workspace-file-breadcrumb.ts +188 -0
- package/dist/assets/ChannelsList-Ita2Zm1_.js +0 -8
- package/dist/assets/DocBrowser-BNwbPHf4.js +0 -1
- package/dist/assets/MarketplacePage-CjX2MWww.js +0 -1
- package/dist/assets/MarketplacePage-D0sDlYX4.js +0 -49
- package/dist/assets/McpMarketplacePage-BGKJm1sJ.js +0 -40
- package/dist/assets/ModelConfig-BzZenCH-.js +0 -1
- package/dist/assets/ProvidersList-BbVzRxjY.js +0 -1
- package/dist/assets/RemoteAccessPage-BaDH_X1Q.js +0 -1
- package/dist/assets/RuntimeConfig-F_XKGgLm.js +0 -1
- package/dist/assets/SearchConfig-BGkzXQP-.js +0 -1
- package/dist/assets/SessionsConfig-ChHQ7M5c.js +0 -2
- package/dist/assets/chat-page-Doe0yTtB.js +0 -58
- package/dist/assets/chat-session-display-cw78aiI_.js +0 -1
- package/dist/assets/config-layout-CHs0mAaR.js +0 -1
- package/dist/assets/desktop-update-config-Dpcf4BKG.js +0 -1
- package/dist/assets/index-CF9xve0E.js +0 -6
- package/dist/assets/index-FgA52VBt.css +0 -1
- package/dist/assets/loader-circle-ACM1s51e.js +0 -1
- package/dist/assets/play-CFUwCA2E.js +0 -1
- package/dist/assets/plus-rYsv72JG.js +0 -1
- package/dist/assets/popover-Bg1VoTZ6.js +0 -1
- package/dist/assets/search-3kFR_zh9.js +0 -1
- package/dist/assets/security-config-BWaiARNk.js +0 -1
- package/dist/assets/select-DJ2MUjBB.js +0 -41
- package/dist/assets/skeleton-ByQepn0M.js +0 -1
- package/dist/assets/x-ByDbItbq.js +0 -1
- package/src/components/agents/AgentDialogs.tsx +0 -400
- package/src/components/agents/AgentsPage.test.tsx +0 -217
- package/src/components/agents/AgentsPage.tsx +0 -352
- package/src/components/config/config-layout.ts +0 -10
- package/src/components/marketplace/MarketplacePage.test.tsx +0 -322
- package/src/components/marketplace/MarketplacePage.tsx +0 -827
- package/src/components/marketplace/mcp/McpMarketplacePage.test.tsx +0 -208
- package/src/components/marketplace/mcp/McpMarketplacePage.tsx +0 -580
- package/src/components/remote/RemoteAccessPage.test.tsx +0 -103
- package/src/components/remote/RemoteAccessPage.tsx +0 -144
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { fireEvent, render, screen, waitFor } from
|
|
2
|
-
import userEvent from
|
|
3
|
-
import type * as ReactQueryModule from
|
|
4
|
-
import { ChannelsList } from
|
|
1
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import type * as ReactQueryModule from "@tanstack/react-query";
|
|
4
|
+
import { ChannelsList } from "@/components/config/ChannelsList";
|
|
5
5
|
|
|
6
6
|
const mocks = vi.hoisted(() => ({
|
|
7
7
|
updateChannelMutate: vi.fn(),
|
|
@@ -13,88 +13,90 @@ const mocks = vi.hoisted(() => ({
|
|
|
13
13
|
channels: {
|
|
14
14
|
weixin: {
|
|
15
15
|
enabled: false,
|
|
16
|
-
defaultAccountId:
|
|
17
|
-
baseUrl:
|
|
16
|
+
defaultAccountId: "1344b2b24720@im.bot",
|
|
17
|
+
baseUrl: "https://ilinkai.weixin.qq.com",
|
|
18
18
|
pollTimeoutMs: 35000,
|
|
19
|
-
allowFrom: [
|
|
19
|
+
allowFrom: ["o9cq804svxfyCCTIqzddDqRBeMC0@im.wechat"],
|
|
20
20
|
accounts: {
|
|
21
|
-
|
|
22
|
-
enabled: true
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
21
|
+
"1344b2b24720@im.bot": {
|
|
22
|
+
enabled: true,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
27
|
},
|
|
28
|
-
isLoading: false
|
|
28
|
+
isLoading: false,
|
|
29
29
|
},
|
|
30
30
|
metaQuery: {
|
|
31
31
|
data: {
|
|
32
32
|
channels: [
|
|
33
33
|
{
|
|
34
|
-
name:
|
|
35
|
-
displayName:
|
|
36
|
-
enabled: false
|
|
37
|
-
}
|
|
38
|
-
]
|
|
39
|
-
}
|
|
34
|
+
name: "weixin",
|
|
35
|
+
displayName: "Weixin",
|
|
36
|
+
enabled: false,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
40
|
},
|
|
41
41
|
schemaQuery: {
|
|
42
42
|
data: {
|
|
43
43
|
uiHints: {
|
|
44
|
-
|
|
45
|
-
label:
|
|
46
|
-
help:
|
|
44
|
+
"channels.weixin": {
|
|
45
|
+
label: "Weixin",
|
|
46
|
+
help: "Weixin QR login + getupdates long-poll channel",
|
|
47
|
+
},
|
|
48
|
+
"channels.weixin.baseUrl": {
|
|
49
|
+
label: "API Base URL",
|
|
47
50
|
},
|
|
48
|
-
'channels.weixin.baseUrl': {
|
|
49
|
-
label: 'API Base URL'
|
|
50
|
-
}
|
|
51
51
|
},
|
|
52
|
-
actions: []
|
|
53
|
-
}
|
|
54
|
-
}
|
|
52
|
+
actions: [],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
55
|
}));
|
|
56
56
|
|
|
57
|
-
vi.mock(
|
|
58
|
-
toDataURL: vi.fn().mockResolvedValue(
|
|
57
|
+
vi.mock("qrcode", () => ({
|
|
58
|
+
toDataURL: vi.fn().mockResolvedValue("data:image/png;base64,weixin-qr"),
|
|
59
59
|
}));
|
|
60
60
|
|
|
61
|
-
vi.mock(
|
|
62
|
-
const actual = await vi.importActual<typeof ReactQueryModule>(
|
|
61
|
+
vi.mock("@tanstack/react-query", async () => {
|
|
62
|
+
const actual = await vi.importActual<typeof ReactQueryModule>(
|
|
63
|
+
"@tanstack/react-query",
|
|
64
|
+
);
|
|
63
65
|
return {
|
|
64
66
|
...actual,
|
|
65
67
|
useQueryClient: () => ({
|
|
66
|
-
invalidateQueries: vi.fn().mockResolvedValue(undefined)
|
|
67
|
-
})
|
|
68
|
+
invalidateQueries: vi.fn().mockResolvedValue(undefined),
|
|
69
|
+
}),
|
|
68
70
|
};
|
|
69
71
|
});
|
|
70
72
|
|
|
71
|
-
vi.mock(
|
|
73
|
+
vi.mock("@/hooks/useConfig", () => ({
|
|
72
74
|
useConfig: () => mocks.configQuery,
|
|
73
75
|
useConfigMeta: () => mocks.metaQuery,
|
|
74
76
|
useConfigSchema: () => mocks.schemaQuery,
|
|
75
77
|
useUpdateChannel: () => ({
|
|
76
78
|
mutate: mocks.updateChannelMutate,
|
|
77
79
|
mutateAsync: mocks.updateChannelMutateAsync,
|
|
78
|
-
isPending: false
|
|
80
|
+
isPending: false,
|
|
79
81
|
}),
|
|
80
82
|
useExecuteConfigAction: () => ({
|
|
81
83
|
mutateAsync: vi.fn(),
|
|
82
|
-
isPending: false
|
|
83
|
-
})
|
|
84
|
+
isPending: false,
|
|
85
|
+
}),
|
|
84
86
|
}));
|
|
85
87
|
|
|
86
|
-
vi.mock(
|
|
88
|
+
vi.mock("@/hooks/use-channel-auth", () => ({
|
|
87
89
|
useStartChannelAuth: () => ({
|
|
88
90
|
mutateAsync: mocks.startChannelAuthMutateAsync,
|
|
89
|
-
isPending: false
|
|
91
|
+
isPending: false,
|
|
90
92
|
}),
|
|
91
93
|
usePollChannelAuth: () => ({
|
|
92
94
|
mutateAsync: mocks.pollChannelAuthMutateAsync,
|
|
93
|
-
isPending: false
|
|
94
|
-
})
|
|
95
|
+
isPending: false,
|
|
96
|
+
}),
|
|
95
97
|
}));
|
|
96
98
|
|
|
97
|
-
describe(
|
|
99
|
+
describe("ChannelsList", () => {
|
|
98
100
|
beforeEach(() => {
|
|
99
101
|
mocks.updateChannelMutate.mockReset();
|
|
100
102
|
mocks.updateChannelMutateAsync.mockReset();
|
|
@@ -104,68 +106,76 @@ describe('ChannelsList', () => {
|
|
|
104
106
|
channels: {
|
|
105
107
|
weixin: {
|
|
106
108
|
enabled: false,
|
|
107
|
-
defaultAccountId:
|
|
108
|
-
baseUrl:
|
|
109
|
+
defaultAccountId: "1344b2b24720@im.bot",
|
|
110
|
+
baseUrl: "https://ilinkai.weixin.qq.com",
|
|
109
111
|
pollTimeoutMs: 35000,
|
|
110
|
-
allowFrom: [
|
|
112
|
+
allowFrom: ["o9cq804svxfyCCTIqzddDqRBeMC0@im.wechat"],
|
|
111
113
|
accounts: {
|
|
112
|
-
|
|
113
|
-
enabled: true
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
114
|
+
"1344b2b24720@im.bot": {
|
|
115
|
+
enabled: true,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
118
120
|
};
|
|
119
121
|
mocks.metaQuery.data = {
|
|
120
122
|
channels: [
|
|
121
123
|
{
|
|
122
|
-
name:
|
|
123
|
-
displayName:
|
|
124
|
-
enabled: false
|
|
125
|
-
}
|
|
126
|
-
]
|
|
124
|
+
name: "weixin",
|
|
125
|
+
displayName: "Weixin",
|
|
126
|
+
enabled: false,
|
|
127
|
+
},
|
|
128
|
+
],
|
|
127
129
|
};
|
|
128
130
|
});
|
|
129
131
|
|
|
130
|
-
it(
|
|
132
|
+
it("renders weixin qr auth card and starts channel auth", async () => {
|
|
131
133
|
const user = userEvent.setup();
|
|
132
134
|
mocks.startChannelAuthMutateAsync.mockResolvedValue({
|
|
133
|
-
channel:
|
|
134
|
-
kind:
|
|
135
|
-
sessionId:
|
|
136
|
-
qrCode:
|
|
137
|
-
qrCodeUrl:
|
|
138
|
-
expiresAt:
|
|
135
|
+
channel: "weixin",
|
|
136
|
+
kind: "qr_code",
|
|
137
|
+
sessionId: "session-1",
|
|
138
|
+
qrCode: "qr-token",
|
|
139
|
+
qrCodeUrl: "https://example.com/weixin-qr.png",
|
|
140
|
+
expiresAt: "2026-03-23T10:00:00.000Z",
|
|
139
141
|
intervalMs: 60_000,
|
|
140
|
-
note:
|
|
142
|
+
note: "请扫码",
|
|
141
143
|
});
|
|
142
144
|
|
|
143
145
|
render(<ChannelsList />);
|
|
144
146
|
|
|
145
|
-
await user.click(
|
|
147
|
+
await user.click(
|
|
148
|
+
await screen.findByRole("button", { name: /All Channels/i }),
|
|
149
|
+
);
|
|
146
150
|
|
|
147
|
-
expect((await screen.findAllByText(
|
|
148
|
-
expect(
|
|
149
|
-
|
|
151
|
+
expect((await screen.findAllByText("Weixin")).length).toBeGreaterThan(0);
|
|
152
|
+
expect(
|
|
153
|
+
await screen.findByRole("button", { name: "Reconnect with QR" }),
|
|
154
|
+
).toBeTruthy();
|
|
155
|
+
expect(
|
|
156
|
+
screen.getByText("Weixin now uses QR login as the primary setup flow."),
|
|
157
|
+
).toBeTruthy();
|
|
150
158
|
|
|
151
|
-
await user.click(screen.getByRole(
|
|
159
|
+
await user.click(screen.getByRole("button", { name: "Reconnect with QR" }));
|
|
152
160
|
|
|
153
161
|
await waitFor(() => {
|
|
154
162
|
expect(mocks.startChannelAuthMutateAsync).toHaveBeenCalledWith({
|
|
155
|
-
channel:
|
|
163
|
+
channel: "weixin",
|
|
156
164
|
data: expect.objectContaining({
|
|
157
|
-
accountId:
|
|
158
|
-
baseUrl:
|
|
159
|
-
})
|
|
165
|
+
accountId: "1344b2b24720@im.bot",
|
|
166
|
+
baseUrl: "https://ilinkai.weixin.qq.com",
|
|
167
|
+
}),
|
|
160
168
|
});
|
|
161
169
|
});
|
|
162
170
|
|
|
163
171
|
await waitFor(() => {
|
|
164
|
-
expect(
|
|
172
|
+
expect(
|
|
173
|
+
screen.getByAltText("Weixin login QR code").getAttribute("src"),
|
|
174
|
+
).toBe("data:image/png;base64,weixin-qr");
|
|
165
175
|
});
|
|
166
176
|
});
|
|
167
177
|
|
|
168
|
-
it(
|
|
178
|
+
it("keeps Weixin, Feishu, Discord, and QQ at the front of the channel list", async () => {
|
|
169
179
|
const user = userEvent.setup();
|
|
170
180
|
mocks.configQuery.data = {
|
|
171
181
|
channels: {
|
|
@@ -173,92 +183,126 @@ describe('ChannelsList', () => {
|
|
|
173
183
|
qq: { enabled: false },
|
|
174
184
|
discord: { enabled: false },
|
|
175
185
|
weixin: { enabled: false },
|
|
176
|
-
feishu: { enabled: false }
|
|
177
|
-
}
|
|
186
|
+
feishu: { enabled: false },
|
|
187
|
+
},
|
|
178
188
|
} as unknown as typeof mocks.configQuery.data;
|
|
179
189
|
mocks.metaQuery.data = {
|
|
180
190
|
channels: [
|
|
181
|
-
{ name:
|
|
182
|
-
{ name:
|
|
183
|
-
{ name:
|
|
184
|
-
{ name:
|
|
185
|
-
{ name:
|
|
186
|
-
]
|
|
191
|
+
{ name: "telegram", displayName: "Telegram", enabled: false },
|
|
192
|
+
{ name: "qq", displayName: "QQ", enabled: false },
|
|
193
|
+
{ name: "discord", displayName: "Discord", enabled: false },
|
|
194
|
+
{ name: "weixin", displayName: "Weixin", enabled: false },
|
|
195
|
+
{ name: "feishu", displayName: "Feishu", enabled: false },
|
|
196
|
+
],
|
|
187
197
|
} as typeof mocks.metaQuery.data;
|
|
188
198
|
|
|
189
199
|
const { container } = render(<ChannelsList />);
|
|
190
200
|
|
|
191
|
-
await user.click(
|
|
201
|
+
await user.click(
|
|
202
|
+
await screen.findByRole("button", { name: /All Channels/i }),
|
|
203
|
+
);
|
|
192
204
|
|
|
193
|
-
const sidebarSection = container.querySelector(
|
|
205
|
+
const sidebarSection = container.querySelector("section");
|
|
194
206
|
if (!(sidebarSection instanceof HTMLElement)) {
|
|
195
|
-
throw new Error(
|
|
207
|
+
throw new Error("channel sidebar not found");
|
|
196
208
|
}
|
|
197
209
|
|
|
198
|
-
const channelButtons = Array.from(
|
|
199
|
-
|
|
200
|
-
))
|
|
210
|
+
const channelButtons = Array.from(
|
|
211
|
+
sidebarSection.querySelectorAll('button[type="button"]'),
|
|
212
|
+
).filter((button) =>
|
|
213
|
+
["Weixin", "Feishu", "Discord", "QQ", "Telegram"].some((label) =>
|
|
214
|
+
button.textContent?.includes(label),
|
|
215
|
+
),
|
|
216
|
+
);
|
|
201
217
|
|
|
202
218
|
expect(channelButtons.map((button) => button.textContent)).toEqual([
|
|
203
|
-
expect.stringContaining(
|
|
204
|
-
expect.stringContaining(
|
|
205
|
-
expect.stringContaining(
|
|
206
|
-
expect.stringContaining(
|
|
207
|
-
expect.stringContaining(
|
|
219
|
+
expect.stringContaining("Weixin"),
|
|
220
|
+
expect.stringContaining("Feishu"),
|
|
221
|
+
expect.stringContaining("Discord"),
|
|
222
|
+
expect.stringContaining("QQ"),
|
|
223
|
+
expect.stringContaining("Telegram"),
|
|
208
224
|
]);
|
|
209
225
|
});
|
|
210
226
|
|
|
211
|
-
it(
|
|
227
|
+
it("only enables internal scroll chaining on desktop-sized panes", async () => {
|
|
228
|
+
const user = userEvent.setup();
|
|
229
|
+
const { container } = render(<ChannelsList />);
|
|
230
|
+
|
|
231
|
+
await user.click(
|
|
232
|
+
await screen.findByRole("button", { name: /All Channels/i }),
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const scrollRegions = Array.from(container.querySelectorAll("div")).filter(
|
|
236
|
+
(node) => {
|
|
237
|
+
const classTokens = node.className.split(/\s+/).filter(Boolean);
|
|
238
|
+
return classTokens.includes("xl:overflow-y-auto");
|
|
239
|
+
},
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
expect(scrollRegions.length).toBeGreaterThanOrEqual(2);
|
|
243
|
+
scrollRegions.forEach((region) => {
|
|
244
|
+
const classTokens = region.className.split(/\s+/).filter(Boolean);
|
|
245
|
+
expect(classTokens).toContain("xl:overscroll-contain");
|
|
246
|
+
expect(classTokens).not.toContain("overflow-y-auto");
|
|
247
|
+
expect(classTokens).not.toContain("overscroll-contain");
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it("saves weixin advanced settings from the advanced section", async () => {
|
|
212
252
|
const user = userEvent.setup();
|
|
213
253
|
|
|
214
254
|
const { container } = render(<ChannelsList />);
|
|
215
255
|
|
|
216
|
-
await user.click(
|
|
217
|
-
|
|
256
|
+
await user.click(
|
|
257
|
+
await screen.findByRole("button", { name: /All Channels/i }),
|
|
258
|
+
);
|
|
259
|
+
await user.click(await screen.findByText("Advanced settings"));
|
|
218
260
|
|
|
219
|
-
const timeoutInput = await screen.findByLabelText(
|
|
261
|
+
const timeoutInput = await screen.findByLabelText("Long Poll Timeout (ms)");
|
|
220
262
|
await user.clear(timeoutInput);
|
|
221
|
-
await user.type(timeoutInput,
|
|
263
|
+
await user.type(timeoutInput, "45000");
|
|
222
264
|
|
|
223
|
-
const accountsJson = container.querySelector(
|
|
265
|
+
const accountsJson = container.querySelector(
|
|
266
|
+
"textarea#accounts",
|
|
267
|
+
) as HTMLTextAreaElement | null;
|
|
224
268
|
expect(accountsJson).toBeTruthy();
|
|
225
269
|
if (!accountsJson) {
|
|
226
|
-
throw new Error(
|
|
270
|
+
throw new Error("accounts textarea not found");
|
|
227
271
|
}
|
|
228
272
|
await user.clear(accountsJson);
|
|
229
273
|
fireEvent.change(accountsJson, {
|
|
230
274
|
target: {
|
|
231
275
|
value: JSON.stringify(
|
|
232
276
|
{
|
|
233
|
-
|
|
277
|
+
"1344b2b24720@im.bot": {
|
|
234
278
|
enabled: true,
|
|
235
|
-
baseUrl:
|
|
236
|
-
}
|
|
279
|
+
baseUrl: "https://ilinkai.weixin.qq.com",
|
|
280
|
+
},
|
|
237
281
|
},
|
|
238
282
|
null,
|
|
239
|
-
2
|
|
240
|
-
)
|
|
241
|
-
}
|
|
283
|
+
2,
|
|
284
|
+
),
|
|
285
|
+
},
|
|
242
286
|
});
|
|
243
287
|
|
|
244
|
-
await user.click(screen.getByRole(
|
|
288
|
+
await user.click(screen.getByRole("button", { name: /save/i }));
|
|
245
289
|
|
|
246
290
|
await waitFor(() => {
|
|
247
291
|
expect(mocks.updateChannelMutate).toHaveBeenCalledWith({
|
|
248
|
-
channel:
|
|
292
|
+
channel: "weixin",
|
|
249
293
|
data: expect.objectContaining({
|
|
250
294
|
enabled: false,
|
|
251
|
-
defaultAccountId:
|
|
252
|
-
baseUrl:
|
|
295
|
+
defaultAccountId: "1344b2b24720@im.bot",
|
|
296
|
+
baseUrl: "https://ilinkai.weixin.qq.com",
|
|
253
297
|
pollTimeoutMs: 45000,
|
|
254
|
-
allowFrom: [
|
|
298
|
+
allowFrom: ["o9cq804svxfyCCTIqzddDqRBeMC0@im.wechat"],
|
|
255
299
|
accounts: {
|
|
256
|
-
|
|
300
|
+
"1344b2b24720@im.bot": {
|
|
257
301
|
enabled: true,
|
|
258
|
-
baseUrl:
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
})
|
|
302
|
+
baseUrl: "https://ilinkai.weixin.qq.com",
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
}),
|
|
262
306
|
});
|
|
263
307
|
});
|
|
264
308
|
});
|