Skip to content

Commit 2f85f75

Browse files
committed
feat: implement unit tests for plugin-next-internalional
1 parent 9a00346 commit 2f85f75

File tree

13 files changed

+1106
-182
lines changed

13 files changed

+1106
-182
lines changed

package-lock.json

Lines changed: 830 additions & 65 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,22 +181,28 @@
181181
"devDependencies": {
182182
"@playwright/test": "^1.55.0",
183183
"@rollup/plugin-node-resolve": "^16.0.1",
184+
"@testing-library/dom": "^10.4.1",
185+
"@testing-library/jest-dom": "^6.9.1",
186+
"@testing-library/react": "^16.3.1",
184187
"@tiny-codes/code-style-all-in-one": "^1.1.5",
185188
"@types/jest": "^29.5.14",
186189
"@types/node": "^22.18.0",
187190
"@vitest/browser": "^4.0.0-beta.17",
191+
"baseline-browser-mapping": "^2.9.11",
188192
"cross-env": "^10.0.0",
189193
"esbuild": "^0.25.9",
190194
"father": "^4.6.3",
191195
"glob": "^11.1.0",
192196
"jest": "^29.7.0",
197+
"jest-environment-jsdom": "^30.2.0",
193198
"jsoneo": "^1.0.3",
194199
"npm-run-all2": "^8.0.4",
195200
"playwright": "^1.55.0",
196201
"rollup": "^4.50.0",
197202
"serve": "^14.2.4",
198203
"shx": "^0.4.0",
199204
"ts-jest": "^29.4.1",
205+
"ts-node": "^10.9.2",
200206
"tsx": "^4.20.5",
201207
"typescript": "^5.9.2",
202208
"vite": "^7.1.9"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function RootLayout({ children }: { children: React.ReactNode }) {
2+
return (
3+
<html>
4+
<body>{children}</body>
5+
</html>
6+
);
7+
}
Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
1-
/** @type {import('jest').Config} */
2-
module.exports = {
3-
preset: 'ts-jest',
4-
testEnvironment: 'node',
5-
testMatch: ['<rootDir>/test/**/*.{spec,test}.{ts,tsx}'],
6-
setupFilesAfterEnv: ['<rootDir>/test/jest.setup.ts'],
7-
collectCoverage: true,
8-
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
9-
moduleNameMapper: {
10-
'^@enum-plus/test/(.*)$': '<rootDir>/../../tslib/test/$1',
11-
'^@enum-plus/(.*)$': '<rootDir>/../../lib/$1',
12-
'^@enum-plus': '<rootDir>/../../lib',
13-
},
14-
coverageReporters: ['json', 'lcov', 'text', 'clover'],
15-
coverageThreshold: {
16-
global: {
17-
branches: 100,
18-
functions: 100,
19-
lines: 100,
20-
statements: 100,
1+
import nextJest from 'next/jest.js';
2+
3+
const createJestConfig = nextJest({
4+
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
5+
dir: './',
6+
});
7+
8+
export default createJestConfig(
9+
/** @type {import('jest').Config} */ {
10+
// preset: 'ts-jest',
11+
testEnvironment: 'jsdom',
12+
testMatch: ['<rootDir>/test/**/*.{spec,test}.{ts,tsx}'],
13+
setupFilesAfterEnv: ['<rootDir>/test/jest.setup.ts'],
14+
moduleNameMapper: {
15+
'^@enum-plus/test/(.*)$': '<rootDir>/../../tslib/test/$1',
16+
'^@enum-plus/(.*)$': '<rootDir>/../../lib/$1',
17+
'^@enum-plus': '<rootDir>/../../lib',
2118
},
22-
},
23-
};
19+
coverageProvider: 'v8',
20+
collectCoverage: true,
21+
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
22+
coveragePathIgnorePatterns: ['src/serverLocalize.ts'],
23+
coverageReporters: ['json', 'lcov', 'text', 'clover'],
24+
coverageThreshold: {
25+
global: {
26+
branches: 100,
27+
functions: 100,
28+
lines: 100,
29+
statements: 100,
30+
},
31+
},
32+
}
33+
);

packages/plugin-next-international/src/PatchedI18nProviderClient.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type FC, useEffect } from 'react';
1+
import { type FC, type ReactNode, useEffect } from 'react';
22
import type { ImportedLocales } from 'international-types';
33
import type { createI18nClient } from 'next-international/client';
44
import { runtime } from './internal';
@@ -15,14 +15,25 @@ export type PatchedI18nProviderClientProps = Parameters<
1515

1616
const PatchedI18nProviderClient: FC<PatchedI18nProviderClientProps> = (props) => {
1717
const { I18n, children, ...rest } = props;
18-
const { useI18n, I18nProviderClient } = I18n;
18+
const { I18nProviderClient } = I18n;
19+
20+
return (
21+
<I18nProviderClient {...rest}>
22+
<Inner I18n={I18n}>{children}</Inner>
23+
</I18nProviderClient>
24+
);
25+
};
26+
27+
function Inner(props: { I18n: ReturnType<typeof createI18nClient<ImportedLocales>>; children: ReactNode }) {
28+
const { I18n, children } = props;
29+
const { useI18n } = I18n;
1930
const t = useI18n();
2031

2132
useEffect(() => {
2233
runtime.t = t;
2334
}, [t]);
2435

25-
return <I18nProviderClient {...rest}>{children}</I18nProviderClient>;
26-
};
36+
return children;
37+
}
2738

