@q9labsai/chalk-react-native 4.1.11 → 4.1.13
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/dist/android.js +4 -2
- package/dist/{chunk-MMBE2BVK.js → chunk-4NLA35HE.js} +649 -407
- package/dist/{chunk-LZ67SXGB.js → chunk-MK6OO4V7.js} +6 -0
- package/dist/client.js +1 -1
- package/dist/components/Entrance.d.ts.map +1 -1
- package/dist/components/logo/Logo.d.ts +11 -0
- package/dist/components/logo/Logo.d.ts.map +1 -0
- package/dist/components/native-space-view/SpaceTopBar.android.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/ios-pad.js +4 -2
- package/dist/ios-phone.js +4 -2
- package/dist/ios.js +4 -2
- package/dist/macos.js +4 -2
- package/package.json +4 -4
- package/src/callkit/callkit.test.ts +12 -10
- package/src/components/Entrance.tsx +2 -1
- package/src/components/logo/Logo.tsx +309 -0
- package/src/components/native-space-view/SpaceTopBar.android.tsx +2 -1
- package/src/index.ts +2 -0
- package/src/space-client/create-native-space-client.ts +8 -0
- package/dist/components/LogoElements.d.ts +0 -6
- package/dist/components/LogoElements.d.ts.map +0 -1
- package/dist/test-support/test-renderer.d.ts +0 -20
- package/dist/test-support/test-renderer.d.ts.map +0 -1
- package/src/android/connection-service.test.ts +0 -114
- package/src/callkit/resolve-space-callkit-options.test.ts +0 -68
- package/src/client.test.ts +0 -12
- package/src/clipboard.test.ts +0 -79
- package/src/components/Chalk.test.tsx +0 -223
- package/src/components/EmbeddedWhiteboard.test.tsx +0 -92
- package/src/components/Entrance.test.tsx +0 -19
- package/src/components/EntranceView.android.test.tsx +0 -100
- package/src/components/EntranceView.ios-pad.test.tsx +0 -160
- package/src/components/EntranceView.ios-phone.test.tsx +0 -137
- package/src/components/EntranceView.test.tsx +0 -15
- package/src/components/LogoElements.test.tsx +0 -60
- package/src/components/LogoElements.tsx +0 -119
- package/src/components/ReactionPicker.test.tsx +0 -19
- package/src/components/RtcVideoView.test.tsx +0 -12
- package/src/components/SpaceView.android.test.tsx +0 -11
- package/src/components/SpaceView.ios-pad.test.tsx +0 -11
- package/src/components/SpaceView.ios-phone.test.tsx +0 -11
- package/src/components/SpaceView.macos.test.tsx +0 -11
- package/src/components/SpaceView.shared.test.tsx +0 -116
- package/src/components/SpaceView.test.tsx +0 -15
- package/src/components/android-connection-service-controller.test.ts +0 -80
- package/src/components/episode-diagnostic-render-observer.test.ts +0 -24
- package/src/components/native-animation-controller.test.ts +0 -47
- package/src/components/native-chat.test.ts +0 -22
- package/src/components/native-entrance/entrance-controller-store.test.ts +0 -75
- package/src/components/native-media-visibility.test.ts +0 -77
- package/src/components/native-space-callkit-controller.test.ts +0 -62
- package/src/components/native-space-view/AppearanceSettings.test.tsx +0 -56
- package/src/components/native-space-view/ParticipantActionSheet.test.tsx +0 -18
- package/src/components/native-space-view/SettingsSheet.test.tsx +0 -17
- package/src/components/native-space-view/SpaceActionMenu.test.tsx +0 -126
- package/src/components/native-space-view/SpaceBottomDock.android.test.tsx +0 -61
- package/src/components/native-space-view/SpaceChatSheet.test.tsx +0 -18
- package/src/components/native-space-view/SpaceMoreSheet.test.tsx +0 -15
- package/src/components/native-space-view/SpacePanelSheet.test.tsx +0 -15
- package/src/components/native-space-view/SpacePeopleSheet.test.tsx +0 -13
- package/src/components/native-space-view/SpaceSurfacePrimitives.test.tsx +0 -12
- package/src/components/native-space-view/SpaceTopBar.android.test.tsx +0 -43
- package/src/components/native-space-view/SpaceWhiteboardSurface.test.tsx +0 -85
- package/src/components/native-space-view/diagnostics.test.ts +0 -31
- package/src/components/native-space-view/format-space-duration.test.ts +0 -13
- package/src/components/native-space-view/native-space-grid-keys.test.ts +0 -11
- package/src/components/native-space-view/native-space-page-store.test.ts +0 -23
- package/src/components/native-space-view/space-chat-attachments.test.ts +0 -33
- package/src/components/native-space-view/space-progressive-surface-helpers.test.ts +0 -24
- package/src/components/native-space-view/useSpaceViewController.contract.test.ts +0 -10
- package/src/components/native-space-view/useSpaceViewController.test.ts +0 -106
- package/src/components/native-space-view/useSpaceViewDerived.test.ts +0 -58
- package/src/components/native-space-view/useSpaceViewPanels.controlled.test.ts +0 -40
- package/src/components/native-space-view/useSpaceViewPanels.test.ts +0 -73
- package/src/components/space-lifecycle.test.ts +0 -19
- package/src/context/space-client-context.test.tsx +0 -10
- package/src/diagnostics.test.ts +0 -29
- package/src/hooks/entrance-preview-store.test.ts +0 -79
- package/src/hooks/space-hooks.test.ts +0 -76
- package/src/hooks/useLayout.test.ts +0 -16
- package/src/invites.test.ts +0 -51
- package/src/public-surface.test.ts +0 -26
- package/src/runtime.test.ts +0 -83
- package/src/space-client/cloudflare-rtk-native.test.ts +0 -118
- package/src/space-client/create-native-space-client.test.ts +0 -300
- package/src/telemetry.test.ts +0 -143
- package/src/test-support/test-renderer.test.ts +0 -9
- package/src/test-support/test-renderer.ts +0 -28
- package/src/ui/appearance.test.ts +0 -61
- package/src/ui/native-appearance-context.controlled.test.tsx +0 -41
- package/src/ui/native-appearance-context.test.tsx +0 -14
- package/src/ui/native-theme.test.ts +0 -34
- package/src/ui/native-theme.test.tsx +0 -14
- package/src/ui/participant-avatar.test.ts +0 -29
- package/src/ui/theme-tokens.test.ts +0 -14
- package/src/ui/theme.test.ts +0 -36
- package/src/utils/build-chalk-invite-link.test.ts +0 -12
- package/src/utils/ios-simulator.test.ts +0 -96
- package/src/utils/native-space-layout.test.ts +0 -170
- package/src/whiteboard/embedded-whiteboard-assets.test.ts +0 -31
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
// @vitest-environment happy-dom
|
|
2
|
-
|
|
3
|
-
import type { ReactNode } from "react";
|
|
4
|
-
import { describe, expect, it, vi } from "vitest";
|
|
5
|
-
|
|
6
|
-
import { act, renderHook } from "../test-support/test-renderer";
|
|
7
|
-
|
|
8
|
-
vi.mock("react-native", () => ({
|
|
9
|
-
StyleSheet: { absoluteFill: {}, create: (styles: unknown) => styles },
|
|
10
|
-
View: ({ children }: { readonly children?: ReactNode }) => children,
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
import { NativeAppearanceProvider, useNativeAppearance } from "./native-appearance-context";
|
|
14
|
-
|
|
15
|
-
describe("NativeAppearanceProvider controlled initial appearance", () => {
|
|
16
|
-
it("updates palette and texture without replacing its child", () => {
|
|
17
|
-
let initialAppearance = { palette: "light" as const, texture: "none" as const };
|
|
18
|
-
const wrapper = ({ children }: { readonly children?: ReactNode }): React.JSX.Element => <NativeAppearanceProvider initialAppearance={initialAppearance}>{children}</NativeAppearanceProvider>;
|
|
19
|
-
const { result, rerender } = renderHook(() => useNativeAppearance(), { wrapper });
|
|
20
|
-
|
|
21
|
-
expect(result.current.appearance.palette).toBe("light");
|
|
22
|
-
expect(result.current.appearance.texture).toBe("none");
|
|
23
|
-
|
|
24
|
-
initialAppearance = { palette: "warm-porcelain", texture: "paper" };
|
|
25
|
-
rerender();
|
|
26
|
-
|
|
27
|
-
expect(result.current.appearance.palette).toBe("warm-porcelain");
|
|
28
|
-
expect(result.current.appearance.texture).toBe("paper");
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("updates appearance when controls change", () => {
|
|
32
|
-
const wrapper = ({ children }: { readonly children?: ReactNode }): React.JSX.Element => <NativeAppearanceProvider>{children}</NativeAppearanceProvider>;
|
|
33
|
-
const { result } = renderHook(() => useNativeAppearance(), { wrapper });
|
|
34
|
-
|
|
35
|
-
act(() => result.current.setPalette("warm-porcelain"));
|
|
36
|
-
act(() => result.current.setTexture("paper"));
|
|
37
|
-
|
|
38
|
-
expect(result.current.appearance.palette).toBe("warm-porcelain");
|
|
39
|
-
expect(result.current.appearance.texture).toBe("paper");
|
|
40
|
-
});
|
|
41
|
-
});
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
|
|
4
|
-
const source = readFileSync(new URL("./native-appearance-context.tsx", import.meta.url), "utf8");
|
|
5
|
-
|
|
6
|
-
describe("NativeAppearanceProvider", () => {
|
|
7
|
-
it("resolves local selections and renders inert textures", () => {
|
|
8
|
-
expect(source).toContain("resolveNativeAppearance(selection)");
|
|
9
|
-
expect(source).toContain("setPalette:");
|
|
10
|
-
expect(source).toContain("setTexture:");
|
|
11
|
-
expect(source).toContain('pointerEvents="none"');
|
|
12
|
-
expect(source).toContain("must be used inside NativeAppearanceProvider");
|
|
13
|
-
});
|
|
14
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { resolveNativeAppearance } from "./appearance";
|
|
4
|
-
import { resolveNativeTheme } from "./native-theme";
|
|
5
|
-
|
|
6
|
-
describe("resolveNativeTheme", () => {
|
|
7
|
-
it("resolves the public color scheme, accent, and token overrides into semantic colors", () => {
|
|
8
|
-
const theme = resolveNativeTheme({
|
|
9
|
-
colorScheme: "light",
|
|
10
|
-
accent: "#7c3aed",
|
|
11
|
-
tokens: { canvas: "#fefce8", text: "#1c1917" },
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
expect(theme.colorScheme).toBe("light");
|
|
15
|
-
expect(theme.colors).toMatchObject({
|
|
16
|
-
background: "#fefce8",
|
|
17
|
-
foreground: "#1c1917",
|
|
18
|
-
primary: "#7c3aed",
|
|
19
|
-
ring: "#7c3aed",
|
|
20
|
-
stageBackground: "#e2e8f0",
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("projects the selected native palette into rendered theme colors", () => {
|
|
25
|
-
const appearance = resolveNativeAppearance({ palette: "warm-porcelain", texture: "paper" });
|
|
26
|
-
const theme = resolveNativeTheme(undefined, appearance);
|
|
27
|
-
|
|
28
|
-
expect(theme.colorScheme).toBe("light");
|
|
29
|
-
expect(theme.colors.background).toBe(appearance.tokens.canvas);
|
|
30
|
-
expect(theme.colors.foreground).toBe(appearance.tokens.text);
|
|
31
|
-
expect(theme.colors.stageBackground).toBe(appearance.tokens.stage);
|
|
32
|
-
expect(theme.colors.primary).toBe(appearance.tokens.controlPrimary);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { resolveNativeTheme } from "./native-theme";
|
|
4
|
-
|
|
5
|
-
describe("resolveNativeTheme", () => {
|
|
6
|
-
it("uses dark defaults while allowing semantic token overrides", () => {
|
|
7
|
-
const theme = resolveNativeTheme({ tokens: { focus: "#f59e0b", positive: "#15803d" } });
|
|
8
|
-
|
|
9
|
-
expect(theme.colorScheme).toBe("dark");
|
|
10
|
-
expect(theme.colors.darkCanvas).toBe("#0a0a0b");
|
|
11
|
-
expect(theme.colors.ring).toBe("#f59e0b");
|
|
12
|
-
expect(theme.colors.success).toBe("#15803d");
|
|
13
|
-
});
|
|
14
|
-
});
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { getParticipantAvatarRecipe, getParticipantColor, getParticipantInitials, PARTICIPANT_AVATAR_PALETTE } from "./participant-avatar";
|
|
3
|
-
|
|
4
|
-
describe("participant identity colors", () => {
|
|
5
|
-
it("chooses one approved flat identity color deterministically", () => {
|
|
6
|
-
const first = getParticipantColor("Avery Chen");
|
|
7
|
-
const second = getParticipantColor("Avery Chen");
|
|
8
|
-
|
|
9
|
-
expect(first).toEqual(second);
|
|
10
|
-
expect(PARTICIPANT_AVATAR_PALETTE.map(({ primary }) => primary)).toContain(first.primary);
|
|
11
|
-
expect(first.gradientEnd).toBe(first.primary);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("keeps avatar recipes flat for compatibility consumers", () => {
|
|
15
|
-
const recipe = getParticipantAvatarRecipe("Avery Chen");
|
|
16
|
-
|
|
17
|
-
expect(recipe.gradientStops).toEqual([
|
|
18
|
-
{ color: recipe.colors.primary, offset: "0%" },
|
|
19
|
-
{ color: recipe.colors.primary, offset: "100%" },
|
|
20
|
-
]);
|
|
21
|
-
expect(recipe.facehashColors).toEqual([recipe.colors.primary, recipe.colors.primary, recipe.colors.primary]);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("returns one or two initials without making identity noun assumptions", () => {
|
|
25
|
-
expect(getParticipantInitials("Avery Chen")).toBe("AC");
|
|
26
|
-
expect(getParticipantInitials("Avery")).toBe("A");
|
|
27
|
-
expect(getParticipantInitials()).toBe("?");
|
|
28
|
-
});
|
|
29
|
-
});
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { DEFAULT_CHALK_THEME_TOKENS, LIGHT_CHALK_THEME_TOKENS } from "./theme-tokens";
|
|
4
|
-
|
|
5
|
-
describe("Chalk theme tokens", () => {
|
|
6
|
-
it("keeps dark and light themes on the same semantic token contract", () => {
|
|
7
|
-
const tokenNames = ["canvas", "chrome", "surface", "stage", "text", "mutedText", "line", "accent", "accentText", "positive", "danger", "dangerSurface", "focus", "shadow"];
|
|
8
|
-
|
|
9
|
-
expect(Object.keys(DEFAULT_CHALK_THEME_TOKENS)).toEqual(tokenNames);
|
|
10
|
-
expect(Object.keys(LIGHT_CHALK_THEME_TOKENS)).toEqual(tokenNames);
|
|
11
|
-
expect(DEFAULT_CHALK_THEME_TOKENS.canvas).not.toBe(LIGHT_CHALK_THEME_TOKENS.canvas);
|
|
12
|
-
expect(DEFAULT_CHALK_THEME_TOKENS.accent).not.toBe(LIGHT_CHALK_THEME_TOKENS.accent);
|
|
13
|
-
});
|
|
14
|
-
});
|
package/src/ui/theme.test.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { createNativeTheme, Theme } from "./theme";
|
|
3
|
-
|
|
4
|
-
describe("native theme aliases", () => {
|
|
5
|
-
it("keeps progressive surfaces on the active token set", () => {
|
|
6
|
-
expect(Theme.colors.paper).toBe(Theme.colors.background);
|
|
7
|
-
expect(Theme.colors.ink).toBe(Theme.colors.foreground);
|
|
8
|
-
expect(Theme.colors.lineStrong).toBe(Theme.colors.border);
|
|
9
|
-
expect(Theme.colors.chalkGreen).toBe(Theme.colors.success);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it("resolves the same aliases for light mode", () => {
|
|
13
|
-
const light = createNativeTheme(
|
|
14
|
-
{
|
|
15
|
-
canvas: "#f8fafc",
|
|
16
|
-
chrome: "#ffffff",
|
|
17
|
-
surface: "#f1f5f9",
|
|
18
|
-
stage: "#e2e8f0",
|
|
19
|
-
text: "#0f172a",
|
|
20
|
-
mutedText: "#475569",
|
|
21
|
-
line: "#cbd5e1",
|
|
22
|
-
accent: "#0f766e",
|
|
23
|
-
accentText: "#ffffff",
|
|
24
|
-
positive: "#15803d",
|
|
25
|
-
danger: "#b91c1c",
|
|
26
|
-
dangerSurface: "#fee2e2",
|
|
27
|
-
focus: "#0f766e",
|
|
28
|
-
shadow: "#0f172a",
|
|
29
|
-
},
|
|
30
|
-
"light",
|
|
31
|
-
);
|
|
32
|
-
expect(light.colors.paper).toBe(light.colors.background);
|
|
33
|
-
expect(light.colors.ink).toBe(light.colors.foreground);
|
|
34
|
-
expect(light.colors.primary).toBe("#0f766e");
|
|
35
|
-
});
|
|
36
|
-
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { buildChalkInviteLink } from "./build-chalk-invite-link";
|
|
3
|
-
|
|
4
|
-
describe("buildChalkInviteLink", () => {
|
|
5
|
-
it("builds a canonical Space invite link with the public host", () => {
|
|
6
|
-
expect(buildChalkInviteLink("design-lab", "cspi1.key.payload.signature")).toBe("https://chalkmeet.com/space/design-lab#spaceInviteToken=cspi1.key.payload.signature");
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it("normalizes trailing slashes on custom origins", () => {
|
|
10
|
-
expect(buildChalkInviteLink("design-lab", "cspi1.key.payload.signature", "https://chalk.q9labs.ai/")).toBe("https://chalk.q9labs.ai/space/design-lab#spaceInviteToken=cspi1.key.payload.signature");
|
|
11
|
-
});
|
|
12
|
-
});
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
const nativeModules = {
|
|
4
|
-
ChalkRuntimeInfo: { isSimulator: true },
|
|
5
|
-
WebRTCModule: {
|
|
6
|
-
receiverGetCapabilities: vi.fn(() => {
|
|
7
|
-
throw new Error("native receiver call should be bypassed on simulator");
|
|
8
|
-
}),
|
|
9
|
-
senderGetCapabilities: vi.fn(() => {
|
|
10
|
-
throw new Error("native sender call should be bypassed on simulator");
|
|
11
|
-
}),
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const mockedMediaDevices = {
|
|
16
|
-
enumerateDevices: vi.fn(async () => {
|
|
17
|
-
throw new Error("mediaDevices.enumerateDevices should be bypassed on simulator");
|
|
18
|
-
}),
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
vi.mock("react-native", () => ({
|
|
22
|
-
NativeModules: nativeModules,
|
|
23
|
-
Platform: { OS: "ios" },
|
|
24
|
-
}));
|
|
25
|
-
|
|
26
|
-
describe("ensureIosSimulatorWebRtcSafety", () => {
|
|
27
|
-
beforeEach(() => {
|
|
28
|
-
vi.resetModules();
|
|
29
|
-
nativeModules.ChalkRuntimeInfo.isSimulator = true;
|
|
30
|
-
nativeModules.WebRTCModule.receiverGetCapabilities = vi.fn(() => {
|
|
31
|
-
throw new Error("native receiver call should be bypassed on simulator");
|
|
32
|
-
});
|
|
33
|
-
nativeModules.WebRTCModule.senderGetCapabilities = vi.fn(() => {
|
|
34
|
-
throw new Error("native sender call should be bypassed on simulator");
|
|
35
|
-
});
|
|
36
|
-
delete (globalThis as Record<string, unknown>).RTCRtpReceiver;
|
|
37
|
-
delete (globalThis as Record<string, unknown>).RTCRtpSender;
|
|
38
|
-
mockedMediaDevices.enumerateDevices = vi.fn(async () => {
|
|
39
|
-
throw new Error("mediaDevices.enumerateDevices should be bypassed on simulator");
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("patches native capability lookups on the iOS simulator", async () => {
|
|
44
|
-
const { ensureIosSimulatorWebRtcSafety } = await import("./ios-simulator");
|
|
45
|
-
|
|
46
|
-
ensureIosSimulatorWebRtcSafety();
|
|
47
|
-
|
|
48
|
-
expect(nativeModules.WebRTCModule.receiverGetCapabilities("video")).toEqual({
|
|
49
|
-
codecs: [],
|
|
50
|
-
headerExtensions: [],
|
|
51
|
-
});
|
|
52
|
-
expect(nativeModules.WebRTCModule.senderGetCapabilities("audio")).toEqual({
|
|
53
|
-
codecs: [],
|
|
54
|
-
headerExtensions: [],
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("patches exported RTCRtp capability helpers when available", async () => {
|
|
59
|
-
const { ensureIosSimulatorWebRtcSafety } = await import("./ios-simulator");
|
|
60
|
-
|
|
61
|
-
const receiver = {
|
|
62
|
-
getCapabilities: vi.fn(() => {
|
|
63
|
-
throw new Error("receiver helper should be patched");
|
|
64
|
-
}),
|
|
65
|
-
};
|
|
66
|
-
const sender = {
|
|
67
|
-
getCapabilities: vi.fn(() => {
|
|
68
|
-
throw new Error("sender helper should be patched");
|
|
69
|
-
}),
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
ensureIosSimulatorWebRtcSafety({
|
|
73
|
-
RTCRtpReceiver: receiver,
|
|
74
|
-
RTCRtpSender: sender,
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
expect(receiver.getCapabilities("video")).toEqual({
|
|
78
|
-
codecs: [],
|
|
79
|
-
headerExtensions: [],
|
|
80
|
-
});
|
|
81
|
-
expect(sender.getCapabilities("audio")).toEqual({
|
|
82
|
-
codecs: [],
|
|
83
|
-
headerExtensions: [],
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("patches media device enumeration on the iOS simulator", async () => {
|
|
88
|
-
const { ensureIosSimulatorWebRtcSafety } = await import("./ios-simulator");
|
|
89
|
-
|
|
90
|
-
ensureIosSimulatorWebRtcSafety({
|
|
91
|
-
mediaDevices: mockedMediaDevices,
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
await expect(mockedMediaDevices.enumerateDevices()).resolves.toEqual([]);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import type { NativeParticipantState } from "../ui/native-types";
|
|
3
|
-
import { buildCompactParticipantPages, normalizeStageParticipants, resolveNativeSpaceLayout } from "./native-space-layout";
|
|
4
|
-
|
|
5
|
-
type SpaceParticipant = NativeParticipantState["participants"][number];
|
|
6
|
-
|
|
7
|
-
function track(readyState: "live" | "ended" = "live"): MediaStreamTrack {
|
|
8
|
-
return { readyState } as MediaStreamTrack;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function participant(overrides: Partial<SpaceParticipant> = {}): SpaceParticipant {
|
|
12
|
-
return {
|
|
13
|
-
id: overrides.id ?? "participant-1",
|
|
14
|
-
displayName: overrides.displayName ?? "Participant",
|
|
15
|
-
role: overrides.role ?? "participant",
|
|
16
|
-
isLocal: overrides.isLocal ?? false,
|
|
17
|
-
videoEnabled: overrides.videoEnabled ?? false,
|
|
18
|
-
audioEnabled: overrides.audioEnabled ?? true,
|
|
19
|
-
isScreenSharing: overrides.isScreenSharing ?? false,
|
|
20
|
-
isSpeaking: overrides.isSpeaking ?? false,
|
|
21
|
-
handRaised: overrides.handRaised ?? false,
|
|
22
|
-
connectionQuality: overrides.connectionQuality ?? 100,
|
|
23
|
-
videoTrack: overrides.videoTrack ?? undefined,
|
|
24
|
-
audioTrack: overrides.audioTrack ?? undefined,
|
|
25
|
-
screenShareTrack: overrides.screenShareTrack ?? undefined,
|
|
26
|
-
screenShareAudioTrack: overrides.screenShareAudioTrack ?? undefined,
|
|
27
|
-
joinedAt: overrides.joinedAt ?? new Date(),
|
|
28
|
-
metadata: overrides.metadata ?? {},
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
describe("native-space-layout", () => {
|
|
33
|
-
it("normalizes participants to local-first without duplication", () => {
|
|
34
|
-
const local = participant({ id: "local", isLocal: true });
|
|
35
|
-
const remote = participant({ id: "remote" });
|
|
36
|
-
|
|
37
|
-
expect(normalizeStageParticipants([remote, local], local).map((item) => item.id)).toEqual(["local", "remote"]);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("builds compact grid pages for higher participant counts", () => {
|
|
41
|
-
const participants = Array.from({ length: 9 }, (_, index) => participant({ id: `p-${index + 1}` }));
|
|
42
|
-
|
|
43
|
-
expect(buildCompactParticipantPages(participants).map((page) => page.length)).toEqual([4, 4, 1]);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("stays in grid mode without whiteboard or a live screen share track", () => {
|
|
47
|
-
const local = participant({ id: "local", isLocal: true });
|
|
48
|
-
|
|
49
|
-
const result = resolveNativeSpaceLayout({
|
|
50
|
-
participants: [local],
|
|
51
|
-
localParticipant: local,
|
|
52
|
-
screenShare: {
|
|
53
|
-
isActive: false,
|
|
54
|
-
isLocalSharing: false,
|
|
55
|
-
sharerParticipantId: null,
|
|
56
|
-
videoTrack: null,
|
|
57
|
-
},
|
|
58
|
-
isWhiteboardOpen: false,
|
|
59
|
-
isCompactViewport: true,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
expect(result.primaryContent).toBe("grid");
|
|
63
|
-
expect(result.isStageMode).toBe(false);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("uses remote screen share as stage content when a live track exists", () => {
|
|
67
|
-
const local = participant({ id: "local", isLocal: true });
|
|
68
|
-
const remote = participant({ id: "remote", isScreenSharing: true, screenShareTrack: track() });
|
|
69
|
-
|
|
70
|
-
const result = resolveNativeSpaceLayout({
|
|
71
|
-
participants: [local, remote],
|
|
72
|
-
localParticipant: local,
|
|
73
|
-
screenShare: {
|
|
74
|
-
isActive: true,
|
|
75
|
-
isLocalSharing: false,
|
|
76
|
-
sharerParticipantId: "remote",
|
|
77
|
-
videoTrack: track(),
|
|
78
|
-
},
|
|
79
|
-
isWhiteboardOpen: false,
|
|
80
|
-
isCompactViewport: true,
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
expect(result.primaryContent).toBe("screen-share");
|
|
84
|
-
expect(result.screenSharer?.id).toBe("remote");
|
|
85
|
-
expect(result.isStageMode).toBe(true);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("suppresses local screen share into a placeholder stage mode", () => {
|
|
89
|
-
const local = participant({ id: "local", isLocal: true, isScreenSharing: true, screenShareTrack: track() });
|
|
90
|
-
|
|
91
|
-
const result = resolveNativeSpaceLayout({
|
|
92
|
-
participants: [local],
|
|
93
|
-
localParticipant: local,
|
|
94
|
-
screenShare: {
|
|
95
|
-
isActive: true,
|
|
96
|
-
isLocalSharing: true,
|
|
97
|
-
sharerParticipantId: "local",
|
|
98
|
-
videoTrack: track(),
|
|
99
|
-
},
|
|
100
|
-
isWhiteboardOpen: false,
|
|
101
|
-
isCompactViewport: true,
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
expect(result.primaryContent).toBe("screen-share-placeholder");
|
|
105
|
-
expect(result.isLocalScreenShare).toBe(true);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("prefers whiteboard over screen share on compact view", () => {
|
|
109
|
-
const local = participant({ id: "local", isLocal: true });
|
|
110
|
-
const remote = participant({ id: "remote", isScreenSharing: true, screenShareTrack: track() });
|
|
111
|
-
|
|
112
|
-
const result = resolveNativeSpaceLayout({
|
|
113
|
-
participants: [local, remote],
|
|
114
|
-
localParticipant: local,
|
|
115
|
-
screenShare: {
|
|
116
|
-
isActive: true,
|
|
117
|
-
isLocalSharing: false,
|
|
118
|
-
sharerParticipantId: "remote",
|
|
119
|
-
videoTrack: track(),
|
|
120
|
-
},
|
|
121
|
-
isWhiteboardOpen: true,
|
|
122
|
-
isCompactViewport: true,
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
expect(result.primaryContent).toBe("whiteboard");
|
|
126
|
-
expect(result.isSplit).toBe(false);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it("uses split stage for whiteboard plus screen share on wide view", () => {
|
|
130
|
-
const local = participant({ id: "local", isLocal: true });
|
|
131
|
-
const remote = participant({ id: "remote", isScreenSharing: true, screenShareTrack: track() });
|
|
132
|
-
|
|
133
|
-
const result = resolveNativeSpaceLayout({
|
|
134
|
-
participants: [local, remote],
|
|
135
|
-
localParticipant: local,
|
|
136
|
-
screenShare: {
|
|
137
|
-
isActive: true,
|
|
138
|
-
isLocalSharing: false,
|
|
139
|
-
sharerParticipantId: "remote",
|
|
140
|
-
videoTrack: track(),
|
|
141
|
-
},
|
|
142
|
-
isWhiteboardOpen: true,
|
|
143
|
-
isCompactViewport: false,
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
expect(result.primaryContent).toBe("split");
|
|
147
|
-
expect(result.isSplit).toBe(true);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("ignores stale screen share state when no live track exists", () => {
|
|
151
|
-
const local = participant({ id: "local", isLocal: true });
|
|
152
|
-
const remote = participant({ id: "remote", isScreenSharing: true, screenShareTrack: track("ended") });
|
|
153
|
-
|
|
154
|
-
const result = resolveNativeSpaceLayout({
|
|
155
|
-
participants: [local, remote],
|
|
156
|
-
localParticipant: local,
|
|
157
|
-
screenShare: {
|
|
158
|
-
isActive: true,
|
|
159
|
-
isLocalSharing: false,
|
|
160
|
-
sharerParticipantId: "remote",
|
|
161
|
-
videoTrack: null,
|
|
162
|
-
},
|
|
163
|
-
isWhiteboardOpen: false,
|
|
164
|
-
isCompactViewport: true,
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
expect(result.primaryContent).toBe("grid");
|
|
168
|
-
expect(result.showScreenShare).toBe(false);
|
|
169
|
-
});
|
|
170
|
-
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
vi.mock("react-native", () => ({
|
|
4
|
-
NativeModules: {},
|
|
5
|
-
Platform: { OS: "android" },
|
|
6
|
-
}));
|
|
7
|
-
|
|
8
|
-
import { isEmbeddedWhiteboardNavigationAllowed, rendererURLWithContext } from "./embedded-whiteboard-assets";
|
|
9
|
-
|
|
10
|
-
describe("embedded whiteboard asset policy", () => {
|
|
11
|
-
it("adds only non-credential bridge context to the local renderer URL", () => {
|
|
12
|
-
const url = rendererURLWithContext("file:///android_asset/chalk-whiteboard/index.html", {
|
|
13
|
-
journeyId: "journey-1",
|
|
14
|
-
rendererGeneration: "generation-1",
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
expect(url).toBe("file:///android_asset/chalk-whiteboard/index.html?journeyId=journey-1&rendererGeneration=generation-1");
|
|
18
|
-
expect(url).not.toMatch(/token|credential|signature/iu);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("allows renderer-local assets and rejects every remote navigation", () => {
|
|
22
|
-
const renderer = "file:///android_asset/chalk-whiteboard/index.html";
|
|
23
|
-
|
|
24
|
-
expect(isEmbeddedWhiteboardNavigationAllowed(renderer, renderer)).toBe(true);
|
|
25
|
-
expect(isEmbeddedWhiteboardNavigationAllowed("file:///android_asset/chalk-whiteboard/fonts/Assistant/Assistant-Regular.woff2", renderer)).toBe(true);
|
|
26
|
-
expect(isEmbeddedWhiteboardNavigationAllowed("file:///android_asset/chalk-whiteboard/../../data/user/0/secret", renderer)).toBe(false);
|
|
27
|
-
expect(isEmbeddedWhiteboardNavigationAllowed("file:///android_asset/chalk-whiteboard/%2e%2e/%2e%2e/data/user/0/secret", renderer)).toBe(false);
|
|
28
|
-
expect(isEmbeddedWhiteboardNavigationAllowed("https://cdn.jsdelivr.net/npm/@excalidraw/excalidraw/index.css", renderer)).toBe(false);
|
|
29
|
-
expect(isEmbeddedWhiteboardNavigationAllowed("javascript:alert(1)", renderer)).toBe(false);
|
|
30
|
-
});
|
|
31
|
-
});
|