@redacto.io/consent-sdk-react 3.4.0 → 4.0.0
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.log +21 -0
- package/CHANGELOG.md +12 -0
- package/dist/index.d.mts +49 -2
- package/dist/index.d.ts +49 -2
- package/dist/index.js +1806 -345
- package/dist/index.mjs +1805 -345
- package/package.json +1 -1
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.test.tsx +201 -118
- package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +1192 -93
- package/src/RedactoNoticeConsent/api/index.ts +335 -246
- package/src/RedactoNoticeConsent/api/types.ts +138 -41
- package/src/RedactoNoticeConsent/components/DigiLockerCallback.tsx +536 -0
- package/src/RedactoNoticeConsent/components/index.ts +7 -0
- package/src/RedactoNoticeConsent/injectStyles.ts +11 -0
- package/src/RedactoNoticeConsent/services/storage.ts +122 -0
- package/src/RedactoNoticeConsent/types.ts +2 -0
- package/src/RedactoNoticeConsentInline/api/index.ts +26 -64
- package/src/RedactoNoticeConsentInline/api/types.ts +0 -25
- package/src/index.ts +10 -0
- package/src/utils/isLegacyApi.ts +0 -12
package/package.json
CHANGED
|
@@ -22,30 +22,10 @@ import type { ConsentContent } from "./api/types";
|
|
|
22
22
|
vi.mock("./api", () => ({
|
|
23
23
|
fetchConsentContent: vi.fn(),
|
|
24
24
|
submitConsentEvent: vi.fn(),
|
|
25
|
-
submitGuardianInfo: vi.fn(),
|
|
26
|
-
fetchTTSAudioUrls: vi.fn().mockResolvedValue({
|
|
27
|
-
code: 200,
|
|
28
|
-
status: "success",
|
|
29
|
-
detail: {
|
|
30
|
-
uuid: "notice-123",
|
|
31
|
-
language: "en",
|
|
32
|
-
notice_text_audio_url: "",
|
|
33
|
-
additional_text_audio_url: "",
|
|
34
|
-
notice_summary_audio_url: "",
|
|
35
|
-
confirm_button_text_audio_url: "",
|
|
36
|
-
decline_button_text_audio_url: "",
|
|
37
|
-
privacy_policy_prefix_text_audio_url: "",
|
|
38
|
-
privacy_policy_anchor_text_audio_url: "",
|
|
39
|
-
purpose_section_heading_audio_url: "",
|
|
40
|
-
notice_banner_heading_audio_url: "",
|
|
41
|
-
purposes_audio: {
|
|
42
|
-
"purpose-1": { name_audio_url: "", description_audio_url: "" },
|
|
43
|
-
"purpose-2": { name_audio_url: "", description_audio_url: "" },
|
|
44
|
-
},
|
|
45
|
-
data_elements_audio: {},
|
|
46
|
-
},
|
|
47
|
-
}),
|
|
48
25
|
clearApiCache: vi.fn(),
|
|
26
|
+
fetchTTSAudioUrls: vi.fn(),
|
|
27
|
+
initiateGuardianVerification: vi.fn(),
|
|
28
|
+
verifyGuardianStatus: vi.fn(),
|
|
49
29
|
}));
|
|
50
30
|
|
|
51
31
|
// Mock jwt-decode
|
|
@@ -67,16 +47,36 @@ vi.mock("./injectStyles", () => ({
|
|
|
67
47
|
injectCheckboxStyles: vi.fn(),
|
|
68
48
|
}));
|
|
69
49
|
|
|
50
|
+
// Mock storage service
|
|
51
|
+
vi.mock("./services/storage", () => ({
|
|
52
|
+
saveConsentState: vi.fn(),
|
|
53
|
+
getConsentState: vi.fn(() => null),
|
|
54
|
+
clearConsentState: vi.fn(),
|
|
55
|
+
updateConsentState: vi.fn(),
|
|
56
|
+
hasPendingVerification: vi.fn(() => false),
|
|
57
|
+
getVerificationResult: vi.fn(() => null),
|
|
58
|
+
getVerificationReference: vi.fn(() => null),
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
// Mock window.open for popup tests
|
|
62
|
+
const mockWindowOpen = vi.fn(() => ({
|
|
63
|
+
closed: false,
|
|
64
|
+
focus: vi.fn(),
|
|
65
|
+
close: vi.fn(),
|
|
66
|
+
}));
|
|
67
|
+
Object.defineProperty(window, 'open', {
|
|
68
|
+
writable: true,
|
|
69
|
+
value: mockWindowOpen,
|
|
70
|
+
});
|
|
71
|
+
|
|
70
72
|
describe("RedactoNoticeConsent", () => {
|
|
71
|
-
const mockedApi = vi.mocked(api
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
const mockedApi = vi.mocked(api);
|
|
74
|
+
|
|
75
|
+
const setupCommonMocks = () => {
|
|
74
76
|
// Mock API calls synchronously to avoid act() warnings
|
|
75
|
-
mockedApi.fetchConsentContent.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
mockedApi.submitConsentEvent.mockReturnValue(Promise.resolve(undefined));
|
|
79
|
-
mockedApi.submitGuardianInfo.mockReturnValue(Promise.resolve(undefined));
|
|
77
|
+
mockedApi.fetchConsentContent.mockResolvedValue(mockConsentContent);
|
|
78
|
+
mockedApi.submitConsentEvent.mockResolvedValue(undefined);
|
|
79
|
+
// Mock TTS audio URLs API
|
|
80
80
|
mockedApi.fetchTTSAudioUrls.mockResolvedValue({
|
|
81
81
|
code: 200,
|
|
82
82
|
status: "success",
|
|
@@ -99,6 +99,35 @@ describe("RedactoNoticeConsent", () => {
|
|
|
99
99
|
data_elements_audio: {},
|
|
100
100
|
},
|
|
101
101
|
});
|
|
102
|
+
// Mock guardian verification APIs (DigiLocker flow)
|
|
103
|
+
mockedApi.initiateGuardianVerification.mockResolvedValue({
|
|
104
|
+
success: true,
|
|
105
|
+
already_verified: false,
|
|
106
|
+
session_token: "gvs_test-session-token",
|
|
107
|
+
digilocker_redirect_url: "https://digilocker.example.com/authorize?session=xyz",
|
|
108
|
+
expires_at: new Date(Date.now() + 600000).toISOString(),
|
|
109
|
+
guardian_info_uuid: "test-guardian-uuid",
|
|
110
|
+
});
|
|
111
|
+
mockedApi.verifyGuardianStatus.mockResolvedValue({
|
|
112
|
+
status: "verified" as const,
|
|
113
|
+
guardian_info_uuid: "test-guardian-uuid",
|
|
114
|
+
verification_reference: "VER_ABC123",
|
|
115
|
+
guardian_details: {
|
|
116
|
+
verified_name: "John Doe",
|
|
117
|
+
is_guardian_adult: true,
|
|
118
|
+
verified_at: new Date().toISOString(),
|
|
119
|
+
verification_reference: "VER_ABC123",
|
|
120
|
+
},
|
|
121
|
+
error: null,
|
|
122
|
+
error_code: null,
|
|
123
|
+
can_retry: false,
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
beforeEach(() => {
|
|
128
|
+
vi.resetAllMocks();
|
|
129
|
+
mockWindowOpen.mockClear();
|
|
130
|
+
setupCommonMocks();
|
|
102
131
|
});
|
|
103
132
|
|
|
104
133
|
afterEach(() => {
|
|
@@ -113,7 +142,7 @@ describe("RedactoNoticeConsent", () => {
|
|
|
113
142
|
});
|
|
114
143
|
|
|
115
144
|
it("renders consent content after loading", async () => {
|
|
116
|
-
act(() => {
|
|
145
|
+
await act(async () => {
|
|
117
146
|
render(<RedactoNoticeConsent {...defaultProps} />);
|
|
118
147
|
});
|
|
119
148
|
await waitFor(
|
|
@@ -125,8 +154,6 @@ describe("RedactoNoticeConsent", () => {
|
|
|
125
154
|
expect(
|
|
126
155
|
screen.getByRole("link", { name: /Privacy Policy/i })
|
|
127
156
|
).toBeInTheDocument();
|
|
128
|
-
expect(screen.getByText("[Vendors List]")).toBeInTheDocument();
|
|
129
|
-
expect(screen.getByText("Manage What You Share")).toBeInTheDocument();
|
|
130
157
|
expect(screen.getByText("Marketing")).toBeInTheDocument();
|
|
131
158
|
},
|
|
132
159
|
{ timeout: 3000 }
|
|
@@ -169,7 +196,9 @@ describe("RedactoNoticeConsent", () => {
|
|
|
169
196
|
});
|
|
170
197
|
|
|
171
198
|
it("handles language selection", async () => {
|
|
172
|
-
|
|
199
|
+
await act(async () => {
|
|
200
|
+
render(<RedactoNoticeConsent {...defaultProps} />);
|
|
201
|
+
});
|
|
173
202
|
await waitFor(
|
|
174
203
|
() => {
|
|
175
204
|
expect(screen.getByRole("button", { name: "en" })).toBeInTheDocument();
|
|
@@ -271,7 +300,9 @@ describe("RedactoNoticeConsent", () => {
|
|
|
271
300
|
});
|
|
272
301
|
|
|
273
302
|
it("disables required data element checkboxes", async () => {
|
|
274
|
-
|
|
303
|
+
await act(async () => {
|
|
304
|
+
render(<RedactoNoticeConsent {...defaultProps} />);
|
|
305
|
+
});
|
|
275
306
|
await waitFor(
|
|
276
307
|
() => {
|
|
277
308
|
expect(screen.getByText("Marketing")).toBeInTheDocument();
|
|
@@ -314,7 +345,9 @@ describe("RedactoNoticeConsent", () => {
|
|
|
314
345
|
});
|
|
315
346
|
|
|
316
347
|
it("calls submitConsentEvent on accept", async () => {
|
|
317
|
-
|
|
348
|
+
await act(async () => {
|
|
349
|
+
render(<RedactoNoticeConsent {...defaultProps} />);
|
|
350
|
+
});
|
|
318
351
|
await waitFor(
|
|
319
352
|
() => {
|
|
320
353
|
expect(screen.getByText("Accept")).toBeInTheDocument();
|
|
@@ -362,6 +395,7 @@ describe("RedactoNoticeConsent", () => {
|
|
|
362
395
|
],
|
|
363
396
|
declined: false,
|
|
364
397
|
meta_data: undefined,
|
|
398
|
+
guardianVerificationReference: undefined,
|
|
365
399
|
signal: expect.any(AbortSignal),
|
|
366
400
|
});
|
|
367
401
|
expect(defaultProps.onAccept).toHaveBeenCalled();
|
|
@@ -441,30 +475,10 @@ describe("RedactoNoticeConsent", () => {
|
|
|
441
475
|
|
|
442
476
|
describe("reconsent functionality", () => {
|
|
443
477
|
beforeEach(() => {
|
|
478
|
+
vi.resetAllMocks();
|
|
479
|
+
mockWindowOpen.mockClear();
|
|
480
|
+
setupCommonMocks();
|
|
444
481
|
mockedApi.fetchConsentContent.mockResolvedValue(mockReconsentContent);
|
|
445
|
-
mockedApi.submitConsentEvent.mockResolvedValue(undefined);
|
|
446
|
-
mockedApi.fetchTTSAudioUrls.mockResolvedValue({
|
|
447
|
-
code: 200,
|
|
448
|
-
status: "success",
|
|
449
|
-
detail: {
|
|
450
|
-
uuid: "notice-123",
|
|
451
|
-
language: "en",
|
|
452
|
-
notice_text_audio_url: "",
|
|
453
|
-
additional_text_audio_url: "",
|
|
454
|
-
notice_summary_audio_url: "",
|
|
455
|
-
confirm_button_text_audio_url: "",
|
|
456
|
-
decline_button_text_audio_url: "",
|
|
457
|
-
privacy_policy_prefix_text_audio_url: "",
|
|
458
|
-
privacy_policy_anchor_text_audio_url: "",
|
|
459
|
-
purpose_section_heading_audio_url: "",
|
|
460
|
-
notice_banner_heading_audio_url: "",
|
|
461
|
-
purposes_audio: {
|
|
462
|
-
"purpose-1": { name_audio_url: "", description_audio_url: "" },
|
|
463
|
-
"purpose-2": { name_audio_url: "", description_audio_url: "" },
|
|
464
|
-
},
|
|
465
|
-
data_elements_audio: {},
|
|
466
|
-
},
|
|
467
|
-
});
|
|
468
482
|
});
|
|
469
483
|
|
|
470
484
|
it("shows reconsent UI with visual indicators for different consent states", async () => {
|
|
@@ -499,9 +513,11 @@ describe("RedactoNoticeConsent", () => {
|
|
|
499
513
|
});
|
|
500
514
|
|
|
501
515
|
it("passes validate_against parameter to API", async () => {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
516
|
+
await act(async () => {
|
|
517
|
+
render(
|
|
518
|
+
<RedactoNoticeConsent {...defaultProps} validateAgainst="required" />
|
|
519
|
+
);
|
|
520
|
+
});
|
|
505
521
|
await waitFor(
|
|
506
522
|
() => {
|
|
507
523
|
expect(api.fetchConsentContent).toHaveBeenCalledWith({
|
|
@@ -521,7 +537,9 @@ describe("RedactoNoticeConsent", () => {
|
|
|
521
537
|
});
|
|
522
538
|
|
|
523
539
|
it("defaults to validate_against 'all' when not specified", async () => {
|
|
524
|
-
|
|
540
|
+
await act(async () => {
|
|
541
|
+
render(<RedactoNoticeConsent {...defaultProps} />);
|
|
542
|
+
});
|
|
525
543
|
await waitFor(
|
|
526
544
|
() => {
|
|
527
545
|
expect(api.fetchConsentContent).toHaveBeenCalledWith({
|
|
@@ -596,9 +614,10 @@ describe("RedactoNoticeConsent", () => {
|
|
|
596
614
|
|
|
597
615
|
describe("age verification and minor consent functionality", () => {
|
|
598
616
|
beforeEach(() => {
|
|
617
|
+
vi.resetAllMocks();
|
|
618
|
+
mockWindowOpen.mockClear();
|
|
619
|
+
setupCommonMocks();
|
|
599
620
|
mockedApi.fetchConsentContent.mockResolvedValue(mockMinorConsentContent);
|
|
600
|
-
mockedApi.submitConsentEvent.mockResolvedValue(undefined);
|
|
601
|
-
mockedApi.submitGuardianInfo.mockResolvedValue(undefined);
|
|
602
621
|
});
|
|
603
622
|
|
|
604
623
|
it("shows age verification when is_minor is true", async () => {
|
|
@@ -653,11 +672,30 @@ describe("RedactoNoticeConsent", () => {
|
|
|
653
672
|
screen.queryByText("Age Verification Required")
|
|
654
673
|
).not.toBeInTheDocument();
|
|
655
674
|
expect(
|
|
656
|
-
screen.queryByText("Guardian
|
|
675
|
+
screen.queryByText("Guardian Verification")
|
|
657
676
|
).not.toBeInTheDocument();
|
|
658
677
|
},
|
|
659
678
|
{ timeout: 3000 }
|
|
660
679
|
);
|
|
680
|
+
|
|
681
|
+
// Verify the adult can submit consent without guardian verification
|
|
682
|
+
const acceptButton = screen.getByText("Accept");
|
|
683
|
+
fireEvent.click(acceptButton);
|
|
684
|
+
|
|
685
|
+
await waitFor(
|
|
686
|
+
() => {
|
|
687
|
+
expect(mockedApi.submitConsentEvent).toHaveBeenCalledWith(
|
|
688
|
+
expect.objectContaining({
|
|
689
|
+
accessToken: "mock-token",
|
|
690
|
+
noticeUuid: "notice-123",
|
|
691
|
+
declined: false,
|
|
692
|
+
guardianVerificationReference: undefined,
|
|
693
|
+
})
|
|
694
|
+
);
|
|
695
|
+
expect(defaultProps.onAccept).toHaveBeenCalled();
|
|
696
|
+
},
|
|
697
|
+
{ timeout: 3000 }
|
|
698
|
+
);
|
|
661
699
|
});
|
|
662
700
|
|
|
663
701
|
it("shows guardian form when user clicks No (under 18)", async () => {
|
|
@@ -678,7 +716,7 @@ describe("RedactoNoticeConsent", () => {
|
|
|
678
716
|
await waitFor(
|
|
679
717
|
() => {
|
|
680
718
|
expect(
|
|
681
|
-
screen.getByText("Guardian
|
|
719
|
+
screen.getByText("Guardian Verification")
|
|
682
720
|
).toBeInTheDocument();
|
|
683
721
|
expect(
|
|
684
722
|
screen.queryByText("Age Verification Required")
|
|
@@ -688,7 +726,7 @@ describe("RedactoNoticeConsent", () => {
|
|
|
688
726
|
);
|
|
689
727
|
});
|
|
690
728
|
|
|
691
|
-
it("completes full minor flow: age verification -> guardian form ->
|
|
729
|
+
it("completes full minor flow: age verification -> guardian form -> verification screen", async () => {
|
|
692
730
|
render(<RedactoNoticeConsent {...defaultProps} />);
|
|
693
731
|
await waitFor(
|
|
694
732
|
() => {
|
|
@@ -707,35 +745,33 @@ describe("RedactoNoticeConsent", () => {
|
|
|
707
745
|
await waitFor(
|
|
708
746
|
() => {
|
|
709
747
|
expect(
|
|
710
|
-
screen.getByText("Guardian
|
|
748
|
+
screen.getByText("Guardian Verification")
|
|
711
749
|
).toBeInTheDocument();
|
|
712
750
|
expect(
|
|
713
|
-
screen.getByText(/
|
|
751
|
+
screen.getByText(/a legal guardian must verify/i)
|
|
714
752
|
).toBeInTheDocument();
|
|
715
753
|
},
|
|
716
754
|
{ timeout: 3000 }
|
|
717
755
|
);
|
|
718
756
|
|
|
719
757
|
// Fill out guardian form
|
|
720
|
-
const nameInput = screen.getByLabelText(/
|
|
721
|
-
const contactInput = screen.getByLabelText(/
|
|
722
|
-
const relationshipInput = screen.getByLabelText(
|
|
723
|
-
/Relationship to Guardian/i
|
|
724
|
-
);
|
|
725
|
-
|
|
758
|
+
const nameInput = screen.getByLabelText(/Guardian's Full Name/i);
|
|
759
|
+
const contactInput = screen.getByLabelText(/Guardian's Phone Number/i);
|
|
726
760
|
fireEvent.change(nameInput, { target: { value: "John Doe" } });
|
|
727
761
|
fireEvent.change(contactInput, { target: { value: "john@example.com" } });
|
|
728
|
-
|
|
762
|
+
// Select "Father" from the custom dropdown
|
|
763
|
+
const relationshipDropdown = screen.getByRole("combobox", { name: /Relationship/i });
|
|
764
|
+
fireEvent.click(relationshipDropdown);
|
|
765
|
+
fireEvent.click(screen.getByRole("option", { name: "Father" }));
|
|
729
766
|
|
|
730
|
-
const nextButton = screen.getByText("
|
|
767
|
+
const nextButton = screen.getByText("Verify via DigiLocker");
|
|
731
768
|
fireEvent.click(nextButton);
|
|
732
769
|
|
|
733
|
-
// Should transition to
|
|
770
|
+
// Should transition to verification screen (guardian form text should be gone)
|
|
734
771
|
await waitFor(
|
|
735
772
|
() => {
|
|
736
|
-
expect(screen.getByText("Privacy Notice")).toBeInTheDocument();
|
|
737
773
|
expect(
|
|
738
|
-
screen.queryByText(
|
|
774
|
+
screen.queryByText(/a legal guardian must verify/i)
|
|
739
775
|
).not.toBeInTheDocument();
|
|
740
776
|
},
|
|
741
777
|
{ timeout: 3000 }
|
|
@@ -759,17 +795,17 @@ describe("RedactoNoticeConsent", () => {
|
|
|
759
795
|
|
|
760
796
|
await waitFor(
|
|
761
797
|
() => {
|
|
762
|
-
expect(screen.getByAltText("
|
|
798
|
+
expect(screen.getByAltText("Logo")).toBeInTheDocument();
|
|
763
799
|
expect(
|
|
764
|
-
screen.getByLabelText(/
|
|
800
|
+
screen.getByLabelText(/Guardian's Full Name/i)
|
|
765
801
|
).toBeInTheDocument();
|
|
766
802
|
expect(
|
|
767
|
-
screen.getByLabelText(/
|
|
803
|
+
screen.getByLabelText(/Guardian's Phone Number/i)
|
|
768
804
|
).toBeInTheDocument();
|
|
769
805
|
expect(
|
|
770
|
-
screen.
|
|
806
|
+
screen.getByRole("combobox", { name: /Relationship/i })
|
|
771
807
|
).toBeInTheDocument();
|
|
772
|
-
expect(screen.getByText("
|
|
808
|
+
expect(screen.getByText("Verify via DigiLocker")).toBeInTheDocument();
|
|
773
809
|
expect(screen.getByText("Cancel")).toBeInTheDocument();
|
|
774
810
|
},
|
|
775
811
|
{ timeout: 3000 }
|
|
@@ -794,13 +830,13 @@ describe("RedactoNoticeConsent", () => {
|
|
|
794
830
|
await waitFor(
|
|
795
831
|
() => {
|
|
796
832
|
expect(
|
|
797
|
-
screen.getByText("Guardian
|
|
833
|
+
screen.getByText("Guardian Verification")
|
|
798
834
|
).toBeInTheDocument();
|
|
799
835
|
},
|
|
800
836
|
{ timeout: 3000 }
|
|
801
837
|
);
|
|
802
838
|
|
|
803
|
-
const nextButton = screen.getByText("
|
|
839
|
+
const nextButton = screen.getByText("Verify via DigiLocker");
|
|
804
840
|
fireEvent.click(nextButton);
|
|
805
841
|
|
|
806
842
|
await waitFor(
|
|
@@ -819,7 +855,7 @@ describe("RedactoNoticeConsent", () => {
|
|
|
819
855
|
);
|
|
820
856
|
});
|
|
821
857
|
|
|
822
|
-
it("transitions to
|
|
858
|
+
it("transitions to verification screen after filling guardian form", async () => {
|
|
823
859
|
render(<RedactoNoticeConsent {...defaultProps} />);
|
|
824
860
|
await waitFor(
|
|
825
861
|
() => {
|
|
@@ -837,32 +873,30 @@ describe("RedactoNoticeConsent", () => {
|
|
|
837
873
|
await waitFor(
|
|
838
874
|
() => {
|
|
839
875
|
expect(
|
|
840
|
-
screen.getByText("Guardian
|
|
876
|
+
screen.getByText("Guardian Verification")
|
|
841
877
|
).toBeInTheDocument();
|
|
842
878
|
},
|
|
843
879
|
{ timeout: 3000 }
|
|
844
880
|
);
|
|
845
881
|
|
|
846
882
|
// Fill out guardian form
|
|
847
|
-
const nameInput = screen.getByLabelText(/
|
|
848
|
-
const contactInput = screen.getByLabelText(/
|
|
849
|
-
const relationshipInput = screen.getByLabelText(
|
|
850
|
-
/Relationship to Guardian/i
|
|
851
|
-
);
|
|
852
|
-
|
|
883
|
+
const nameInput = screen.getByLabelText(/Guardian's Full Name/i);
|
|
884
|
+
const contactInput = screen.getByLabelText(/Guardian's Phone Number/i);
|
|
853
885
|
fireEvent.change(nameInput, { target: { value: "John Doe" } });
|
|
854
886
|
fireEvent.change(contactInput, { target: { value: "john@example.com" } });
|
|
855
|
-
|
|
887
|
+
// Select "Father" from the custom dropdown
|
|
888
|
+
const relationshipDropdown = screen.getByRole("combobox", { name: /Relationship/i });
|
|
889
|
+
fireEvent.click(relationshipDropdown);
|
|
890
|
+
fireEvent.click(screen.getByRole("option", { name: "Father" }));
|
|
856
891
|
|
|
857
|
-
const nextButton = screen.getByText("
|
|
892
|
+
const nextButton = screen.getByText("Verify via DigiLocker");
|
|
858
893
|
fireEvent.click(nextButton);
|
|
859
894
|
|
|
860
|
-
// Should transition to
|
|
895
|
+
// Should transition to verification screen (guardian form fields should be gone)
|
|
861
896
|
await waitFor(
|
|
862
897
|
() => {
|
|
863
|
-
expect(screen.getByText("Privacy Notice")).toBeInTheDocument();
|
|
864
898
|
expect(
|
|
865
|
-
screen.
|
|
899
|
+
screen.queryByLabelText(/Guardian's Full Name/i)
|
|
866
900
|
).not.toBeInTheDocument();
|
|
867
901
|
},
|
|
868
902
|
{ timeout: 3000 }
|
|
@@ -887,24 +921,20 @@ describe("RedactoNoticeConsent", () => {
|
|
|
887
921
|
await waitFor(
|
|
888
922
|
() => {
|
|
889
923
|
expect(
|
|
890
|
-
screen.getByText("Guardian
|
|
924
|
+
screen.getByText("Guardian Verification")
|
|
891
925
|
).toBeInTheDocument();
|
|
892
926
|
},
|
|
893
927
|
{ timeout: 3000 }
|
|
894
928
|
);
|
|
895
929
|
|
|
896
|
-
// Fill fields with only whitespace
|
|
897
|
-
const nameInput = screen.getByLabelText(/
|
|
898
|
-
const contactInput = screen.getByLabelText(/
|
|
899
|
-
const relationshipInput = screen.getByLabelText(
|
|
900
|
-
/Relationship to Guardian/i
|
|
901
|
-
);
|
|
930
|
+
// Fill fields with only whitespace (leave relationship empty — don't select anything)
|
|
931
|
+
const nameInput = screen.getByLabelText(/Guardian's Full Name/i);
|
|
932
|
+
const contactInput = screen.getByLabelText(/Guardian's Phone Number/i);
|
|
902
933
|
|
|
903
934
|
fireEvent.change(nameInput, { target: { value: " " } });
|
|
904
935
|
fireEvent.change(contactInput, { target: { value: "\t\t" } });
|
|
905
|
-
fireEvent.change(relationshipInput, { target: { value: "\n\n" } });
|
|
906
936
|
|
|
907
|
-
const nextButton = screen.getByText("
|
|
937
|
+
const nextButton = screen.getByText("Verify via DigiLocker");
|
|
908
938
|
fireEvent.click(nextButton);
|
|
909
939
|
|
|
910
940
|
// Should still show validation errors for whitespace-only input
|
|
@@ -942,14 +972,14 @@ describe("RedactoNoticeConsent", () => {
|
|
|
942
972
|
await waitFor(
|
|
943
973
|
() => {
|
|
944
974
|
expect(
|
|
945
|
-
screen.getByText("Guardian
|
|
975
|
+
screen.getByText("Guardian Verification")
|
|
946
976
|
).toBeInTheDocument();
|
|
947
977
|
},
|
|
948
978
|
{ timeout: 3000 }
|
|
949
979
|
);
|
|
950
980
|
|
|
951
981
|
// Click next with empty fields to trigger errors
|
|
952
|
-
const nextButton = screen.getByText("
|
|
982
|
+
const nextButton = screen.getByText("Verify via DigiLocker");
|
|
953
983
|
fireEvent.click(nextButton);
|
|
954
984
|
|
|
955
985
|
await waitFor(
|
|
@@ -962,7 +992,7 @@ describe("RedactoNoticeConsent", () => {
|
|
|
962
992
|
);
|
|
963
993
|
|
|
964
994
|
// Start typing in name field
|
|
965
|
-
const nameInput = screen.getByLabelText(/
|
|
995
|
+
const nameInput = screen.getByLabelText(/Guardian's Full Name/i);
|
|
966
996
|
fireEvent.change(nameInput, { target: { value: "J" } });
|
|
967
997
|
|
|
968
998
|
// Error should be cleared
|
|
@@ -994,7 +1024,7 @@ describe("RedactoNoticeConsent", () => {
|
|
|
994
1024
|
await waitFor(
|
|
995
1025
|
() => {
|
|
996
1026
|
expect(
|
|
997
|
-
screen.getByText("Guardian
|
|
1027
|
+
screen.getByText("Guardian Verification")
|
|
998
1028
|
).toBeInTheDocument();
|
|
999
1029
|
},
|
|
1000
1030
|
{ timeout: 3000 }
|
|
@@ -1029,13 +1059,13 @@ describe("RedactoNoticeConsent", () => {
|
|
|
1029
1059
|
await waitFor(
|
|
1030
1060
|
() => {
|
|
1031
1061
|
expect(
|
|
1032
|
-
screen.getByText("Guardian
|
|
1062
|
+
screen.getByText("Guardian Verification")
|
|
1033
1063
|
).toBeInTheDocument();
|
|
1034
1064
|
},
|
|
1035
1065
|
{ timeout: 3000 }
|
|
1036
1066
|
);
|
|
1037
1067
|
|
|
1038
|
-
const contactInput = screen.getByLabelText(/
|
|
1068
|
+
const contactInput = screen.getByLabelText(/Guardian's Phone Number/i);
|
|
1039
1069
|
|
|
1040
1070
|
// Focus on contact field and type
|
|
1041
1071
|
contactInput.focus();
|
|
@@ -1053,12 +1083,65 @@ describe("RedactoNoticeConsent", () => {
|
|
|
1053
1083
|
expect(contactInput).toHaveValue("john@example.com");
|
|
1054
1084
|
|
|
1055
1085
|
// Test other fields maintain focus too
|
|
1056
|
-
const nameInput = screen.getByLabelText(/
|
|
1086
|
+
const nameInput = screen.getByLabelText(/Guardian's Full Name/i);
|
|
1057
1087
|
nameInput.focus();
|
|
1058
1088
|
fireEvent.change(nameInput, { target: { value: "J" } });
|
|
1059
1089
|
|
|
1060
1090
|
expect(nameInput).toHaveFocus();
|
|
1061
1091
|
expect(nameInput).toHaveValue("J");
|
|
1062
1092
|
});
|
|
1093
|
+
|
|
1094
|
+
it("calls initiateGuardianVerification with correct params", async () => {
|
|
1095
|
+
render(<RedactoNoticeConsent {...defaultProps} />);
|
|
1096
|
+
await waitFor(
|
|
1097
|
+
() => {
|
|
1098
|
+
expect(
|
|
1099
|
+
screen.getByText("Age Verification Required")
|
|
1100
|
+
).toBeInTheDocument();
|
|
1101
|
+
},
|
|
1102
|
+
{ timeout: 3000 }
|
|
1103
|
+
);
|
|
1104
|
+
|
|
1105
|
+
// Navigate to guardian form
|
|
1106
|
+
const noButton = screen.getByText("No, I am under 18");
|
|
1107
|
+
fireEvent.click(noButton);
|
|
1108
|
+
|
|
1109
|
+
await waitFor(
|
|
1110
|
+
() => {
|
|
1111
|
+
expect(
|
|
1112
|
+
screen.getByText("Guardian Verification")
|
|
1113
|
+
).toBeInTheDocument();
|
|
1114
|
+
},
|
|
1115
|
+
{ timeout: 3000 }
|
|
1116
|
+
);
|
|
1117
|
+
|
|
1118
|
+
// Fill out guardian form
|
|
1119
|
+
const nameInput = screen.getByLabelText(/Guardian's Full Name/i);
|
|
1120
|
+
const contactInput = screen.getByLabelText(/Guardian's Phone Number/i);
|
|
1121
|
+
fireEvent.change(nameInput, { target: { value: "John Doe" } });
|
|
1122
|
+
fireEvent.change(contactInput, { target: { value: "john@example.com" } });
|
|
1123
|
+
// Select "Father" from the custom dropdown
|
|
1124
|
+
const relationshipDropdown = screen.getByRole("combobox", { name: /Relationship/i });
|
|
1125
|
+
fireEvent.click(relationshipDropdown);
|
|
1126
|
+
fireEvent.click(screen.getByRole("option", { name: "Father" }));
|
|
1127
|
+
|
|
1128
|
+
const nextButton = screen.getByText("Verify via DigiLocker");
|
|
1129
|
+
fireEvent.click(nextButton);
|
|
1130
|
+
|
|
1131
|
+
// Verify initiateGuardianVerification was called with correct params
|
|
1132
|
+
await waitFor(
|
|
1133
|
+
() => {
|
|
1134
|
+
expect(api.initiateGuardianVerification).toHaveBeenCalledWith(
|
|
1135
|
+
expect.objectContaining({
|
|
1136
|
+
accessToken: "mock-token",
|
|
1137
|
+
guardianName: "John Doe",
|
|
1138
|
+
guardianContact: "john@example.com",
|
|
1139
|
+
guardianRelationship: "Father",
|
|
1140
|
+
})
|
|
1141
|
+
);
|
|
1142
|
+
},
|
|
1143
|
+
{ timeout: 3000 }
|
|
1144
|
+
);
|
|
1145
|
+
});
|
|
1063
1146
|
});
|
|
1064
1147
|
});
|