Skip to content

Commit 1b4ddb6

Browse files
Port tests to vitest and add new tests (gitcoinco#2247)
Co-authored-by: Marcelo Rubini <rubini.marcelo@gmail.com>
1 parent 4f2d4b7 commit 1b4ddb6

49 files changed

Lines changed: 8462 additions & 5943 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/DAI.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# DAI in relation to Allo and Grants Stack
2+
3+
## Permit
4+
5+
- DAI has its own permit that is incompatible with EIP2612.
6+
- It doesn't specify allowance, only `allowed: bool` (infinite approval or zero)
7+
- Its version is always 0
8+
- It's currently used on Ethereum and Polygon PoS
9+
- DAI also supports EIP2612 permit on other chains.
10+
- Some have version 1, some have version 2.
11+
- Contracts that don't have a version method (as it's not part of the 2612 spec) are assumed to have version 1
12+
- There's a difference between bridged DAI and Native DAI on some chains.
13+
14+
## Chains
15+
16+
| Chain | Address | DAI Permit | EIP2612 Permit | Version | transferWithPermit |
17+
|---------------|--------------------------------------------|------------|----------------|---------|--------------------|
18+
| Fantom | 0x8d11ec38a3eb5e956b052f67da8bdc9bef8abf3e | ⛔️ || 1 ||
19+
| Ethereum | 0x6b175474e89094c44da98b954eedeac495271d0f || ⛔️ | 1 | ⛔️ |
20+
| Polygon PoS | 0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063 || ⛔️ | 1 | ⛔️ |
21+
| Optimism | 0xda10009cbd5d07dd0cecc66161fc93d7c9000da1 | ⛔️ || 2 | ⛔️ |
22+
| Arbitrum | 0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1 | ⛔️ || 2 | ⛔️ |
23+
| Polygon zkEVM | 0xc5015b9d9161dca7e18e32f6f25c4ad850731fd4 | ⛔️ || 1 | ⛔️ |
24+
| zkSync Era | No DAI yet | | | | |

packages/common/src/components/RoundsSubNav.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type RoundsGroup = {
1515
rounds: Round[];
1616
};
1717