2839
export default PatchedI18nProviderClient;

packages/plugin-next-international/src/clientLocalize.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const clientLocalizePlugin: PluginFunc<ClientLocalizePluginOptions> = (pluginOpt
1212
'[enum-plus][plugin-next-international] is not initialized properly. Please make sure to use `PatchedI18nProviderClient` to wrap your application.'
1313
);
1414
}
15-
return t?.(key as string, undefined!);
15+
return t(key as string, undefined!);
1616
};
1717
};
1818

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useEffect } from 'react';
2+
import { runtime } from '../data/initClientInstance';
3+
4+
const AutoChangeLangPage = (props: { children?: React.ReactNode }) => {
5+
const { children } = props;
6+
const { useCurrentLocale, useChangeLocale } = runtime.i18n;
7+
8+
const locale = useCurrentLocale();
9+
const change = useChangeLocale();
10+
11+
useEffect(() => {
12+
// Simulate language change after 1 second
13+
const timer = setTimeout(() => {
14+
const newLocale = locale === 'zh-CN' ? 'en' : 'zh-CN';
15+
change(newLocale);
16+
}, 100);
17+
18+
return () => clearTimeout(timer);
19+
}, [locale]);
20+
21+
return children;
22+
};
23+
24+
export default AutoChangeLangPage;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { AppRouterContext } from 'next/dist/shared/lib/app-router-context.shared-runtime';
2+
import { PathnameContext, PathParamsContext } from 'next/dist/shared/lib/hooks-client-context.shared-runtime';
3+
import { PatchedI18nProviderClient } from '../../src/index';
4+
import { runtime } from '../data/initClientInstance';
5+
6+
const Page = (props: { locale?: string; children?: React.ReactNode }) => {
7+
const { locale = 'en', children } = props;
8+
return (
9+
<AppRouterContext
10+
value={{
11+
push: () => Promise.resolve(),
12+
replace: () => Promise.resolve(),
13+
prefetch: () => Promise.resolve(),
14+
back: () => undefined,
15+
forward: () => undefined,
16+
refresh: () => undefined,
17+
}}
18+
>
19+
<PathParamsContext value={{ locale }}>
20+
<PathnameContext value="/">
21+
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
22+
<PatchedI18nProviderClient locale={locale} I18n={runtime.i18n as any}>
23+
{children}
24+
</PatchedI18nProviderClient>
25+
</PathnameContext>
26+
</PathParamsContext>
27+
</AppRouterContext>
28+
);
29+
};
30+
31+
export default Page;

