Skip to content

Commit

Permalink
anoma#10 Unit tests for actions and configure in CI (anoma#36)
Browse files Browse the repository at this point in the history
* Add some unit tests to shielded account actions
* Configure to run in CI
* Adjusted a failing test to reflect a change in a dependency
* Removed a hard-coded value that was used during debugging
* Adjusted setting of the state based on the change in the type of actions result
* Changed the faulty test file name based on PR review
* removed unnecessary import
* Investigating a weird issue where the paths resolve locally but not in CI
* corrected the paths after renaming
  • Loading branch information
memasdeligeorgakis authored Aug 9, 2022
1 parent f4325a2 commit bb7946b
Show file tree
Hide file tree
Showing 20 changed files with 590 additions and 18 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/deploy-wallet-at-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,45 @@ on:
env:
CI: false
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Install dependencies
working-directory: ./packages/anoma-wallet
run: yarn

- name: Install wasm-pack
uses: jetli/wasm-pack-action@v0.3.0
with:
version: "v0.10.3"

- name: Run unit tests
id: run-unit-tests
working-directory: ./packages/anoma-wallet
run: yarn test:coverage
env:
REACT_APP_ALIAS: "Namada Mainnet"
REACT_APP_CHAIN_ID: "anoma-masp-1.5.32ccad5356012a7"
REACT_APP_LEDGER_URL: "https://d3brk13lbhxfdb.cloudfront.net/anoma-masp-1.5.32ccad5356012a7"
REACT_APP_FAUCET: "atest1v4ehgw36gc6yxvpjxccyzvphxycrxw2xxsuyydesxgcnjs3cg9znwv3cxgmnj32yxy6rssf5tcqjm3"

- name: report success
if: steps.run-unit-tests.outcome == 'success'
run: |
curl --header "Content-Type: application/json" \
--request POST \
--data '{"message":"Unit tests succeeded ✅\n \n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\n \nReview\nhttps://pull-request-${{ github.event.number }}--wallet-development-heliax-dev.netlify.app\n \nthe PR\nhttps://github.com/anoma/namada-interface/pull/${{ github.event.number }}"}' \
${{ secrets.SLACK_WEBHOOK_WALLET_PR }}
- name: report failure
if: steps.run-unit-tests.outcome != 'success'
run: |
curl --header "Content-Type: application/json" \
--request POST \
--data '{"message":"Unit tests failed ⛔️ \n \n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\n \nReview\nhttps://pull-request-${{ github.event.number }}--wallet-development-heliax-dev.netlify.app\n \nthe PR\nhttps://github.com/anoma/namada-interface/pull/${{ github.event.number }}"}' \
${{ secrets.SLACK_WEBHOOK_WALLET_PR }}
build:
runs-on: ubuntu-latest
steps:
Expand Down
8 changes: 8 additions & 0 deletions packages/anoma-wallet/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"name": "@namada-interface/anoma-wallet",
"jest": {
"moduleNameMapper": {
"^@anoma/masp-web": "<rootDir>/../../node_modules/@anoma/masp-web"
}
},
"version": "0.1.0",
"main": "./src/index.ts",
"type": "commonjs",
Expand Down Expand Up @@ -48,6 +53,7 @@
"wasm:build-in-ci": "./scripts/build.sh",
"wasm:build:node": "./scripts/build-test.sh",
"test": "yarn wasm:build:node && npx create-react-scripts test",
"test:coverage": "yarn test --coverage --watchAll=false",
"e2e-test": "PLAYWRIGHT_BASE_URL=http://localhost:3000 yarn playwright test",
"e2e-test:headed": "PLAYWRIGHT_BASE_URL=http://localhost:3000 yarn playwright test --project=chromium --headed",
"eject": "npx react-scripts eject"
Expand Down Expand Up @@ -83,6 +89,7 @@
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
"@types/react-qr-reader": "^2.1.4",
"@types/redux-mock-store": "^1.0.3",
"@types/styled-components": "^5.1.22",
"babel-plugin-styled-components": "^2.0.3",
"create-react-scripts": "^0.1.6",
Expand All @@ -97,6 +104,7 @@
"history": "^5.3.0",
"jest-fetch-mock": "^3.0.3",
"prettier": "^2.5.1",
"redux-mock-store": "^1.5.4",
"ts-loader": "^9.2.7"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
BodyText,
} from "./Completion.components";
import { Tokens, TokenType } from "constants/";
import { createShieldedAccount } from "slices/accountsNew/actions";
import { createShieldedAccount } from "slices/AccountsNew/actions";
import { useNavigate } from "react-router-dom";
import { TopLevelRoute } from "App/types";

Expand Down
4 changes: 2 additions & 2 deletions packages/anoma-wallet/src/App/AccountOverview/AddAccount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { useAppDispatch, useAppSelector } from "store";
import { DerivedAccount, AccountsState, addAccount } from "slices/accounts";
import { SettingsState } from "slices/settings";

