Skip to content

Commit

Permalink
Merge pull request #191 from AElfProject/feature/simplified-init-flow
Browse files Browse the repository at this point in the history
chore: refactor wallet-adapter-react to modular files and simplify interfaces
  • Loading branch information
aelf-lxy authored Nov 20, 2024
2 parents 70d75fb + 902a174 commit 566ed4f
Show file tree
Hide file tree
Showing 20 changed files with 469 additions and 263 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

strategy:
matrix:
package: [utils, base]
package: [utils, base, react]

steps:
- name: Checkout repository
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-badge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
package: [utils, base]
package: [utils, base, react]
steps:
- name: Run frontend CI
uses: AElfProject/[email protected]
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lts/iron
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,8 @@
"engines": {
"node": ">=20"
},
"resolutions": {
"@testing-library/dom": "^10.0.0"
},
"repository": "https://github.com/AElfProject/aelf-web-login"
}
6 changes: 5 additions & 1 deletion packages/react/coverage/coverage-summary.json
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
{"total": {"lines":{"total":0,"covered":0,"skipped":0,"pct":"Unknown"},"statements":{"total":0,"covered":0,"skipped":0,"pct":"Unknown"},"functions":{"total":0,"covered":0,"skipped":0,"pct":"Unknown"},"branches":{"total":0,"covered":0,"skipped":0,"pct":"Unknown"},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":"Unknown"}}
{"total": {"lines":{"total":69,"covered":58,"skipped":0,"pct":84.05},"statements":{"total":73,"covered":61,"skipped":0,"pct":83.56},"functions":{"total":17,"covered":12,"skipped":0,"pct":70.58},"branches":{"total":17,"covered":14,"skipped":0,"pct":82.35},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":"Unknown"}}
,"/Users/aelf/Documents/Projects/aelf/aelf-web-login/packages/react/src/context.tsx": {"lines":{"total":15,"covered":13,"skipped":0,"pct":86.66},"functions":{"total":3,"covered":2,"skipped":0,"pct":66.66},"statements":{"total":16,"covered":14,"skipped":0,"pct":87.5},"branches":{"total":4,"covered":3,"skipped":0,"pct":75}}
,"/Users/aelf/Documents/Projects/aelf/aelf-web-login/packages/react/src/init.ts": {"lines":{"total":20,"covered":18,"skipped":0,"pct":90},"functions":{"total":5,"covered":4,"skipped":0,"pct":80},"statements":{"total":21,"covered":19,"skipped":0,"pct":90.47},"branches":{"total":7,"covered":6,"skipped":0,"pct":85.71}}
,"/Users/aelf/Documents/Projects/aelf/aelf-web-login/packages/react/src/useConnectWallet.tsx": {"lines":{"total":23,"covered":16,"skipped":0,"pct":69.56},"functions":{"total":4,"covered":2,"skipped":0,"pct":50},"statements":{"total":23,"covered":16,"skipped":0,"pct":69.56},"branches":{"total":6,"covered":5,"skipped":0,"pct":83.33}}
,"/Users/aelf/Documents/Projects/aelf/aelf-web-login/packages/react/src/useExternalStore.tsx": {"lines":{"total":11,"covered":11,"skipped":0,"pct":100},"functions":{"total":5,"covered":4,"skipped":0,"pct":80},"statements":{"total":13,"covered":12,"skipped":0,"pct":92.3},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}}
}
24 changes: 23 additions & 1 deletion packages/react/jest-report.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="jest tests" tests="0" failures="0" errors="0" time="0.392">
<testsuites name="jest tests" tests="7" failures="0" errors="0" time="1.605">
<testsuite name="useConnectWallet" errors="0" failures="0" skipped="0" timestamp="2024-11-20T04:06:56" time="0.55" tests="1">
<testcase classname="useConnectWallet should render hook" name="useConnectWallet should render hook" time="0.006">
</testcase>
</testsuite>
<testsuite name="WebLoginProvider" errors="0" failures="0" skipped="0" timestamp="2024-11-20T04:06:57" time="0.172" tests="2">
<testcase classname="WebLoginProvider should render children with provided bridgeAPI" name="WebLoginProvider should render children with provided bridgeAPI" time="0.012">
</testcase>
<testcase classname="WebLoginProvider should return null if no bridgeAPI is provided" name="WebLoginProvider should return null if no bridgeAPI is provided" time="0.001">
</testcase>
</testsuite>
<testsuite name="init" errors="0" failures="0" skipped="0" timestamp="2024-11-20T04:06:57" time="0.163" tests="3">
<testcase classname="init should initialize VConsole if showVconsole is true" name="init should initialize VConsole if showVconsole is true" time="0.054">
</testcase>
<testcase classname="init should not initialize VConsole if showVconsole is false" name="init should not initialize VConsole if showVconsole is false" time="0.001">
</testcase>
<testcase classname="init should call initBridge with the given options" name="init should call initBridge with the given options" time="0.001">
</testcase>
</testsuite>
<testsuite name="useExternalStore" errors="0" failures="0" skipped="0" timestamp="2024-11-20T04:06:57" time="0.15" tests="1">
<testcase classname="useExternalStore should render hook" name="useExternalStore should render hook" time="0.005">
</testcase>
</testsuite>
</testsuites>
36 changes: 30 additions & 6 deletions packages/react/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,24 @@
* https://jestjs.io/docs/configuration
*/

import type { Config } from 'jest';

const config: Config = {
import {
createDefaultEsmPreset,
pathsToModuleNameMapper,
type JestConfigWithTsJest,
type TsJestTransformerOptions,
} from 'ts-jest';
import { compilerOptions } from '../../tsconfig.base.json';

const presetConfig = createDefaultEsmPreset({
tsconfig: compilerOptions as TsJestTransformerOptions['tsconfig'],
diagnostics: false, // important - to make ts-jest ignore type-checking. must set here
isolatedModules: true, // perf
});

console.log(Object.values(presetConfig.transform)[0][1].tsconfig);

const config: JestConfigWithTsJest = {
...presetConfig,
// All imported modules in your tests should be mocked automatically
// automock: false,

Expand Down Expand Up @@ -117,7 +132,7 @@ const config: Config = {
// restoreMocks: false,

// The root directory that Jest should scan for tests and modules within
// rootDir: undefined,
// rootDir: './',

// A list of paths to directories that Jest should use to search for files in
// roots: [
Expand Down Expand Up @@ -167,10 +182,14 @@ const config: Config = {

// A map from regular expressions to paths to transformers
transform: {
'^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
...presetConfig.transform,
'^.+\\.(js|jsx)$': 'babel-jest',
},
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [],
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
transformIgnorePatterns: [
'.pnpm/node_modules/(?!((jest-)?react-native(-.*)?|@react-native(-community)?|victory(-.*)?|uuid)|react-navigation|@shopify/react-native-skia|@react-navigation/.*/)',
],

// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
Expand All @@ -183,6 +202,11 @@ const config: Config = {

// Whether to use watchman for file crawling
// watchman: true,
roots: ['<rootDir>'],
modulePaths: ['./', compilerOptions.baseUrl],
moduleDirectories: ['node_modules', 'src'],
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>../../' }),
preset: 'ts-jest',
};

export default config;
8 changes: 5 additions & 3 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@
"scripts": {
"dev": "father dev",
"build": "father build",
"test": "jest --config=jest.config.ts --detectOpenHandles",
"test:coverage": "jest --config=jest.config.ts --coverage --detectOpenHandles"
"test": "NODE_OPTIONS='$NODE_OPTIONS --experimental-vm-modules' jest",
"test:coverage": "pnpm test -- --coverage",
"test:debug": "pnpm test -- --detectOpenHandles"
},
"dependencies": {
"@aelf-web-login/wallet-adapter-base": "workspace:*",
Expand All @@ -40,7 +41,7 @@
"@babel/preset-typescript": "^7.24.7",
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@types/jest": "^29.5.12",
"@types/jest": "^29.5.13",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"babel-jest": "^29.7.0",
Expand All @@ -49,6 +50,7 @@
"jest-junit": "^16.0.0",
"node-fetch": "^3.3.2",
"react-test-renderer": "^18.3.1",
"ts-jest": "^29.2.5",
"typescript": "^5.3.3"
},
"publishConfig": {
Expand Down
27 changes: 27 additions & 0 deletions packages/react/src/__tests__/context.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { render, screen } from '@testing-library/react';
import { WebLoginProvider } from '../context';

const mockBridgeAPI = {
getSignIn: jest.fn((children) => children),
};

describe('WebLoginProvider', () => {
it('should render children with provided bridgeAPI', () => {
render(
<WebLoginProvider bridgeAPI={mockBridgeAPI}>
<div>Test Child</div>
</WebLoginProvider>,
);

expect(screen.getByText('Test Child')).toBeInTheDocument();
});

it('should return null if no bridgeAPI is provided', () => {
const { container } = render(
<WebLoginProvider bridgeAPI={null}>
<div>Test Child</div>
</WebLoginProvider>,
);
expect(container.firstChild).toBeNull();
});
});
7 changes: 4 additions & 3 deletions packages/react/src/__tests__/init.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// import { initBridge, IConfigProps, IBridgeAPI } from '@aelf-web-login/wallet-adapter-bridge';
import VConsole from 'vconsole';
import { init } from '../index';

jest.mock('vconsole');
Expand All @@ -8,14 +7,16 @@ jest.mock('@aelf-web-login/wallet-adapter-bridge', () => ({
}));

describe('init', () => {
it('should initialize VConsole if showVconsole is true', () => {
it('should initialize VConsole if showVconsole is true', async () => {
const options = { baseConfig: { showVconsole: true } };
init(options as any);
const VConsole = await import('vconsole');
expect(VConsole).toHaveBeenCalled();
});
it('should not initialize VConsole if showVconsole is false', () => {
it('should not initialize VConsole if showVconsole is false', async () => {
const options = { baseConfig: { showVconsole: false } };
init(options as any);
const VConsole = await import('vconsole');
expect(VConsole).not.toHaveBeenCalled();
});

Expand Down
26 changes: 0 additions & 26 deletions packages/react/src/__tests__/provider.test.ts

This file was deleted.

29 changes: 29 additions & 0 deletions packages/react/src/__tests__/useConnectWallet.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { render } from '@testing-library/react';
import useConnectWallet from '../useConnectWallet';
import { WebLoginProvider } from '../context';
import { IBridgeAPI } from '@aelf-web-login/wallet-adapter-bridge';

const mockBridgeAPI: IBridgeAPI = {
getSignIn: jest.fn((children) => children),
store: {
getState: () => null,
subscribe: () => null,
},
instance: {
connect: () => null,
},
};

const Comp = () => {
useConnectWallet();
return null;
};
describe('useConnectWallet', () => {
it('should render hook', () => {
render(
<WebLoginProvider bridgeAPI={mockBridgeAPI}>
<Comp />
</WebLoginProvider>,
);
});
});
26 changes: 26 additions & 0 deletions packages/react/src/__tests__/useExternalStore.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { render } from '@testing-library/react';
import useExternalStore from '../useExternalStore';
import { WebLoginProvider } from '../context';
import { IBridgeAPI } from '@aelf-web-login/wallet-adapter-bridge';

const mockBridgeAPI: Partial<IBridgeAPI> = {
getSignIn: jest.fn((children) => children),
store: {
getState: () => null,
subscribe: () => null,
},
};

const Comp = () => {
useExternalStore();
return null;
};
describe('useExternalStore', () => {
it('should render hook', () => {
render(
<WebLoginProvider bridgeAPI={mockBridgeAPI}>
<Comp />
</WebLoginProvider>,
);
});
});
39 changes: 39 additions & 0 deletions packages/react/src/context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { IBridgeAPI } from '@aelf-web-login/wallet-adapter-bridge';
import React from 'react';

const HOOK_ERROR_MESSAGE =
'Must call the provided initialization method`init` method before using hooks.';

const WebLoginContext: React.Context<IBridgeAPI> = React.createContext<IBridgeAPI>(
{} as IBridgeAPI,
);

export default WebLoginContext;

export function useWebLoginContext(): IBridgeAPI {
const bridgeAPI = React.useContext(WebLoginContext);

if (!bridgeAPI) {
throw new Error(HOOK_ERROR_MESSAGE);
}

return bridgeAPI;
}

export interface IWebLoginProviderProps {
children: React.ReactNode;
bridgeAPI: IBridgeAPI;
}

export const WebLoginProvider: React.FC<IWebLoginProviderProps> = ({ children, bridgeAPI }) => {
const { getSignIn } = bridgeAPI ?? {
getSignIn: () => null,
};

if (!bridgeAPI) {
return null;
}
return (
<WebLoginContext.Provider value={bridgeAPI}>{getSignIn(children)}</WebLoginContext.Provider>
);
};
Loading

0 comments on commit 566ed4f

Please sign in to comment.