Skip to content

Commit 059e40a

Browse files
committed
feat(iam, app): adds Bright Id Stamp
- adds BrightId verification steps on iam server failure - adds toast messages - adds procedures to verify and sponsor a user with BrightId Closes #1
1 parent 32ce8f4 commit 059e40a

34 files changed

+940
-16
lines changed

app/__test-fixtures__/databaseStorageFixtures.ts

+5
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ export const facebookStampFixture: Stamp = {
5151
credential,
5252
};
5353

54+
export const brightidStampFixture: Stamp = {
55+
provider: "Brightid",
56+
credential,
57+
};
58+
5459
export const passportFixture: Passport = {
5560
issuanceDate: new Date("2022-01-01"),
5661
expiryDate: new Date("2022-01-02"),

app/__test-fixtures__/verifiableCredentialResults.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ export const SUCCESFUL_ENS_RESULT: VerifiableCredentialRecord = {
3333
version: "0.0.0",
3434
ens: "test.eth",
3535
},
36-
signature:
37-
"0xbdbac10fdb0921e73df7575e47cbda484be550c36570bc146bed90c5dcb7435e64178cb263864f48af1ad6eeee1ee94c9a0794a3812ae861f8898a973233abea1c",
36+
signature: "0xbdbac10fdb0921e73df7575e47cbda484be550c......8af1ad6eeee1ee94c9a0794a3812ae861f8898a973233abea1c",
3837
challenge: credential,
3938
credential: credential,
4039
};
@@ -58,8 +57,20 @@ export const SUCCESFUL_POH_RESULT: VerifiableCredentialRecord = {
5857
version: "0.0.0",
5958
poh: "Is registered",
6059
},
61-
signature:
62-
"0xbdbac10fdb0921e73df7575e47cbda484be550c36570bc146bed90c5dcb7435e64178cb263864f48af1ad6eeee1ee94c9a0794a3812ae861f8898a973233abea1c",
60+
signature: "0xbdbac10fdb0921e73df7575e47cbda484be550c......8af1ad6eeee1ee94c9a0794a3812ae861f8898a973233abea1c",
61+
challenge: credential,
62+
credential: credential,
63+
};
64+
65+
export const SUCCESFUL_BRIGHTID_RESULT: VerifiableCredentialRecord = {
66+
record: {
67+
type: "Brightid",
68+
address: "0xcF323CE817E25b4F784bC1e14c9A99A525fDC50f",
69+
version: "0.0.0",
70+
contextId: "somedata",
71+
meets: "true",
72+
},
73+
signature: "0xbdbac10fdb0921e73df7575e47cbda484be550c......8af1ad6eeee1ee94c9a0794a3812ae861f8898a973233abea1c",
6374
challenge: credential,
6475
credential: credential,
6576
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import React from "react";
2+
import { render, screen, fireEvent, waitFor, waitForElementToBeRemoved } from "@testing-library/react";
3+
import { BrightidCard } from "../../../components/ProviderCards";
4+
5+
import { UserContext, UserContextState } from "../../../context/userContext";
6+
import { mockAddress, mockWallet } from "../../../__test-fixtures__/onboardHookValues";
7+
import { STAMP_PROVIDERS } from "../../../config/providers";
8+
import { brightidStampFixture } from "../../../__test-fixtures__/databaseStorageFixtures";
9+
import { SUCCESFUL_BRIGHTID_RESULT } from "../../../__test-fixtures__/verifiableCredentialResults";
10+
import { fetchVerifiableCredential } from "@dpopp/identity";
11+
12+
jest.mock("@dpopp/identity", () => ({
13+
fetchVerifiableCredential: jest.fn(),
14+
}));
15+
jest.mock("../../../utils/onboard.ts");
16+
17+
const mockHandleConnection = jest.fn();
18+
const mockCreatePassport = jest.fn();
19+
const handleAddStamp = jest.fn();
20+
const mockUserContext: UserContextState = {
21+
userDid: "mockUserDid",
22+
loggedIn: true,
23+
passport: undefined,
24+
isLoadingPassport: false,
25+
allProvidersState: {
26+
Brightid: {
27+
providerSpec: STAMP_PROVIDERS.Brightid,
28+
stamp: undefined,
29+
},
30+
},
31+
handleAddStamp: handleAddStamp,
32+
handleCreatePassport: mockCreatePassport,
33+
handleConnection: mockHandleConnection,
34+
address: mockAddress,
35+
wallet: mockWallet,
36+
signer: undefined,
37+
walletLabel: mockWallet.label,
38+
};
39+
40+
function setupFetchStub(valid: any) {
41+
return function fetchStub(_url: any) {
42+
return new Promise((resolve) => {
43+
resolve({
44+
json: () =>
45+
Promise.resolve({
46+
valid,
47+
}),
48+
});
49+
});
50+
};
51+
}
52+
53+
describe("when user has not verfied with BrightId Provider", () => {
54+
it("should display a verify button", () => {
55+
render(
56+
<UserContext.Provider value={mockUserContext}>
57+
<BrightidCard />
58+
</UserContext.Provider>
59+
);
60+
61+
const initialVerifyButton = screen.queryByTestId("button-verify-brightid");
62+
63+
expect(initialVerifyButton).toBeInTheDocument();
64+
});
65+
});
66+
67+
describe("when user has verified with BrightId Provider", () => {
68+
it("should display that a verified credential was returned", () => {
69+
render(
70+
<UserContext.Provider
71+
value={{
72+
...mockUserContext,
73+
allProvidersState: {
74+
Brightid: {
75+
providerSpec: STAMP_PROVIDERS.Brightid,
76+
stamp: brightidStampFixture,
77+
},
78+
},
79+
}}
80+
>
81+
<BrightidCard />
82+
</UserContext.Provider>
83+
);
84+
85+
const brightidVerified = screen.queryByText(/Verified/);
86+
87+
expect(brightidVerified).toBeInTheDocument();
88+
});
89+
});
90+
91+
describe("when the verify button is clicked", () => {
92+
let originalFetch: any;
93+
beforeEach(() => {
94+
originalFetch = global.fetch;
95+
global.fetch = jest.fn().mockImplementation(setupFetchStub(true));
96+
});
97+
98+
afterEach(() => {
99+
global.fetch = originalFetch;
100+
jest.clearAllMocks();
101+
});
102+
103+
describe("and when a successful BrightId result is returned", () => {
104+
beforeEach(() => {
105+
(fetchVerifiableCredential as jest.Mock).mockResolvedValue(SUCCESFUL_BRIGHTID_RESULT);
106+
});
107+
108+
it("the modal displays the verify button", async () => {
109+
render(
110+
<UserContext.Provider value={mockUserContext}>
111+
<BrightidCard />
112+
</UserContext.Provider>
113+
);
114+
115+
const initialVerifyButton = screen.queryByTestId("button-verify-brightid");
116+
117+
fireEvent.click(initialVerifyButton!);
118+
119+
const verifyModal = await screen.findByRole("dialog");
120+
const verifyModalButton = screen.getByTestId("modal-verify");
121+
122+
expect(verifyModal).toBeInTheDocument();
123+
124+
await waitFor(() => {
125+
expect(verifyModalButton).toBeInTheDocument();
126+
});
127+
});
128+
129+
it("clicking verify adds the stamp", async () => {
130+
render(
131+
<UserContext.Provider value={mockUserContext}>
132+
<BrightidCard />
133+
</UserContext.Provider>
134+
);
135+
136+
const initialVerifyButton = screen.queryByTestId("button-verify-brightid");
137+
138+
// Click verify button on brightid card
139+
fireEvent.click(initialVerifyButton!);
140+
141+
// Wait to see the verify button on the modal
142+
await waitFor(() => {
143+
const verifyModalButton = screen.getByTestId("modal-verify");
144+
expect(verifyModalButton).toBeInTheDocument();
145+
});
146+
147+
const finalVerifyButton = screen.queryByRole("button", {
148+
name: /Verify/,
149+
});
150+
151+
// Click the verify button on modal
152+
fireEvent.click(finalVerifyButton!);
153+
154+
expect(handleAddStamp).toBeCalled();
155+
});
156+
157+
it("clicking cancel closes the modal and a stamp should not be added", async () => {
158+
(fetchVerifiableCredential as jest.Mock).mockResolvedValue(SUCCESFUL_BRIGHTID_RESULT);
159+
render(
160+
<UserContext.Provider value={mockUserContext}>
161+
<BrightidCard />
162+
</UserContext.Provider>
163+
);
164+
165+
const initialVerifyButton = screen.queryByTestId("button-verify-brightid");
166+
167+
fireEvent.click(initialVerifyButton!);
168+
169+
// Wait to see the cancel button on the modal
170+
let modalCancelButton: HTMLElement | null = null;
171+
await waitFor(() => {
172+
modalCancelButton = screen.queryByRole("button", {
173+
name: /Cancel/,
174+
});
175+
expect(modalCancelButton).toBeInTheDocument();
176+
});
177+
178+
fireEvent.click(modalCancelButton!);
179+
180+
expect(handleAddStamp).not.toBeCalled();
181+
182+
await waitForElementToBeRemoved(modalCancelButton);
183+
expect(modalCancelButton).not.toBeInTheDocument();
184+
});
185+
});
186+
187+
describe("and when a failed Bright Id result is returned", () => {
188+
it("modal displays steps to get sponsored", async () => {
189+
global.fetch = jest.fn().mockImplementation(setupFetchStub(false));
190+
(fetchVerifiableCredential as jest.Mock).mockRejectedValue("ERROR");
191+
render(
192+
<UserContext.Provider value={mockUserContext}>
193+
<BrightidCard />
194+
</UserContext.Provider>
195+
);
196+
197+
const initialVerifyButton = screen.queryByTestId("button-verify-brightid");
198+
199+
fireEvent.click(initialVerifyButton!);
200+
201+
const verifyModal = await screen.findByRole("dialog");
202+
const triggerSponsorButton = screen.queryByTestId("button-sponsor-brightid");
203+
const verifyModalText = screen.getByText(
204+
"A verifiable credential was not generated for your address. Follow the steps below to qualify:"
205+
);
206+
207+
expect(verifyModal).toBeInTheDocument();
208+
await waitFor(() => {
209+
expect(verifyModalText).toBeInTheDocument();
210+
});
211+
expect(triggerSponsorButton).toBeInTheDocument();
212+
});
213+
});
214+
});

app/__tests__/components/ProviderCards/EnsCard.test.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const mockHandleConnection = jest.fn();
1818
const mockCreatePassport = jest.fn();
1919
const handleAddStamp = jest.fn();
2020
const mockUserContext: UserContextState = {
21+
userDid: undefined,
2122
loggedIn: true,
2223
passport: undefined,
2324
isLoadingPassport: false,

app/__tests__/components/ProviderCards/FacebookCard.test.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const mockHandleConnection = jest.fn();
1313
const mockCreatePassport = jest.fn();
1414
const handleAddStamp = jest.fn();
1515
const mockUserContext: UserContextState = {
16+
userDid: undefined,
1617
loggedIn: true,
1718
passport: undefined,
1819
isLoadingPassport: false,

app/__tests__/components/ProviderCards/GoogleCard.test.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const mockHandleConnection = jest.fn();
1313
const mockCreatePassport = jest.fn();
1414
const handleAddStamp = jest.fn();
1515
const mockUserContext: UserContextState = {
16+
userDid: undefined,
1617
loggedIn: true,
1718
passport: undefined,
1819
isLoadingPassport: false,

app/__tests__/components/ProviderCards/PoapCard.test.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const mockHandleConnection = jest.fn();
1818
const mockCreatePassport = jest.fn();
1919
const handleAddStamp = jest.fn();
2020
const mockUserContext: UserContextState = {
21+
userDid: undefined,
2122
loggedIn: true,
2223
passport: undefined,
2324
isLoadingPassport: false,

app/__tests__/components/ProviderCards/PohCard.test.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const mockHandleConnection = jest.fn();
1818
const mockCreatePassport = jest.fn();
1919
const handleAddStamp = jest.fn();
2020
const mockUserContext: UserContextState = {
21+
userDid: undefined,
2122
loggedIn: true,
2223
passport: undefined,
2324
isLoadingPassport: false,
@@ -114,7 +115,7 @@ describe("when the verify button is clicked", () => {
114115

115116
const initialVerifyButton = screen.queryByTestId("button-verify-poh");
116117

117-
// Click verify button on ens card
118+
// Click verify button on poh card
118119
fireEvent.click(initialVerifyButton!);
119120

120121
// Wait to see the verify button on the modal

app/__tests__/components/ProviderCards/TwitterCard.test.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const mockHandleConnection = jest.fn();
1313
const mockCreatePassport = jest.fn();
1414
const handleAddStamp = jest.fn();
1515
const mockUserContext: UserContextState = {
16+
userDid: undefined,
1617
loggedIn: true,
1718
passport: undefined,
1819
isLoadingPassport: false,

app/__tests__/pages/Dashboard.test.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const mockHandleConnection = jest.fn();
1212
const mockCreatePassport = jest.fn();
1313
const handleAddStamp = jest.fn();
1414
const mockUserContext: UserContextState = {
15+
userDid: undefined,
1516
loggedIn: true,
1617
passport: undefined,
1718
isLoadingPassport: false,
@@ -40,6 +41,10 @@ const mockUserContext: UserContextState = {
4041
providerSpec: STAMP_PROVIDERS.Facebook,
4142
stamp: undefined,
4243
},
44+
Brightid: {
45+
providerSpec: STAMP_PROVIDERS.Brightid,
46+
stamp: undefined,
47+
},
4348
},
4449
handleAddStamp: handleAddStamp,
4550
handleCreatePassport: mockCreatePassport,

app/__tests__/pages/Home.test.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const mockHandleConnection = jest.fn();
1212
const mockCreatePassport = jest.fn();
1313
const handleAddStamp = jest.fn();
1414
const mockUserContext: UserContextState = {
15+
userDid: undefined,
1516
loggedIn: false,
1617
passport: undefined,
1718
isLoadingPassport: false,
@@ -40,6 +41,10 @@ const mockUserContext: UserContextState = {
4041
providerSpec: STAMP_PROVIDERS.Facebook,
4142
stamp: undefined,
4243
},
44+
Brightid: {
45+
providerSpec: STAMP_PROVIDERS.Brightid,
46+
stamp: undefined,
47+
},
4348
},
4449
handleAddStamp: handleAddStamp,
4550
handleCreatePassport: mockCreatePassport,

app/__tests__/pages/index.test.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const mockHandleConnection = jest.fn();
99
const mockCreatePassport = jest.fn();
1010
const handleAddStamp = jest.fn();
1111
const mockUserContext: UserContextState = {
12+
userDid: undefined,
1213
loggedIn: false,
1314
passport: undefined,
1415
isLoadingPassport: false,
@@ -37,6 +38,10 @@ const mockUserContext: UserContextState = {
3738
providerSpec: STAMP_PROVIDERS.Facebook,
3839
stamp: undefined,
3940
},
41+
Brightid: {
42+
providerSpec: STAMP_PROVIDERS.Brightid,
43+
stamp: undefined,
44+
},
4045
},
4146
handleAddStamp: handleAddStamp,
4247
handleCreatePassport: mockCreatePassport,

app/components/CardList.tsx

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@
22
import React from "react";
33

44
// --- Identity Providers
5-
import { GoogleCard, EnsCard, PohCard, TwitterCard, PoapCard, FacebookCard } from "./ProviderCards";
5+
import { GoogleCard, EnsCard, PohCard, TwitterCard, PoapCard, FacebookCard, BrightidCard } from "./ProviderCards";
66

77
export const CardList = (): JSX.Element => {
88
return (
99
<div className="container mx-auto py-10">
1010
<div className="-m-4 flex flex-wrap">
11+
<FacebookCard />
1112
<GoogleCard />
12-
<EnsCard />
1313
<TwitterCard />
14-
<PohCard />
14+
<BrightidCard />
1515
<PoapCard />
16-
<FacebookCard />
16+
<EnsCard />
17+
<PohCard />
1718
</div>
1819
</div>
1920
);

0 commit comments

Comments
 (0)