Squash merge feat/expo-app: app-expo, .cursor, workflows, package.json, .husky; remove app-android, app-ios, react-app
This commit is contained in:
148
app-expo/tests/i18n/index.test.ts
Normal file
148
app-expo/tests/i18n/index.test.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
type MockLocale = {
|
||||
languageCode?: string;
|
||||
languageTag?: string;
|
||||
};
|
||||
|
||||
type I18nModule = typeof import('@/i18n');
|
||||
|
||||
async function flushMicrotasks() {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
function loadI18nModule(options?: {
|
||||
locales?: MockLocale[];
|
||||
platformOS?: 'android' | 'ios';
|
||||
}) {
|
||||
let currentLocales = options?.locales ?? [{ languageTag: 'zh-CN' }];
|
||||
const platformOS = options?.platformOS ?? 'ios';
|
||||
const remove = jest.fn();
|
||||
const addEventListener = jest.fn(
|
||||
(_type: string, listener: (state: string) => void) => ({
|
||||
listener,
|
||||
remove,
|
||||
}),
|
||||
);
|
||||
|
||||
jest.resetModules();
|
||||
|
||||
jest.doMock('expo-localization', () => ({
|
||||
getLocales: () => currentLocales,
|
||||
}));
|
||||
|
||||
jest.doMock('react-native', () => {
|
||||
return {
|
||||
AppState: {
|
||||
addEventListener,
|
||||
},
|
||||
Platform: {
|
||||
OS: platformOS,
|
||||
select: (config: Record<string, unknown>) =>
|
||||
config[platformOS] ?? config.default,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const module = require('@/i18n') as I18nModule;
|
||||
|
||||
return {
|
||||
...module,
|
||||
addEventListener,
|
||||
remove,
|
||||
setLocales: (nextLocales: MockLocale[]) => {
|
||||
currentLocales = nextLocales;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe('i18n device synchronization', () => {
|
||||
let consoleInfoSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
consoleInfoSpy = jest.spyOn(console, 'info').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
consoleInfoSpy.mockRestore();
|
||||
jest.resetModules();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('resolves zh when the device locale starts with zh', async () => {
|
||||
const { getDeviceLanguage } = loadI18nModule({
|
||||
locales: [{ languageTag: 'zh-Hans-CN' }],
|
||||
});
|
||||
|
||||
expect(getDeviceLanguage()).toBe('zh');
|
||||
});
|
||||
|
||||
test('falls back to zh when the device locale is missing', async () => {
|
||||
const { getDeviceLanguage } = loadI18nModule({
|
||||
locales: [{}],
|
||||
});
|
||||
|
||||
expect(getDeviceLanguage()).toBe('zh');
|
||||
});
|
||||
|
||||
test('syncLanguageWithDevice updates i18n when the current language differs', async () => {
|
||||
const { default: i18n, syncLanguageWithDevice } = loadI18nModule({
|
||||
locales: [{ languageTag: 'en-US' }],
|
||||
});
|
||||
|
||||
await i18n.changeLanguage('zh');
|
||||
await syncLanguageWithDevice();
|
||||
|
||||
expect(i18n.resolvedLanguage).toBe('en');
|
||||
});
|
||||
|
||||
test('startLocaleSync subscribes on Android and refreshes language when the app becomes active', async () => {
|
||||
const {
|
||||
addEventListener,
|
||||
default: i18n,
|
||||
setLocales,
|
||||
startLocaleSync,
|
||||
} = loadI18nModule({
|
||||
locales: [{ languageTag: 'zh-CN' }],
|
||||
platformOS: 'android',
|
||||
});
|
||||
|
||||
await i18n.changeLanguage('zh');
|
||||
|
||||
const subscription = startLocaleSync();
|
||||
|
||||
expect(addEventListener).toHaveBeenCalledWith(
|
||||
'change',
|
||||
expect.any(Function),
|
||||
);
|
||||
|
||||
const listener = addEventListener.mock.calls[0][1] as (
|
||||
state: string,
|
||||
) => void;
|
||||
|
||||
setLocales([{ languageTag: 'en-US' }]);
|
||||
listener('background');
|
||||
await flushMicrotasks();
|
||||
|
||||
expect(i18n.resolvedLanguage).toBe('zh');
|
||||
|
||||
listener('active');
|
||||
await flushMicrotasks();
|
||||
|
||||
expect(i18n.resolvedLanguage).toBe('en');
|
||||
subscription.remove();
|
||||
});
|
||||
|
||||
test('startLocaleSync is a no-op on iOS', async () => {
|
||||
const { addEventListener, startLocaleSync } = loadI18nModule({
|
||||
locales: [{ languageTag: 'en-US' }],
|
||||
platformOS: 'ios',
|
||||
});
|
||||
|
||||
const subscription = startLocaleSync();
|
||||
|
||||
expect(addEventListener).not.toHaveBeenCalled();
|
||||
expect(subscription.remove).toEqual(expect.any(Function));
|
||||
|
||||
subscription.remove();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user