From e56bfbd3330034e130f8b241e180674c3121f482 Mon Sep 17 00:00:00 2001 From: Christian Kaisermann Date: Tue, 26 Nov 2019 13:05:17 -0300 Subject: [PATCH] =?UTF-8?q?test:=20=F0=9F=92=8D=20add=20tests=20for=20conf?= =?UTF-8?q?ig=20and=20utils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cli/extract.ts | 4 +-- src/client/configs.ts | 64 ++++++++++++++++++++++------------ src/client/includes/utils.ts | 21 ++++-------- src/client/stores/locale.ts | 15 ++++---- src/client/types/index.ts | 7 ++-- test/client/configs.test.ts | 61 ++++++++++++++++++++++++++++++--- test/client/index.test.ts | 66 ------------------------------------ test/client/locale.test.ts | 8 ++--- test/client/utils.test.ts | 65 +++++++++++++++++++++++++++++++++++ test/fixtures/formats.json | 12 +++++++ yarn.lock | 5 +++ 11 files changed, 205 insertions(+), 123 deletions(-) create mode 100644 test/client/utils.test.ts create mode 100644 test/fixtures/formats.json diff --git a/src/cli/extract.ts b/src/cli/extract.ts index dde5868..d4c251f 100644 --- a/src/cli/extract.ts +++ b/src/cli/extract.ts @@ -7,7 +7,7 @@ import { CallExpression, Identifier, } from 'estree' -import resolvePath from 'object-resolve-path' +import delve from 'dlv' import { walk } from 'estree-walker' import { Ast } from 'svelte/types/compiler/interfaces' import { parse } from 'svelte/compiler' @@ -209,7 +209,7 @@ export function extractMessages( } else { if ( overwrite === false && - typeof resolvePath(accumulator, message.meta.id) !== 'undefined' + typeof delve(accumulator, message.meta.id) !== 'undefined' ) { return } diff --git a/src/client/configs.ts b/src/client/configs.ts index 83e575a..f7a7a66 100644 --- a/src/client/configs.ts +++ b/src/client/configs.ts @@ -2,9 +2,20 @@ import { getClientLocale } from './includes/utils' import { ConfigureOptions } from './types' import { $locale } from './stores/locale' -let fallbackLocale: string = null -let loadingDelay = 200 -const formats: any = { +interface Formats { + number: Record + date: Record + time: Record +} + +interface Options { + fallbackLocale: string + initialLocale: string + formats: Formats + loadingDelay: number +} + +export const defaultFormats: Formats = { number: { scientific: { notation: 'scientific' }, engineering: { notation: 'engineering' }, @@ -35,33 +46,44 @@ const formats: any = { }, } -export function getFallbackLocale() { - return fallbackLocale +export const defaultOptions: Options = { + fallbackLocale: null, + initialLocale: null, + loadingDelay: 200, + formats: defaultFormats, } -export function getLoadingDelay() { - return loadingDelay -} +const options: Options = defaultOptions -export function getFormats() { - return formats +export function getOptions() { + return options } export function configure(opts: ConfigureOptions) { - fallbackLocale = opts.fallbackLocale + const fallbackLocale = (options.fallbackLocale = opts.fallbackLocale) - if (opts.initialLocale) { - $locale.set(getClientLocale(opts.initialLocale) || fallbackLocale) - } else { - $locale.set(fallbackLocale) - } + const initialLocale = opts.initialLocale + ? typeof opts.initialLocale === 'string' + ? opts.initialLocale + : getClientLocale(opts.initialLocale) || fallbackLocale + : fallbackLocale + + $locale.set(initialLocale) + options.initialLocale = initialLocale if (opts.formats) { - if ('number' in opts.formats) - Object.assign(formats.number, opts.formats.number) - if ('date' in opts.formats) Object.assign(formats.date, opts.formats.date) - if ('time' in opts.formats) Object.assign(formats.time, opts.formats.time) + if ('number' in opts.formats) { + Object.assign(options.formats.number, opts.formats.number) + } + if ('date' in opts.formats) { + Object.assign(options.formats.date, opts.formats.date) + } + if ('time' in opts.formats) { + Object.assign(options.formats.time, opts.formats.time) + } } - if (loadingDelay != null) loadingDelay = loadingDelay + if (opts.loadingDelay != null) { + options.loadingDelay = opts.loadingDelay + } } diff --git a/src/client/includes/utils.ts b/src/client/includes/utils.ts index 0256125..0594919 100644 --- a/src/client/includes/utils.ts +++ b/src/client/includes/utils.ts @@ -16,15 +16,13 @@ export function lower(str: string) { return str.toLocaleLowerCase() } -const getFromURL = (urlPart: string, key: string) => { - const keyVal = urlPart - .substr(1) - .split('&') - .find(i => i.indexOf(key) === 0) +const getFromQueryString = (queryString: string, key: string) => { + const keyVal = queryString.split('&').find(i => i.indexOf(`${key}=`) === 0) if (keyVal) { return keyVal.split('=').pop() } + return null } const getFirstMatch = (base: string, pattern: RegExp) => { @@ -42,7 +40,8 @@ export const getClientLocale = ({ }: GetClientLocaleOptions) => { let locale - if (typeof window === 'undefined') return fallback + // istanbul ignore next + if (typeof window === 'undefined') return null if (hostname) { locale = getFirstMatch(window.location.hostname, hostname) @@ -61,18 +60,12 @@ export const getClientLocale = ({ } if (search) { - locale = - typeof search === 'string' - ? getFromURL(window.location.search, search) - : getFirstMatch(window.location.search, search) + locale = getFromQueryString(window.location.search.substr(1), search) if (locale) return locale } if (hash) { - locale = - typeof hash === 'string' - ? getFromURL(window.location.hash, hash) - : getFirstMatch(window.location.hash, hash) + locale = getFromQueryString(window.location.hash.substr(1), hash) if (locale) return locale } diff --git a/src/client/stores/locale.ts b/src/client/stores/locale.ts index b4822ee..35cabf2 100644 --- a/src/client/stores/locale.ts +++ b/src/client/stores/locale.ts @@ -1,11 +1,9 @@ import { writable } from 'svelte/store' import { flushQueue, hasLocaleQueue } from '../includes/loaderQueue' -import { getClientLocale } from '../includes/utils' -import { GetClientLocaleOptions } from '../types' +import { getOptions } from '../configs' import { getClosestAvailableLocale } from './dictionary' -import { setFallbackLocale, getFallbackLocale } from '../configs' let current: string const $locale = writable(null) @@ -25,8 +23,10 @@ export function isRelatedLocale(localeA: string, localeB: string) { export function getFallbackOf(locale: string) { const index = locale.lastIndexOf('-') if (index > 0) return locale.slice(0, index) - if (getFallbackLocale() && !isRelatedLocale(locale, getFallbackLocale())) { - return getFallbackLocale() + + const { fallbackLocale } = getOptions() + if (fallbackLocale && !isRelatedLocale(locale, fallbackLocale)) { + return fallbackLocale } return null } @@ -36,8 +36,9 @@ export function getRelatedLocalesOf(locale: string): string[] { .split('-') .map((_, i, arr) => arr.slice(0, i + 1).join('-')) - if (getFallbackLocale() && !isRelatedLocale(locale, getFallbackLocale())) { - return locales.concat(getRelatedLocalesOf(getFallbackLocale())) + const { fallbackLocale } = getOptions() + if (fallbackLocale && !isRelatedLocale(locale, fallbackLocale)) { + return locales.concat(getRelatedLocalesOf(fallbackLocale)) } return locales } diff --git a/src/client/types/index.ts b/src/client/types/index.ts index edf8638..15fdcd3 100644 --- a/src/client/types/index.ts +++ b/src/client/types/index.ts @@ -50,16 +50,15 @@ export interface MessagesLoader { export interface GetClientLocaleOptions { navigator?: boolean - hash?: string | RegExp - search?: string | RegExp - default?: string + hash?: string + search?: string pathname?: RegExp hostname?: RegExp } export interface ConfigureOptions { fallbackLocale: string - initialLocale?: GetClientLocaleOptions + initialLocale?: string | GetClientLocaleOptions formats?: Partial loadingDelay?: number } diff --git a/test/client/configs.test.ts b/test/client/configs.test.ts index a9a1000..20063b3 100644 --- a/test/client/configs.test.ts +++ b/test/client/configs.test.ts @@ -1,14 +1,65 @@ +import { get } from 'svelte/store' + import { - getFallbackLocale, - getLoadingDelay, - getFormats, configure, + getOptions, + defaultOptions, + defaultFormats, } from '../../src/client/configs' +import { $locale } from '../../src/client/stores/locale' + +beforeEach(() => { + configure(defaultOptions) +}) test('configures the fallback locale', () => { - expect(getFallbackLocale()).toBe(null) + expect(getOptions().fallbackLocale).toBe(null) configure({ fallbackLocale: 'en', }) - expect(getFallbackLocale()).toBe('en') + expect(getOptions().fallbackLocale).toBe('en') +}) + +test('configures the initial locale by string', () => { + configure({ + fallbackLocale: 'pt', + initialLocale: 'en', + }) + expect(getOptions().initialLocale).toBe('en') + expect(get($locale)).toBe('en') +}) + +test('configures the initial locale by client heuristics', () => { + delete window.location + window.location = { + search: '?lang=en-US&foo', + pathname: '/', + hostname: 'example.com', + hash: '', + } as any + + configure({ + fallbackLocale: 'pt', + initialLocale: { + search: 'lang', + }, + }) + expect(getOptions().initialLocale).toBe('en-US') + expect(get($locale)).toBe('en-US') +}) + +test('adds custom formats for time, date and number values', () => { + const customFormats = require('../fixtures/formats.json') + + configure({ + fallbackLocale: 'en', + formats: customFormats, + }) + expect(getOptions().formats).toMatchObject(defaultFormats) + expect(getOptions().formats).toMatchObject(customFormats) +}) + +test('sets the minimum delay to set the loading store value', () => { + configure({ fallbackLocale: 'en', loadingDelay: 300 }) + expect(getOptions().loadingDelay).toBe(300) }) diff --git a/test/client/index.test.ts b/test/client/index.test.ts index 64b9eaf..485c311 100644 --- a/test/client/index.test.ts +++ b/test/client/index.test.ts @@ -99,47 +99,6 @@ // }) // describe('utilities', () => { -// describe('get locale', () => { -// beforeEach(() => { -// delete window.location -// window.location = { -// pathname: '/', -// hostname: 'example.com', -// hash: '', -// search: '', -// } as any -// }) - -// it('should get the locale based on the passed hash parameter', () => { -// window.location.hash = '#locale=en-US&lang=pt-BR' -// expect(getClientLocale({ hash: 'lang' })).toBe('pt-BR') -// }) - -// it('should get the locale based on the passed search parameter', () => { -// window.location.search = '?locale=en-US&lang=pt-BR' -// expect(getClientLocale({ search: 'lang' })).toBe('pt-BR') -// }) - -// it('should get the locale based on the navigator language', () => { -// expect(getClientLocale({ navigator: true })).toBe( -// window.navigator.language -// ) -// }) - -// it('should get the default locale', () => { -// expect(getClientLocale({ default: 'pt' })).toBe('pt') -// }) - -// it('should get the fallback locale', () => { -// window.location.pathname = '/en-US/foo/' -// expect(getClientLocale({ pathname: /^\/(.*?)\// })).toBe('en-US') -// }) - -// it('should get the fallback locale', () => { -// window.location.hostname = 'pt.example.com' -// expect(getClientLocale({ hostname: /^(.*?)\./ })).toBe('pt') -// }) -// }) // describe('format utils', () => { // beforeAll(async () => { @@ -184,31 +143,6 @@ // }) // describe('custom formats', () => { -// beforeAll(async () => { -// await locale.set('pt-BR') -// }) - -// it('should have default number custom formats', () => { -// expect(customFormats.number).toMatchObject({ -// scientific: { notation: 'scientific' }, -// engineering: { notation: 'engineering' }, -// compactLong: { notation: 'compact', compactDisplay: 'long' }, -// compactShort: { notation: 'compact', compactDisplay: 'short' }, -// }) -// }) - -// it('should allow to add custom formats', () => { -// addCustomFormats({ -// number: { -// usd: { style: 'currency', currency: 'USD' }, -// }, -// }) - -// expect(customFormats.number).toMatchObject({ -// usd: { style: 'currency', currency: 'USD' }, -// }) -// }) - // it('should format messages with custom formats', async () => { // addCustomFormats({ // number: { diff --git a/test/client/locale.test.ts b/test/client/locale.test.ts index f85eff0..02adafa 100644 --- a/test/client/locale.test.ts +++ b/test/client/locale.test.ts @@ -8,7 +8,7 @@ import { $locale, isRelatedLocale, } from '../../src/client/stores/locale' -import { getFallbackLocale, configure } from '../../src/client/configs' +import { getOptions, configure } from '../../src/client/configs' beforeEach(() => { configure({ fallbackLocale: undefined }) @@ -17,7 +17,7 @@ beforeEach(() => { test('sets and gets the fallback locale', () => { configure({ fallbackLocale: 'en' }) - expect(getFallbackLocale()).toBe('en') + expect(getOptions().fallbackLocale).toBe('en') }) test('checks if a locale is a fallback locale of another locale', () => { @@ -104,7 +104,7 @@ test('gets the current locale', () => { test('if no initial locale is set, set the locale to the fallback', () => { configure({ fallbackLocale: 'pt' }) expect(get($locale)).toBe('pt') - expect(getFallbackLocale()).toBe('pt') + expect(getOptions().fallbackLocale).toBe('pt') }) test('if no initial locale was found, set to the fallback locale', () => { @@ -115,5 +115,5 @@ test('if no initial locale was found, set to the fallback locale', () => { }, }) expect(get($locale)).toBe('en') - expect(getFallbackLocale()).toBe('en') + expect(getOptions().fallbackLocale).toBe('en') }) diff --git a/test/client/utils.test.ts b/test/client/utils.test.ts new file mode 100644 index 0000000..76a87c6 --- /dev/null +++ b/test/client/utils.test.ts @@ -0,0 +1,65 @@ +import { + getClientLocale, + capital, + title, + upper, + lower, +} from '../../src/client/includes/utils' + +describe('getting client locale', () => { + beforeEach(() => { + delete window.location + window.location = { + pathname: '/', + hostname: 'example.com', + hash: '', + search: '', + } as any + }) + + test('gets the locale based on the passed hash parameter', () => { + window.location.hash = '#locale=en-US&lang=pt-BR' + expect(getClientLocale({ hash: 'lang' })).toBe('pt-BR') + }) + + test('gets the locale based on the passed search parameter', () => { + window.location.search = '?locale=en-US&lang=pt-BR' + expect(getClientLocale({ search: 'lang' })).toBe('pt-BR') + }) + + test('gets the locale based on the navigator language', () => { + expect(getClientLocale({ navigator: true })).toBe(window.navigator.language) + }) + + test('gets the locale based on the pathname', () => { + window.location.pathname = '/en-US/foo/' + expect(getClientLocale({ pathname: /^\/(.*?)\// })).toBe('en-US') + }) + + test('gets the locale base on the hostname', () => { + window.location.hostname = 'pt.example.com' + expect(getClientLocale({ hostname: /^(.*?)\./ })).toBe('pt') + }) + + test('returns null if no locale was found', () => { + expect(getClientLocale({ search: 'lang' })).toBe(null) + }) +}) + +describe('string utilities', () => { + test('transforms a string into capital case', () => { + expect(capital('lowercase string')).toMatch('Lowercase string') + }) + + test('transforms a string into title case', () => { + expect(title('lowercase string')).toMatch('Lowercase String') + }) + + test('transforms a string into uppercase', () => { + expect(upper('lowercase string')).toMatch('LOWERCASE STRING') + }) + + test('transforms a string into lowercase', () => { + expect(lower('UPPERCASE STRING')).toMatch('uppercase string') + }) +}) diff --git a/test/fixtures/formats.json b/test/fixtures/formats.json new file mode 100644 index 0000000..d0e1edd --- /dev/null +++ b/test/fixtures/formats.json @@ -0,0 +1,12 @@ +{ + "number": { + "usd": { "style": "currency", "currency": "USD" }, + "brl": { "style": "currency", "currency": "BRL" } + }, + "date": { + "customDate": { "year": "numeric", "era": "short" } + }, + "time": { + "customTime": { "hour": "2-digit", "minute": "2-digit" } + } +} diff --git a/yarn.lock b/yarn.lock index 05086bd..d70a8bc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1928,6 +1928,11 @@ diff-sequences@^24.9.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"