cometchat-react-testing — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited cometchat-react-testing (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Test recipes for CometChat React UI Kit integrations. Three layers — unit, component, e2e — three different mocking strategies. This skill writes the configuration and the canonical assertions; real test bodies are app-specific.
Read these other skills first:
cometchat-core — init, login, provider patterns the tests assert againstcometchat-{react,nextjs,react-router,astro}-patterns — framework-specific render gatescometchat-react-calls/references/testing-calls-on-web.md — the calls-specific testing patterns (this skill is for chat)Ground truth: Official docs: https://www.cometchat.com/docs/ui-kit/react/overview · Docs MCP: claude mcp add --transport http cometchat-docs https://www.cometchat.com/docs/mcp (or fetch the URL directly without MCP).
npm install -D vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event jsdomvitest.config.tsimport { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
test: {
environment: "jsdom",
globals: true,
setupFiles: ["./vitest.setup.ts"],
css: false, // skip CSS imports — we test behavior, not styles
exclude: ["**/node_modules/**", "**/e2e/**"], // e2e runs separately under Playwright
},
});vitest.setup.tsimport "@testing-library/jest-dom/vitest";
import { vi, beforeAll, afterEach } from "vitest";
import { cleanup } from "@testing-library/react";
afterEach(() => cleanup());
// jsdom doesn't have WebSocket — polyfill if any test code reaches that far
if (!("WebSocket" in globalThis)) {
(globalThis as Record<string, unknown>).WebSocket = class FakeWS {
addEventListener() {}
removeEventListener() {}
send() {}
close() {}
};
}
// Polyfill matchMedia for component tests using responsive UI Kit components
beforeAll(() => {
Object.defineProperty(window, "matchMedia", {
writable: true,
value: vi.fn().mockImplementation((query: string) => ({
matches: false,
media: query,
onchange: null,
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
});
});// __tests__/mocks/cometchat.ts
import { vi } from "vitest";
export const mockUser = { uid: "cometchat-uid-1", name: "Alice" };
vi.mock("@cometchat/chat-sdk-javascript", () => ({
CometChat: {
init: vi.fn().mockResolvedValue(true),
login: vi.fn().mockResolvedValue(mockUser),
logout: vi.fn().mockResolvedValue(true),
getLoggedinUser: vi.fn().mockResolvedValue(mockUser),
addMessageListener: vi.fn(),
removeMessageListener: vi.fn(),
AppSettingsBuilder: class {
subscribePresenceForAllUsers() { return this; }
setRegion() { return this; }
build() { return {}; }
},
// Regions are flat string statics (REGION_US/REGION_EU/REGION_IN) — there is
// no CometChat.REGION object. Pass the region as a plain string ("us"/"eu"/"in").
REGION_US: "us", REGION_EU: "eu", REGION_IN: "in",
},
}));
vi.mock("@cometchat/chat-uikit-react", () => ({
CometChatUIKit: {
init: vi.fn().mockResolvedValue(true),
login: vi.fn().mockResolvedValue(mockUser),
getLoggedinUser: vi.fn().mockResolvedValue(mockUser),
},
UIKitSettingsBuilder: class {
setAppId() { return this; }
setRegion() { return this; }
setAuthKey() { return this; }
subscribePresenceForAllUsers() { return this; }
build() { return {}; }
},
CometChatConversations: () => null,
CometChatMessageList: () => null,
CometChatMessageHeader: () => null,
CometChatMessageComposer: () => null,
CometChatUsers: () => null,
CometChatGroups: () => null,
CometChatIncomingCall: () => null,
// NOTE: the v6 web kit has NO CometChatThemeProvider (theming is CSS-variable
// based) — don't mock a component that doesn't exist.
}));Import this from any test file:
import "./mocks/cometchat";Override specific behaviors:
import { render, screen } from "@testing-library/react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
it("shows error when login fails", async () => {
vi.mocked(CometChat.login).mockRejectedValueOnce(new Error("401 Unauthorized"));
render(<CometChatProvider />);
await screen.findByText(/login failed/i);
});import { render, screen } from "@testing-library/react";
import { CometChat } from "@cometchat/chat-sdk-javascript";
import { CometChatProvider } from "../src/cometchat/CometChatProvider";
it("does not render children until login resolves", async () => {
let resolveLogin: (user: unknown) => void = () => {};
vi.mocked(CometChat.login).mockImplementation(() =>
new Promise((resolve) => { resolveLogin = resolve; })
);
render(
<CometChatProvider>
<div data-testid="children">app contents</div>
</CometChatProvider>
);
// Children should NOT be present yet
expect(screen.queryByTestId("children")).not.toBeInTheDocument();
// Resolve login
resolveLogin({ uid: "cometchat-uid-1" });
await screen.findByTestId("children");
});it("renders red error UI when init fails", async () => {
vi.mocked(CometChat.init).mockRejectedValueOnce(new Error("Network down"));
render(<CometChatProvider><div>app</div></CometChatProvider>);
const error = await screen.findByText(/network down/i);
expect(error).toBeInTheDocument();
// The skill's rule: errors must be visible in red — assert color
expect(error.closest("[style*='color']")).toHaveStyle("color: red");
});A meta-test that runs against the project's source:
import { readFileSync } from "node:fs";
import { glob } from "glob";
it("no Auth Key hardcoded in source files", async () => {
const files = await glob("src/**/*.{ts,tsx,js,jsx}");
const hexKeyPattern = /[a-f0-9]{32,}/;
for (const file of files) {
const content = readFileSync(file, "utf8");
const matches = content.match(hexKeyPattern);
if (matches) {
// Allow comment / env var name mentions
const lines = content.split("\n");
const realHits = lines.filter(line =>
hexKeyPattern.test(line) && !line.trim().startsWith("//") && !line.includes("AUTH_KEY")
);
expect(realHits, `Possible Auth Key in ${file}: ${realHits.join("\n")}`).toHaveLength(0);
}
}
});css-variables.css imported exactly onceit("css-variables.css imported exactly once", async () => {
const files = await glob("src/**/*.{ts,tsx,css}");
let count = 0;
for (const file of files) {
const content = readFileSync(file, "utf8");
if (content.includes("@cometchat/chat-uikit-react/css-variables.css")) {
count++;
}
}
expect(count).toBe(1);
});npm install -D @playwright/test
npx playwright installplaywright.config.tsimport { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "./e2e",
use: {
baseURL: process.env.E2E_BASE_URL ?? "http://localhost:5173",
trace: "on-first-retry",
},
projects: [
{ name: "chromium", use: devices["Desktop Chrome"] },
{ name: "firefox", use: devices["Desktop Firefox"] },
],
webServer: {
command: "npm run dev",
url: "http://localhost:5173",
reuseExistingServer: !process.env.CI,
},
});import { test, expect, type Page } from "@playwright/test";
async function loginAs(page: Page, uid: string) {
await page.goto("/");
await page.evaluate((uid) => localStorage.setItem("cc-test-uid", uid), uid);
await page.reload();
await expect(page.getByText("Welcome")).toBeVisible({ timeout: 10_000 });
}
test("two users send messages back and forth", async ({ browser }) => {
const aliceCtx = await browser.newContext();
const bobCtx = await browser.newContext();
const alice = await aliceCtx.newPage();
const bob = await bobCtx.newPage();
await loginAs(alice, "cometchat-uid-1");
await loginAs(bob, "cometchat-uid-2");
await alice.goto("/messages");
await alice.getByRole("button", { name: "cometchat-uid-2" }).click();
await alice.getByPlaceholder("Type a message").fill("Hello Bob");
await alice.getByRole("button", { name: "Send" }).click();
await bob.goto("/messages");
await bob.getByRole("button", { name: "cometchat-uid-1" }).click();
await expect(bob.getByText("Hello Bob")).toBeVisible({ timeout: 10_000 });
});The 10-second timeout is generous — message delivery via WebSocket is usually <500ms but CI hosts have variable latency.
Composer selectors (v6 DOM — for E2E only). The example above uses role/placeholder selectors for readability, but the v6 kit (@cometchat/[email protected]) renders the message input as a `contenteditable` div, not a `<textarea>` — driving it with.fill()on a placeholder/role can fail with "no input found". The reliable selectors (verified live, 2026-06-14): - input:.cometchat-message-composer__input[contenteditable="plaintext-only"]→ focus it, thenpage.keyboard.type("…")(orlocator.fill()works on the contenteditable in Playwright ≥1.4x) - send button:.cometchat-message-composer__send-button
>
This is a testing-only fact about the rendered DOM — the §8.7 rule "never target internal cometchat-* class names for styling" still stands; for styling use the CSS variables. (Real-time delivery between two live clients measured ~1.0–1.3s reload-free in this setup.)CometChat dev mode pre-seeds five test users (cometchat-uid-1 through cometchat-uid-5). Use them in e2e — never create new users in the test app via Auth Key flows that leak credentials.
name: tests
on: [push, pull_request]
jobs:
unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- run: npm ci
- run: npm run test # Vitest
env:
# Test app credentials — never your prod app
VITE_COMETCHAT_APP_ID: ${{ secrets.TEST_COMETCHAT_APP_ID }}
VITE_COMETCHAT_REGION: ${{ secrets.TEST_COMETCHAT_REGION }}
VITE_COMETCHAT_AUTH_KEY: ${{ secrets.TEST_COMETCHAT_AUTH_KEY }}
e2e:
runs-on: ubuntu-latest
needs: unit
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- run: npm ci
- run: npx playwright install --with-deps chromium firefox
- run: npm run test:e2e
env:
VITE_COMETCHAT_APP_ID: ${{ secrets.TEST_COMETCHAT_APP_ID }}
VITE_COMETCHAT_REGION: ${{ secrets.TEST_COMETCHAT_REGION }}
VITE_COMETCHAT_AUTH_KEY: ${{ secrets.TEST_COMETCHAT_AUTH_KEY }}
- if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/Critical: use a dedicated test app (Dashboard → New App → "ci-tests") for CI. Never share with production. Rotate Auth Key if a CI log accidentally captures it.
() => null and test the wiring around it.afterEach(cleanup)..cc-message-list__bubble. Brittle to UI Kit upgrades. Assert on text/role/test-id instead.vitest.config.ts with environment: "jsdom"vitest.setup.ts with cleanup + matchMedia polyfill.skip() / .only() left in committed test filescometchat-react-calls/references/testing-calls-on-web.md — calls-specific testing patternscometchat-core — init/login patterns the tests assert againstcometchat-troubleshooting — when tests pass but the integration is still broken~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.