import { NewAccountDetails } from "slices/accountsNew";
import { createShieldedAccount, reset } from "slices/accountsNew/actions";
import { NewAccountDetails } from "slices/AccountsNew";
import { createShieldedAccount, reset } from "slices/AccountsNew/actions";

import { Label } from "components/Input/input.components";
import { Toggle } from "components/Toggle";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useAppDispatch, useAppSelector } from "store";
import { AccountsState } from "slices/accounts";
import { BalancesState, fetchBalances } from "slices/balances";
import { SettingsState } from "slices/settings";
import { updateShieldedBalances } from "slices/accountsNew";
import { updateShieldedBalances } from "slices/AccountsNew";
import { Symbols, Tokens, TokenType } from "constants/";
import { formatRoute } from "utils/helpers";
import { TransfersState } from "slices/transfers";
Expand Down
2 changes: 1 addition & 1 deletion packages/anoma-wallet/src/App/Token/TokenDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from "slices/accounts";
import { TransfersState } from "slices/transfers";
import { SettingsState } from "slices/settings";
import { updateShieldedBalances } from "slices/accountsNew";
import { updateShieldedBalances } from "slices/AccountsNew";
import { useAppDispatch, useAppSelector } from "store";
import { formatRoute, stringFromTimestamp } from "utils/helpers";
import { ChainsState } from "slices/chains";
Expand Down
2 changes: 1 addition & 1 deletion packages/anoma-wallet/src/lib/tx/Transfer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ describe("Transfer wasm and class methods", () => {
});

expect(hash.length).toBe(64);
expect(bytes.length).toBe(596);
expect(bytes.length).toBe(597);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
import { jest } from "@jest/globals";
import { AnyAction } from "redux";
import createMockStore from "redux-mock-store";
import thunk, { ThunkDispatch } from "redux-thunk";
import { RootState } from "store/store";
import { mockAppState } from "store/mocks";
import { updateShieldedBalances } from "../actions";
import { ShieldedAccount } from "slices/accounts";
import { AccountErrors } from "../types";

// imports containing mocked units
import * as shieldedTransfer from "slices/shieldedTransfer";
jest.mock("slices/shieldedTransfer");

type DispatchExts = ThunkDispatch<RootState, void, AnyAction>;
const middleware = [thunk];
const mockStore = createMockStore<RootState, DispatchExts>(middleware);
const store = mockStore(mockAppState);