packages/plugin-next-international/test/data/altLocale.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,23 @@ export const getAltData = (options: {
1111
(acc, key) => {
1212
acc[key as keyof typeof StandardWeekConfig] = {
1313
...StandardWeekConfig[key as keyof typeof StandardWeekConfig],
14-
label: `alternative:${noLocale[key as keyof typeof noLocale]}` as never,
14+
label: `alternative.${noLocale[key as keyof typeof noLocale]}` as never,
1515
} as never;
1616
return acc;
1717
},
1818
{} as {
1919
-readonly [key in keyof typeof StandardWeekConfig]: Omit<(typeof StandardWeekConfig)[key], 'label'> & {
20-
label: `alternative:${(typeof noLocale)[key]}`;
20+
label: `alternative.${(typeof noLocale)[key]}`;
2121
};
2222
}
2323
);
2424
const AltLocales = Object.keys(noLocale).reduce(
2525
(acc, key) => {
26-
acc[`alternative:${noLocale[key as keyof typeof noLocale]}`] =
26+
acc[`alternative.${noLocale[key as keyof typeof noLocale]}`] =
2727
`${(locales as Record<string, string>)[key]} 2` as never;
2828
return acc;
2929
},
30-
{} as { -readonly [key in `alternative:${(typeof noLocale)[keyof typeof noLocale]}`]: string }
30+
{} as { -readonly [key in `alternative.${(typeof noLocale)[keyof typeof noLocale]}`]: string }
3131
);
3232
return {
3333
AltStandardWeekConfig: altStandardWeekConfig,

packages/plugin-next-international/test/data/initClientInstance.ts

Lines changed: 43 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,56 +4,58 @@
44
import { localeCN, localeEN, noLocale } from '@enum-plus/test/data/week-config';
55
import { createI18nClient } from 'next-international/client';
66

7-
let instance: ReturnType<
8-
typeof createI18nClient<{ en: () => Promise<LocalesType>; 'zh-CN': () => Promise<LocalesType> }>
9-
> = undefined!;
10-
11-
export default instance;
7+
export const runtime = {} as {
8+
i18n: ReturnType<typeof createI18nClient<{ en: () => Promise<LocalesType>; 'zh-CN': () => Promise<LocalesType> }>>;
9+
};
1210

1311
export const initClientInstance = () => {
14-
instance = createI18nClient({
12+
runtime.i18n = createI18nClient({
1513
en: async () => ({
16-
...Object.keys(noLocale).reduce(
17-
(acc, key) => {
18-
acc[('default.' + noLocale[key as keyof typeof noLocale]) as `default.${keyof typeof localeEN}`] = (
19-
localeEN as Record<string, string>
20-
)[key] as never;
21-
return acc;
22-
},
23-
{} as { -readonly [key in `default.${keyof typeof localeEN}`]: string }
24-
),
25-
...Object.keys(noLocale).reduce(
26-
(acc, key) => {
27-
acc[('alternative.' + noLocale[key as keyof typeof noLocale]) as `alternative.${keyof typeof localeEN}`] =
28-
`${(localeEN as Record<string, string>)[key]} 2` as never;
29-
return acc;
30-
},
31-
{} as { -readonly [key in `alternative.${keyof typeof localeEN}`]: string }
32-
),
14+
default: {
15+
...Object.keys(noLocale).reduce(
16+
(acc, key) => {
17+
acc[noLocale[key as keyof typeof noLocale] as keyof typeof localeEN] = (localeEN as Record<string, string>)[
18+
key
19+
] as never;
20+
return acc;
21+
},
22+
{} as { -readonly [key in keyof typeof localeEN]: string }
23+
),
24+
...Object.keys(noLocale).reduce(
25+
(acc, key) => {
26+
acc[('alternative.' + noLocale[key as keyof typeof noLocale]) as `alternative.${keyof typeof localeEN}`] =
27+
`${(localeEN as Record<string, string>)[key]} 2` as never;
28+
return acc;
29+
},
30+
{} as { -readonly [key in `alternative.${keyof typeof localeEN}`]: string }
31+
),
32+
},
3333
}),
3434
'zh-CN': async () => ({
35-
...Object.keys(noLocale).reduce(
36-
(acc, key) => {
37-
acc[('default.' + noLocale[key as keyof typeof noLocale]) as `default.${keyof typeof localeEN}`] = (
38-
localeCN as Record<string, string>
39-
)[key] as never;
40-
return acc;
41-
},
42-
{} as { -readonly [key in `default.${keyof typeof localeEN}`]: string }
43-
),
44-
...Object.keys(noLocale).reduce(
45-
(acc, key) => {
46-
acc[('alternative.' + noLocale[key as keyof typeof noLocale]) as `alternative.${keyof typeof localeEN}`] =
47-
`${(localeCN as Record<string, string>)[key]} 2` as never;
48-
return acc;
49-
},
50-
{} as { -readonly [key in `alternative.${keyof typeof localeEN}`]: string }
51-
),
35+
default: {
36+
...Object.keys(noLocale).reduce(
37+
(acc, key) => {
38+
acc[noLocale[key as keyof typeof noLocale] as keyof typeof localeEN] = (localeCN as Record<string, string>)[
39+
key
40+
] as never;
41+
return acc;
42+
},
43+
{} as { -readonly [key in keyof typeof localeEN]: string }
44+
),
45+
...Object.keys(noLocale).reduce(
46+
(acc, key) => {
47+
acc[('alternative.' + noLocale[key as keyof typeof noLocale]) as `alternative.${keyof typeof localeEN}`] =
48+
`${(localeCN as Record<string, string>)[key]} 2` as never;
49+
return acc;
50+
},
51+
{} as { -readonly [key in `alternative.${keyof typeof localeEN}`]: string }
52+
),
53+
},
5254
}),
5355
// eslint-disable-next-line @typescript-eslint/no-explicit-any
5456
}) as any;
5557
};
5658

57-
export type LocalesType = { -readonly [key in `default.${keyof typeof localeEN}`]: string } & {
59+
export type LocalesType = { -readonly [key in keyof typeof localeEN]: string } & {
5860
-readonly [key in `alternative.${keyof typeof localeEN}`]: string;
5961
};

0 commit comments

Comments
 (0)