Skip to content

Commit

Permalink
Added tests
Browse files Browse the repository at this point in the history
- added mocks for all SDK requests
- really generic handlers created, taking all requests to accounts,
  elections and censuses and returning the same json
- add more tests thanks to these mocks
- Change github action job name
- Do not clear session data if no census meta is set
  • Loading branch information
elboletaire committed Aug 29, 2023
1 parent 863eda5 commit 62b7857
Show file tree
Hide file tree
Showing 15 changed files with 763 additions and 13 deletions.
11 changes: 5 additions & 6 deletions .github/workflows/lint.yml → .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: lint and type checks
name: lint and test

on:
push:
Expand All @@ -7,7 +7,7 @@ on:
branches: [main]

jobs:
lint:
test:
runs-on: ubuntu-latest

steps:
Expand All @@ -18,8 +18,7 @@ jobs:
node-version: 18
- name: Install dependencies
run: yarn
- name: Run prettier in whole project
- name: Run all lint tasks
run: yarn lint
- name: Run type checks in packages
run: yarn lint
working-directory: packages/chakra-components
- name: Test everything
run: yarn test --verbose
19 changes: 19 additions & 0 deletions jest.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const config = {
testEnvironment: 'jsdom',
collectCoverageFrom: ['packages/**/*.{ts,tsx}'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
modulePathIgnorePatterns: ['<rootDir>/templates'],
transform: {
'^.+\\.(ts|tsx|js|jsx)?$': '@swc-node/jest',
},
transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$'],
setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect', './setup-tests.ts'],
globals: {
'ts-jest': {
tsconfig: 'tsconfig/react-library.json',
},
},
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
}

export default config
119 changes: 119 additions & 0 deletions mocks/handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { rest } from 'msw'

export const handlers = [
rest.get('https://api*.vocdoni.net/v2/accounts/*', (req, res, ctx) => {
const id = req.params[1]
return res(
ctx.status(200),
ctx.json({
address: id,
nonce: 62,
balance: 4987085,
electionIndex: 42,
infoURL: 'ipfs://bafybeiebhal46gmhx3y7qbrfsgm5zsmrio5bmzwutg6fodkfysxr2e5t34',
metadata: {
version: '1.0',
name: { default: 'testing account' },
description: { default: '' },
newsFeed: { default: '' },
media: { avatar: 'https://pbs.twimg.com/profile_images/1546527030329606150/BmyiyT2I_400x400.jpg' },
meta: {},
},
})
)
}),
rest.get('https://api*.vocdoni.net/v2/elections/*', (req, res, ctx) => {
const id = req.params[1]
return res(
ctx.status(200),
ctx.json({
electionId: id,
organizationId: '389b35ebcf34f9e0e8b32b5468216a699fde0c97',
status: 'RESULTS',
startDate: '2023-08-03T15:10:57.829010914Z',
endDate: '2023-09-01T01:25:08.672843671Z',
voteCount: 1,
finalResults: true,
result: [
['0', '1', '0'],
['0', '1', '0'],
],
census: {
censusOrigin: 'OFF_CHAIN_TREE_WEIGHTED',
censusRoot: 'cc6c247592db36bbe2b7ecee18716ef28605c97f3c5a52479c154bc20ecafa8f',
postRegisterCensusRoot: '',
censusURL: 'ipfs://bafybeia4w63agvgs5x5zhmcxtvfzlnahltzzvbubragtne26e6ua7rivvm',
maxCensusSize: 2,
},
metadataURL: 'ipfs://bafybeiaszamrceo4n5kcipcr5cnr5iozbg5zgj3nys2s5tjae7kubyl2fe',
creationTime: '2023-08-03T15:10:47Z',
voteMode: {
serial: false,
anonymous: false,
encryptedVotes: false,
uniqueValues: false,
costFromWeight: false,
},
electionMode: {
autoStart: true,
interruptible: true,
dynamicCensus: false,
encryptedMetaData: false,
preRegister: false,
},
tallyMode: { maxCount: 2, maxValue: 2, maxVoteOverwrites: 1, maxTotalCost: 0, costExponent: 10000 },
metadata: {
title: { default: 'mocked process' },
version: '1.1',
description: { default: '' },
media: {},
meta: { sdk: { version: '0.0.16' } },
questions: [
{
choices: [
{ title: { default: '一' }, value: 0 },
{ title: { default: '二' }, value: 1 },
{ title: { default: '三' }, value: 2 },
],
description: { default: '' },
title: { default: '最初の質問' },
},
{
choices: [
{ title: { default: 'eins' }, value: 0 },
{ title: { default: 'zwei' }, value: 1 },
],
description: { default: '' },
title: { default: 'das ist eine andere frage' },
},
],
results: { aggregation: 'discrete-counting', display: 'multiple-question' },
},
})
)
}),
rest.get('https://api*.vocdoni.net/v2/censuses/*/size', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
size: 2,
})
)
}),
rest.get('https://api*.vocdoni.net/v2/censuses/*/weight', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
weight: 2,
})
)
}),
rest.get('https://api*.vocdoni.net/v2/censuses/*/type', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
type: 'weighted',
})
)
}),
]
4 changes: 4 additions & 0 deletions mocks/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { setupServer } from 'msw/node'
import { handlers } from './handlers'

