@prosopo/procaptcha-react 2.9.97 → 2.9.99

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.
Files changed (66) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +8 -8
  2. package/.turbo/turbo-build$colon$tsc.log +21 -21
  3. package/.turbo/turbo-build.log +8 -8
  4. package/CHANGELOG.md +30 -0
  5. package/dist/cjs/components/CaptchaComponent.cjs +4 -5
  6. package/dist/cjs/components/CaptchaWidget.cjs +4 -12
  7. package/dist/cjs/components/ProcaptchaWidget.cjs +8 -6
  8. package/dist/components/CaptchaComponent.d.ts.map +1 -1
  9. package/dist/components/CaptchaComponent.js +4 -5
  10. package/dist/components/CaptchaComponent.js.map +1 -1
  11. package/dist/components/CaptchaWidget.d.ts.map +1 -1
  12. package/dist/components/CaptchaWidget.js +4 -12
  13. package/dist/components/CaptchaWidget.js.map +1 -1
  14. package/dist/components/ProcaptchaWidget.d.ts.map +1 -1
  15. package/dist/components/ProcaptchaWidget.js +8 -6
  16. package/dist/components/ProcaptchaWidget.js.map +1 -1
  17. package/dist/tests/button.unit.test.d.ts +2 -0
  18. package/dist/tests/button.unit.test.d.ts.map +1 -0
  19. package/dist/tests/button.unit.test.js +113 -0
  20. package/dist/tests/button.unit.test.js.map +1 -0
  21. package/dist/tests/captchaComponent.unit.test.d.ts +2 -0
  22. package/dist/tests/captchaComponent.unit.test.d.ts.map +1 -0
  23. package/dist/tests/captchaComponent.unit.test.js +141 -0
  24. package/dist/tests/captchaComponent.unit.test.js.map +1 -0
  25. package/dist/tests/captchaWidget.unit.test.d.ts +2 -0
  26. package/dist/tests/captchaWidget.unit.test.d.ts.map +1 -0
  27. package/dist/tests/captchaWidget.unit.test.js +191 -0
  28. package/dist/tests/captchaWidget.unit.test.js.map +1 -0
  29. package/dist/tests/collector.unit.test.d.ts +2 -0
  30. package/dist/tests/collector.unit.test.d.ts.map +1 -0
  31. package/dist/tests/collector.unit.test.js +109 -0
  32. package/dist/tests/collector.unit.test.js.map +1 -0
  33. package/dist/tests/harness.d.ts +12 -0
  34. package/dist/tests/harness.d.ts.map +1 -0
  35. package/dist/tests/harness.js +69 -0
  36. package/dist/tests/harness.js.map +1 -0
  37. package/dist/tests/modal.unit.test.d.ts +2 -0
  38. package/dist/tests/modal.unit.test.d.ts.map +1 -0
  39. package/dist/tests/modal.unit.test.js +72 -0
  40. package/dist/tests/modal.unit.test.js.map +1 -0
  41. package/dist/tests/procaptcha.unit.test.d.ts +2 -0
  42. package/dist/tests/procaptcha.unit.test.d.ts.map +1 -0
  43. package/dist/tests/procaptcha.unit.test.js +57 -0
  44. package/dist/tests/procaptcha.unit.test.js.map +1 -0
  45. package/dist/tests/procaptchaReact.test-d.d.ts +2 -0
  46. package/dist/tests/procaptchaReact.test-d.d.ts.map +1 -0
  47. package/dist/tests/procaptchaReact.test-d.js +72 -0
  48. package/dist/tests/procaptchaReact.test-d.js.map +1 -0
  49. package/dist/tests/procaptchaWidget.unit.test.d.ts +2 -0
  50. package/dist/tests/procaptchaWidget.unit.test.d.ts.map +1 -0
  51. package/dist/tests/procaptchaWidget.unit.test.js +403 -0
  52. package/dist/tests/procaptchaWidget.unit.test.js.map +1 -0
  53. package/dist/tests/render.d.ts +23 -0
  54. package/dist/tests/render.d.ts.map +1 -0
  55. package/dist/tests/render.js +77 -0
  56. package/dist/tests/render.js.map +1 -0
  57. package/dist/tests/setup.d.ts +5 -0
  58. package/dist/tests/setup.d.ts.map +1 -0
  59. package/dist/tests/setup.js +3 -0
  60. package/dist/tests/setup.js.map +1 -0
  61. package/dist/tests/util.unit.test.d.ts +2 -0
  62. package/dist/tests/util.unit.test.d.ts.map +1 -0
  63. package/dist/tests/util.unit.test.js +84 -0
  64. package/dist/tests/util.unit.test.js.map +1 -0
  65. package/package.json +9 -5
  66. package/vite.test.config.ts +28 -0
