From 4942591bd8191ce3e78eb49894a1263fc014e4db Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 27 Jun 2026 13:13:16 +0000 Subject: [PATCH] Add unit tests for useI18n composable 9 tests covering: - initialization: valid store returns { t } function - initialization: null/undefined store throws descriptive error - t(): delegates to stringFor with all args (key, args, raw) - t(): passes undefined args/raw when omitted - t(): returns key as fallback when store is null - t(): logs console.warn when store is null in dev mode - t(): no warning when store is null outside dev mode - t(): module-level store is replaced by most recent useI18n call Uses jest.unmock() + jest.resetModules() to bypass the global setup mock in jest.setup.js and test the real implementation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shell/composables/useI18n.test.ts | 122 ++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 shell/composables/useI18n.test.ts diff --git a/shell/composables/useI18n.test.ts b/shell/composables/useI18n.test.ts new file mode 100644 index 00000000000..68de1f6053e --- /dev/null +++ b/shell/composables/useI18n.test.ts @@ -0,0 +1,122 @@ +describe('useI18n', () => { + let useI18nFn: (store: any) => { t: (key: string, args?: unknown, raw?: boolean) => string }; + let mockStringFor: jest.Mock; + + beforeEach(() => { + mockStringFor = jest.fn(); + jest.resetModules(); + jest.unmock('@shell/composables/useI18n'); + jest.mock('@shell/plugins/i18n', () => ({ stringFor: mockStringFor })); + useI18nFn = require('@shell/composables/useI18n').useI18n; + }); + + describe('initialization', () => { + it('returns an object with a t function when called with a valid store', () => { + const mockStore = { getters: {} }; + const result = useI18nFn(mockStore); + + expect(typeof result.t).toStrictEqual('function'); + }); + + it.each([ + { + desc: 'null store', + store: null, + }, + { + desc: 'undefined store', + store: undefined, + }, + ])('throws when called with $desc', ({ store }) => { + expect(() => useI18nFn(store)).toThrow('usI18n() must be called from setup()'); + }); + }); + + describe('t()', () => { + it('delegates to stringFor with store, key, args, and raw when store is set', () => { + const mockStore = { getters: {} }; + + mockStringFor.mockReturnValue('translated value'); + + const { t } = useI18nFn(mockStore); + const result = t('my.translation.key', { count: 5 }, true); + + expect(mockStringFor).toHaveBeenCalledWith(mockStore, 'my.translation.key', { count: 5 }, true); + expect(result).toStrictEqual('translated value'); + }); + + it('passes undefined args and raw to stringFor when not supplied', () => { + const mockStore = { getters: {} }; + + mockStringFor.mockReturnValue('plain'); + + const { t } = useI18nFn(mockStore); + + t('plain.key'); + + expect(mockStringFor).toHaveBeenCalledWith(mockStore, 'plain.key', undefined, undefined); + }); + + it('returns the key when store is null after a failed useI18n call', () => { + const { t } = useI18nFn({ getters: {} }); + + expect(() => useI18nFn(null)).toThrow(); + + const result = t('fallback.key'); + + expect(mockStringFor).not.toHaveBeenCalled(); + expect(result).toStrictEqual('fallback.key'); + }); + + it('logs a console warning when store is null and process.env.dev is set', () => { + const { t } = useI18nFn({ getters: {} }); + + expect(() => useI18nFn(null)).toThrow(); + + const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + const originalDev = process.env.dev; + + process.env.dev = 'true'; + + t('some.key'); + + expect(warnSpy).toHaveBeenCalledWith('useI18n: store not available'); + + process.env.dev = originalDev; + warnSpy.mockRestore(); + }); + + it('does not log a warning when store is null and process.env.dev is not set', () => { + const { t } = useI18nFn({ getters: {} }); + + expect(() => useI18nFn(null)).toThrow(); + + const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + const originalDev = process.env.dev; + + delete process.env.dev; + + t('some.key'); + + expect(warnSpy).not.toHaveBeenCalled(); + + process.env.dev = originalDev; + warnSpy.mockRestore(); + }); + + it('uses the most recently bound store across multiple useI18n calls', () => { + const store1 = { id: 'store1' }; + const store2 = { id: 'store2' }; + + mockStringFor.mockReturnValue('ok'); + + const { t } = useI18nFn(store1); + + useI18nFn(store2); + + t('key'); + + expect(mockStringFor).toHaveBeenCalledWith(store2, 'key', undefined, undefined); + }); + }); +});