Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: react lib testing #130

Merged
merged 3 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 28 additions & 30 deletions packages/dapp-kit-react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,50 +23,48 @@ yarn build
yarn add @vechainfoundation/dapp-kit-react
```

```typescript
import type { Options } from '@vechain/connex';

const nodeOptions: Omit<Options, 'signer'> = {
node: 'https://testnet.vechain.org/',
network: 'test',
};
```

- Optional: Configure wallet connect options

```typescript
import type { WalletConnectOptions } from '@vechainfoundation/dapp-kit';
import type { WalletConnectOptions } from '@vechain/dapp-kit';

const walletConnectOptions: WalletConnectOptions = {
projectId: '<PROJECT_ID>', // Create your project here: https://cloud.walletconnect.com/sign-up
// Create your project here: https://cloud.walletconnect.com/sign-up
projectId: '<PROJECT_ID>',
metadata: {
name: 'My dApp',
description: 'My dApp description',
url: window.location.origin, // Your app URL
icons: [`${window.location.origin}/images/my-dapp-icon.png`], // Your app Icon
// Your app URL
url: window.location.origin,
// Your app Icon
icons: [`${window.location.origin}/images/my-dapp-icon.png`],
},
};
```

- Initialise the `ConnexVendor`
- Initialise the `DAppKitProvider`

```typescript jsx
import { DAppKitProvider } from '@vechainfoundation/dapp-kit-react';

