@prosopo/procaptcha-frictionless 2.13.7 → 2.13.9

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.
@@ -0,0 +1,104 @@
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 } from "vitest";
16
+ import {
17
+ MISSING_CAPTCHA_TYPE_MESSAGE,
18
+ evaluateFrictionlessResult,
19
+ } from "../frictionlessResultGuard.js";
20
+
21
+ // This guard exists so a `/frictionless` response with no `captchaType`
22
+ // (the shape emitted by request-time short-circuits — access-policy
23
+ // hard-block, decision-machine autoBan, domain / header middleware
24
+ // rejections — whose `{ error: "..." }` bare-string bodies slip past
25
+ // `error.message` inspection) cannot fall through into
26
+ // `renderForCaptchaType(undefined, ...)` and mount ProcaptchaPow with an
27
+ // undefined sessionId, which the provider then rejects as
28
+ // API.INCORRECT_CAPTCHA_TYPE for a frictionless sitekey.
29
+
30
+ describe("evaluateFrictionlessResult", () => {
31
+ it("passes a well-formed response with a captchaType", () => {
32
+ expect(
33
+ evaluateFrictionlessResult({
34
+ captchaType: "pow",
35
+ }),
36
+ ).toEqual({ kind: "pass" });
37
+ });
38
+
39
+ it("passes when a captchaType is present even if error is a bare undefined", () => {
40
+ expect(
41
+ evaluateFrictionlessResult({
42
+ captchaType: "image",
43
+ error: undefined,
44
+ }),
45
+ ).toEqual({ kind: "pass" });
46
+ });
47
+
48
+ it("returns a structured error when the server emits `{ error: { message } }`", () => {
49
+ expect(
50
+ evaluateFrictionlessResult({
51
+ captchaType: "pow",
52
+ error: { message: "Boom", key: "API.SOMETHING" },
53
+ }),
54
+ ).toEqual({ kind: "error", message: "Boom", key: "API.SOMETHING" });
55
+ });
56
+
57
+ it("omits key from the outcome when the server error carries no key", () => {
58
+ const outcome = evaluateFrictionlessResult({
59
+ captchaType: "pow",
60
+ error: { message: "Boom" },
61
+ });
62
+ expect(outcome.kind).toBe("error");
63
+ if (outcome.kind !== "error") throw new Error("expected error outcome");
64
+ expect(outcome.message).toBe("Boom");
65
+ expect("key" in outcome).toBe(false);
66
+ });
67
+
68
+ it("halts when captchaType is missing (bare-string 401 body shape from access-policy / autoBan / middleware)", () => {
69
+ // Access-policy hard-block / decision-machine autoBan / domain
70
+ // middleware all return `{"error":"Unauthorized"}` — a bare
71
+ // string, not `{message}`, so `error.message` is undefined and
72
+ // would slip past the earlier error check. HttpClientBase does
73
+ // not throw on 4xx JSON so the widget receives this as a
74
+ // valid-looking result.
75
+ expect(
76
+ evaluateFrictionlessResult({
77
+ // Simulating the JSON parse: `error` came in as a string on
78
+ // the wire but is not the object shape the guard reads.
79
+ error: undefined,
80
+ }),
81
+ ).toEqual({ kind: "error", message: MISSING_CAPTCHA_TYPE_MESSAGE });
82
+ });
83
+
84
+ it("halts when both captchaType and error are absent (opaque parse failure)", () => {
85
+ expect(evaluateFrictionlessResult({})).toEqual({
86
+ kind: "error",
87
+ message: MISSING_CAPTCHA_TYPE_MESSAGE,
88
+ });
89
+ });
90
+
91
+ it("prefers the structured error over the missing-captchaType message when both apply", () => {
92
+ // If the server ever emits BOTH a structured error and no captchaType,
93
+ // the structured error is the more informative signal.
94
+ expect(
95
+ evaluateFrictionlessResult({
96
+ error: { message: "Server error", key: "API.SERVER_ERROR" },
97
+ }),
98
+ ).toEqual({
99
+ kind: "error",
100
+ message: "Server error",
101
+ key: "API.SERVER_ERROR",
102
+ });
103
+ });
104
+ });