describe("shielded balances", () => {
describe("fetchContactPageContent", () => {
beforeEach(() => {
store.clearActions();
});

// this is not a focused case, but a very generic one just testing a happy path
// of updateShieldedBalances.
it("should behave as expected in happy path of updateShieldedBalances", async () => {
// setup data
const testChainId1 = "testChainId1";
const testspendingKey1 = "testspendingKey1";
const testBalanceAmount = 100;
const shieldedAccountUuidInClient = "shieldedAccountUuidInClient1";
const shieldedAccounts: {
[key: string]: { [key: string]: ShieldedAccount };
} = {
testChainId1: {
[shieldedAccountUuidInClient]: {
chainId: "testChainId1",
shieldedKeysAndPaymentAddress: {
viewingKey: "viewingKey",
spendingKey: testspendingKey1,
paymentAddress: "paymentAddress",
},
isShielded: true,
alias: "Namada",
balance: 0,
address: "this_should_be_the_current_masp_address",
tokenType: "ETH",
signingKey: "signingKey",
publicKey: "publicKey",
id: shieldedAccountUuidInClient,
},
},
};
// we create this here and set in the mocked function below to be able to assert that
// it was called with the right data
let expectedChainId;
let expectedSpendingKey;

const store = mockStore({
...mockAppState,
settings: { ...mockAppState.settings, chainId: testChainId1 },
accounts: {
...mockAppState.accounts,
shieldedAccounts: shieldedAccounts,
},
});
// let's define a mock function that our action is calling
jest
.spyOn(shieldedTransfer, "getShieldedBalance")
.mockImplementation(
async (
chainId: string,
inputAddress: string,
_tokenAddress: string
): Promise<string> => {
// we want to assert later that the correct values are being passed from the action
expectedChainId = chainId;
expectedSpendingKey = inputAddress;

// we return the test balance amount
return Promise.resolve(`${testBalanceAmount}`);
}
);

// run unit under test
await store.dispatch(updateShieldedBalances());

// assert results
// ensure that the parameter was correctly passed from action to a dependency
expect(expectedChainId).toEqual(testChainId1);
expect(expectedSpendingKey).toEqual(testspendingKey1);

// then ensure that the actions was resolving with expected results
const actionsReceivedByStore = await store.getActions();
expect(actionsReceivedByStore[0].type).toEqual(
updateShieldedBalances.pending.type
);
expect(actionsReceivedByStore[1].type).toEqual(
updateShieldedBalances.fulfilled.type
);

// lets get the returned balance of the account from the payload
const balanceOfShieldedAccountUuidInClient =
actionsReceivedByStore[1].payload.shieldedBalances[
shieldedAccountUuidInClient
];
expect(balanceOfShieldedAccountUuidInClient).toEqual(testBalanceAmount);
expect(balanceOfShieldedAccountUuidInClient).not.toEqual(
`${testBalanceAmount}`
);
});

it("should behave as expected in faulty response from wasm call in updateShieldedBalances", async () => {
// setup data
const testChainId1 = "testChainId1";
const testBalanceAmount = "aaa";
const shieldedAccountUuidInClient = "shieldedAccountUuidInClient1";
const shieldedAccounts: {
[key: string]: { [key: string]: ShieldedAccount };
} = {
testChainId1: {
[shieldedAccountUuidInClient]: {
chainId: testChainId1,
shieldedKeysAndPaymentAddress: {
viewingKey: "viewingKey",
spendingKey: "testspendingKey1",
paymentAddress: "paymentAddress",
},
isShielded: true,
alias: "Namada",
balance: 0,
address: "this_should_be_the_current_masp_address",
tokenType: "ETH",
signingKey: "signingKey",
publicKey: "publicKey",
id: shieldedAccountUuidInClient,
},
},
};
// we create this here and set in the mocked function below to be able to assert that
// it was called with the right data

const store = mockStore({
...mockAppState,
settings: { ...mockAppState.settings, chainId: testChainId1 },
accounts: {
...mockAppState.accounts,
shieldedAccounts: shieldedAccounts,
},
});
// let's define a mock function that our action is calling
jest
.spyOn(shieldedTransfer, "getShieldedBalance")
.mockImplementation(
async (
_chainId: string,
_inputAddress: string,
_tokenAddress: string
): Promise<string> => {
// we return the test balance amount
return Promise.resolve(`${testBalanceAmount}`);
}
);

// run unit under test
await store.dispatch(updateShieldedBalances());

// then ensure that the actions was resolving with expected results
const actionsReceivedByStore = await store.getActions();
expect(actionsReceivedByStore[0].type).toEqual(
updateShieldedBalances.pending.type
);
expect(actionsReceivedByStore[1].type).toEqual(
updateShieldedBalances.fulfilled.type
);

// lets get the returned balance of the account from the payload
const balanceOfShieldedAccountUuidInClient =
actionsReceivedByStore[1].payload.shieldedBalances[
shieldedAccountUuidInClient
];
expect(balanceOfShieldedAccountUuidInClient).toEqual(
AccountErrors.NonNumericShieldedBalanceReturned
);
});

it("should behave as expected when the action throws", async () => {
// setup data
const testChainId1 = "testChainId1";
const testBalanceAmount = "aaa";
const shieldedAccountUuidInClient = "shieldedAccountUuidInClient1";
const shieldedAccounts: {
[key: string]: { [key: string]: ShieldedAccount };
} = {
[testChainId1 + "wrong_key"]: {
[shieldedAccountUuidInClient]: {
chainId: testChainId1,
shieldedKeysAndPaymentAddress: {
viewingKey: "viewingKey",
spendingKey: "testspendingKey1",
paymentAddress: "paymentAddress",
},
isShielded: true,
alias: "Namada",
balance: 0,
address: "this_should_be_the_current_masp_address",
tokenType: "ETH",
signingKey: "signingKey",
publicKey: "publicKey",
id: shieldedAccountUuidInClient,
},
},
};
// we create this here and set in the mocked function below to be able to assert that
// it was called with the right data

const store = mockStore({
...mockAppState,
settings: { ...mockAppState.settings, chainId: testChainId1 },
accounts: {
...mockAppState.accounts,
shieldedAccounts: shieldedAccounts,
},
});
// let's define a mock function that our action is calling
jest
.spyOn(shieldedTransfer, "getShieldedBalance")
.mockImplementation(
async (
_chainId: string,
_inputAddress: string,
_tokenAddress: string
): Promise<string> => {
// we return the test balance amount
return Promise.resolve(`${testBalanceAmount}`);
}
);

// run unit under test
await store.dispatch(updateShieldedBalances());

// then ensure that the actions was resolving with expected results
const actionsReceivedByStore = await store.getActions();
expect(actionsReceivedByStore[0].type).toEqual(
updateShieldedBalances.pending.type
);
expect(actionsReceivedByStore[1].type).toEqual(
updateShieldedBalances.rejected.type
);
expect(actionsReceivedByStore[1].payload).toEqual(
AccountErrors.RetrievingShieldedBalancesFailed
);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import ReactTestUtils from "react-dom/test-utils";

describe("ShieldedTransfer", () => {
it.skip("testing the component's behavior", async () => {});
});
Loading

0 comments on commit bb7946b

Please sign in to comment.