export const App = (): JSX.Element => {
return (
<>
<DAppKitProvider
key="connex"
nodeOptions={nodeOptions}
persistState={false} // Optional - default: false - If true, account and source will be persisted in local storage
walletConnectOptions={walletConnectOptions}
>
<YourApp />
</DAppKitProvider>
</>
);
};
import { DAppKitProvider } from '@vechain/dapp-kit-react';

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<DAppKitProvider
// REQUIRED: The URL of the node you want to connect to
nodeUrl={'https://testnet.vechain.org/'}
// OPTIONAL: Required if you're not connecting to the main net
genesis={'test'}
// OPTIONAL: Whether or not to persist state in local storage (account, wallet source)
usePersistence={true}
// OPTIONAL: Options to enable wallet connect
walletConnectOptions={walletConnectOptions}
// OPTIONAL: A log level for console logs
logLevel="DEBUG"
>
<App />
</DAppKitProvider>
</React.StrictMode>,
);
```

- Use the hooks provided by the `DAppKitProvider`
Expand Down
8 changes: 6 additions & 2 deletions packages/dapp-kit-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"clean": "rm -rf dist .turbo",
"dev": "tsup src/index.tsx --format esm --watch --dts --external react",
"lint": "tsc --noEmit && eslint src --ext .js,.jsx,.ts,.tsx",
"purge": "yarn clean && rm -rf node_modules"
"purge": "yarn clean && rm -rf node_modules",
"test": "vitest run --coverage"
},
"dependencies": {
"@heroicons/react": "^2.0.18",
Expand All @@ -34,12 +35,15 @@
"valtio": "^1.12.1"
},
"devDependencies": {
"@testing-library/react": "^14.1.2",
"@types/react": "^18.2.28",
"@types/react-dom": "^18.2.13",
"@vechain/repo-config": "https://github.com/vechainfoundation/repo-config#v0.0.1",
"eslint": "*",
"react": "^18.2.0",
"tsup": "*",
"typescript": "*"
"typescript": "*",
"vite": "^4.5.0",
"vitest": "^0.34.6"
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useContext, useMemo } from 'react';
import { ThemeContext } from '../../../provider/ThemeProvider';
import { useMemo } from 'react';
import { useThemeSelector } from '../../../provider/ThemeProvider';
import { createButtonWithModal } from './Wrapped/ConnectModalWithButtonWrapped';

export const ConnectButtonWithModal = () => {
const { theme } = useContext(ThemeContext);
const { theme } = useThemeSelector();

const ModalWithButton = useMemo(() => createButtonWithModal(), []);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const createButtonWithModal = () =>
interface ConnectWalletProps {
onConnectError?: (err: unknown) => void;
onConnected?: (res: ConnectResponse) => void;
isOpen: boolean;
}

/**
Expand All @@ -26,6 +27,7 @@ interface ConnectWalletProps {
export const ConnectWalletModal: React.FC<ConnectWalletProps> = ({
onConnectError,
onConnected,
isOpen,
}) => {
const Modal = useMemo(() => createButtonWithModal(), []);

Expand All @@ -41,5 +43,5 @@ export const ConnectWalletModal: React.FC<ConnectWalletProps> = ({
[onConnectError, onConnected, connect, setSource],
);

return <Modal onSourceClick={onSourceClick} />;
return <Modal onSourceClick={onSourceClick} open={isOpen} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const createButtonWithModal = () =>

interface SelectWalletProps {
onSelected?: (source: WalletSource) => void;
isOpen: boolean;
}

/**
Expand All @@ -23,6 +24,7 @@ interface SelectWalletProps {
*/
export const SelectWalletModal: React.FC<SelectWalletProps> = ({
onSelected,
isOpen,
}) => {
const Modal = useMemo(() => createButtonWithModal(), []);

Expand All @@ -38,5 +40,5 @@ export const SelectWalletModal: React.FC<SelectWalletProps> = ({
[onSelected, setSource],
);

return <Modal onSourceClick={onSourceClick} />;
return <Modal onSourceClick={onSourceClick} open={isOpen} />;
};
6 changes: 3 additions & 3 deletions packages/dapp-kit-react/src/Components/ThemeSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// ThemeSelector.js

import React, { useContext } from 'react';
import React from 'react';
// eslint-disable-next-line import/no-named-as-default
import styled from 'styled-components';
import { ThemeContext } from '../provider/ThemeProvider';
import { useThemeSelector } from '../provider/ThemeProvider';

const Button = styled.button``;

const ThemeSelector = () => {
const { toggleTheme } = useContext(ThemeContext);
const { toggleTheme } = useThemeSelector();

return <Button onClick={toggleTheme}>Toggle Theme</Button>;
};
Expand Down
13 changes: 11 additions & 2 deletions packages/dapp-kit-react/src/DAppKitProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,17 @@ export const DAppKitProvider: React.FC<DAppKitProviderOptions> = ({
);

useEffect(() => {
subscribeKey(connex.wallet.state, 'address', (v) => setAccount(v));
subscribeKey(connex.wallet.state, 'source', (v) => setSource(v));
const addressSub = subscribeKey(connex.wallet.state, 'address', (v) =>
setAccount(v),
);
const sourceSub = subscribeKey(connex.wallet.state, 'source', (v) =>
setSource(v),
);

return () => {
addressSub();
sourceSub();
};
}, [connex.wallet.state]);

const openModal = useCallback(() => {
Expand Down
22 changes: 15 additions & 7 deletions packages/dapp-kit-react/src/provider/ThemeProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface Theme {
mode: ThemeMode;
}

interface ContextProperties {
interface ThemeContextProperties {
theme: Theme;
toggleTheme: () => void;
}
Expand All @@ -18,11 +18,9 @@ const defaultTheme: Theme = {
mode: 'LIGHT',
};

const ThemeContext = createContext<ContextProperties>({
theme: defaultTheme,
// eslint-disable-next-line @typescript-eslint/no-empty-function
toggleTheme: () => {},
});
const ThemeContext = createContext<ThemeContextProperties | undefined>(
undefined,
);

const ThemeProvider = ({ children }: { children: ReactNode }) => {
const [currentTheme, setCurrentTheme] = useState(defaultTheme);
Expand All @@ -46,4 +44,14 @@ const ThemeProvider = ({ children }: { children: ReactNode }) => {
);
};

export { ThemeProvider, ThemeContext };
export const useThemeSelector = (): ThemeContextProperties => {
const context = React.useContext(ThemeContext);

if (context === undefined) {
throw new Error('useThemeSelector must be used within a ThemeProvider');
}

return context;
};

export { ThemeProvider, ThemeContext, type ThemeContextProperties };
18 changes: 18 additions & 0 deletions packages/dapp-kit-react/test/helpers/react-test-helpers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {
ConnectWalletButtonWithModal,
ConnectWalletModal,
DAppKitProvider,
SelectWalletModal,
} from '../../src';
import { ThemeProvider } from '../../src/provider/ThemeProvider';

export const wrapper = ({ children }: { children?: React.ReactNode }) => (
<DAppKitProvider nodeUrl="https://testnet.vechain.org">
<ThemeProvider>
{children}
<ConnectWalletButtonWithModal />
<SelectWalletModal isOpen={false} />
<ConnectWalletModal isOpen={false} />
</ThemeProvider>
</DAppKitProvider>
);
26 changes: 26 additions & 0 deletions packages/dapp-kit-react/test/provider/ThemeProvider.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { renderHook, waitFor } from '@testing-library/react';
import { useContext } from 'react';
import { describe, expect, it } from 'vitest';
import { ThemeContext } from '../../src/provider/ThemeProvider';
import { wrapper } from '../helpers/react-test-helpers';

describe('useThemeContext', () => {
it('should be able to toggle themes', async () => {
const { result } = renderHook(() => useContext(ThemeContext), {
wrapper,
});

expect(result.current).toBeDefined();

expect(result.current?.theme).toEqual({ mode: 'LIGHT' });

result.current?.toggleTheme();

await waitFor(
() => {
expect(result.current?.theme).toEqual({ mode: 'DARK' });
},
{ timeout: 3000 },
);
});
});
28 changes: 28 additions & 0 deletions packages/dapp-kit-react/test/setup/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { vi } from 'vitest';

vi.mock('@walletconnect/modal');

Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(), // deprecated
removeListener: vi.fn(), // deprecated
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
});
global.ResizeObserver = class ResizeObserver {
observe() {
// do nothing
}
unobserve() {
// do nothing
}
disconnect() {
// do nothing
}
};
16 changes: 16 additions & 0 deletions packages/dapp-kit-react/test/useConnex.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { describe, it } from 'vitest';
import { renderHook } from '@testing-library/react';
import { useConnex } from '../src';
import { wrapper } from './helpers/react-test-helpers';

describe('useConnex', () => {
it('connex should get initialised', () => {
const { result } = renderHook(() => useConnex(), { wrapper });

expect(result.current).toBeDefined();

expect(result.current.thor.genesis.id).toBe(
'0x00000000851caf3cfdb6e899cf5958bfb1ac3413d346d43539627e6be7ec1b4a',
);
});
});
18 changes: 18 additions & 0 deletions packages/dapp-kit-react/test/useWallet.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, it } from 'vitest';
import { renderHook, waitFor } from '@testing-library/react';
import { useWallet } from '../src';
import { wrapper } from './helpers/react-test-helpers';

describe('useWallet', () => {
it('should be able to set the source', async () => {
const { result } = renderHook(() => useWallet(), { wrapper });

expect(result.current).toBeDefined();

result.current.setSource('sync2');

await waitFor(() => {
expect(result.current.source).toBe('sync2');
});
});
});
20 changes: 20 additions & 0 deletions packages/dapp-kit-react/test/useWalletModal.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { describe, expect, it } from 'vitest';
import { renderHook, waitFor } from '@testing-library/react';
import { useWalletModal } from '../src';
import { wrapper } from './helpers/react-test-helpers';

describe('useWalletModal', () => {
it('should be able to open the modal', async () => {
const { result } = renderHook(() => useWalletModal(), { wrapper });

expect(result.current).toBeDefined();

result.current.open();

await waitFor(() => {
const modal = window.document.querySelector('vwk-connect-modal');

expect(modal).toBeDefined();
});
});
});
Loading
Loading