export const server = setupServer(...handlers)
19 changes: 17 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@
"templates/*"
],
"devDependencies": {
"@swc-node/jest": "^1.6.7",
"@swc/core": "^1.3.78",
"@swc/helpers": "^0.5.1",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "14.0.0",
"@types/jest": "^29.5.3",
"@types/testing-library__jest-dom": "5.14.8",
"axios": "^0.27.0",
"eslint-plugin-testing-library": "5.5.1",
"ethers": "^5.7.0",
"jest": "^29.6.2",
"jest-environment-jsdom": "^29.6.2",
"jest-watch-typeahead": "^2.2.2",
"msw": "^1.2.5",
"prettier": "^2.8.7",
"turbo": "^1.8.6"
},
Expand All @@ -21,8 +35,9 @@
"scripts": {
"build": "turbo build --filter=@vocdoni*",
"lint:fix": "prettier -c . --write",
"lint": "prettier -c .",
"clean": "turbo clean --filter=@vocdoni* && rm -fr node_modules"
"lint": "turbo lint && prettier -c .",
"clean": "turbo clean --filter=@vocdoni* && rm -fr node_modules",
"test": "jest"
},
"dependencies": {
"tsup": "^7.2.0"
Expand Down
2 changes: 2 additions & 0 deletions packages/react-providers/.eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ extends:
parserOptions:
ecmaVersion: latest
sourceType: module
plugins:
- testing-library
rules: {}
143 changes: 143 additions & 0 deletions packages/react-providers/src/client/ClientProvider.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { Wallet } from '@ethersproject/wallet'
import { act, render, renderHook, waitFor } from '@testing-library/react'
import { VocdoniCensus3Client } from '@vocdoni/sdk'
import { ApiUrl, CensusUrls, properProps } from '../test-utils'
import { ClientProvider, useClient } from './ClientProvider'

describe('<ClientProvider />', () => {
it('renders child elements', () => {
const { getByText } = render(
<ClientProvider>
<p>is rendered</p>
</ClientProvider>
)

expect(getByText('is rendered')).toBeInTheDocument()
})

it('has expected defaults defined if no props are passed', () => {
const wrapper = (props: any) => <ClientProvider {...properProps(props)} />
const { result } = renderHook(() => useClient(), {
wrapper,
})

expect(result.current.env).toBe('prod')

// no signer is passed, so none should be defined
expect(result.current.signer).toBeUndefined()
expect(result.current.client.wallet).toBeUndefined()
expect(result.current.connected).toBeFalsy()

// and there should be no account connected either
expect(result.current.account).toBeUndefined()

// ensures prod is taken by default in the client
expect(result.current.client.url).toEqual(ApiUrl.prod)

// check census3 client is setup
expect(result.current.census3).not.toBeUndefined()
expect(result.current.census3).toBeInstanceOf(VocdoniCensus3Client)
expect(result.current.census3.url).not.toBeUndefined()
})

it('sets proper environment', () => {
const wrapper = (props: any) => <ClientProvider {...properProps(props)} />
const { result, rerender } = renderHook(() => useClient(), {
wrapper,
initialProps: { env: 'stg' },
})

expect(result.current.env).toBe('stg')
expect(result.current.client.url).toEqual(ApiUrl.stg)
expect(result.current.census3.url).toEqual(CensusUrls.stg)

// change it to dev
rerender({ env: 'dev' })

expect(result.current.env).toBe('dev')
// ensure other related instances are updated too
expect(result.current.client.url).toEqual(ApiUrl.dev)
expect(result.current.census3.url).toEqual(CensusUrls.dev)
})

it('sets proper signer', async () => {
const signer = Wallet.createRandom()
const wrapper = (props: any) => <ClientProvider {...properProps(props)} />
const { result, rerender } = renderHook(() => useClient(), {
wrapper,
initialProps: { signer, env: 'dev' },
})

await waitFor(() => {
expect(result.current.connected).toBeTruthy()
})

expect(result.current.signer).toEqual(signer)
expect(result.current.client.wallet).toEqual(signer)

const newsigner = Wallet.createRandom()

// @ts-ignore
rerender({ signer: newsigner })

expect(result.current.signer).not.toEqual(signer)
expect(result.current.signer).toEqual(newsigner)
expect(result.current.client.wallet).toEqual(newsigner)
// ensure env has not changed after changing the signer
expect(result.current.env).toEqual('dev')
expect(result.current.client.url).toEqual(ApiUrl.dev)
})

it('"creates" an account', async () => {
const wrapper = (props: any) => <ClientProvider {...properProps(props)} />
const signer = Wallet.createRandom()
const { result } = renderHook(() => useClient(), {
wrapper,
initialProps: { env: 'dev', signer },
})

// fetch account request is mocked, so we're actually not creating accounts, just fetching them
await act(async () => {
await result.current.createAccount()
})

await waitFor(() => {
expect(result.current.loaded.account).toBeTruthy()
})

expect(result.current.loading.account).toBeFalsy()
expect(result.current.account).not.toBeUndefined()
expect(result.current.errors.account).toBeNull()
})

it('properly clears session data', async () => {
const wrapper = (props: any) => <ClientProvider {...properProps(props)} />
const signer = Wallet.createRandom()
const { result } = renderHook(() => useClient(), {
wrapper,
initialProps: { env: 'dev', signer },
})

// fetch account request is mocked, so we're actually not creating accounts, just fetching them
await act(async () => {
await result.current.createAccount()
})

await waitFor(() => {
expect(result.current.loaded.account).toBeTruthy()
})

expect(result.current.connected).toBeTruthy()
expect(result.current.balance).not.toEqual(-1)

// clear session data
await act(() => {
result.current.clear()
})

expect(result.current.connected).toBeFalsy()
expect(result.current.client.wallet).toBeUndefined()
expect(result.current.signer).toStrictEqual({})
expect(result.current.balance).toEqual(-1)
})
})
6 changes: 3 additions & 3 deletions packages/react-providers/src/client/use-client-reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,14 @@ export interface ClientState {
}

export const clientStateEmpty = (env: EnvOptions, client: VocdoniSDKClient, signer: Wallet | Signer): ClientState => ({
env,
env: env || EnvOptions.PROD,
signer,
client:
client ||
new VocdoniSDKClient({
env,
env: env || EnvOptions.PROD,
}),
census3: new VocdoniCensus3Client({ env }),
census3: new VocdoniCensus3Client({ env: env || EnvOptions.PROD }),
account: undefined,
balance: -1,
connected: false,
Expand Down
Loading

0 comments on commit 62b7857

Please sign in to comment.