@cometchat/skills 4.0.0 → 4.2.0
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/README.md +42 -2
- package/bin/install.js +448 -57
- package/package.json +1 -1
- package/skills/cometchat/SKILL.md +337 -80
- package/skills/cometchat-a11y/SKILL.md +476 -0
- package/skills/cometchat-android-v5/SKILL.md +1 -1
- package/skills/cometchat-android-v5-calls/SKILL.md +413 -0
- package/skills/cometchat-android-v5-calls/references/README.md +53 -0
- package/skills/cometchat-android-v5-calls/references/add-calls-to-existing-chat.md +195 -0
- package/skills/cometchat-android-v5-calls/references/audio-controls.md +91 -0
- package/skills/cometchat-android-v5-calls/references/background-handling.md +168 -0
- package/skills/cometchat-android-v5-calls/references/call-layouts.md +123 -0
- package/skills/cometchat-android-v5-calls/references/call-logs.md +113 -0
- package/skills/cometchat-android-v5-calls/references/call-session.md +274 -0
- package/skills/cometchat-android-v5-calls/references/custom-ui.md +150 -0
- package/skills/cometchat-android-v5-calls/references/event-listeners.md +123 -0
- package/skills/cometchat-android-v5-calls/references/group-calls.md +205 -0
- package/skills/cometchat-android-v5-calls/references/idle-timeout.md +111 -0
- package/skills/cometchat-android-v5-calls/references/in-call-chat.md +134 -0
- package/skills/cometchat-android-v5-calls/references/join-session.md +120 -0
- package/skills/cometchat-android-v5-calls/references/migration-v4-to-v5.md +206 -0
- package/skills/cometchat-android-v5-calls/references/participant-management.md +110 -0
- package/skills/cometchat-android-v5-calls/references/picture-in-picture.md +106 -0
- package/skills/cometchat-android-v5-calls/references/raise-hand.md +242 -0
- package/skills/cometchat-android-v5-calls/references/recording.md +101 -0
- package/skills/cometchat-android-v5-calls/references/ringing-integration.md +152 -0
- package/skills/cometchat-android-v5-calls/references/screen-sharing.md +82 -0
- package/skills/cometchat-android-v5-calls/references/server-fcm-voip.md +215 -0
- package/skills/cometchat-android-v5-calls/references/session-settings.md +121 -0
- package/skills/cometchat-android-v5-calls/references/setup.md +137 -0
- package/skills/cometchat-android-v5-calls/references/share-invite.md +151 -0
- package/skills/cometchat-android-v5-calls/references/video-controls.md +87 -0
- package/skills/cometchat-android-v5-calls/references/voip-calling.md +526 -0
- package/skills/cometchat-android-v5-components/SKILL.md +1 -1
- package/skills/cometchat-android-v5-core/SKILL.md +1 -1
- package/skills/cometchat-android-v5-customization/SKILL.md +1 -1
- package/skills/cometchat-android-v5-extensions/SKILL.md +1 -1
- package/skills/cometchat-android-v5-features/SKILL.md +1 -1
- package/skills/cometchat-android-v5-placement/SKILL.md +1 -1
- package/skills/cometchat-android-v5-production/SKILL.md +1 -1
- package/skills/cometchat-android-v5-push/SKILL.md +1 -1
- package/skills/cometchat-android-v5-testing/SKILL.md +1 -1
- package/skills/cometchat-android-v5-theming/SKILL.md +1 -1
- package/skills/cometchat-android-v5-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-android-v6/SKILL.md +1 -1
- package/skills/cometchat-android-v6-builder-settings/SKILL.md +1 -1
- package/skills/cometchat-android-v6-calls/SKILL.md +375 -0
- package/skills/cometchat-android-v6-calls/references/add-calls-to-existing-chat.md +97 -0
- package/skills/cometchat-android-v6-calls/references/call-layouts.md +91 -0
- package/skills/cometchat-android-v6-calls/references/call-session.md +163 -0
- package/skills/cometchat-android-v6-calls/references/device-management.md +171 -0
- package/skills/cometchat-android-v6-calls/references/group-calls.md +191 -0
- package/skills/cometchat-android-v6-calls/references/idle-timeout.md +107 -0
- package/skills/cometchat-android-v6-calls/references/in-call-chat.md +136 -0
- package/skills/cometchat-android-v6-calls/references/raise-hand.md +186 -0
- package/skills/cometchat-android-v6-calls/references/ringing-integration.md +176 -0
- package/skills/cometchat-android-v6-calls/references/server-fcm-voip.md +127 -0
- package/skills/cometchat-android-v6-calls/references/share-invite.md +105 -0
- package/skills/cometchat-android-v6-compose-components/SKILL.md +2 -2
- package/skills/cometchat-android-v6-compose-customization/SKILL.md +1 -1
- package/skills/cometchat-android-v6-compose-placement/SKILL.md +1 -1
- package/skills/cometchat-android-v6-compose-theming/SKILL.md +1 -1
- package/skills/cometchat-android-v6-core/SKILL.md +1 -1
- package/skills/cometchat-android-v6-events/SKILL.md +1 -1
- package/skills/cometchat-android-v6-extensions/SKILL.md +1 -1
- package/skills/cometchat-android-v6-features/SKILL.md +1 -1
- package/skills/cometchat-android-v6-kotlin-components/SKILL.md +1 -1
- package/skills/cometchat-android-v6-kotlin-customization/SKILL.md +1 -1
- package/skills/cometchat-android-v6-kotlin-placement/SKILL.md +1 -1
- package/skills/cometchat-android-v6-kotlin-theming/SKILL.md +1 -1
- package/skills/cometchat-android-v6-migration/SKILL.md +398 -0
- package/skills/cometchat-android-v6-production/SKILL.md +1 -1
- package/skills/cometchat-android-v6-push/SKILL.md +1 -1
- package/skills/cometchat-android-v6-testing/SKILL.md +1 -1
- package/skills/cometchat-android-v6-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-angular-calls/SKILL.md +511 -0
- package/skills/cometchat-angular-calls/references/add-calls-to-existing-chat.md +95 -0
- package/skills/cometchat-angular-calls/references/call-layouts.md +106 -0
- package/skills/cometchat-angular-calls/references/call-session.md +167 -0
- package/skills/cometchat-angular-calls/references/custom-ui.md +231 -0
- package/skills/cometchat-angular-calls/references/device-management.md +135 -0
- package/skills/cometchat-angular-calls/references/group-calls.md +412 -0
- package/skills/cometchat-angular-calls/references/idle-timeout.md +145 -0
- package/skills/cometchat-angular-calls/references/in-call-chat.md +148 -0
- package/skills/cometchat-angular-calls/references/lazy-loading-pitfalls.md +205 -0
- package/skills/cometchat-angular-calls/references/migration-v4-to-v5.md +53 -0
- package/skills/cometchat-angular-calls/references/ngzone-and-async-callbacks.md +183 -0
- package/skills/cometchat-angular-calls/references/picture-in-picture.md +285 -0
- package/skills/cometchat-angular-calls/references/raise-hand.md +167 -0
- package/skills/cometchat-angular-calls/references/recording-screen-share.md +267 -0
- package/skills/cometchat-angular-calls/references/ringing-integration.md +131 -0
- package/skills/cometchat-angular-calls/references/server-web-push-vapid.md +100 -0
- package/skills/cometchat-angular-calls/references/share-invite.md +116 -0
- package/skills/cometchat-angular-components/SKILL.md +1 -1
- package/skills/cometchat-angular-core/SKILL.md +1 -1
- package/skills/cometchat-angular-customization/SKILL.md +1 -1
- package/skills/cometchat-angular-features/SKILL.md +1 -1
- package/skills/cometchat-angular-patterns/SKILL.md +1 -1
- package/skills/cometchat-angular-placement/SKILL.md +1 -1
- package/skills/cometchat-angular-production/SKILL.md +1 -1
- package/skills/cometchat-angular-push/SKILL.md +403 -0
- package/skills/cometchat-angular-testing/SKILL.md +333 -0
- package/skills/cometchat-angular-theming/SKILL.md +1 -1
- package/skills/cometchat-angular-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-astro-patterns/SKILL.md +1 -1
- package/skills/cometchat-calls/SKILL.md +358 -0
- package/skills/cometchat-calls/references/use-case-broadcast.md +194 -0
- package/skills/cometchat-calls/references/use-case-marketplace.md +158 -0
- package/skills/cometchat-calls/references/use-case-support.md +190 -0
- package/skills/cometchat-calls/references/use-case-team.md +169 -0
- package/skills/cometchat-calls/references/use-case-telehealth.md +154 -0
- package/skills/cometchat-components/SKILL.md +1 -1
- package/skills/cometchat-core/SKILL.md +1 -1
- package/skills/cometchat-customization/SKILL.md +2 -2
- package/skills/cometchat-features/SKILL.md +2 -2
- package/skills/cometchat-flutter-v5/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-calls/SKILL.md +325 -94
- package/skills/cometchat-flutter-v5-calls/references/add-calls-to-existing-chat.md +169 -0
- package/skills/cometchat-flutter-v5-calls/references/call-layouts.md +98 -0
- package/skills/cometchat-flutter-v5-calls/references/call-session.md +330 -0
- package/skills/cometchat-flutter-v5-calls/references/device-management.md +120 -0
- package/skills/cometchat-flutter-v5-calls/references/group-calls.md +187 -0
- package/skills/cometchat-flutter-v5-calls/references/idle-timeout.md +97 -0
- package/skills/cometchat-flutter-v5-calls/references/in-call-chat.md +132 -0
- package/skills/cometchat-flutter-v5-calls/references/migration-v4-to-v5.md +160 -0
- package/skills/cometchat-flutter-v5-calls/references/raise-hand.md +244 -0
- package/skills/cometchat-flutter-v5-calls/references/ringing-integration.md +193 -0
- package/skills/cometchat-flutter-v5-calls/references/server-push-bridge.md +101 -0
- package/skills/cometchat-flutter-v5-calls/references/share-invite.md +142 -0
- package/skills/cometchat-flutter-v5-conversations/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-core/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-customization/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-events/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-messages/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-production/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-push/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-testing/SKILL.md +390 -0
- package/skills/cometchat-flutter-v5-theming/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-users-groups/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-calls/SKILL.md +308 -243
- package/skills/cometchat-flutter-v6-calls/references/add-calls-to-existing-chat.md +134 -0
- package/skills/cometchat-flutter-v6-calls/references/call-layouts.md +102 -0
- package/skills/cometchat-flutter-v6-calls/references/call-session.md +229 -0
- package/skills/cometchat-flutter-v6-calls/references/device-management.md +99 -0
- package/skills/cometchat-flutter-v6-calls/references/group-calls.md +190 -0
- package/skills/cometchat-flutter-v6-calls/references/idle-timeout.md +80 -0
- package/skills/cometchat-flutter-v6-calls/references/in-call-chat.md +122 -0
- package/skills/cometchat-flutter-v6-calls/references/raise-hand.md +268 -0
- package/skills/cometchat-flutter-v6-calls/references/ringing-integration.md +171 -0
- package/skills/cometchat-flutter-v6-calls/references/server-push-bridge.md +93 -0
- package/skills/cometchat-flutter-v6-calls/references/share-invite.md +102 -0
- package/skills/cometchat-flutter-v6-components/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-conversations/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-core/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-customization/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-events/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-features/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-messages/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-migration/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-placement/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-production/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-push/SKILL.md +452 -0
- package/skills/cometchat-flutter-v6-testing/SKILL.md +319 -0
- package/skills/cometchat-flutter-v6-theming/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-users-groups/SKILL.md +1 -1
- package/skills/cometchat-i18n/SKILL.md +358 -0
- package/skills/cometchat-ios/SKILL.md +5 -5
- package/skills/cometchat-ios-calls/SKILL.md +409 -0
- package/skills/cometchat-ios-calls/references/add-calls-to-existing-chat.md +172 -0
- package/skills/cometchat-ios-calls/references/avaudiosession-routing.md +189 -0
- package/skills/cometchat-ios-calls/references/call-layouts.md +136 -0
- package/skills/cometchat-ios-calls/references/call-session.md +221 -0
- package/skills/cometchat-ios-calls/references/callkit-and-pushkit.md +353 -0
- package/skills/cometchat-ios-calls/references/custom-ui.md +322 -0
- package/skills/cometchat-ios-calls/references/device-management.md +171 -0
- package/skills/cometchat-ios-calls/references/group-calls.md +396 -0
- package/skills/cometchat-ios-calls/references/idle-timeout.md +185 -0
- package/skills/cometchat-ios-calls/references/in-call-chat.md +183 -0
- package/skills/cometchat-ios-calls/references/migration-v4-to-v5.md +138 -0
- package/skills/cometchat-ios-calls/references/picture-in-picture.md +246 -0
- package/skills/cometchat-ios-calls/references/raise-hand.md +239 -0
- package/skills/cometchat-ios-calls/references/recording-screen-share.md +339 -0
- package/skills/cometchat-ios-calls/references/ringing-integration.md +142 -0
- package/skills/cometchat-ios-calls/references/server-apns-pushkit.md +267 -0
- package/skills/cometchat-ios-calls/references/share-invite.md +152 -0
- package/skills/cometchat-ios-calls/references/swiftui-uikit-hosting.md +246 -0
- package/skills/cometchat-ios-calls/references/voip-cert-setup.md +166 -0
- package/skills/cometchat-ios-components/SKILL.md +1 -1
- package/skills/cometchat-ios-core/SKILL.md +1 -1
- package/skills/cometchat-ios-customization/SKILL.md +1 -1
- package/skills/cometchat-ios-features/SKILL.md +1 -1
- package/skills/cometchat-ios-placement/SKILL.md +1 -1
- package/skills/cometchat-ios-production/SKILL.md +1 -1
- package/skills/cometchat-ios-push/SKILL.md +1 -1
- package/skills/cometchat-ios-testing/SKILL.md +371 -0
- package/skills/cometchat-ios-theming/SKILL.md +1 -1
- package/skills/cometchat-ios-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-native-bare-patterns/SKILL.md +45 -3
- package/skills/cometchat-native-calls/SKILL.md +599 -0
- package/skills/cometchat-native-calls/references/add-calls-to-existing-chat.md +203 -0
- package/skills/cometchat-native-calls/references/call-layouts.md +124 -0
- package/skills/cometchat-native-calls/references/call-session.md +182 -0
- package/skills/cometchat-native-calls/references/custom-ui.md +250 -0
- package/skills/cometchat-native-calls/references/device-management.md +135 -0
- package/skills/cometchat-native-calls/references/expo-vs-bare.md +179 -0
- package/skills/cometchat-native-calls/references/group-calls.md +291 -0
- package/skills/cometchat-native-calls/references/idle-timeout.md +128 -0
- package/skills/cometchat-native-calls/references/in-call-chat.md +143 -0
- package/skills/cometchat-native-calls/references/migration-v4-to-v5.md +72 -0
- package/skills/cometchat-native-calls/references/picture-in-picture.md +276 -0
- package/skills/cometchat-native-calls/references/raise-hand.md +203 -0
- package/skills/cometchat-native-calls/references/recording-screen-share.md +290 -0
- package/skills/cometchat-native-calls/references/ringing-integration.md +118 -0
- package/skills/cometchat-native-calls/references/server-push-bridge.md +135 -0
- package/skills/cometchat-native-calls/references/server-push-payloads.md +193 -0
- package/skills/cometchat-native-calls/references/share-invite.md +144 -0
- package/skills/cometchat-native-calls/references/voip-push-end-to-end.md +317 -0
- package/skills/cometchat-native-components/SKILL.md +1 -1
- package/skills/cometchat-native-core/SKILL.md +1 -1
- package/skills/cometchat-native-customization/SKILL.md +1 -1
- package/skills/cometchat-native-expo-patterns/SKILL.md +18 -1
- package/skills/cometchat-native-features/SKILL.md +1 -1
- package/skills/cometchat-native-placement/SKILL.md +1 -1
- package/skills/cometchat-native-production/SKILL.md +1 -1
- package/skills/cometchat-native-push/SKILL.md +1 -1
- package/skills/cometchat-native-testing/SKILL.md +1 -1
- package/skills/cometchat-native-theming/SKILL.md +1 -1
- package/skills/cometchat-native-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-nextjs-patterns/SKILL.md +1 -1
- package/skills/cometchat-placement/SKILL.md +29 -1
- package/skills/cometchat-production/SKILL.md +1 -1
- package/skills/cometchat-react-calls/SKILL.md +539 -0
- package/skills/cometchat-react-calls/references/add-calls-to-existing-chat.md +145 -0
- package/skills/cometchat-react-calls/references/call-layouts.md +161 -0
- package/skills/cometchat-react-calls/references/call-session.md +235 -0
- package/skills/cometchat-react-calls/references/custom-ui.md +201 -0
- package/skills/cometchat-react-calls/references/device-management.md +206 -0
- package/skills/cometchat-react-calls/references/group-calls.md +262 -0
- package/skills/cometchat-react-calls/references/idle-timeout.md +175 -0
- package/skills/cometchat-react-calls/references/in-call-chat.md +212 -0
- package/skills/cometchat-react-calls/references/migration-v4-to-v5.md +172 -0
- package/skills/cometchat-react-calls/references/picture-in-picture.md +245 -0
- package/skills/cometchat-react-calls/references/raise-hand.md +238 -0
- package/skills/cometchat-react-calls/references/recording-screen-share.md +143 -0
- package/skills/cometchat-react-calls/references/ringing-integration.md +255 -0
- package/skills/cometchat-react-calls/references/server-web-push-vapid.md +241 -0
- package/skills/cometchat-react-calls/references/share-invite.md +176 -0
- package/skills/cometchat-react-calls/references/testing-calls-on-web.md +198 -0
- package/skills/cometchat-react-calls/references/voip-and-web-push.md +165 -0
- package/skills/cometchat-react-patterns/SKILL.md +56 -8
- package/skills/cometchat-react-push/SKILL.md +448 -0
- package/skills/cometchat-react-router-patterns/SKILL.md +1 -1
- package/skills/cometchat-react-testing/SKILL.md +404 -0
- package/skills/cometchat-theming/SKILL.md +3 -3
- package/skills/cometchat-troubleshooting/SKILL.md +2 -2
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Testing calls on web
|
|
2
|
+
|
|
3
|
+
Calls are the hardest CometChat surface to unit-test because they touch native browser APIs (`getUserMedia`, `RTCPeerConnection`) that don't exist in jsdom. Three layers, three strategies:
|
|
4
|
+
|
|
5
|
+
1. **Unit tests** — mock the entire Calls SDK; assert your code calls the right methods in the right order.
|
|
6
|
+
2. **Component tests** — Vitest + React Testing Library; mock `getUserMedia` and `RTCPeerConnection`; assert your custom UI renders correctly given mocked SDK events.
|
|
7
|
+
3. **E2E tests** — Playwright with the headless WebRTC flag; real two-tab call between two test users.
|
|
8
|
+
|
|
9
|
+
This reference covers all three. For chat-side testing patterns, see `cometchat-react-testing` (sibling skill).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Unit tests — mock the SDK module
|
|
14
|
+
|
|
15
|
+
Vitest's `vi.mock` with a hoisted factory:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
// __tests__/CallButton.test.tsx
|
|
19
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
20
|
+
import { render, screen, fireEvent } from "@testing-library/react";
|
|
21
|
+
import { CometChatCalls } from "@cometchat/calls-sdk-javascript";
|
|
22
|
+
import { CallButton } from "../src/components/CallButton";
|
|
23
|
+
|
|
24
|
+
vi.mock("@cometchat/chat-sdk-javascript", () => ({
|
|
25
|
+
CometChat: {
|
|
26
|
+
initiateCall: vi.fn().mockResolvedValue({ getSessionId: () => "session-123" }),
|
|
27
|
+
Call: vi.fn((uid, type, recv) => ({ uid, type, recv })),
|
|
28
|
+
CALL_TYPE: { VOICE: "voice", VIDEO: "video" },
|
|
29
|
+
RECEIVER_TYPE: { USER: "user" },
|
|
30
|
+
},
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
vi.mock("@cometchat/calls-sdk-javascript", () => ({
|
|
34
|
+
CometChatCalls: {
|
|
35
|
+
init: vi.fn(),
|
|
36
|
+
generateToken: vi.fn().mockResolvedValue({ token: "rtc-token" }),
|
|
37
|
+
startSession: vi.fn(),
|
|
38
|
+
endSession: vi.fn(),
|
|
39
|
+
CallSettingsBuilder: class {
|
|
40
|
+
setSessionID() { return this; }
|
|
41
|
+
setIsAudioOnly() { return this; }
|
|
42
|
+
enableDefaultLayout() { return this; }
|
|
43
|
+
build() { return {}; }
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
beforeEach(() => vi.clearAllMocks());
|
|
49
|
+
|
|
50
|
+
describe("CallButton", () => {
|
|
51
|
+
it("initiates a video call when video button clicked", async () => {
|
|
52
|
+
const { CometChat } = await import("@cometchat/chat-sdk-javascript");
|
|
53
|
+
render(<CallButton user={{ uid: "alice" }} />);
|
|
54
|
+
|
|
55
|
+
await fireEvent.click(screen.getByLabelText("Start video call"));
|
|
56
|
+
|
|
57
|
+
expect(CometChat.initiateCall).toHaveBeenCalledWith(
|
|
58
|
+
expect.objectContaining({ uid: "alice", type: "video" }),
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 2. Component tests — mock `getUserMedia` + `RTCPeerConnection`
|
|
67
|
+
|
|
68
|
+
For tests that exercise your custom UI's reaction to track-arrived events:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
// vitest.setup.ts
|
|
72
|
+
import { vi } from "vitest";
|
|
73
|
+
|
|
74
|
+
beforeAll(() => {
|
|
75
|
+
// jsdom doesn't have navigator.mediaDevices — polyfill it
|
|
76
|
+
Object.defineProperty(navigator, "mediaDevices", {
|
|
77
|
+
writable: true,
|
|
78
|
+
value: {
|
|
79
|
+
getUserMedia: vi.fn().mockResolvedValue({
|
|
80
|
+
getTracks: () => [
|
|
81
|
+
{ kind: "audio", stop: vi.fn() },
|
|
82
|
+
{ kind: "video", stop: vi.fn() },
|
|
83
|
+
],
|
|
84
|
+
}),
|
|
85
|
+
getDisplayMedia: vi.fn().mockResolvedValue({ getTracks: () => [{ stop: vi.fn() }] }),
|
|
86
|
+
enumerateDevices: vi.fn().mockResolvedValue([]),
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Polyfill RTCPeerConnection
|
|
91
|
+
(global as Record<string, unknown>).RTCPeerConnection = class {
|
|
92
|
+
addTrack = vi.fn();
|
|
93
|
+
addEventListener = vi.fn();
|
|
94
|
+
createOffer = vi.fn().mockResolvedValue({ sdp: "fake-offer", type: "offer" });
|
|
95
|
+
createAnswer = vi.fn().mockResolvedValue({ sdp: "fake-answer", type: "answer" });
|
|
96
|
+
setLocalDescription = vi.fn();
|
|
97
|
+
setRemoteDescription = vi.fn();
|
|
98
|
+
close = vi.fn();
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Reference this in `vitest.config.ts`:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
export default defineConfig({
|
|
107
|
+
test: {
|
|
108
|
+
setupFiles: ["./vitest.setup.ts"],
|
|
109
|
+
environment: "jsdom",
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 3. E2E tests — Playwright with WebRTC enabled
|
|
117
|
+
|
|
118
|
+
Playwright supports real WebRTC in headless mode with the right Chromium flag:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
// playwright.config.ts
|
|
122
|
+
import { defineConfig } from "@playwright/test";
|
|
123
|
+
|
|
124
|
+
export default defineConfig({
|
|
125
|
+
use: {
|
|
126
|
+
launchOptions: {
|
|
127
|
+
args: [
|
|
128
|
+
"--use-fake-ui-for-media-stream", // auto-grants getUserMedia
|
|
129
|
+
"--use-fake-device-for-media-stream", // synthetic camera (animated pattern)
|
|
130
|
+
"--allow-running-insecure-content",
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Then a two-page test exercises the full call flow:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { test, expect, type Page } from "@playwright/test";
|
|
141
|
+
|
|
142
|
+
async function login(page: Page, uid: string) {
|
|
143
|
+
await page.goto("/");
|
|
144
|
+
await page.evaluate((uid) => localStorage.setItem("cc-test-uid", uid), uid);
|
|
145
|
+
await page.reload();
|
|
146
|
+
await expect(page.getByTestId("logged-in-banner")).toBeVisible();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
test("alice calls bob; bob answers; both see two-way video", async ({ browser }) => {
|
|
150
|
+
const aliceCtx = await browser.newContext();
|
|
151
|
+
const bobCtx = await browser.newContext();
|
|
152
|
+
const alice = await aliceCtx.newPage();
|
|
153
|
+
const bob = await bobCtx.newPage();
|
|
154
|
+
|
|
155
|
+
await login(alice, "cometchat-uid-1");
|
|
156
|
+
await login(bob, "cometchat-uid-2");
|
|
157
|
+
|
|
158
|
+
// Alice clicks call on Bob's profile
|
|
159
|
+
await alice.goto("/users/cometchat-uid-2");
|
|
160
|
+
await alice.getByLabel("Start video call").click();
|
|
161
|
+
|
|
162
|
+
// Bob's IncomingCall overlay appears
|
|
163
|
+
await expect(bob.getByText("Incoming call from")).toBeVisible({ timeout: 10_000 });
|
|
164
|
+
await bob.getByRole("button", { name: "Accept" }).click();
|
|
165
|
+
|
|
166
|
+
// Both see the ongoing-call view
|
|
167
|
+
await expect(alice.getByTestId("ongoing-call")).toBeVisible();
|
|
168
|
+
await expect(bob.getByTestId("ongoing-call")).toBeVisible();
|
|
169
|
+
|
|
170
|
+
// Verify two video elements are playing (synthetic streams)
|
|
171
|
+
const aliceVideos = await alice.locator("video").count();
|
|
172
|
+
expect(aliceVideos).toBeGreaterThanOrEqual(2); // local + remote
|
|
173
|
+
|
|
174
|
+
// Hangup
|
|
175
|
+
await alice.getByRole("button", { name: "End" }).click();
|
|
176
|
+
await expect(alice.getByTestId("ongoing-call")).not.toBeVisible();
|
|
177
|
+
await expect(bob.getByTestId("ongoing-call")).not.toBeVisible();
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Synthetic stream gotcha:** the `--use-fake-device-for-media-stream` flag makes Chrome stream a green-and-blue animated pattern. Real cameras / mics are NOT used, so you cannot assert "this video shows the user's face." You CAN assert "two video elements with `srcObject` set," which is what 90% of tests need.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## CI gotchas
|
|
186
|
+
|
|
187
|
+
- **Linux GPU + WebRTC:** GitHub Actions Ubuntu runners have headless Chrome but no GPU. Pass `--disable-gpu` if you see WebRTC errors about GPU process crash; the synthetic stream still works.
|
|
188
|
+
- **Test isolation:** real CometChat App ID + Auth Key in CI. Use a dedicated test app (Dashboard → New App → "ci-tests"); don't reuse production credentials.
|
|
189
|
+
- **Rate limits:** the CometChat backend rate-limits `initiateCall` per user. Tests that loop call/hangup quickly hit limits; throttle to ~5 calls/minute per test UID or rotate UIDs (`cometchat-uid-1` through `cometchat-uid-5` are pre-seeded).
|
|
190
|
+
- **Cleanup:** kill any active calls between tests via `CometChatCalls.leaveSession()` in afterEach. Stale sessions cause the next test's `initiateCall` to fail.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## What NOT to mock
|
|
195
|
+
|
|
196
|
+
- **Don't mock `useEffect` cleanup.** It's where rule 1.3's track-stop happens; mocking hides bugs in the cleanup path.
|
|
197
|
+
- **Don't mock `navigator.mediaDevices` to return synchronous values.** Real APIs are async; mocks should be too, otherwise you mask race conditions in your real code.
|
|
198
|
+
- **Don't mock the kit components themselves** (`<CometChatIncomingCall />` etc.) for unit tests — they're tested by CometChat. Mock the SDK that the kit calls.
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# VoIP push on web — there isn't any
|
|
2
|
+
|
|
3
|
+
Web browsers do not have VoIP push. You cannot ring a user whose tab is closed. The closest equivalents are:
|
|
4
|
+
|
|
5
|
+
1. **Web Push** (Service Worker + Push API + Notification API) — can fire a system-level notification while the browser is open even if the tab is backgrounded. Limitations:
|
|
6
|
+
- Service Worker must be installed (one prior visit required)
|
|
7
|
+
- User must have granted notification permission
|
|
8
|
+
- Browser must be running (Chrome/Edge can deliver to closed tabs IF the browser process is alive; Safari requires Safari to be running; Firefox stops Service Workers aggressively)
|
|
9
|
+
- Does NOT bypass closed browser. Production calling apps on web pair this with email/SMS fallback for missed calls.
|
|
10
|
+
|
|
11
|
+
2. **Email/SMS missed-call fallback** — server-side. When a call rings unanswered for ~30 seconds, send a "missed call from X" email/SMS with a deep link back into the app.
|
|
12
|
+
|
|
13
|
+
This reference covers Web Push end-to-end. Email/SMS is out of scope (server-side only, not skill-scaffoldable).
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Web Push setup (Service Worker + push subscription)
|
|
18
|
+
|
|
19
|
+
### 1. Generate VAPID keys (server-side, one-time)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx web-push generate-vapid-keys
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Stores public + private keys. Public goes to the client; private stays on the push server.
|
|
26
|
+
|
|
27
|
+
### 2. Register the Service Worker
|
|
28
|
+
|
|
29
|
+
`public/sw.js`:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
self.addEventListener("push", (event) => {
|
|
33
|
+
if (!event.data) return;
|
|
34
|
+
const payload = event.data.json();
|
|
35
|
+
if (payload.type !== "incoming_call") return;
|
|
36
|
+
|
|
37
|
+
event.waitUntil(
|
|
38
|
+
self.registration.showNotification("Incoming call", {
|
|
39
|
+
body: `${payload.callerName} is calling`,
|
|
40
|
+
icon: "/icons/call.png",
|
|
41
|
+
tag: `call-${payload.sessionId}`, // dedupe — only one ring per session
|
|
42
|
+
requireInteraction: true, // don't auto-dismiss
|
|
43
|
+
data: { sessionId: payload.sessionId, callerUid: payload.callerUid },
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
self.addEventListener("notificationclick", (event) => {
|
|
49
|
+
event.notification.close();
|
|
50
|
+
event.waitUntil(
|
|
51
|
+
clients.matchAll({ type: "window", includeUncontrolled: true }).then((wins) => {
|
|
52
|
+
// Focus existing tab if open, otherwise open a new one
|
|
53
|
+
for (const w of wins) {
|
|
54
|
+
if (w.url.includes(self.registration.scope)) {
|
|
55
|
+
w.focus();
|
|
56
|
+
w.postMessage({ type: "incoming_call_click", ...event.notification.data });
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return clients.openWindow(`/calls/incoming?sessionId=${event.notification.data.sessionId}`);
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 3. Register from the app
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
// cometchat/registerWebPush.ts
|
|
70
|
+
const VAPID_PUBLIC = import.meta.env.VITE_VAPID_PUBLIC_KEY;
|
|
71
|
+
|
|
72
|
+
export async function registerWebPushForCalls(uid: string): Promise<void> {
|
|
73
|
+
if (!("serviceWorker" in navigator) || !("PushManager" in window)) return;
|
|
74
|
+
|
|
75
|
+
const reg = await navigator.serviceWorker.register("/sw.js");
|
|
76
|
+
const permission = await Notification.requestPermission();
|
|
77
|
+
if (permission !== "granted") return;
|
|
78
|
+
|
|
79
|
+
const subscription = await reg.pushManager.subscribe({
|
|
80
|
+
userVisibleOnly: true,
|
|
81
|
+
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Send subscription to YOUR server, keyed by uid
|
|
85
|
+
await fetch("/api/push/subscribe", {
|
|
86
|
+
method: "POST",
|
|
87
|
+
headers: { "Content-Type": "application/json" },
|
|
88
|
+
body: JSON.stringify({ uid, subscription }),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function urlBase64ToUint8Array(base64: string): Uint8Array {
|
|
93
|
+
const padding = "=".repeat((4 - (base64.length % 4)) % 4);
|
|
94
|
+
const raw = atob((base64 + padding).replace(/-/g, "+").replace(/_/g, "/"));
|
|
95
|
+
return Uint8Array.from(raw, (c) => c.charCodeAt(0));
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 4. Listen for SW messages in your React app
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
// CometChatProvider.tsx
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
const handler = (event: MessageEvent) => {
|
|
105
|
+
if (event.data?.type === "incoming_call_click") {
|
|
106
|
+
// Navigate to the call screen and let CometChatIncomingCall pick it up
|
|
107
|
+
navigate(`/calls/incoming?sessionId=${event.data.sessionId}`);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
navigator.serviceWorker.addEventListener("message", handler);
|
|
111
|
+
return () => navigator.serviceWorker.removeEventListener("message", handler);
|
|
112
|
+
}, [navigate]);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 5. Server-side push send (signaling integration)
|
|
116
|
+
|
|
117
|
+
Your CometChat backend (or webhook listener) detects an incoming call event and sends Web Push to the receiver's subscription:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
// server (Node.js) — runs when CometChat fires the call.received webhook
|
|
121
|
+
import webpush from "web-push";
|
|
122
|
+
|
|
123
|
+
webpush.setVapidDetails("mailto:you@example.com", VAPID_PUBLIC, VAPID_PRIVATE);
|
|
124
|
+
|
|
125
|
+
async function sendIncomingCallPush(receiverUid: string, sessionId: string, callerName: string, callerUid: string) {
|
|
126
|
+
const subs = await getSubscriptionsForUid(receiverUid); // your DB
|
|
127
|
+
await Promise.allSettled(
|
|
128
|
+
subs.map((sub) =>
|
|
129
|
+
webpush.sendNotification(sub, JSON.stringify({
|
|
130
|
+
type: "incoming_call",
|
|
131
|
+
sessionId,
|
|
132
|
+
callerName,
|
|
133
|
+
callerUid,
|
|
134
|
+
})),
|
|
135
|
+
),
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The webhook setup itself is server-side; the skill scaffolds the client side and points users at this template for the server.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Browser support matrix
|
|
145
|
+
|
|
146
|
+
| Browser | Push API | Notification while tab closed | Service Worker terminated background |
|
|
147
|
+
|---|---|---|---|
|
|
148
|
+
| Chrome/Edge desktop | ✓ | ✓ if browser process alive | After ~30s idle |
|
|
149
|
+
| Chrome/Edge mobile | ✓ | ✓ | Aggressive; Web Push is best-effort |
|
|
150
|
+
| Firefox desktop | ✓ | ✓ | After ~30s idle |
|
|
151
|
+
| Safari 16+ desktop | ✓ | ✓ if Safari running | Tighter |
|
|
152
|
+
| Safari iOS 16.4+ | ✓ | Only when added to Home Screen as PWA | N/A — PWA only |
|
|
153
|
+
| Mobile Web (any) | Limited | Often not delivered when browser killed | OS-driven |
|
|
154
|
+
|
|
155
|
+
**Bottom line:** Web Push is "best-effort ring" — it works often enough to be worth wiring, but production calling apps must have a missed-call email/SMS fallback. The skill warns the user about this explicitly during scaffold.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## When NOT to bother with Web Push
|
|
160
|
+
|
|
161
|
+
- Internal tools where users keep the tab open during work hours — `<CometChatIncomingCall />` mounted at root rings the tab itself, which is enough.
|
|
162
|
+
- Apps where the call surface is a route the user navigates to deliberately (telehealth waiting room) — the ring happens when they arrive on the route; they don't need to be paged.
|
|
163
|
+
- Greenfield projects that haven't shipped to production yet — add it later when missed-calls become a real complaint.
|
|
164
|
+
|
|
165
|
+
The dispatcher's Step 3 in `cometchat-react-calls/SKILL.md` asks the user whether to scaffold Web Push. Default = no for "just exploring" / "messaging app" intents; default = yes for "support" / "marketplace" intents (where users may receive a call without expecting it).
|
|
@@ -3,7 +3,7 @@ name: cometchat-react-patterns
|
|
|
3
3
|
description: "Framework-specific patterns for integrating CometChat React UI Kit v6 into React projects (Vite or CRA). Covers provider setup, routing, layout integration, env vars, and common pitfalls."
|
|
4
4
|
license: "MIT"
|
|
5
5
|
compatibility: "Node.js >=18; React >=18; Vite >=4 or react-scripts (CRA); @cometchat/chat-uikit-react ^6; @cometchat/chat-sdk-javascript ^4"
|
|
6
|
-
allowed-tools: "
|
|
6
|
+
allowed-tools: "shell, file-read, file-search, file-list"
|
|
7
7
|
metadata:
|
|
8
8
|
author: "CometChat"
|
|
9
9
|
version: "3.0.0"
|
|
@@ -468,30 +468,78 @@ Vite's HMR replaces modules without a full page reload. CometChat's SDK holds a
|
|
|
468
468
|
|
|
469
469
|
However, if you change the provider file itself during development, HMR may re-execute the module. The `initialized` flag prevents double-init, but the WebSocket connection from the previous module instance may linger. If you see duplicate messages or connection issues during development, do a full page reload (`Ctrl+Shift+R`).
|
|
470
470
|
|
|
471
|
-
### Container height
|
|
471
|
+
### Container height (and the flex-shrink trap that breaks chat layouts)
|
|
472
472
|
|
|
473
|
-
CometChat components fill 100% of their container.
|
|
473
|
+
CometChat components fill 100% of their container. Two visual bugs to avoid:
|
|
474
|
+
|
|
475
|
+
**Bug 1 — zero height:** components render with zero height because the container has no explicit dimensions.
|
|
476
|
+
**Bug 2 — message list grows past the viewport:** the list scrolls fine until it has too many messages, then pushes the composer below the fold. **This is the classic flex-shrink trap.**
|
|
477
|
+
|
|
478
|
+
The hard rule for two-pane / header+list+composer layouts: every flex container in the chain MUST have `minHeight: 0` (and `minWidth: 0` for horizontal flex). Without it, browsers default flex-children to `min-height: auto` (their intrinsic content size), so the list grows beyond the parent's bounds as messages accumulate.
|
|
474
479
|
|
|
475
480
|
```tsx
|
|
476
|
-
/* CORRECT: explicit height */
|
|
481
|
+
/* ✓ CORRECT: explicit height for a single-component surface */
|
|
477
482
|
<div style={{ height: "100vh" }}>
|
|
478
483
|
<CometChatConversations ... />
|
|
479
484
|
</div>
|
|
480
485
|
|
|
481
|
-
/* CORRECT:
|
|
486
|
+
/* ✓ CORRECT: header + list + composer with the flex-shrink trap fixed */
|
|
482
487
|
<div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
|
|
483
488
|
<nav>...</nav>
|
|
484
|
-
<div style={{
|
|
485
|
-
|
|
489
|
+
<div style={{
|
|
490
|
+
flex: 1,
|
|
491
|
+
display: "flex",
|
|
492
|
+
flexDirection: "column",
|
|
493
|
+
minHeight: 0, // ← THIS IS THE HARD RULE — without it, list grows past the viewport
|
|
494
|
+
}}>
|
|
495
|
+
<div style={{ flex: "0 0 auto" }}>
|
|
496
|
+
<CometChatMessageHeader user={user} />
|
|
497
|
+
</div>
|
|
498
|
+
<div style={{ flex: "1 1 0", minHeight: 0, overflow: "hidden" }}>
|
|
499
|
+
<CometChatMessageList user={user} />
|
|
500
|
+
</div>
|
|
501
|
+
<div style={{ flex: "0 0 auto" }}>
|
|
502
|
+
<CometChatMessageComposer user={user} />
|
|
503
|
+
</div>
|
|
486
504
|
</div>
|
|
487
505
|
</div>
|
|
488
506
|
|
|
489
|
-
/*
|
|
507
|
+
/* ✓ CORRECT: two-pane (sidebar + active chat) — both axes need min-{width,height}: 0 */
|
|
508
|
+
<div style={{ display: "flex", height: "100vh" }}>
|
|
509
|
+
<div style={{ width: 360, display: "flex", flexDirection: "column" }}>
|
|
510
|
+
<CometChatConversations onItemClick={...} />
|
|
511
|
+
</div>
|
|
512
|
+
<div style={{
|
|
513
|
+
flex: 1,
|
|
514
|
+
display: "flex",
|
|
515
|
+
flexDirection: "column",
|
|
516
|
+
minWidth: 0, // ← horizontal flex parent: prevents content from forcing horizontal scroll
|
|
517
|
+
minHeight: 0, // ← vertical flex (the inner column): prevents the list-overflow bug
|
|
518
|
+
}}>
|
|
519
|
+
{/* header / list / composer wrapped as above */}
|
|
520
|
+
</div>
|
|
521
|
+
</div>
|
|
522
|
+
|
|
523
|
+
/* ✗ WRONG: no height constraint — component collapses to zero */
|
|
490
524
|
<div>
|
|
491
525
|
<CometChatConversations ... />
|
|
492
526
|
</div>
|
|
527
|
+
|
|
528
|
+
/* ✗ WRONG: flex parent without minHeight: 0 — list grows past the viewport once
|
|
529
|
+
you accumulate more messages than fit on-screen. Composer falls off the bottom. */
|
|
530
|
+
<div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
|
|
531
|
+
<CometChatMessageHeader user={user} />
|
|
532
|
+
<CometChatMessageList user={user} /> {/* takes intrinsic content height */}
|
|
533
|
+
<CometChatMessageComposer user={user} /> {/* gets pushed below the fold */}
|
|
534
|
+
</div>
|
|
493
535
|
```
|
|
494
536
|
|
|
537
|
+
**Why `minHeight: 0` is non-obvious:** the W3C spec sets the default `min-height` of a flex item to `auto` (its intrinsic content height). For a scrollable list inside a flex column, this means the list refuses to shrink below its content even when the parent is bounded. Setting `minHeight: 0` on the flex parent overrides this, letting the list shrink and scroll within the bounded container instead of overflowing it.
|
|
538
|
+
|
|
539
|
+
**The rule, restated as something to grep for:**
|
|
540
|
+
|
|
541
|
+
> Every flex container that holds `CometChatMessageList` (directly or indirectly) MUST have `minHeight: 0` on it. Same for `minWidth: 0` on horizontal flex parents. Skip this and the layout works for short conversations and breaks for long ones.
|
|
542
|
+
|
|
495
543
|
### Vite dependency optimization
|
|
496
544
|
|
|
497
545
|
Vite pre-bundles dependencies for faster dev startup. CometChat's packages are large and may trigger Vite's "new dependency found, reloading" message on first load. This is normal and only happens once. If it causes issues, you can pre-include the packages:
|