@@ -0,0 +1,113 @@
1
+ import { jsx as _jsx } from "@emotion/react/jsx-runtime";
2
+ import { darkTheme, lightTheme } from "@prosopo/widget-skeleton";
3
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
4
+ import Button from "../components/Button.js";
5
+ import { asRgb, fire, fireAndReturn, mount } from "./render.js";
6
+ let mounted;
7
+ const onClick = vi.fn();
8
+ const render = (props = {}) => {
9
+ mounted.render(_jsx(Button, { themeColor: props.themeColor ?? "light", buttonType: props.buttonType ?? "next", text: props.text ?? "Next", onClick: onClick }));
10
+ const button = mounted.container.querySelector("button");
11
+ if (!button)
12
+ throw new Error("expected a button to be rendered");
13
+ return button;
14
+ };
15
+ beforeEach(() => {
16
+ vi.clearAllMocks();
17
+ process.env.NODE_ENV = "test";
18
+ mounted = mount();
19
+ });
20
+ afterEach(() => {
21
+ mounted.unmount();
22
+ });
23
+ describe("what the button renders", () => {
24
+ test("shows its text and labels itself with it", () => {
25
+ const button = render({ text: "Submit" });
26
+ expect(button.textContent).toBe("Submit");
27
+ expect(button.getAttribute("aria-label")).toBe("Submit");
28
+ });
29
+ test("renders an empty label without falling over", () => {
30
+ const button = render({ text: "" });
31
+ expect(button.textContent).toBe("");
32
+ });
33
+ test("carries the test hook naming its role", () => {
34
+ const button = render({ buttonType: "cancel" });
35
+ expect(button.getAttribute("data-cy")).toBe("button-cancel");
36
+ });
37
+ test("drops the test hook in a production build", () => {
38
+ process.env.NODE_ENV = "production";
39
+ const button = render({ buttonType: "cancel" });
40
+ expect(button.getAttribute("data-cy")).toBeNull();
41
+ });
42
+ });
43
+ describe("theming", () => {
44
+ test("a light button takes its border from the light theme", () => {
45
+ const button = render({ themeColor: "light" });
46
+ expect(button.style.borderColor).toBe(asRgb(lightTheme.palette.grey[500]));
47
+ });
48
+ test("a dark button takes its border from the dark theme", () => {
49
+ const button = render({ themeColor: "dark" });
50
+ expect(button.style.borderColor).toBe(asRgb(darkTheme.palette.grey[500]));
51
+ });
52
+ test("a cancel button is transparent until it is hovered", () => {
53
+ const button = render({ buttonType: "cancel" });
54
+ expect(button.style.backgroundColor).toBe("transparent");
55
+ });
56
+ test("a next button is filled with the background colour", () => {
57
+ const button = render({ buttonType: "next" });
58
+ expect(button.style.backgroundColor).toBe(asRgb(lightTheme.palette.background.default));
59
+ });
60
+ test("hovering a cancel button reveals the grey fill", () => {
61
+ const button = render({ buttonType: "cancel" });
62
+ fire(button, "mouseover");
63
+ expect(button.style.backgroundColor).toBe(asRgb(lightTheme.palette.grey[600]));
64
+ });
65
+ test("hovering a next button reveals the primary fill", () => {
66
+ const button = render({ buttonType: "next" });
67
+ fire(button, "mouseover");
68
+ expect(button.style.backgroundColor).toBe(asRgb(lightTheme.palette.primary.main));
69
+ });
70
+ test("leaving the button puts the resting fill back", () => {
71
+ const button = render({ buttonType: "next" });
72
+ fire(button, "mouseover");
73
+ fire(button, "mouseout");
74
+ expect(button.style.backgroundColor).toBe(asRgb(lightTheme.palette.background.default));
75
+ });
76
+ test("hover switches the text to the contrast colour", () => {
77
+ const button = render();
78
+ expect(button.style.color).toBe(asRgb(lightTheme.palette.background.contrastText));
79
+ fire(button, "mouseover");
80
+ expect(button.style.color).toBe(asRgb(lightTheme.palette.primary.contrastText));
81
+ });
82
+ test("re-rendering with the same theme keeps the styling stable", () => {
83
+ const button = render({ themeColor: "dark" });
84
+ const before = button.getAttribute("style");
85
+ render({ themeColor: "dark" });
86
+ expect(button.getAttribute("style")).toBe(before);
87
+ });
88
+ });
89
+ describe("clicking", () => {
90
+ test("a real click calls the handler", () => {
91
+ fire(render(), "click");
92
+ expect(onClick).toHaveBeenCalledTimes(1);
93
+ });
94
+ test("a synthetic click is ignored", () => {
95
+ fire(render(), "click", { trusted: false });
96
+ expect(onClick).not.toHaveBeenCalled();
97
+ });
98
+ test("a real click does not submit the surrounding form", () => {
99
+ const event = fireAndReturn(render(), "click");
100
+ expect(event.defaultPrevented).toBe(true);
101
+ });
102
+ test("an ignored click leaves the event alone", () => {
103
+ const event = fireAndReturn(render(), "click", { trusted: false });
104
+ expect(event.defaultPrevented).toBe(false);
105
+ });
106
+ test("every click is reported, not just the first", () => {
107
+ const button = render();
108
+ fire(button, "click");
109
+ fire(button, "click");
110
+ expect(onClick).toHaveBeenCalledTimes(2);
111
+ });
112
+ });
113
+ //# sourceMappingURL=button.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button.unit.test.js","sourceRoot":"","sources":["../../src/tests/button.unit.test.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=captchaComponent.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captchaComponent.unit.test.d.ts","sourceRoot":"","sources":["../../src/tests/captchaComponent.unit.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,141 @@
1
+ import { jsx as _jsx } from "@emotion/react/jsx-runtime";
2
+ import { darkTheme, lightTheme } from "@prosopo/widget-skeleton";
3
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
4
+ import CaptchaComponent from "../components/CaptchaComponent.js";
5
+ import { captcha, challengeResponse } from "./harness.js";
6
+ import { asRgb, fire, mount } from "./render.js";
7
+ vi.mock("@prosopo/locale", async (importOriginal) => {
8
+ const actual = await importOriginal();
9
+ return { ...actual, useTranslation: () => ({ t: (key) => key }) };
10
+ });
11
+ let mounted;
12
+ const onSubmit = vi.fn();
13
+ const onCancel = vi.fn();
14
+ const onClick = vi.fn();
15
+ const onNext = vi.fn();
16
+ const onReload = vi.fn();
17
+ const twoRoundChallenge = () => challengeResponse({
18
+ captchas: [
19
+ captcha({ target: "bus" }),
20
+ captcha({ captchaId: "captcha-id-2", target: "train" }),
21
+ ],
22
+ });
23
+ const render = (props = {}) => {
24
+ mounted.render(_jsx(CaptchaComponent, { challenge: props.challenge ?? challengeResponse(), index: props.index ?? 0, solutions: props.solutions ?? [[]], onSubmit: onSubmit, onCancel: onCancel, onClick: onClick, onNext: onNext, onReload: onReload, themeColor: props.themeColor ?? "light" }));
25
+ };
26
+ const buttonLabelled = (label) => {
27
+ const button = mounted.container.querySelector(`button[aria-label="${label}"]`);
28
+ if (!button)
29
+ throw new Error(`expected a button labelled ${label}`);
30
+ return button;
31
+ };
32
+ const text = () => mounted.container.textContent ?? "";
33
+ beforeEach(() => {
34
+ vi.clearAllMocks();
35
+ mounted = mount();
36
+ });
37
+ afterEach(() => {
38
+ mounted.unmount();
39
+ });
40
+ describe("the prompt", () => {
41
+ test("names what the user has to pick out", () => {
42
+ render();
43
+ expect(text()).toContain("WIDGET.SELECT_ALL");
44
+ expect(text()).toContain("bus");
45
+ });
46
+ test("tells the user what to do when nothing matches", () => {
47
+ render();
48
+ expect(text()).toContain("WIDGET.IF_NONE_CLICK_NEXT");
49
+ });
50
+ test("shows the target of the round being displayed, not the first", () => {
51
+ render({ challenge: twoRoundChallenge(), index: 1, solutions: [[], []] });
52
+ expect(text()).toContain("train");
53
+ });
54
+ });
55
+ describe("the grid", () => {
56
+ test("renders the images of the current round", () => {
57
+ render();
58
+ expect(mounted.container.querySelectorAll("img")).toHaveLength(2);
59
+ });
60
+ test("passes clicks straight through with their coordinates", () => {
61
+ render();
62
+ const image = mounted.container.querySelector("img");
63
+ if (!image)
64
+ throw new Error("expected an image");
65
+ fire(image, "click", { clientX: 8, clientY: 9 });
66
+ expect(onClick).toHaveBeenCalledWith("hash-1", 8, 9);
67
+ });
68
+ test("marks the images already selected for this round", () => {
69
+ render({ solutions: [[["hash-1", 0, 0]]] });
70
+ const overlay = mounted.container.querySelector("svg")?.parentElement;
71
+ expect(overlay?.style.visibility).toBe("visible");
72
+ });
73
+ test("carries the test hook naming the round on screen", () => {
74
+ render({ challenge: twoRoundChallenge(), index: 1, solutions: [[], []] });
75
+ expect(mounted.container.querySelector('[data-cy="captcha-1"]')).not.toBeNull();
76
+ });
77
+ test("refuses a round the challenge does not have", () => {
78
+ expect(() => render({ index: 5 })).toThrow(/larger than array length/);
79
+ });
80
+ test("refuses a round with no solution slot", () => {
81
+ expect(() => render({ solutions: [] })).toThrow();
82
+ });
83
+ });
84
+ describe("the controls", () => {
85
+ test("offers cancel, reload and a forward action", () => {
86
+ render();
87
+ expect(mounted.container.querySelectorAll("button")).toHaveLength(3);
88
+ });
89
+ test("cancelling tells the manager to close the challenge", () => {
90
+ render();
91
+ fire(buttonLabelled("WIDGET.CANCEL"), "click");
92
+ expect(onCancel).toHaveBeenCalledTimes(1);
93
+ });
94
+ test("reloading asks for a fresh challenge", () => {
95
+ render();
96
+ const reload = mounted.container.querySelectorAll("button")[1];
97
+ if (!reload)
98
+ throw new Error("expected a reload button");
99
+ fire(reload, "click");
100
+ expect(onReload).toHaveBeenCalledTimes(1);
101
+ });
102
+ test("the last round submits rather than advancing", () => {
103
+ render();
104
+ expect(buttonLabelled("WIDGET.SUBMIT")).toBeDefined();
105
+ fire(buttonLabelled("WIDGET.SUBMIT"), "click");
106
+ expect(onSubmit).toHaveBeenCalledTimes(1);
107
+ expect(onNext).not.toHaveBeenCalled();
108
+ });
109
+ test("an earlier round advances rather than submitting", () => {
110
+ render({ challenge: twoRoundChallenge(), index: 0, solutions: [[], []] });
111
+ fire(buttonLabelled("WIDGET.NEXT"), "click");
112
+ expect(onNext).toHaveBeenCalledTimes(1);
113
+ expect(onSubmit).not.toHaveBeenCalled();
114
+ });
115
+ test("the final round of a multi-round challenge submits", () => {
116
+ render({ challenge: twoRoundChallenge(), index: 1, solutions: [[], []] });
117
+ expect(buttonLabelled("WIDGET.SUBMIT")).toBeDefined();
118
+ });
119
+ test("a synthetic click on cancel is ignored", () => {
120
+ render();
121
+ fire(buttonLabelled("WIDGET.CANCEL"), "click", { trusted: false });
122
+ expect(onCancel).not.toHaveBeenCalled();
123
+ });
124
+ });
125
+ describe("theming", () => {
126
+ test("uses the light palette for the panel", () => {
127
+ render({ themeColor: "light" });
128
+ const panel = mounted.container.firstElementChild;
129
+ expect(panel?.style.backgroundColor).toBe(asRgb(lightTheme.palette.background.default));
130
+ });
131
+ test("uses the dark palette for the panel", () => {
132
+ render({ themeColor: "dark" });
133
+ const panel = mounted.container.firstElementChild;
134
+ expect(panel?.style.backgroundColor).toBe(asRgb(darkTheme.palette.background.default));
135
+ });
136
+ test("passes the theme down to the buttons", () => {
137
+ render({ themeColor: "dark" });
138
+ expect(buttonLabelled("WIDGET.CANCEL").style.borderColor).toBe(asRgb(darkTheme.palette.grey[500]));
139
+ });
140
+ });
141
+ //# sourceMappingURL=captchaComponent.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captchaComponent.unit.test.js","sourceRoot":"","sources":["../../src/tests/captchaComponent.unit.test.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=captchaWidget.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captchaWidget.unit.test.d.ts","sourceRoot":"","sources":["../../src/tests/captchaWidget.unit.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,191 @@
1
+ import { jsx as _jsx } from "@emotion/react/jsx-runtime";
2
+ import { CaptchaItemTypes } from "@prosopo/types";
3
+ import { darkTheme, lightTheme } from "@prosopo/widget-skeleton";
4
+ import { act } from "react";
5
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
6
+ import { CaptchaWidget } from "../components/CaptchaWidget.js";
7
+ import { captcha } from "./harness.js";
8
+ import { asRgb, fire, mount } from "./render.js";
9
+ let mounted;
10
+ const onClick = vi.fn();
11
+ const item = (hash, data = `https://provider.one/${hash}.png`) => ({
12
+ hash,
13
+ data,
14
+ type: CaptchaItemTypes.Image,
15
+ });
16
+ const render = (props = {}) => {
17
+ mounted.render(_jsx(CaptchaWidget, { challenge: captcha(props.items ? { items: props.items } : {}), solution: props.solution ?? [], onClick: onClick, themeColor: props.themeColor ?? "light" }));
18
+ };
19
+ const tiles = () => Array.from(mounted.container.querySelectorAll("img"));
20
+ const clickable = (index) => {
21
+ const image = tiles()[index];
22
+ const target = image?.parentElement;
23
+ if (!target)
24
+ throw new Error(`expected a tile at index ${index}`);
25
+ return target;
26
+ };
27
+ const overlays = () => Array.from(mounted.container.querySelectorAll("svg")).map((svg) => {
28
+ const parent = svg.parentElement;
29
+ if (!parent)
30
+ throw new Error("expected an overlay around the check icon");
31
+ return parent;
32
+ });
33
+ beforeEach(() => {
34
+ vi.clearAllMocks();
35
+ mounted = mount();
36
+ });
37
+ afterEach(() => {
38
+ mounted.unmount();
39
+ });
40
+ describe("what the grid renders", () => {
41
+ test("shows one image per item", () => {
42
+ render();
43
+ expect(tiles()).toHaveLength(2);
44
+ });
45
+ test("points each image at the url the provider gave", () => {
46
+ render();
47
+ expect(tiles()[0]?.getAttribute("src")).toBe("https://provider.one/img/1.png");
48
+ });
49
+ test("numbers the alt text from one, for a screen reader", () => {
50
+ render();
51
+ expect(tiles().map((image) => image.alt)).toEqual([
52
+ "Captcha image 1",
53
+ "Captcha image 2",
54
+ ]);
55
+ });
56
+ test("renders an empty grid for a captcha with no items", () => {
57
+ render({ items: [] });
58
+ expect(tiles()).toHaveLength(0);
59
+ expect(mounted.container.firstElementChild).not.toBeNull();
60
+ });
61
+ test("renders a single-item captcha", () => {
62
+ render({ items: [item("only")] });
63
+ expect(tiles()).toHaveLength(1);
64
+ });
65
+ test("keeps a large grid intact", () => {
66
+ const many = Array.from({ length: 9 }, (_, index) => item(`hash-${index}`));
67
+ render({ items: many });
68
+ expect(tiles()).toHaveLength(9);
69
+ });
70
+ test("renders an item with no image url as an empty image", () => {
71
+ render({ items: [item("hash-1", "")] });
72
+ expect(tiles()[0]?.getAttribute("src")).toBe("");
73
+ });
74
+ test("refuses to render an item with no hash", () => {
75
+ expect(() => render({ items: [item("")] })).toThrow("CAPTCHA.MISSING_ITEM_HASH");
76
+ });
77
+ });
78
+ describe("theming", () => {
79
+ test("borders the images with the light theme grey", () => {
80
+ render({ themeColor: "light" });
81
+ expect(tiles()[0]?.style.borderColor).toBe(asRgb(lightTheme.palette.grey[300]));
82
+ });
83
+ test("borders the images with the dark theme grey", () => {
84
+ render({ themeColor: "dark" });
85
+ expect(tiles()[0]?.style.borderColor).toBe(asRgb(darkTheme.palette.grey[300]));
86
+ });
87
+ });
88
+ describe("selection", () => {
89
+ test("hides the tick on an unselected image", () => {
90
+ render();
91
+ expect(overlays()[0]?.style.visibility).toBe("hidden");
92
+ });
93
+ test("shows the tick on the image the user picked", () => {
94
+ render({ solution: [["hash-2", 1, 2]] });
95
+ expect(overlays()[0]?.style.visibility).toBe("hidden");
96
+ expect(overlays()[1]?.style.visibility).toBe("visible");
97
+ });
98
+ test("shows every selected image at once", () => {
99
+ render({
100
+ solution: [
101
+ ["hash-1", 0, 0],
102
+ ["hash-2", 0, 0],
103
+ ],
104
+ });
105
+ expect(overlays().map((o) => o.style.visibility)).toEqual([
106
+ "visible",
107
+ "visible",
108
+ ]);
109
+ });
110
+ test("ignores a selection naming an image that is not on screen", () => {
111
+ render({ solution: [["hash-missing", 0, 0]] });
112
+ expect(overlays().map((o) => o.style.visibility)).toEqual([
113
+ "hidden",
114
+ "hidden",
115
+ ]);
116
+ });
117
+ });
118
+ describe("clicking an image", () => {
119
+ test("reports the hash and where the user clicked", () => {
120
+ render();
121
+ fire(clickable(0), "click", { clientX: 12, clientY: 34 });
122
+ expect(onClick).toHaveBeenCalledWith("hash-1", 12, 34);
123
+ });
124
+ test("reports the second image by its own hash", () => {
125
+ render();
126
+ fire(clickable(1), "click", { clientX: 1, clientY: 2 });
127
+ expect(onClick).toHaveBeenCalledWith("hash-2", 1, 2);
128
+ });
129
+ test("ignores a synthetic click", () => {
130
+ render();
131
+ fire(clickable(0), "click", { trusted: false, clientX: 5, clientY: 6 });
132
+ expect(onClick).not.toHaveBeenCalled();
133
+ });
134
+ test("reports a click at the origin as (0, 0)", () => {
135
+ render();
136
+ fire(clickable(0), "click");
137
+ expect(onClick).toHaveBeenCalledWith("hash-1", 0, 0);
138
+ });
139
+ test("reports every click, so a second one can deselect", () => {
140
+ render();
141
+ fire(clickable(0), "click", { clientX: 1, clientY: 1 });
142
+ fire(clickable(0), "click", { clientX: 2, clientY: 2 });
143
+ expect(onClick).toHaveBeenCalledTimes(2);
144
+ });
145
+ test("a click on the image itself still reports the tile", () => {
146
+ render();
147
+ const image = tiles()[0];
148
+ if (!image)
149
+ throw new Error("expected an image");
150
+ fire(image, "click", { clientX: 3, clientY: 4 });
151
+ expect(onClick).toHaveBeenCalledWith("hash-1", 3, 4);
152
+ });
153
+ });
154
+ describe("images that fail to load", () => {
155
+ const failLoad = (image) => {
156
+ act(() => {
157
+ image.dispatchEvent(new Event("error", { bubbles: true }));
158
+ });
159
+ };
160
+ test("retries with a cache-busting url", () => {
161
+ render();
162
+ const image = tiles()[0];
163
+ if (!image)
164
+ throw new Error("expected an image");
165
+ failLoad(image);
166
+ expect(image.getAttribute("src")).toMatch(/^https:\/\/provider\.one\/img\/1\.png\?retry=\d+$/);
167
+ });
168
+ test("gives up after three retries rather than looping forever", () => {
169
+ render();
170
+ const image = tiles()[0];
171
+ if (!image)
172
+ throw new Error("expected an image");
173
+ for (let attempt = 0; attempt < 3; attempt++)
174
+ failLoad(image);
175
+ const afterThird = image.getAttribute("src");
176
+ failLoad(image);
177
+ expect(image.getAttribute("src")).toBe(afterThird);
178
+ expect(image.dataset.retryCount).toBe("4");
179
+ });
180
+ test("counts retries per image, not across the grid", () => {
181
+ render();
182
+ const [first, second] = tiles();
183
+ if (!first || !second)
184
+ throw new Error("expected two images");
185
+ for (let attempt = 0; attempt < 4; attempt++)
186
+ failLoad(first);
187
+ failLoad(second);
188
+ expect(second.getAttribute("src")).toMatch(/\?retry=\d+$/);
189
+ });
190
+ });
191
+ //# sourceMappingURL=captchaWidget.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captchaWidget.unit.test.js","sourceRoot":"","sources":["../../src/tests/captchaWidget.unit.test.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=collector.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collector.unit.test.d.ts","sourceRoot":"","sources":["../../src/tests/collector.unit.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,109 @@
1
+ import { jsx as _jsx } from "@emotion/react/jsx-runtime";
2
+ import { act } from "react";
3
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
4
+ import Collector from "../components/collector.js";
5
+ import { account } from "./harness.js";
6
+ import { mount } from "./render.js";
7
+ const started = vi.fn();
8
+ vi.mock("@prosopo/procaptcha", () => ({
9
+ startCollector: (...args) => started(...args),
10
+ }));
11
+ const defaultAccount = account();
12
+ let mounted;
13
+ const onProcessData = vi.fn();
14
+ const render = (props = {}) => {
15
+ mounted.render(_jsx(Collector, { onProcessData: onProcessData, sendData: props.sendData ?? true, account: "account" in props ? props.account : defaultAccount }));
16
+ };
17
+ beforeEach(() => {
18
+ vi.clearAllMocks();
19
+ mounted = mount();
20
+ });
21
+ afterEach(() => {
22
+ mounted.unmount();
23
+ });
24
+ describe("wiring the collector up", () => {
25
+ test("starts collecting against its own element", () => {
26
+ render();
27
+ expect(started).toHaveBeenCalledTimes(1);
28
+ const rootElement = started.mock.calls[0]?.[3];
29
+ expect(rootElement).toBe(mounted.container.firstElementChild);
30
+ });
31
+ test("hands the collector setters that feed this component's state", () => {
32
+ render();
33
+ const [setMouse, setTouch, setKeyboard] = started.mock.calls[0] ?? [];
34
+ expect(typeof setMouse).toBe("function");
35
+ expect(typeof setTouch).toBe("function");
36
+ expect(typeof setKeyboard).toBe("function");
37
+ });
38
+ test("starts collecting once, however many times it re-renders", () => {
39
+ render();
40
+ render();
41
+ render();
42
+ expect(started).toHaveBeenCalledTimes(1);
43
+ });
44
+ test("renders a single empty div to anchor itself in the page", () => {
45
+ render();
46
+ const element = mounted.container.firstElementChild;
47
+ expect(element?.tagName).toBe("DIV");
48
+ expect(element?.childNodes).toHaveLength(0);
49
+ });
50
+ });
51
+ describe("handing the events back", () => {
52
+ test("reports as soon as there is an account to report against", () => {
53
+ render();
54
+ expect(onProcessData).toHaveBeenCalledTimes(1);
55
+ });
56
+ test("reports empty event lists before the user has done anything", () => {
57
+ render();
58
+ expect(onProcessData).toHaveBeenCalledWith({
59
+ mouseEvents: [],
60
+ touchEvents: [],
61
+ keyboardEvents: [],
62
+ });
63
+ });
64
+ test("stays quiet until an account exists", () => {
65
+ render({ account: undefined });
66
+ expect(onProcessData).not.toHaveBeenCalled();
67
+ });
68
+ test("reports once the account arrives", () => {
69
+ render({ account: undefined });
70
+ render({ account: defaultAccount });
71
+ expect(onProcessData).toHaveBeenCalledTimes(1);
72
+ });
73
+ test("reports the events the collector has gathered so far", () => {
74
+ render();
75
+ const [setMouse, setTouch, setKeyboard] = started.mock.calls[0] ?? [];
76
+ if (!setMouse || !setTouch || !setKeyboard) {
77
+ throw new Error("expected the collector to be handed its setters");
78
+ }
79
+ const mouseEvents = [{ x: 1, y: 2, timestamp: 3 }];
80
+ const touchEvents = [{ x: 4, y: 5, timestamp: 6 }];
81
+ const keyboardEvents = [
82
+ { key: "a", timestamp: 7, isShiftKey: false, isCtrlKey: false },
83
+ ];
84
+ act(() => {
85
+ setMouse(mouseEvents);
86
+ setTouch(touchEvents);
87
+ setKeyboard(keyboardEvents);
88
+ });
89
+ expect(onProcessData).toHaveBeenCalledTimes(1);
90
+ render();
91
+ expect(onProcessData).toHaveBeenCalledTimes(1);
92
+ render({ account: { account: { address: "later" } } });
93
+ expect(onProcessData).toHaveBeenLastCalledWith({
94
+ mouseEvents,
95
+ touchEvents,
96
+ keyboardEvents,
97
+ });
98
+ });
99
+ test("reports again when the account changes", () => {
100
+ render();
101
+ render({ account: { account: { address: "different-address" } } });
102
+ expect(onProcessData).toHaveBeenCalledTimes(2);
103
+ });
104
+ test("reports regardless of sendData, which it does not read", () => {
105
+ render({ sendData: false });
106
+ expect(onProcessData).toHaveBeenCalledTimes(1);
107
+ });
108
+ });
109
+ //# sourceMappingURL=collector.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collector.unit.test.js","sourceRoot":"","sources":["../../src/tests/collector.unit.test.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { type Account, type Captcha, type CaptchaResponseBody, type FrictionlessState, type ProcaptchaClientConfigInput, type ProcaptchaState, type RandomProvider } from "@prosopo/types";
2
+ export declare const PROVIDER_URL = "https://provider.one";
3
+ export declare const USER_ADDRESS = "user-address";
4
+ export declare const SITE_KEY = "site-key";
5
+ export declare const config: (overrides?: Partial<ProcaptchaClientConfigInput>) => ProcaptchaClientConfigInput;
6
+ export declare const state: (overrides?: Partial<ProcaptchaState>) => ProcaptchaState;
7
+ export declare const account: () => Account;
8
+ export declare const randomProvider: (url?: string) => RandomProvider;
9
+ export declare const captcha: (overrides?: Partial<Captcha>) => Captcha;
10
+ export declare const challengeResponse: (overrides?: Partial<CaptchaResponseBody>) => CaptchaResponseBody;
11
+ export declare const frictionless: (overrides?: Partial<FrictionlessState>) => FrictionlessState;
12
+ //# sourceMappingURL=harness.d.ts.map