test: 💍 add tests for config and utils

This commit is contained in:
Christian Kaisermann 2019-11-26 13:05:17 -03:00
parent 817adb6e4e
commit e56bfbd333
11 changed files with 205 additions and 123 deletions

View File

@ -7,7 +7,7 @@ import {
CallExpression, CallExpression,
Identifier, Identifier,
} from 'estree' } from 'estree'
import resolvePath from 'object-resolve-path' import delve from 'dlv'
import { walk } from 'estree-walker' import { walk } from 'estree-walker'
import { Ast } from 'svelte/types/compiler/interfaces' import { Ast } from 'svelte/types/compiler/interfaces'
import { parse } from 'svelte/compiler' import { parse } from 'svelte/compiler'
@ -209,7 +209,7 @@ export function extractMessages(
} else { } else {
if ( if (
overwrite === false && overwrite === false &&
typeof resolvePath(accumulator, message.meta.id) !== 'undefined' typeof delve(accumulator, message.meta.id) !== 'undefined'
) { ) {
return return
} }

View File

@ -2,9 +2,20 @@ import { getClientLocale } from './includes/utils'
import { ConfigureOptions } from './types' import { ConfigureOptions } from './types'
import { $locale } from './stores/locale' import { $locale } from './stores/locale'
let fallbackLocale: string = null interface Formats {
let loadingDelay = 200 number: Record<string, any>
const formats: any = { date: Record<string, any>
time: Record<string, any>
}
interface Options {
fallbackLocale: string
initialLocale: string
formats: Formats
loadingDelay: number
}
export const defaultFormats: Formats = {
number: { number: {
scientific: { notation: 'scientific' }, scientific: { notation: 'scientific' },
engineering: { notation: 'engineering' }, engineering: { notation: 'engineering' },
@ -35,33 +46,44 @@ const formats: any = {
}, },
} }
export function getFallbackLocale() { export const defaultOptions: Options = {
return fallbackLocale fallbackLocale: null,
initialLocale: null,
loadingDelay: 200,
formats: defaultFormats,
} }
export function getLoadingDelay() { const options: Options = defaultOptions
return loadingDelay
}
export function getFormats() { export function getOptions() {
return formats return options
} }
export function configure(opts: ConfigureOptions) { export function configure(opts: ConfigureOptions) {
fallbackLocale = opts.fallbackLocale const fallbackLocale = (options.fallbackLocale = opts.fallbackLocale)
if (opts.initialLocale) { const initialLocale = opts.initialLocale
$locale.set(getClientLocale(opts.initialLocale) || fallbackLocale) ? typeof opts.initialLocale === 'string'
} else { ? opts.initialLocale
$locale.set(fallbackLocale) : getClientLocale(opts.initialLocale) || fallbackLocale
} : fallbackLocale
$locale.set(initialLocale)
options.initialLocale = initialLocale
if (opts.formats) { if (opts.formats) {
if ('number' in opts.formats) if ('number' in opts.formats) {
Object.assign(formats.number, opts.formats.number) Object.assign(options.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 ('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
}
} }

View File

@ -16,15 +16,13 @@ export function lower(str: string) {
return str.toLocaleLowerCase() return str.toLocaleLowerCase()
} }
const getFromURL = (urlPart: string, key: string) => { const getFromQueryString = (queryString: string, key: string) => {
const keyVal = urlPart const keyVal = queryString.split('&').find(i => i.indexOf(`${key}=`) === 0)
.substr(1)
.split('&')
.find(i => i.indexOf(key) === 0)
if (keyVal) { if (keyVal) {
return keyVal.split('=').pop() return keyVal.split('=').pop()
} }
return null
} }
const getFirstMatch = (base: string, pattern: RegExp) => { const getFirstMatch = (base: string, pattern: RegExp) => {
@ -42,7 +40,8 @@ export const getClientLocale = ({
}: GetClientLocaleOptions) => { }: GetClientLocaleOptions) => {
let locale let locale
if (typeof window === 'undefined') return fallback // istanbul ignore next
if (typeof window === 'undefined') return null
if (hostname) { if (hostname) {
locale = getFirstMatch(window.location.hostname, hostname) locale = getFirstMatch(window.location.hostname, hostname)
@ -61,18 +60,12 @@ export const getClientLocale = ({
} }
if (search) { if (search) {
locale = locale = getFromQueryString(window.location.search.substr(1), search)
typeof search === 'string'
? getFromURL(window.location.search, search)
: getFirstMatch(window.location.search, search)
if (locale) return locale if (locale) return locale
} }
if (hash) { if (hash) {
locale = locale = getFromQueryString(window.location.hash.substr(1), hash)
typeof hash === 'string'
? getFromURL(window.location.hash, hash)
: getFirstMatch(window.location.hash, hash)
if (locale) return locale if (locale) return locale
} }

View File

@ -1,11 +1,9 @@
import { writable } from 'svelte/store' import { writable } from 'svelte/store'
import { flushQueue, hasLocaleQueue } from '../includes/loaderQueue' import { flushQueue, hasLocaleQueue } from '../includes/loaderQueue'
import { getClientLocale } from '../includes/utils' import { getOptions } from '../configs'
import { GetClientLocaleOptions } from '../types'
import { getClosestAvailableLocale } from './dictionary' import { getClosestAvailableLocale } from './dictionary'
import { setFallbackLocale, getFallbackLocale } from '../configs'
let current: string let current: string
const $locale = writable(null) const $locale = writable(null)
@ -25,8 +23,10 @@ export function isRelatedLocale(localeA: string, localeB: string) {
export function getFallbackOf(locale: string) { export function getFallbackOf(locale: string) {
const index = locale.lastIndexOf('-') const index = locale.lastIndexOf('-')
if (index > 0) return locale.slice(0, index) 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 return null
} }
@ -36,8 +36,9 @@ export function getRelatedLocalesOf(locale: string): string[] {
.split('-') .split('-')
.map((_, i, arr) => arr.slice(0, i + 1).join('-')) .map((_, i, arr) => arr.slice(0, i + 1).join('-'))
if (getFallbackLocale() && !isRelatedLocale(locale, getFallbackLocale())) { const { fallbackLocale } = getOptions()
return locales.concat(getRelatedLocalesOf(getFallbackLocale())) if (fallbackLocale && !isRelatedLocale(locale, fallbackLocale)) {
return locales.concat(getRelatedLocalesOf(fallbackLocale))
} }
return locales return locales
} }

View File

@ -50,16 +50,15 @@ export interface MessagesLoader {
export interface GetClientLocaleOptions { export interface GetClientLocaleOptions {
navigator?: boolean navigator?: boolean
hash?: string | RegExp hash?: string
search?: string | RegExp search?: string
default?: string
pathname?: RegExp pathname?: RegExp
hostname?: RegExp hostname?: RegExp
} }
export interface ConfigureOptions { export interface ConfigureOptions {
fallbackLocale: string fallbackLocale: string
initialLocale?: GetClientLocaleOptions initialLocale?: string | GetClientLocaleOptions
formats?: Partial<Formats> formats?: Partial<Formats>
loadingDelay?: number loadingDelay?: number
} }

View File

@ -1,14 +1,65 @@
import { get } from 'svelte/store'
import { import {
getFallbackLocale,
getLoadingDelay,
getFormats,
configure, configure,
getOptions,
defaultOptions,
defaultFormats,
} from '../../src/client/configs' } from '../../src/client/configs'
import { $locale } from '../../src/client/stores/locale'
beforeEach(() => {
configure(defaultOptions)
})
test('configures the fallback locale', () => { test('configures the fallback locale', () => {
expect(getFallbackLocale()).toBe(null) expect(getOptions().fallbackLocale).toBe(null)
configure({ configure({
fallbackLocale: 'en', 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)
}) })

View File

@ -99,47 +99,6 @@
// }) // })
// describe('utilities', () => { // 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', () => { // describe('format utils', () => {
// beforeAll(async () => { // beforeAll(async () => {
@ -184,31 +143,6 @@
// }) // })
// describe('custom formats', () => { // 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 () => { // it('should format messages with custom formats', async () => {
// addCustomFormats({ // addCustomFormats({
// number: { // number: {

View File

@ -8,7 +8,7 @@ import {
$locale, $locale,
isRelatedLocale, isRelatedLocale,
} from '../../src/client/stores/locale' } from '../../src/client/stores/locale'
import { getFallbackLocale, configure } from '../../src/client/configs' import { getOptions, configure } from '../../src/client/configs'
beforeEach(() => { beforeEach(() => {
configure({ fallbackLocale: undefined }) configure({ fallbackLocale: undefined })
@ -17,7 +17,7 @@ beforeEach(() => {
test('sets and gets the fallback locale', () => { test('sets and gets the fallback locale', () => {
configure({ fallbackLocale: 'en' }) configure({ fallbackLocale: 'en' })
expect(getFallbackLocale()).toBe('en') expect(getOptions().fallbackLocale).toBe('en')
}) })
test('checks if a locale is a fallback locale of another locale', () => { 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', () => { test('if no initial locale is set, set the locale to the fallback', () => {
configure({ fallbackLocale: 'pt' }) configure({ fallbackLocale: 'pt' })
expect(get($locale)).toBe('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', () => { 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(get($locale)).toBe('en')
expect(getFallbackLocale()).toBe('en') expect(getOptions().fallbackLocale).toBe('en')
}) })

65
test/client/utils.test.ts Normal file
View File

@ -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')
})
})

12
test/fixtures/formats.json vendored Normal file
View File

@ -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" }
}
}

View File

@ -1928,6 +1928,11 @@ diff-sequences@^24.9.0:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5"
integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== 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: doctrine@1.5.0:
version "1.5.0" version "1.5.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"