@prosopo/procaptcha-common 2.9.23 → 2.10.18
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/.turbo/turbo-build$colon$cjs.log +22 -15
- package/.turbo/turbo-build$colon$tsc.log +22 -16
- package/.turbo/turbo-build.log +22 -16
- package/CHANGELOG.md +355 -0
- package/dist/cjs/elements/window.cjs +7 -0
- package/dist/cjs/index.cjs +5 -0
- package/dist/cjs/reactComponents/Checkbox.cjs +11 -7
- package/dist/cjs/reactComponents/Honeypot.cjs +67 -0
- package/dist/cjs/reactComponents/TestModeBanner.cjs +47 -0
- package/dist/elements/window.d.ts +1 -0
- package/dist/elements/window.d.ts.map +1 -1
- package/dist/elements/window.js +8 -1
- package/dist/elements/window.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/reactComponents/Checkbox.d.ts.map +1 -1
- package/dist/reactComponents/Checkbox.js +11 -7
- package/dist/reactComponents/Checkbox.js.map +1 -1
- package/dist/reactComponents/Honeypot.d.ts +6 -0
- package/dist/reactComponents/Honeypot.d.ts.map +1 -0
- package/dist/reactComponents/Honeypot.js +67 -0
- package/dist/reactComponents/Honeypot.js.map +1 -0
- package/dist/reactComponents/TestModeBanner.d.ts +7 -0
- package/dist/reactComponents/TestModeBanner.d.ts.map +1 -0
- package/dist/reactComponents/TestModeBanner.js +47 -0
- package/dist/reactComponents/TestModeBanner.js.map +1 -0
- package/dist/tests/window.test.js +22 -1
- package/dist/tests/window.test.js.map +1 -1
- package/package.json +11 -8
- package/src/callbacks/defaultCallbacks.ts +197 -0
- package/src/callbacks/defaultEvents.ts +21 -0
- package/src/elements/form.ts +41 -0
- package/src/elements/window.ts +39 -0
- package/src/extensionLoader.ts +17 -0
- package/src/index.ts +24 -0
- package/src/providers.ts +49 -0
- package/src/reactComponents/Checkbox.tsx +203 -0
- package/src/reactComponents/Honeypot.tsx +133 -0
- package/src/reactComponents/Reload.tsx +99 -0
- package/src/reactComponents/TestModeBanner.tsx +75 -0
- package/src/state/builder.ts +137 -0
- package/src/tests/defaultCallbacks.test.ts +372 -0
- package/src/tests/defaultEvents.test.ts +80 -0
- package/src/tests/extensionLoader.test.ts +41 -0
- package/src/tests/form.test.ts +154 -0
- package/src/tests/providers.test.ts +175 -0
- package/src/tests/state-builder.test.ts +264 -0
- package/src/tests/window.test.ts +137 -0
- package/tsconfig.cjs.json +32 -0
- package/tsconfig.json +33 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsconfig.types.json +9 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import { ApiParams } from "@prosopo/types";
|
|
16
|
+
import type { ProcaptchaRenderOptions, ProcaptchaToken } from "@prosopo/types";
|
|
17
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
18
|
+
import {
|
|
19
|
+
getDefaultCallbacks,
|
|
20
|
+
setUserCallbacks,
|
|
21
|
+
} from "../callbacks/defaultCallbacks.js";
|
|
22
|
+
|
|
23
|
+
describe("callbacks/defaultCallbacks", () => {
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
document.body.innerHTML = "";
|
|
26
|
+
vi.clearAllMocks();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe("getDefaultCallbacks", () => {
|
|
30
|
+
it("should return default callbacks object", () => {
|
|
31
|
+
const callbacks = getDefaultCallbacks();
|
|
32
|
+
|
|
33
|
+
expect(callbacks).toHaveProperty("onHuman");
|
|
34
|
+
expect(callbacks).toHaveProperty("onChallengeExpired");
|
|
35
|
+
expect(callbacks).toHaveProperty("onExtensionNotFound");
|
|
36
|
+
expect(callbacks).toHaveProperty("onExpired");
|
|
37
|
+
expect(callbacks).toHaveProperty("onError");
|
|
38
|
+
expect(callbacks).toHaveProperty("onClose");
|
|
39
|
+
expect(callbacks).toHaveProperty("onOpen");
|
|
40
|
+
expect(callbacks).toHaveProperty("onFailed");
|
|
41
|
+
expect(callbacks).toHaveProperty("onReset");
|
|
42
|
+
expect(callbacks).toHaveProperty("onReload");
|
|
43
|
+
|
|
44
|
+
expect(typeof callbacks.onHuman).toBe("function");
|
|
45
|
+
expect(typeof callbacks.onChallengeExpired).toBe("function");
|
|
46
|
+
expect(typeof callbacks.onError).toBe("function");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("onHuman should add hidden input to form with token", () => {
|
|
50
|
+
const form = document.createElement("form");
|
|
51
|
+
const widget = document.createElement("div");
|
|
52
|
+
form.appendChild(widget);
|
|
53
|
+
document.body.appendChild(form);
|
|
54
|
+
|
|
55
|
+
const callbacks = getDefaultCallbacks(widget);
|
|
56
|
+
const token = "test-token-123" as ProcaptchaToken;
|
|
57
|
+
|
|
58
|
+
callbacks.onHuman(token);
|
|
59
|
+
|
|
60
|
+
const inputs = form.querySelectorAll(
|
|
61
|
+
`input[name="${ApiParams.procaptchaResponse}"]`,
|
|
62
|
+
);
|
|
63
|
+
expect(inputs.length).toBe(1);
|
|
64
|
+
expect((inputs[0] as HTMLInputElement).value).toBe(token);
|
|
65
|
+
expect((inputs[0] as HTMLInputElement).type).toBe("hidden");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("onHuman should handle missing parent form gracefully", () => {
|
|
69
|
+
const widget = document.createElement("div");
|
|
70
|
+
document.body.appendChild(widget);
|
|
71
|
+
|
|
72
|
+
const consoleErrorSpy = vi
|
|
73
|
+
.spyOn(console, "error")
|
|
74
|
+
.mockImplementation(() => {});
|
|
75
|
+
|
|
76
|
+
const callbacks = getDefaultCallbacks(widget);
|
|
77
|
+
const token = "test-token-123" as ProcaptchaToken;
|
|
78
|
+
|
|
79
|
+
callbacks.onHuman(token);
|
|
80
|
+
|
|
81
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
82
|
+
"Parent form not found for the element:",
|
|
83
|
+
widget,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
consoleErrorSpy.mockRestore();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("onChallengeExpired should remove procaptcha response", () => {
|
|
90
|
+
const input = document.createElement("input");
|
|
91
|
+
input.name = ApiParams.procaptchaResponse;
|
|
92
|
+
document.body.appendChild(input);
|
|
93
|
+
|
|
94
|
+
const callbacks = getDefaultCallbacks();
|
|
95
|
+
callbacks.onChallengeExpired();
|
|
96
|
+
|
|
97
|
+
expect(
|
|
98
|
+
document.getElementsByName(ApiParams.procaptchaResponse).length,
|
|
99
|
+
).toBe(0);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("onExpired should remove procaptcha response", () => {
|
|
103
|
+
const input = document.createElement("input");
|
|
104
|
+
input.name = ApiParams.procaptchaResponse;
|
|
105
|
+
document.body.appendChild(input);
|
|
106
|
+
|
|
107
|
+
const callbacks = getDefaultCallbacks();
|
|
108
|
+
callbacks.onExpired();
|
|
109
|
+
|
|
110
|
+
expect(
|
|
111
|
+
document.getElementsByName(ApiParams.procaptchaResponse).length,
|
|
112
|
+
).toBe(0);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("onError should remove procaptcha response and log error", () => {
|
|
116
|
+
const input = document.createElement("input");
|
|
117
|
+
input.name = ApiParams.procaptchaResponse;
|
|
118
|
+
document.body.appendChild(input);
|
|
119
|
+
|
|
120
|
+
const consoleErrorSpy = vi
|
|
121
|
+
.spyOn(console, "error")
|
|
122
|
+
.mockImplementation(() => {});
|
|
123
|
+
const callbacks = getDefaultCallbacks();
|
|
124
|
+
const error = new Error("Test error");
|
|
125
|
+
|
|
126
|
+
callbacks.onError(error);
|
|
127
|
+
|
|
128
|
+
expect(
|
|
129
|
+
document.getElementsByName(ApiParams.procaptchaResponse).length,
|
|
130
|
+
).toBe(0);
|
|
131
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(error);
|
|
132
|
+
|
|
133
|
+
consoleErrorSpy.mockRestore();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("onFailed should show alert", () => {
|
|
137
|
+
const alertSpy = vi.spyOn(window, "alert").mockImplementation(() => {});
|
|
138
|
+
const callbacks = getDefaultCallbacks();
|
|
139
|
+
|
|
140
|
+
callbacks.onFailed();
|
|
141
|
+
|
|
142
|
+
expect(alertSpy).toHaveBeenCalledWith(
|
|
143
|
+
"Captcha challenge failed. Please try again",
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
alertSpy.mockRestore();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("onReset should remove procaptcha response", () => {
|
|
150
|
+
const input = document.createElement("input");
|
|
151
|
+
input.name = ApiParams.procaptchaResponse;
|
|
152
|
+
document.body.appendChild(input);
|
|
153
|
+
|
|
154
|
+
const callbacks = getDefaultCallbacks();
|
|
155
|
+
callbacks.onReset();
|
|
156
|
+
|
|
157
|
+
expect(
|
|
158
|
+
document.getElementsByName(ApiParams.procaptchaResponse).length,
|
|
159
|
+
).toBe(0);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe("setUserCallbacks", () => {
|
|
164
|
+
it("should wrap user callback for onHuman", () => {
|
|
165
|
+
const form = document.createElement("form");
|
|
166
|
+
const widget = document.createElement("div");
|
|
167
|
+
form.appendChild(widget);
|
|
168
|
+
document.body.appendChild(form);
|
|
169
|
+
|
|
170
|
+
const userCallback = vi.fn();
|
|
171
|
+
const renderOptions: Partial<ProcaptchaRenderOptions> = {
|
|
172
|
+
callback: userCallback,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const callbacks = getDefaultCallbacks(widget);
|
|
176
|
+
setUserCallbacks(
|
|
177
|
+
renderOptions as ProcaptchaRenderOptions,
|
|
178
|
+
callbacks,
|
|
179
|
+
widget,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const token = "test-token" as ProcaptchaToken;
|
|
183
|
+
callbacks.onHuman(token);
|
|
184
|
+
|
|
185
|
+
expect(userCallback).toHaveBeenCalledWith(token);
|
|
186
|
+
// Also verify that default behavior (adding to form) still happens
|
|
187
|
+
const inputs = form.querySelectorAll(
|
|
188
|
+
`input[name="${ApiParams.procaptchaResponse}"]`,
|
|
189
|
+
);
|
|
190
|
+
expect(inputs.length).toBeGreaterThan(0);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("should use data attribute callback over renderOptions", () => {
|
|
194
|
+
const form = document.createElement("form");
|
|
195
|
+
const widget = document.createElement("div");
|
|
196
|
+
form.appendChild(widget);
|
|
197
|
+
document.body.appendChild(form);
|
|
198
|
+
|
|
199
|
+
const dataCallback = vi.fn();
|
|
200
|
+
const renderCallback = vi.fn();
|
|
201
|
+
|
|
202
|
+
// biome-ignore lint/suspicious/noExplicitAny: Test setup
|
|
203
|
+
(window as any).dataCallbackFn = dataCallback;
|
|
204
|
+
widget.setAttribute("data-callback", "dataCallbackFn");
|
|
205
|
+
|
|
206
|
+
const renderOptions: Partial<ProcaptchaRenderOptions> = {
|
|
207
|
+
callback: renderCallback,
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const callbacks = getDefaultCallbacks(widget);
|
|
211
|
+
setUserCallbacks(
|
|
212
|
+
renderOptions as ProcaptchaRenderOptions,
|
|
213
|
+
callbacks,
|
|
214
|
+
widget,
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
const token = "test-token" as ProcaptchaToken;
|
|
218
|
+
callbacks.onHuman(token);
|
|
219
|
+
|
|
220
|
+
expect(dataCallback).toHaveBeenCalledWith(token);
|
|
221
|
+
expect(renderCallback).not.toHaveBeenCalled();
|
|
222
|
+
|
|
223
|
+
// biome-ignore lint/suspicious/noExplicitAny: Test cleanup
|
|
224
|
+
(window as any).dataCallbackFn = undefined;
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it("should handle string callback name in renderOptions", () => {
|
|
228
|
+
const form = document.createElement("form");
|
|
229
|
+
const widget = document.createElement("div");
|
|
230
|
+
form.appendChild(widget);
|
|
231
|
+
document.body.appendChild(form);
|
|
232
|
+
|
|
233
|
+
const namedCallback = vi.fn();
|
|
234
|
+
// biome-ignore lint/suspicious/noExplicitAny: Test setup
|
|
235
|
+
(window as any).myCallback = namedCallback;
|
|
236
|
+
|
|
237
|
+
const renderOptions: Partial<ProcaptchaRenderOptions> = {
|
|
238
|
+
// biome-ignore lint/suspicious/noExplicitAny: Test string callback
|
|
239
|
+
callback: "myCallback" as any,
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const callbacks = getDefaultCallbacks(widget);
|
|
243
|
+
setUserCallbacks(
|
|
244
|
+
renderOptions as ProcaptchaRenderOptions,
|
|
245
|
+
callbacks,
|
|
246
|
+
widget,
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
const token = "test-token" as ProcaptchaToken;
|
|
250
|
+
callbacks.onHuman(token);
|
|
251
|
+
|
|
252
|
+
expect(namedCallback).toHaveBeenCalledWith(token);
|
|
253
|
+
|
|
254
|
+
// biome-ignore lint/suspicious/noExplicitAny: Test cleanup
|
|
255
|
+
(window as any).myCallback = undefined;
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("should set expired callback from renderOptions", () => {
|
|
259
|
+
const widget = document.createElement("div");
|
|
260
|
+
const expiredCallback = vi.fn();
|
|
261
|
+
|
|
262
|
+
const renderOptions: Partial<ProcaptchaRenderOptions> = {
|
|
263
|
+
"expired-callback": expiredCallback,
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const callbacks = getDefaultCallbacks(widget);
|
|
267
|
+
setUserCallbacks(
|
|
268
|
+
renderOptions as ProcaptchaRenderOptions,
|
|
269
|
+
callbacks,
|
|
270
|
+
widget,
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
callbacks.onExpired();
|
|
274
|
+
|
|
275
|
+
expect(expiredCallback).toHaveBeenCalled();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("should set error callback from renderOptions", () => {
|
|
279
|
+
const widget = document.createElement("div");
|
|
280
|
+
const errorCallback = vi.fn();
|
|
281
|
+
|
|
282
|
+
const renderOptions: Partial<ProcaptchaRenderOptions> = {
|
|
283
|
+
"error-callback": errorCallback,
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const callbacks = getDefaultCallbacks(widget);
|
|
287
|
+
setUserCallbacks(
|
|
288
|
+
renderOptions as ProcaptchaRenderOptions,
|
|
289
|
+
callbacks,
|
|
290
|
+
widget,
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
const error = new Error("Test error");
|
|
294
|
+
callbacks.onError(error);
|
|
295
|
+
|
|
296
|
+
expect(errorCallback).toHaveBeenCalledWith(error);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("should set failed callback from renderOptions", () => {
|
|
300
|
+
const widget = document.createElement("div");
|
|
301
|
+
const failedCallback = vi.fn();
|
|
302
|
+
|
|
303
|
+
const renderOptions: Partial<ProcaptchaRenderOptions> = {
|
|
304
|
+
"failed-callback": failedCallback,
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const callbacks = getDefaultCallbacks(widget);
|
|
308
|
+
setUserCallbacks(
|
|
309
|
+
renderOptions as ProcaptchaRenderOptions,
|
|
310
|
+
callbacks,
|
|
311
|
+
widget,
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
callbacks.onFailed();
|
|
315
|
+
|
|
316
|
+
expect(failedCallback).toHaveBeenCalled();
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("should set reset callback from renderOptions", () => {
|
|
320
|
+
const widget = document.createElement("div");
|
|
321
|
+
const resetCallback = vi.fn();
|
|
322
|
+
|
|
323
|
+
const renderOptions: Partial<ProcaptchaRenderOptions> = {
|
|
324
|
+
"reset-callback": resetCallback,
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const callbacks = getDefaultCallbacks(widget);
|
|
328
|
+
setUserCallbacks(
|
|
329
|
+
renderOptions as ProcaptchaRenderOptions,
|
|
330
|
+
callbacks,
|
|
331
|
+
widget,
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
callbacks.onReset();
|
|
335
|
+
|
|
336
|
+
expect(resetCallback).toHaveBeenCalled();
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it("should handle undefined renderOptions gracefully", () => {
|
|
340
|
+
const widget = document.createElement("div");
|
|
341
|
+
const callbacks = getDefaultCallbacks(widget);
|
|
342
|
+
|
|
343
|
+
expect(() =>
|
|
344
|
+
setUserCallbacks(undefined, callbacks, widget),
|
|
345
|
+
).not.toThrow();
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it("should set open and close callbacks", () => {
|
|
349
|
+
const widget = document.createElement("div");
|
|
350
|
+
const openCallback = vi.fn();
|
|
351
|
+
const closeCallback = vi.fn();
|
|
352
|
+
|
|
353
|
+
const renderOptions: Partial<ProcaptchaRenderOptions> = {
|
|
354
|
+
"open-callback": openCallback,
|
|
355
|
+
"close-callback": closeCallback,
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const callbacks = getDefaultCallbacks(widget);
|
|
359
|
+
setUserCallbacks(
|
|
360
|
+
renderOptions as ProcaptchaRenderOptions,
|
|
361
|
+
callbacks,
|
|
362
|
+
widget,
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
callbacks.onOpen();
|
|
366
|
+
callbacks.onClose();
|
|
367
|
+
|
|
368
|
+
expect(openCallback).toHaveBeenCalled();
|
|
369
|
+
expect(closeCallback).toHaveBeenCalled();
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import { describe, expect, it, vi } from "vitest";
|
|
16
|
+
import { getDefaultEvents } from "../callbacks/defaultEvents.js";
|
|
17
|
+
|
|
18
|
+
describe("callbacks/defaultEvents", () => {
|
|
19
|
+
describe("getDefaultEvents", () => {
|
|
20
|
+
it("should merge default callbacks with provided callbacks", () => {
|
|
21
|
+
const customCallback = vi.fn();
|
|
22
|
+
const events = getDefaultEvents({
|
|
23
|
+
onHuman: customCallback,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
expect(events.onHuman).toBe(customCallback);
|
|
27
|
+
expect(typeof events.onError).toBe("function");
|
|
28
|
+
expect(typeof events.onExpired).toBe("function");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should return all default callbacks when no custom callbacks provided", () => {
|
|
32
|
+
const events = getDefaultEvents({});
|
|
33
|
+
|
|
34
|
+
expect(typeof events.onHuman).toBe("function");
|
|
35
|
+
expect(typeof events.onChallengeExpired).toBe("function");
|
|
36
|
+
expect(typeof events.onExtensionNotFound).toBe("function");
|
|
37
|
+
expect(typeof events.onExpired).toBe("function");
|
|
38
|
+
expect(typeof events.onError).toBe("function");
|
|
39
|
+
expect(typeof events.onClose).toBe("function");
|
|
40
|
+
expect(typeof events.onOpen).toBe("function");
|
|
41
|
+
expect(typeof events.onFailed).toBe("function");
|
|
42
|
+
expect(typeof events.onReset).toBe("function");
|
|
43
|
+
expect(typeof events.onReload).toBe("function");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should allow overriding multiple callbacks", () => {
|
|
47
|
+
const customOnError = vi.fn();
|
|
48
|
+
const customOnExpired = vi.fn();
|
|
49
|
+
const customOnClose = vi.fn();
|
|
50
|
+
|
|
51
|
+
const events = getDefaultEvents({
|
|
52
|
+
onError: customOnError,
|
|
53
|
+
onExpired: customOnExpired,
|
|
54
|
+
onClose: customOnClose,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
expect(events.onError).toBe(customOnError);
|
|
58
|
+
expect(events.onExpired).toBe(customOnExpired);
|
|
59
|
+
expect(events.onClose).toBe(customOnClose);
|
|
60
|
+
expect(typeof events.onHuman).toBe("function");
|
|
61
|
+
expect(typeof events.onFailed).toBe("function");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should preserve default behavior for non-overridden callbacks", () => {
|
|
65
|
+
const customOnHuman = vi.fn();
|
|
66
|
+
|
|
67
|
+
const events = getDefaultEvents({
|
|
68
|
+
onHuman: customOnHuman,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Test that custom callback is used
|
|
72
|
+
expect(events.onHuman).toBe(customOnHuman);
|
|
73
|
+
|
|
74
|
+
// Test that other callbacks still exist and are functions
|
|
75
|
+
expect(typeof events.onError).toBe("function");
|
|
76
|
+
expect(typeof events.onExpired).toBe("function");
|
|
77
|
+
expect(typeof events.onChallengeExpired).toBe("function");
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import { describe, expect, it, vi } from "vitest";
|
|
16
|
+
import { ExtensionLoader } from "../extensionLoader.js";
|
|
17
|
+
|
|
18
|
+
// Mock the imports
|
|
19
|
+
vi.mock("@prosopo/account/extension/ExtensionWeb2", () => ({
|
|
20
|
+
default: { name: "ExtensionWeb2" },
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
vi.mock("@prosopo/account/extension/ExtensionWeb3", () => ({
|
|
24
|
+
default: { name: "ExtensionWeb3" },
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
describe("extensionLoader", () => {
|
|
28
|
+
describe("ExtensionLoader", () => {
|
|
29
|
+
it("should load ExtensionWeb2 when web2 is true", async () => {
|
|
30
|
+
const result = await ExtensionLoader(true);
|
|
31
|
+
|
|
32
|
+
expect(result).toEqual({ name: "ExtensionWeb2" });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should load ExtensionWeb3 when web2 is false", async () => {
|
|
36
|
+
const result = await ExtensionLoader(false);
|
|
37
|
+
|
|
38
|
+
expect(result).toEqual({ name: "ExtensionWeb3" });
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Copyright 2021-2026 Prosopo (UK) Ltd.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import { ApiParams } from "@prosopo/types";
|
|
16
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
17
|
+
import { getParentForm, removeProcaptchaResponse } from "../elements/form.js";
|
|
18
|
+
|
|
19
|
+
describe("elements/form", () => {
|
|
20
|
+
describe("getParentForm", () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
document.body.innerHTML = "";
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should find parent form for regular DOM element", () => {
|
|
26
|
+
const form = document.createElement("form");
|
|
27
|
+
const div = document.createElement("div");
|
|
28
|
+
const widget = document.createElement("div");
|
|
29
|
+
|
|
30
|
+
form.appendChild(div);
|
|
31
|
+
div.appendChild(widget);
|
|
32
|
+
document.body.appendChild(form);
|
|
33
|
+
|
|
34
|
+
const result = getParentForm(widget);
|
|
35
|
+
expect(result).toBe(form);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should return null if no parent form exists", () => {
|
|
39
|
+
const widget = document.createElement("div");
|
|
40
|
+
document.body.appendChild(widget);
|
|
41
|
+
|
|
42
|
+
const result = getParentForm(widget);
|
|
43
|
+
expect(result).toBeNull();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should find parent form for element in shadow DOM", () => {
|
|
47
|
+
const form = document.createElement("form");
|
|
48
|
+
const host = document.createElement("div");
|
|
49
|
+
const shadowRoot = host.attachShadow({ mode: "open" });
|
|
50
|
+
const widget = document.createElement("div");
|
|
51
|
+
|
|
52
|
+
shadowRoot.appendChild(widget);
|
|
53
|
+
form.appendChild(host);
|
|
54
|
+
document.body.appendChild(form);
|
|
55
|
+
|
|
56
|
+
const result = getParentForm(widget);
|
|
57
|
+
expect(result).toBe(form);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should handle nested forms and return closest one", () => {
|
|
61
|
+
const outerForm = document.createElement("form");
|
|
62
|
+
outerForm.id = "outer";
|
|
63
|
+
const innerForm = document.createElement("form");
|
|
64
|
+
innerForm.id = "inner";
|
|
65
|
+
const widget = document.createElement("div");
|
|
66
|
+
|
|
67
|
+
innerForm.appendChild(widget);
|
|
68
|
+
outerForm.appendChild(innerForm);
|
|
69
|
+
document.body.appendChild(outerForm);
|
|
70
|
+
|
|
71
|
+
const result = getParentForm(widget);
|
|
72
|
+
expect(result).toBe(innerForm);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("removeProcaptchaResponse", () => {
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
document.body.innerHTML = "";
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("should remove all procaptcha response elements", () => {
|
|
82
|
+
const input1 = document.createElement("input");
|
|
83
|
+
input1.name = ApiParams.procaptchaResponse;
|
|
84
|
+
input1.value = "token1";
|
|
85
|
+
|
|
86
|
+
const input2 = document.createElement("input");
|
|
87
|
+
input2.name = ApiParams.procaptchaResponse;
|
|
88
|
+
input2.value = "token2";
|
|
89
|
+
|
|
90
|
+
document.body.appendChild(input1);
|
|
91
|
+
document.body.appendChild(input2);
|
|
92
|
+
|
|
93
|
+
expect(
|
|
94
|
+
document.getElementsByName(ApiParams.procaptchaResponse).length,
|
|
95
|
+
).toBe(2);
|
|
96
|
+
|
|
97
|
+
removeProcaptchaResponse();
|
|
98
|
+
|
|
99
|
+
expect(
|
|
100
|
+
document.getElementsByName(ApiParams.procaptchaResponse).length,
|
|
101
|
+
).toBe(0);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("should not remove other input elements", () => {
|
|
105
|
+
const procaptchaInput = document.createElement("input");
|
|
106
|
+
procaptchaInput.name = ApiParams.procaptchaResponse;
|
|
107
|
+
|
|
108
|
+
const otherInput = document.createElement("input");
|
|
109
|
+
otherInput.name = "other-field";
|
|
110
|
+
|
|
111
|
+
document.body.appendChild(procaptchaInput);
|
|
112
|
+
document.body.appendChild(otherInput);
|
|
113
|
+
|
|
114
|
+
removeProcaptchaResponse();
|
|
115
|
+
|
|
116
|
+
expect(
|
|
117
|
+
document.getElementsByName(ApiParams.procaptchaResponse).length,
|
|
118
|
+
).toBe(0);
|
|
119
|
+
expect(document.getElementsByName("other-field").length).toBe(1);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("should handle case when no procaptcha response elements exist", () => {
|
|
123
|
+
expect(() => removeProcaptchaResponse()).not.toThrow();
|
|
124
|
+
expect(
|
|
125
|
+
document.getElementsByName(ApiParams.procaptchaResponse).length,
|
|
126
|
+
).toBe(0);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("should remove elements from different parts of the DOM", () => {
|
|
130
|
+
const form1 = document.createElement("form");
|
|
131
|
+
const input1 = document.createElement("input");
|
|
132
|
+
input1.name = ApiParams.procaptchaResponse;
|
|
133
|
+
form1.appendChild(input1);
|
|
134
|
+
|
|
135
|
+
const form2 = document.createElement("form");
|
|
136
|
+
const input2 = document.createElement("input");
|
|
137
|
+
input2.name = ApiParams.procaptchaResponse;
|
|
138
|
+
form2.appendChild(input2);
|
|
139
|
+
|
|
140
|
+
document.body.appendChild(form1);
|
|
141
|
+
document.body.appendChild(form2);
|
|
142
|
+
|
|
143
|
+
expect(
|
|
144
|
+
document.getElementsByName(ApiParams.procaptchaResponse).length,
|
|
145
|
+
).toBe(2);
|
|
146
|
+
|
|
147
|
+
removeProcaptchaResponse();
|
|
148
|
+
|
|
149
|
+
expect(
|
|
150
|
+
document.getElementsByName(ApiParams.procaptchaResponse).length,
|
|
151
|
+
).toBe(0);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|