diff --git a/build.config.ts b/build.config.ts index 807357a..ddee8e5 100644 --- a/build.config.ts +++ b/build.config.ts @@ -2,6 +2,9 @@ import { defineBuildConfig } from 'unbuild' export default defineBuildConfig({ declaration: true, + replace: { + 'import.meta.vitest': undefined, + }, rollup: { emitCJS: true, inlineDependencies: true, @@ -21,4 +24,18 @@ export default defineBuildConfig({ }, ], externals: ['h3'], + hooks: { + 'rollup:options': (_ctx, options) => { + // deno-lint-ignore no-explicit-any + ;(options.plugins as any).push({ + name: 'workaround-strip-in-source-test', + transform(code: string, _id: string) { + return { + code: code.replace(/import.meta.vitest/g, 'false'), + map: null, + } + }, + }) + }, + }, }) diff --git a/src/env.d.ts b/src/env.d.ts index c8f7bad..d16960f 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -6,3 +6,5 @@ declare namespace NodeJS { LANGUAGE?: string } } + +declare let __TEST__: boolean diff --git a/src/node.test.ts b/src/node.test.ts index 25f0180..769fe0a 100644 --- a/src/node.test.ts +++ b/src/node.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, test } from 'vitest' +import { describe, expect, test } from 'vitest' import supertest from 'supertest' import { getAcceptLanguage, @@ -6,8 +6,6 @@ import { getAcceptLocale, getAcceptLocales, getCookieLocale, - getNavigatorLanguage, - getNavigatorLanguages, setCookieLocale, } from './node.ts' import { createServer, IncomingMessage, OutgoingMessage } from 'node:http' @@ -246,49 +244,3 @@ describe('setCookieLocale', () => { .toThrowError(/locale is invalid: j/) }) }) - -describe('getNavigatorLanguages', () => { - let orgEnv = {} - beforeEach(() => { - orgEnv = process.env - }) - afterEach(() => { - process.env = orgEnv - }) - - test('basic', () => { - process.env.LC_ALL = 'en-GB' - process.env.LC_MESSAGES = 'en-US' - process.env.LANG = 'ja-JP' - process.env.LANGUAGE = 'en' - - const values = [ - 'en-GB', - 'en-US', - 'ja-JP', - 'en', - ] - expect(getNavigatorLanguages()).toEqual(values) - expect(getNavigatorLanguages()).toEqual(values) - }) -}) - -describe('getNavigatorLanguage', () => { - let orgEnv = {} - beforeEach(() => { - orgEnv = process.env - }) - afterEach(() => { - process.env = orgEnv - }) - - test('basic', () => { - process.env.LC_ALL = 'en-GB' - process.env.LC_MESSAGES = 'en-US' - process.env.LANG = 'ja-JP' - process.env.LANGUAGE = 'en' - - expect(getNavigatorLanguage()).toEqual('en-GB') - expect(getNavigatorLanguage()).toEqual('en-GB') - }) -}) diff --git a/src/node.ts b/src/node.ts index bf801af..298f5ad 100644 --- a/src/node.ts +++ b/src/node.ts @@ -220,7 +220,7 @@ let navigatorLanguages: string[] | undefined * @returns {Array} {@link https://datatracker.ietf.org/doc/html/rfc4646#section-2.1 | BCP 47 language tags}, if you can't get the language tag, return an empty array. */ export function getNavigatorLanguages(): readonly string[] { - if (navigatorLanguages) { + if (navigatorLanguages && navigatorLanguages.length > 0) { return navigatorLanguages } @@ -235,7 +235,65 @@ export function getNavigatorLanguages(): readonly string[] { return navigatorLanguages = [...langs].filter(Boolean) } -let navigatorLanguage: string | undefined +/** + * in-source testing for `getNavigatorLanguages` + */ +if (import.meta.vitest) { + const { describe, test, expect, afterEach, vi } = import.meta.vitest + + describe('getNavigatorLanguages', () => { + afterEach(() => { + vi.resetAllMocks() + navigatorLanguages = undefined + }) + + test('basic', () => { + vi.spyOn(process, 'env', 'get').mockReturnValue({ + LC_ALL: 'en-GB', + LC_MESSAGES: 'en-US', + LANG: 'ja-JP', + LANGUAGE: 'en', + }) + + const values = [ + 'en-GB', + 'en-US', + 'ja-JP', + 'en', + ] + expect(getNavigatorLanguages()).toEqual(values) + // cache checking + expect(navigatorLanguages).toEqual(values) + }) + + test('duplicate language', () => { + vi.spyOn(process, 'env', 'get').mockReturnValue({ + LC_ALL: 'en-US', + LC_MESSAGES: 'en-US', + LANG: 'ja-JP', + LANGUAGE: 'ja-JP', + }) + + const values = [ + 'en-US', + 'ja-JP', + ] + expect(getNavigatorLanguages()).toEqual(values) + // cache checking + expect(navigatorLanguages).toEqual(values) + }) + + test('language nothing', () => { + const mockEnv = vi.spyOn(process, 'env', 'get').mockReturnValue({}) + expect(getNavigatorLanguages()).toEqual([]) + expect(navigatorLanguages).toEqual([]) + expect(getNavigatorLanguages()).toEqual([]) + expect(mockEnv).toHaveBeenCalledTimes(2) + }) + }) +} + +let navigatorLanguage = '' /** * get navigator languages @@ -249,3 +307,52 @@ export function getNavigatorLanguage(): string { return navigatorLanguage || (navigatorLanguage = getNavigatorLanguages()[0] || '') } + +/** + * in-source testing for `getNavigatorLanguage` + */ +if (import.meta.vitest) { + const { describe, test, expect, afterEach, vi } = import.meta.vitest + + describe('getNavigatorLanguage', () => { + afterEach(() => { + vi.resetAllMocks() + navigatorLanguages = undefined + navigatorLanguage = '' + }) + + test('basic', () => { + vi.spyOn(process, 'env', 'get').mockReturnValue({ + LC_ALL: 'en-GB', + LC_MESSAGES: 'en-US', + LANG: 'ja-JP', + LANGUAGE: 'en', + }) + + expect(getNavigatorLanguage()).toEqual('en-GB') + // cache checking + expect(navigatorLanguage).toEqual('en-GB') + }) + + test('duplicate language', () => { + vi.spyOn(process, 'env', 'get').mockReturnValue({ + LC_ALL: 'en-US', + LC_MESSAGES: 'en-US', + LANG: 'ja-JP', + LANGUAGE: 'ja-JP', + }) + + expect(getNavigatorLanguage()).toEqual('en-US') + // cache checking + expect(navigatorLanguage).toEqual('en-US') + }) + + test('language nothing', () => { + const mockEnv = vi.spyOn(process, 'env', 'get').mockReturnValue({}) + expect(getNavigatorLanguage()).toBe('') + expect(navigatorLanguage).toBe('') + expect(getNavigatorLanguage()).toBe('') + expect(mockEnv).toHaveBeenCalledTimes(2) + }) + }) +} diff --git a/tsconfig.json b/tsconfig.json index 0ccdee7..9da47a2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -32,7 +32,9 @@ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + "types": [ + "vitest/importMeta" + ], /* Specify type package names to be included without being referenced in a source file. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..a93210a --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + includeSource: ['src/**/*.{js,ts}'], + }, +})