Skip to content

Commit d1753c3

Browse files
authored
Use correct rageshake URL when running in embedded package + tests (#3132)
* Use correct rageshake URL when running in embedded package It was incorrectly trying to use the one from config.json * Refactor to add tests * Empty mock config
1 parent e4c222a commit d1753c3

File tree

4 files changed

+235
-78
lines changed

4 files changed

+235
-78
lines changed

src/settings/SettingsModal.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { PreferencesSettingsTab } from "./PreferencesSettingsTab";
3030
import { Slider } from "../Slider";
3131
import { DeviceSelection } from "./DeviceSelection";
3232
import { DeveloperSettingsTab } from "./DeveloperSettingsTab";
33-
import { isRageshakeAvailable } from "./submit-rageshake";
33+
import { useSubmitRageshake } from "./submit-rageshake";
3434

3535
type SettingsTab =
3636
| "audio"
@@ -71,6 +71,8 @@ export const SettingsModal: FC<Props> = ({
7171

7272
const [showDeveloperSettingsTab] = useSetting(developerMode);
7373

74+
const { available: isRageshakeAvailable } = useSubmitRageshake();
75+
7476
const audioTab: Tab<SettingsTab> = {
7577
key: "audio",
7678
name: t("common.audio"),
@@ -148,7 +150,7 @@ export const SettingsModal: FC<Props> = ({
148150
const tabs = [audioTab, videoTab];
149151
if (widget === null) tabs.push(profileTab);
150152
tabs.push(preferencesTab);
151-
if (isRageshakeAvailable() || import.meta.env.VITE_PACKAGE === "full") {
153+
if (isRageshakeAvailable || import.meta.env.VITE_PACKAGE === "full") {
152154
// for full package we want to show the analytics consent checkbox
153155
// even if rageshake is not available
154156
tabs.push(feedbackTab);

src/settings/submit-rageshake.test.ts

Lines changed: 1 addition & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -15,78 +15,12 @@ import {
1515
beforeEach,
1616
} from "vitest";
1717

18-
import {
19-
getRageshakeSubmitUrl,
20-
isRageshakeAvailable,
21-
} from "./submit-rageshake";
18+
import { getRageshakeSubmitUrl } from "./submit-rageshake";
2219
import { getUrlParams } from "../UrlParams";
2320
import { mockConfig } from "../utils/test";
2421

2522
vi.mock("../UrlParams", () => ({ getUrlParams: vi.fn() }));
2623

27-
describe("isRageshakeAvailable", () => {
28-
beforeEach(() => {
29-
(getUrlParams as Mock).mockReturnValue({});
30-
mockConfig({});
31-
});
32-
33-
afterEach(() => {
34-
vi.unstubAllEnvs();
35-
vi.clearAllMocks();
36-
});
37-
38-
describe("embedded package", () => {
39-
beforeEach(() => {
40-
vi.stubEnv("VITE_PACKAGE", "embedded");
41-
});
42-
43-
it("returns false with no rageshakeSubmitUrl URL param", () => {
44-
expect(isRageshakeAvailable()).toBe(false);
45-
});
46-
47-
it("ignores config value and returns false with no rageshakeSubmitUrl URL param", () => {
48-
mockConfig({
49-
rageshake: {
50-
submit_url: "https://config.example.com.localhost",
51-
},
52-
});
53-
expect(isRageshakeAvailable()).toBe(false);
54-
});
55-
56-
it("returns true with rageshakeSubmitUrl URL param", () => {
57-
(getUrlParams as Mock).mockReturnValue({
58-
rageshakeSubmitUrl: "https://url.example.com.localhost",
59-
});
60-
expect(isRageshakeAvailable()).toBe(true);
61-
});
62-
});
63-
64-
describe("full package", () => {
65-
beforeEach(() => {
66-
vi.stubEnv("VITE_PACKAGE", "full");
67-
});
68-
it("returns false with no config value", () => {
69-
expect(isRageshakeAvailable()).toBe(false);
70-
});
71-
72-
it("ignores rageshakeSubmitUrl URL param and returns false with no config value", () => {
73-
(getUrlParams as Mock).mockReturnValue({
74-
rageshakeSubmitUrl: "https://url.example.com.localhost",
75-
});
76-
expect(isRageshakeAvailable()).toBe(false);
77-
});
78-
79-
it("returns true with config value", () => {
80-
mockConfig({
81-
rageshake: {
82-
submit_url: "https://config.example.com.localhost",
83-
},
84-
});
85-
expect(isRageshakeAvailable()).toBe(true);
86-
});
87-
});
88-
});
89-
9024
describe("getRageshakeSubmitUrl", () => {
9125
beforeEach(() => {
9226
(getUrlParams as Mock).mockReturnValue({});

src/settings/submit-rageshake.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,9 @@ export function getRageshakeSubmitUrl(): string | undefined {
131131
return undefined;
132132
}
133133

134-
export function isRageshakeAvailable(): boolean {
135-
return !!getRageshakeSubmitUrl();
136-
}
137-
138-
export function useSubmitRageshake(): {
134+
export function useSubmitRageshake(
135+
injectedGetRageshakeSubmitUrl = getRageshakeSubmitUrl,
136+
): {
139137
submitRageshake: (opts: RageShakeSubmitOptions) => Promise<void>;
140138
sending: boolean;
141139
sent: boolean;
@@ -158,7 +156,8 @@ export function useSubmitRageshake(): {
158156
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
159157
// @ts-ignore
160158
async (opts) => {
161-
if (!getRageshakeSubmitUrl()) {
159+
const submitUrl = injectedGetRageshakeSubmitUrl();
160+
if (!submitUrl) {
162161
throw new Error("No rageshake URL is configured");
163162
}
164163

@@ -292,7 +291,7 @@ export function useSubmitRageshake(): {
292291
);
293292
}
294293

295-
const res = await fetch(Config.get().rageshake!.submit_url, {
294+
const res = await fetch(submitUrl, {
296295
method: "POST",
297296
body,
298297
});
@@ -309,15 +308,15 @@ export function useSubmitRageshake(): {
309308
logger.error(error);
310309
}
311310
},
312-
[client, sending],
311+
[client, sending, injectedGetRageshakeSubmitUrl],
313312
);
314313

315314
return {
316315
submitRageshake,
317316
sending,
318317
sent,
319318
error,
320-
available: isRageshakeAvailable(),
319+
available: !!injectedGetRageshakeSubmitUrl(),
321320
};
322321
}
323322

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/*
2+
Copyright 2025 New Vector Ltd.
3+
4+
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
Please see LICENSE in the repository root for full details.
6+
*/
7+
8+
import {
9+
expect,
10+
describe,
11+
it,
12+
vi,
13+
beforeEach,
14+
afterEach,
15+
type Mock,
16+
} from "vitest";
17+
import { useState, type ReactElement } from "react";
18+
import { render, screen, waitFor } from "@testing-library/react";
19+
import { type MatrixClient } from "matrix-js-sdk/lib/client";
20+
21+
import { useSubmitRageshake, getRageshakeSubmitUrl } from "./submit-rageshake";
22+
import { ClientContextProvider } from "../ClientContext";
23+
import { getUrlParams } from "../UrlParams";
24+
import { mockConfig } from "../utils/test";
25+
26+
vi.mock("../UrlParams", () => ({ getUrlParams: vi.fn() }));
27+
28+
const TestComponent = ({
29+
sendLogs,
30+
getRageshakeSubmitUrl,
31+
}: {
32+
sendLogs: boolean;
33+
getRageshakeSubmitUrl: () => string | undefined;
34+
}): ReactElement => {
35+
const [clickError, setClickError] = useState<Error | null>(null);
36+
const { available, sending, sent, submitRageshake, error } =
37+
useSubmitRageshake(getRageshakeSubmitUrl);
38+
39+
const onClick = (): void => {
40+
submitRageshake({
41+
sendLogs,
42+
}).catch((e) => {
43+
setClickError(e);
44+
});
45+
};
46+
47+
return (
48+
<div>
49+
<p data-testid="available">{available ? "true" : "false"}</p>
50+
<p data-testid="sending">{sending ? "true" : "false"}</p>
51+
<p data-testid="sent">{sent ? "true" : "false"}</p>
52+
<p data-testid="error">{error?.message}</p>
53+
<p data-testid="clickError">{clickError?.message}</p>
54+
<button onClick={onClick} data-testid="submit">
55+
submit
56+
</button>
57+
</div>
58+
);
59+
};
60+
61+
function renderWithMockClient(
62+
getRageshakeSubmitUrl: () => string | undefined,
63+
sendLogs: boolean,
64+
): void {
65+
const client = vi.mocked<MatrixClient>({
66+
getUserId: vi.fn().mockReturnValue("@user:localhost"),
67+
getUser: vi.fn().mockReturnValue(null),
68+
credentials: {
69+
userId: "@user:localhost",
70+
},
71+
getCrypto: vi.fn().mockReturnValue(undefined),
72+
} as unknown as MatrixClient);
73+
74+
render(
75+
<ClientContextProvider
76+
value={{
77+
state: "valid",
78+
disconnected: false,
79+
supportedFeatures: {
80+
reactions: true,
81+
thumbnails: true,
82+
},
83+
setClient: vi.fn(),
84+
authenticated: {
85+
client,
86+
isPasswordlessUser: true,
87+
changePassword: vi.fn(),
88+
logout: vi.fn(),
89+
},
90+
}}
91+
>
92+
<TestComponent
93+
sendLogs={sendLogs}
94+
getRageshakeSubmitUrl={getRageshakeSubmitUrl}
95+
/>
96+
</ClientContextProvider>,
97+
);
98+
}
99+
100+
describe("useSubmitRageshake", () => {
101+
describe("available", () => {
102+
beforeEach(() => {
103+
(getUrlParams as Mock).mockReturnValue({});
104+
mockConfig({});
105+
});
106+
107+
afterEach(() => {
108+
vi.unstubAllEnvs();
109+
vi.clearAllMocks();
110+
});
111+
112+
describe("embedded package", () => {
113+
beforeEach(() => {
114+
vi.stubEnv("VITE_PACKAGE", "embedded");
115+
});
116+
117+
it("returns false with no rageshakeSubmitUrl URL param", () => {
118+
renderWithMockClient(getRageshakeSubmitUrl, false);
119+
expect(screen.getByTestId("available").textContent).toBe("false");
120+
});
121+
122+
it("ignores config value and returns false with no rageshakeSubmitUrl URL param", () => {
123+
mockConfig({
124+
rageshake: {
125+
submit_url: "https://config.example.com.localhost",
126+
},
127+
});
128+
renderWithMockClient(getRageshakeSubmitUrl, false);
129+
expect(screen.getByTestId("available").textContent).toBe("false");
130+
});
131+
132+
it("returns true with rageshakeSubmitUrl URL param", () => {
133+
(getUrlParams as Mock).mockReturnValue({
134+
rageshakeSubmitUrl: "https://url.example.com.localhost",
135+
});
136+
renderWithMockClient(getRageshakeSubmitUrl, false);
137+
expect(screen.getByTestId("available").textContent).toBe("true");
138+
});
139+
});
140+
141+
describe("full package", () => {
142+
beforeEach(() => {
143+
mockConfig({});
144+
vi.stubEnv("VITE_PACKAGE", "full");
145+
});
146+
it("returns false with no config value", () => {
147+
renderWithMockClient(getRageshakeSubmitUrl, false);
148+
expect(screen.getByTestId("available").textContent).toBe("false");
149+
});
150+
151+
it("ignores rageshakeSubmitUrl URL param and returns false with no config value", () => {
152+
(getUrlParams as Mock).mockReturnValue({
153+
rageshakeSubmitUrl: "https://url.example.com.localhost",
154+
});
155+
renderWithMockClient(getRageshakeSubmitUrl, false);
156+
expect(screen.getByTestId("available").textContent).toBe("false");
157+
});
158+
159+
it("returns true with config value", () => {
160+
mockConfig({
161+
rageshake: {
162+
submit_url: "https://config.example.com.localhost",
163+
},
164+
});
165+
renderWithMockClient(getRageshakeSubmitUrl, false);
166+
expect(screen.getByTestId("available").textContent).toBe("true");
167+
});
168+
});
169+
});
170+
171+
describe("when rageshake is available", () => {
172+
beforeEach(() => {
173+
mockConfig({});
174+
vi.unstubAllGlobals();
175+
});
176+
177+
it("starts unsent", () => {
178+
renderWithMockClient(() => "https://rageshake.localhost/foo", false);
179+
expect(screen.getByTestId("sending").textContent).toBe("false");
180+
expect(screen.getByTestId("sent").textContent).toBe("false");
181+
});
182+
183+
it("submitRageshake fetches expected URL", async () => {
184+
const fetchFn = vi.fn().mockResolvedValue({
185+
status: 200,
186+
});
187+
vi.stubGlobal("fetch", fetchFn);
188+
189+
renderWithMockClient(() => "https://rageshake.localhost/foo", false);
190+
screen.getByTestId("submit").click();
191+
await waitFor(() => {
192+
expect(screen.getByTestId("sent").textContent).toBe("true");
193+
});
194+
expect(fetchFn).toHaveBeenCalledExactlyOnceWith(
195+
"https://rageshake.localhost/foo",
196+
expect.objectContaining({
197+
method: "POST",
198+
}),
199+
);
200+
expect(screen.getByTestId("clickError").textContent).toBe("");
201+
expect(screen.getByTestId("error").textContent).toBe("");
202+
});
203+
});
204+
205+
describe("when rageshake is not available", () => {
206+
it("starts unsent", () => {
207+
renderWithMockClient(() => undefined, false);
208+
expect(screen.getByTestId("sending").textContent).toBe("false");
209+
expect(screen.getByTestId("sent").textContent).toBe("false");
210+
});
211+
212+
it("submitRageshake throws error", async () => {
213+
renderWithMockClient(() => undefined, false);
214+
screen.getByTestId("submit").click();
215+
await waitFor(() => {
216+
expect(screen.getByTestId("clickError").textContent).toBe(
217+
"No rageshake URL is configured",
218+
);
219+
});
220+
});
221+
});
222+
});

0 commit comments

Comments
 (0)