18-
const ROUNDS_GROUPS: RoundGroups[] = [
18+
const ROUNDS_GROUPS: RoundsGroup[] = [
1919
{
2020
name: "Core Rounds",
2121
rounds: [
@@ -106,7 +106,11 @@ export function RoundsSubNav(props: Props) {
106106
</a>
107107
</div>
108108
<div className="relative">
109-
<span className="cursor-pointer" onClick={() => setOpen(!open)}>
109+
<span
110+
className="cursor-pointer"
111+
aria-label={"Open Grants Subnav"}
112+
onClick={() => setOpen(!open)}
113+
>
110114
<GG18Icon className="inline mr-2" />
111115

112116
<DropdownIcon
@@ -129,6 +133,7 @@ export function RoundsSubNav(props: Props) {
129133
{group.rounds.map((round: Round) => (
130134
<li key={round.id}>
131135
<a
136+
data-testid={`round-link-${round.id}`}
132137
href="#"
133138
onClick={(
134139
e: React.MouseEvent<HTMLAnchorElement>

packages/grant-explorer/package.json

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010
"lint:fix": "eslint ./src --fix",
1111
"typecheck": "tsc --noEmit",
1212
"start": "craco start",
13-
"test": "craco test --watchAll=false --silent",
14-
"test-log": "craco test --watchAll=false",
15-
"test:coverage": "craco test --watchAll=false --silent --coverage"
13+
"test": "vitest",
14+
"test:coverage": "vitest --coverage"
1615
},
1716
"browserslist": {
1817
"production": [
@@ -28,12 +27,6 @@
2827
"last 1 safari version"
2928
]
3029
},
31-
"jest": {
32-
"resetMocks": false,
33-
"transformIgnorePatterns": [
34-
"/round-manager/node_modules/(?!(@spruceid))/"
35-
]
36-
},
3730
"dependencies": {
3831
"@chakra-ui/react": "^2.1.2",
3932
"@craco/craco": "^7.0.0",
@@ -58,6 +51,7 @@
5851
"@testing-library/jest-dom": "^5.16.4",
5952
"@testing-library/react": "^13.0.1",
6053
"@testing-library/user-event": "^14.1.1",
54+
"@vitejs/plugin-react": "^4.0.4",
6155
"@wagmi/core": "1.3.8",
6256
"@walletconnect/modal": "^2.5.9",
6357
"allo-indexer-client": "github:gitcoinco/allo-indexer-client#0549b473941a916b38426b32f59250f87ec3274d",
@@ -75,10 +69,8 @@
7569
"https-browserify": "^1.0.0",
7670
"ipfs-core": "^0.14.3",
7771
"ipfs-core-types": "^0.14.0",
78-
"jest-fetch-mock": "^3.0.3",
7972
"lodash-es": "^4.17.21",
8073
"moment": "^2.29.3",
81-
"msw": "^0.47.4",
8274
"node-fetch": "^3.3.1",
8375
"os-browserify": "^0.3.0",
8476
"process": "^0.11.10",
@@ -116,21 +108,23 @@
116108
"@testing-library/dom": ">=7.21.4 ",
117109
"@types/dompurify": "^2.4.0",
118110
"@types/ethereum-blockies": "^0.1.0",
119-
"@types/jest": "^27.4.1",
120111
"@types/lodash": "^4.14.192",
121112
"@types/lodash-es": "^4.17.8",
122113
"@types/node": "^17.0.25",
123114
"@types/react": "^18.0.6",
124115
"@types/react-dom": "^18.0.2",
125116
"@types/react-gtm-module": "^2.0.1",
126117
"@types/testing-library__jest-dom": "^5.14.5",
118+
"@vitest/coverage-v8": "^0.34.2",
127119
"autoprefixer": "^10.4.7",
128120
"eslint-config-gitcoin": "workspace:*",
129-
"jest-localstorage-mock": "^2.4.22",
130121
"postcss": "^8.4.14",
131122
"prettier": "^2.8.3",
132123
"tailwind-styled-components": "^2.1.7",
133-
"tailwindcss": "^3.0.24"
124+
"tailwindcss": "^3.0.24",
125+
"vite": "^4.4.9",
126+
"vitest": "^0.34.2",
127+
"vitest-fetch-mock": "^0.2.2"
134128
},
135129
"engines": {
136130
"node": ">=16.15.0",
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
// cartStore.test.ts
2+
3+
import { useCartStorage } from "./store";
4+
import { ChainId } from "common";
5+
import {
6+
ApplicationStatus,
7+
CartProject,
8+
PayoutToken,
9+
} from "./features/api/types";
10+
import { makeApprovedProjectData } from "./test-utils";
11+
import { payoutTokensMap } from "./features/api/utils";
12+
13+
describe("useCartStorage Zustand store", () => {
14+
beforeEach(() => {
15+
// Clear localStorage before each test to ensure a clean state
16+
localStorage.clear();
17+
});
18+
19+
test("should add projects to the store", () => {
20+
const project: CartProject = makeApprovedProjectData();
21+
22+
useCartStorage.getState().add(project);
23+
24+
expect(useCartStorage.getState().projects).toContain(project);
25+
});
26+
27+
test("should remove a project from the store", () => {
28+
const project = makeApprovedProjectData();
29+
30+
useCartStorage.getState().add(project);
31+
useCartStorage.getState().remove(project.grantApplicationId);
32+
33+
expect(useCartStorage.getState().projects).not.toContain(project);
34+
});
35+
36+
test("should clear all projects from the store", () => {
37+
const project1: CartProject = makeApprovedProjectData();
38+
39+
const project2: CartProject = makeApprovedProjectData();
40+
41+
useCartStorage.getState().add(project1);
42+
useCartStorage.getState().add(project2);
43+
useCartStorage.getState().clear();
44+
45+
expect(useCartStorage.getState().projects).toHaveLength(0);
46+
});
47+
48+
test("should update donation amount for a specific project", () => {
49+
const project: CartProject = makeApprovedProjectData();
50+
51+
const updatedAmount = "100"; // Mock amount
52+
53+
useCartStorage.getState().add(project);
54+
useCartStorage
55+
.getState()
56+
.updateDonationAmount(project.grantApplicationId, updatedAmount);
57+
58+
const updatedProject = useCartStorage
59+
.getState()
60+
.projects.find(
61+
(p) => p.grantApplicationId === project.grantApplicationId
62+
);
63+
expect(updatedProject?.amount).toBe(updatedAmount);
64+
});
65+
66+
test("should set payout token for a specific chain", () => {
67+
const chainId: ChainId = ChainId.MAINNET; // Mock ChainId
68+
const payoutToken: PayoutToken = payoutTokensMap[ChainId.MAINNET][0];
69+
70+
useCartStorage.getState().setPayoutTokenForChain(chainId, payoutToken);
71+
72+
expect(useCartStorage.getState().chainToPayoutToken[chainId]).toEqual(
73+
payoutToken
74+
);
75+
});
76+
77+
test("should not add duplicate projects to the store", () => {
78+
const project: CartProject = makeApprovedProjectData();
79+
80+
useCartStorage.getState().add(project);
81+
useCartStorage.getState().add(project);
82+
83+
// Assert that the project was only added once
84+
const matchingProjects = useCartStorage
85+
.getState()
86+
.projects.filter(
87+
(p) => p.grantApplicationId === project.grantApplicationId
88+
);
89+
expect(matchingProjects).toHaveLength(1);
90+
});
91+
92+
test("should update donations for all projects with a specific chain", () => {
93+
const chainId: ChainId = ChainId.MAINNET; // Mock ChainId
94+
95+
const project1: CartProject = makeApprovedProjectData();
96+
const project2: CartProject = makeApprovedProjectData();
97+
98+
const updatedAmount = "200"; // Mock amount
99+
100+
useCartStorage.getState().add(project1);
101+
useCartStorage.getState().add(project2);
102+
useCartStorage.getState().updateDonationsForChain(chainId, updatedAmount);
103+
104+
const updatedProjects = useCartStorage
105+
.getState()
106+
.projects.filter((p) => p.chainId === chainId);
107+
updatedProjects.forEach((proj) => {
108+
expect(proj.amount).toBe(updatedAmount);
109+
});
110+
});
111+
112+
test("should not update donation amount for a non-existing project", () => {
113+
const nonExistingId = "1234"; // Mock ID
114+
const initialProjects = [...useCartStorage.getState().projects];
115+
116+
useCartStorage.getState().updateDonationAmount(nonExistingId, "500");
117+
118+
// Assert that the store state remains unchanged
119+
expect(useCartStorage.getState().projects).toEqual(initialProjects);
120+
});
121+
122+
test("should override payout token for a specific chain", () => {
123+
const chainId: ChainId = ChainId.MAINNET; // Mock ChainId
124+
const initialPayoutToken: PayoutToken = payoutTokensMap[ChainId.MAINNET][0];
125+
const newPayoutToken: PayoutToken = payoutTokensMap[ChainId.MAINNET][1];
126+
127+
useCartStorage
128+
.getState()
129+
.setPayoutTokenForChain(chainId, initialPayoutToken);
130+
useCartStorage.getState().setPayoutTokenForChain(chainId, newPayoutToken);
131+
132+
expect(useCartStorage.getState().chainToPayoutToken[chainId]).toEqual(
133+
newPayoutToken
134+
);
135+
});
136+
137+
test("should handle removal of non-existing project gracefully", () => {
138+
const nonExistingId = "1234"; // Mock ID
139+
const initialProjects = [...useCartStorage.getState().projects];
140+
141+
useCartStorage.getState().remove(nonExistingId);
142+
143+
// Assert that the store state remains unchanged
144+
expect(useCartStorage.getState().projects).toEqual(initialProjects);
145+
});
146+
147+
test("should handle adding a project with duplicate ID gracefully", () => {
148+
const project: CartProject = makeApprovedProjectData();
149+
150+
const modifiedProject: CartProject = {
151+
...project,
152+
status: ApplicationStatus.REJECTED,
153+
};
154+
155+
useCartStorage.getState().add(project);
156+
useCartStorage.getState().add(modifiedProject);
157+
158+
expect(useCartStorage.getState().projects).toContainEqual(modifiedProject);
159+
});
160+
161+
test("should handle invalid donation amount updates gracefully", () => {
162+
const project: CartProject = makeApprovedProjectData();
163+
164+
useCartStorage.getState().add(project);
165+
useCartStorage
166+
.getState()
167+
.updateDonationAmount(project.grantApplicationId, "-50");
168+
169+
// Assuming negative donations are invalid and thus unchanged:
170+
expect(
171+
useCartStorage
172+
.getState()
173+
.projects.find(
174+
(p) => p.grantApplicationId === project.grantApplicationId
175+
)?.amount
176+
).not.toBe("-50");
177+
});
178+
179+
test("should handle setting payout token for non-existing chain gracefully", () => {
180+
const nonExistingChainId = 123123; // Mock a non-existing ChainId
181+
const payoutToken: PayoutToken = payoutTokensMap[ChainId.MAINNET][0];
182+
183+
const initialChainToPayoutToken = {
184+
...useCartStorage.getState().chainToPayoutToken,
185+
};
186+
187+
useCartStorage
188+
.getState()
189+
// @ts-expect-error We purposefully pass a wrong ChainId here
190+
.setPayoutTokenForChain(nonExistingChainId, payoutToken);
191+
192+
// The state should remain unchanged unchanged.
193+
expect(useCartStorage.getState().chainToPayoutToken).toEqual(
194+
initialChainToPayoutToken
195+
);
196+
});
197+
});
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { useCheckoutStore } from "./checkoutStore";
2+
import { ProgressStatus } from "./features/api/types";
3+
import { ChainId } from "common";
4+
import { beforeEach } from "vitest";
5+
6+
const store = useCheckoutStore;
7+
const initialState = store.getState();
8+
9+
describe("Checkout Store", () => {
10+
/** Reset the store before each test */
11+
beforeEach(() => {
12+
store.setState(initialState);
13+
});
14+
15+
it("permitStatus manipulation", async () => {
16+
const store = useCheckoutStore;
17+
const chain: ChainId = ChainId.MAINNET;
18+
19+
// Initially, the status should be NOT_STARTED for all chains
20+
expect(store.getState().permitStatus[chain]).toEqual(
21+
ProgressStatus.NOT_STARTED
22+
);
23+
24+
// Update the status
25+
store.getState().setPermitStatusForChain(chain, ProgressStatus.IN_PROGRESS);
26+
expect(store.getState().permitStatus[chain]).toEqual(
27+
ProgressStatus.IN_PROGRESS
28+
);
29+
});
30+
31+
it("voteStatus manipulation", async () => {
32+
const chain: ChainId = ChainId.MAINNET;
33+
34+
expect(store.getState().voteStatus[chain]).toEqual(
35+
ProgressStatus.NOT_STARTED
36+
);
37+
38+
store.getState().setVoteStatusForChain(chain, ProgressStatus.IN_PROGRESS);
39+
expect(store.getState().voteStatus[chain]).toEqual(
40+
ProgressStatus.IN_PROGRESS
41+
);
42+
});
43+
44+
it("chainSwitchStatus manipulation", async () => {
45+
const chain: ChainId = ChainId.MAINNET;
46+
47+
expect(store.getState().chainSwitchStatus[chain]).toEqual(
48+
ProgressStatus.NOT_STARTED
49+
);
50+
51+
store
52+
.getState()
53+
.setChainSwitchStatusForChain(chain, ProgressStatus.IN_PROGRESS);
54+
expect(store.getState().chainSwitchStatus[chain]).toEqual(
55+
ProgressStatus.IN_PROGRESS
56+
);
57+
});
58+
59+
it("chainsToCheckout manipulation", async () => {
60+
expect(store.getState().chainsToCheckout).toEqual([]);
61+
62+
store
63+
.getState()
64+
.setChainsToCheckout([ChainId.MAINNET, ChainId.GOERLI_CHAIN_ID]);
65+
expect(store.getState().chainsToCheckout).toEqual([
66+
ChainId.MAINNET,
67+
ChainId.GOERLI_CHAIN_ID,
68+
]);
69+
});
70+
});

0 commit comments

Comments
 (0)