Add jest tests

Adds tests for src/core/Utils

Ports translator tests to jest

Adds tests for core/index

Added code coverage report

Adds tests for core/UppySocket

Adds tests to src/core and cleanup of test directory

Switched from import to require as the project supports node v4

Runs tests using Babel for backwards compatibilty

Adds src/core state tests

Adds src/core reset & close tests

Updates stagnant snapshot

Adds tests for preprocessors and postprocessors in src/core/Core.js

Adds tests for uploaders and metadata in src/core/Core

Adds tests for adding and removing files in src/core/Core

Adds test for getFile
This commit is contained in:
Rich Willars 2017-08-31 13:18:57 +01:00
parent ef477ff258
commit 540c02b6b0
23 changed files with 1488 additions and 482 deletions

View file

@ -2,10 +2,12 @@
"extends": "standard",
"env": {
"browser": true,
"node": true
"node": true,
"jest": true
},
"globals": {
"window": true,
"hexo": true
}
},
"plugins": ["jest"]
}

1
.gitignore vendored
View file

@ -6,6 +6,7 @@ node_modules
dist/
lib/
coverage/
website/db.json
website/*.log

View file

@ -31,6 +31,7 @@
"babel-cli": "6.11.4",
"babel-core": "6.13.2",
"babel-eslint": "6.1.2",
"babel-jest": "^20.0.3",
"babel-plugin-add-module-exports": "0.2.1",
"babel-plugin-es6-promise": "1.0.0",
"babel-plugin-transform-object-assign": "6.8.0",
@ -47,12 +48,14 @@
"disc": "1.3.2",
"eslint": "3.16.1",
"eslint-config-standard": "6.2.1",
"eslint-plugin-jest": "^20.0.3",
"eslint-plugin-promise": "3.4.2",
"eslint-plugin-standard": "2.0.1",
"exorcist": "0.4.0",
"fakefile": "0.0.8",
"glob": "7.1.1",
"isomorphic-fetch": "2.2.1",
"jest": "^20.0.4",
"lint-staged": "2.0.2",
"minifyify": "7.3.3",
"mkdirp": "0.5.1",
@ -69,7 +72,6 @@
"sass": "0.5.0",
"selenium-webdriver": "2.53.3",
"tap-spec": "4.1.1",
"tape": "4.6.3",
"uppy-server": "0.0.7",
"watchify": "3.7.0"
},
@ -112,8 +114,9 @@
"start": "npm-run-all --parallel watch start:server web:preview",
"test:acceptance:handleservers": "bin/bootandkill-servers node test/acceptance/index.js",
"test:acceptance": "node test/acceptance/index.js",
"test:unit": "node test/unit/index.js | tap-spec",
"test:unit": "jest --testPathPattern=src --coverage",
"test": "npm run lint && npm run test:unit",
"test:watch": "jest --watch --testPathPattern=src",
"test:serve": "npm-run-all web:build --parallel start:server web:serve",
"test:next-update": "next-update",
"travis:deletecache": "travis cache --delete",

767
src/core/Core.test.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,38 @@
import Core from '../../src/core/index.js'
import russian from '../../src/locales/ru_RU.js'
import english from '../../src/locales/en_US.js'
describe('core/translator', () => {
describe('translate', () => {
it('should translate a string', () => {
const core = new Core({ locale: russian })
expect(core.translator.translate('chooseFile')).toEqual('Выберите файл')
})
})
describe('interpolation', () => {
it('should interpolate a string', () => {
const core = new Core({ locale: english })
expect(
core.translator.translate('youHaveChosen', { fileName: 'img.jpg' })
).toEqual('You have chosen: img.jpg')
})
})
describe('pluralization', () => {
it('should translate a string', () => {
const core = new Core({ locale: russian })
expect(
core.translator.translate('filesChosen', { smart_count: '18' })
).toEqual('Выбрано 18 файлов')
expect(
core.translator.translate('filesChosen', { smart_count: '1' })
).toEqual('Выбран 1 файл')
expect(
core.translator.translate('filesChosen', { smart_count: '0' })
).toEqual('Выбрано 0 файлов')
})
})
})

169
src/core/UppySocket.test.js Normal file
View file

@ -0,0 +1,169 @@
import UppySocket from './UppySocket'
describe('core/uppySocket', () => {
let webSocketConstructorSpy
let webSocketCloseSpy
let webSocketSendSpy
beforeEach(() => {
webSocketConstructorSpy = jest.fn()
webSocketCloseSpy = jest.fn()
webSocketSendSpy = jest.fn()
global.WebSocket = class WebSocket {
constructor (target) {
webSocketConstructorSpy(target)
}
close (args) {
webSocketCloseSpy(args)
}
send (json) {
webSocketSendSpy(json)
}
triggerOpen () {
this.onopen()
}
triggerClose () {
this.onclose()
}
}
})
afterEach(() => {
global.WebSocket = undefined
})
it('should expose a class', () => {
expect(UppySocket.name).toEqual('UppySocket')
expect(
new UppySocket({
target: 'foo'
}) instanceof UppySocket
)
})
it('should setup a new WebSocket', () => {
new UppySocket({ target: 'foo' }) // eslint-disable-line no-new
expect(webSocketConstructorSpy.mock.calls[0][0]).toEqual('foo')
})
it('should send a message via the websocket if the connection is open', () => {
const uppySocket = new UppySocket({ target: 'foo' })
const webSocketInstance = uppySocket.socket
webSocketInstance.triggerOpen()
uppySocket.send('bar', 'boo')
expect(webSocketSendSpy.mock.calls.length).toEqual(1)
expect(webSocketSendSpy.mock.calls[0]).toEqual([
JSON.stringify({ action: 'bar', payload: 'boo' })
])
})
it('should queue the message for the websocket if the connection is not open', () => {
const uppySocket = new UppySocket({ target: 'foo' })
uppySocket.send('bar', 'boo')
expect(uppySocket.queued).toEqual([{ action: 'bar', payload: 'boo' }])
expect(webSocketSendSpy.mock.calls.length).toEqual(0)
})
it('should queue any messages for the websocket if the connection is not open, then send them when the connection is open', () => {
const uppySocket = new UppySocket({ target: 'foo' })
const webSocketInstance = uppySocket.socket
uppySocket.send('bar', 'boo')
uppySocket.send('moo', 'baa')
expect(uppySocket.queued).toEqual([
{ action: 'bar', payload: 'boo' },
{ action: 'moo', payload: 'baa' }
])
expect(webSocketSendSpy.mock.calls.length).toEqual(0)
webSocketInstance.triggerOpen()
expect(uppySocket.queued).toEqual([])
expect(webSocketSendSpy.mock.calls.length).toEqual(2)
expect(webSocketSendSpy.mock.calls[0]).toEqual([
JSON.stringify({ action: 'bar', payload: 'boo' })
])
expect(webSocketSendSpy.mock.calls[1]).toEqual([
JSON.stringify({ action: 'moo', payload: 'baa' })
])
})
it('should start queuing any messages when the websocket connection is closed', () => {
const uppySocket = new UppySocket({ target: 'foo' })
const webSocketInstance = uppySocket.socket
webSocketInstance.triggerOpen()
uppySocket.send('bar', 'boo')
expect(uppySocket.queued).toEqual([])
webSocketInstance.triggerClose()
uppySocket.send('bar', 'boo')
expect(uppySocket.queued).toEqual([{ action: 'bar', payload: 'boo' }])
})
it('should close the websocket when it is force closed', () => {
const uppySocket = new UppySocket({ target: 'foo' })
const webSocketInstance = uppySocket.socket
webSocketInstance.triggerOpen()
uppySocket.close()
expect(webSocketCloseSpy.mock.calls.length).toEqual(1)
})
it('should be able to subscribe to messages received on the websocket', () => {
const uppySocket = new UppySocket({ target: 'foo' })
const webSocketInstance = uppySocket.socket
const emitterListenerMock = jest.fn()
uppySocket.on('hi', emitterListenerMock)
webSocketInstance.triggerOpen()
webSocketInstance.onmessage({
data: JSON.stringify({ action: 'hi', payload: 'ho' })
})
expect(emitterListenerMock.mock.calls).toEqual([
['ho', undefined, undefined, undefined, undefined, undefined]
])
})
it('should be able to emit messages and subscribe to them', () => {
const uppySocket = new UppySocket({ target: 'foo' })
const emitterListenerMock = jest.fn()
uppySocket.on('hi', emitterListenerMock)
uppySocket.emit('hi', 'ho')
uppySocket.emit('hi', 'ho')
uppySocket.emit('hi', 'off to work we go')
expect(emitterListenerMock.mock.calls).toEqual([
['ho', undefined, undefined, undefined, undefined, undefined],
['ho', undefined, undefined, undefined, undefined, undefined],
[
'off to work we go',
undefined,
undefined,
undefined,
undefined,
undefined
]
])
})
it('should be able to subscribe to the first event for a particular action', () => {
const uppySocket = new UppySocket({ target: 'foo' })
const emitterListenerMock = jest.fn()
uppySocket.once('hi', emitterListenerMock)
uppySocket.emit('hi', 'ho')
uppySocket.emit('hi', 'ho')
uppySocket.emit('hi', 'off to work we go')
expect(emitterListenerMock.mock.calls.length).toEqual(1)
expect(emitterListenerMock.mock.calls).toEqual([
['ho', undefined, undefined, undefined, undefined, undefined]
])
})
})

404
src/core/Utils.test.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`src/Core plugins should not be able to add a plugin that has no id 1`] = `"Your plugin must have an id"`;
exports[`src/Core plugins should not be able to add a plugin that has no type 1`] = `"Your plugin must have a type"`;
exports[`src/Core plugins should not be able to add an invalid plugin 1`] = `"Expected a plugin class, but got object. Please verify that the plugin was imported and spelled correctly."`;
exports[`src/Core plugins should prevent the same plugin from being added more than once 1`] = `
"Already found a plugin named 'TestSelector1'.
Tried to use: 'TestSelector1'.
Uppy is currently limited to running one of every plugin.
Share your use case with us over at
https://github.com/transloadit/uppy/issues/
if you want us to reconsider."
`;

10
src/core/index.test.js Normal file
View file

@ -0,0 +1,10 @@
import Core from './index'
describe('core/index', () => {
it('should expose the uppy core as the default export', () => {
expect(typeof Core).toEqual('function')
const core = new Core({})
expect(typeof core).toEqual('object')
expect(core.constructor.name).toEqual('Uppy')
})
})

View file

@ -0,0 +1,34 @@
import Plugin from '../../src/plugins/Plugin.js'
export default class TestSelector1 extends Plugin {
constructor (core, opts) {
super(core, opts)
this.type = 'acquirer'
this.id = 'TestSelector1'
this.name = this.constructor.name
this.mocks = {
run: jest.fn(),
update: jest.fn(),
uninstall: jest.fn()
}
}
run (results) {
this.core.log({
class: this.constructor.name,
method: 'run',
results: results
})
this.mocks.run(results)
return Promise.resolve('success')
}
update (state) {
this.mocks.update(state)
}
uninstall () {
this.mocks.uninstall()
}
}

View file

@ -0,0 +1,34 @@
import Plugin from '../../src/plugins/Plugin.js'
export default class TestSelector2 extends Plugin {
constructor (core, opts) {
super(core, opts)
this.type = 'acquirer'
this.id = 'TestSelector2'
this.name = this.constructor.name
this.mocks = {
run: jest.fn(),
update: jest.fn(),
uninstall: jest.fn()
}
}
run (results) {
this.core.log({
class: this.constructor.name,
method: 'run',
results: results
})
this.mocks.run(results)
return Promise.resolve('success')
}
update (state) {
this.mocks.update(state)
}
uninstall () {
this.mocks.uninstall()
}
}

View file

@ -0,0 +1 @@
export default {}

View file

@ -1,10 +1,9 @@
import Plugin from '../../../src/plugins/Plugin.js'
import Plugin from '../../src/plugins/Plugin.js'
export default class TestSelector1 extends Plugin {
export default class InvalidPluginWithoutName extends Plugin {
constructor (core, opts) {
super(core, opts)
this.type = 'acquirer'
this.id = 'TestSelector1'
this.name = this.constructor.name
}

View file

@ -1,10 +1,9 @@
import Plugin from '../../../src/plugins/Plugin.js'
import Plugin from '../../src/plugins/Plugin.js'
export default class TestSelector2 extends Plugin {
export default class InvalidPluginWithoutType extends Plugin {
constructor (core, opts) {
super(core, opts)
this.type = 'acquirer'
this.id = 'TestSelector2'
this.id = 'InvalidPluginWithoutType'
this.name = this.constructor.name
}

View file

@ -1,122 +0,0 @@
import test from 'tape'
import nock from 'nock'
import Core from '../../src/core/Core'
import Utils from '../../src/core/Utils'
import Google from '../../src/plugins/GoogleDrive'
test('checkAuthentication success', function (t) {
nock('http://localhost:3020')
.get('/google/authorize')
.reply(200, {
isAuthenticated: true
})
var core = new Core()
var GoogleDrive = new Google(core, {host: 'http://localhost:3020'})
GoogleDrive.checkAuthentication()
.then((isAuthed) => {
t.equal(isAuthed, true)
t.end()
})
})
test('checkAuthentication fail', function (t) {
nock('http://localhost:3020')
.get('/google/authorize')
.reply(200, {
isAuthenticated: false
})
var core = new Core()
var GoogleDrive = new Google(core, {host: 'http://localhost:3020'})
GoogleDrive.checkAuthentication()
.then((isAuthed) => {
t.equal(isAuthed, false)
t.end()
})
})
test('getFile: success', function (t) {
nock('http://localhost:3020')
.get('/google/get?fileId=12345')
.reply(201, {
ok: true,
id: '12345'
})
var core = new Core()
var GoogleDrive = new Google(core, {host: 'http://localhost:3020'})
GoogleDrive.getFile('12345')
.then((result) => {
t.equal(result.ok, true)
t.end()
})
})
test('getFile: fileId not a string', function (t) {
var core = new Core()
var GoogleDrive = new Google(core, {host: 'http://localhost:3020'})
var result = GoogleDrive.getFile()
t.equal(result instanceof Error, true)
t.end()
})
test('getFolder: success', function (t) {
nock('http://localhost:3020')
.get('/google/list?dir=root')
.reply(200, {
items: [{
mimeType: 'application/vnd.google-apps.folder'
}, {
mimeType: 'application/vnd.google-apps.spreadsheet'
}, {
mimeType: 'application/vnd.google-apps.spreadsheet'
}, {
mimeType: 'application/vnd.google-apps.folder'
}]
})
var core = new Core()
var GoogleDrive = new Google(core, {host: 'http://localhost:3020'})
GoogleDrive.getFolder('root')
.then((res) => {
const allFolders = Utils.every(res.folders, function (folder) {
return folder.mimeType === 'application/vnd.google-apps.folder'
})
const allFiles = Utils.every(res.files, (file) => {
return file.mimeType !== 'application/vnd.google-apps.folder'
})
t.equal(allFolders && allFiles, true)
t.end()
})
})
test('getFolder: fail', function (t) {
nock('http://localhost:3020')
.get('/google/list')
.reply(500, 'Not authenticated')
var core = new Core()
var GoogleDrive = new Google(core, {host: 'http://localhost:3020'})
GoogleDrive.getFolder('/')
.then((err) => {
t.equal(err instanceof Error, true)
t.end()
})
})

View file

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Jasmine Spec Runner v2.2.0</title>
<link rel="shortcut icon" type="image/png" href="lib/jasmine-2.2.0/jasmine_favicon.png">
<link rel="stylesheet" href="lib/jasmine-2.2.0/jasmine.css">
<script src="lib/jasmine-2.2.0/jasmine.js"></script>
<script src="lib/jasmine-2.2.0/jasmine-html.js"></script>
<script src="lib/jasmine-2.2.0/boot.js"></script>
<script type="text/javascript" src="lib/jasmine-2.2.0/console.js"></script>
<script src="lib/mock-ajax.js"></script>
<script src="lib/jasmine-jsreporter.js"></script>
<!-- include source files here... -->
<script src="../build/uppy.js"></script>
<script>
// Add JSReporter to Jasmine
jasmine.getEnv().addReporter(new jasmine.JSReporter2())
</script>
<!-- include spec files here... -->
<script src="spec/fakeBlob.js"></script>
<script src="spec/upload.js"></script>
</head>
<body>
</body>
</html>

View file

@ -1,186 +0,0 @@
import test from 'tape'
import Core from '../../src/core/Core'
import Transloadit from '../../src/plugins/Transloadit'
test('Transloadit: Throws errors if options are missing', (t) => {
const uppy = new Core()
t.throws(() => {
uppy.use(Transloadit, { params: {} })
}, /The `params\.auth\.key` option is required/)
t.end()
})
test('Transloadit: Accepts a JSON string as `params` for signature authentication', (t) => {
const uppy = new Core()
t.throws(() => {
uppy.use(Transloadit, {
params: 'not json'
})
}, /The `params` option is a malformed JSON string/)
t.throws(() => {
uppy.use(Transloadit, {
params: '{"template_id":"some template id string"}'
})
}, /The `params\.auth\.key` option is required/)
t.doesNotThrow(() => {
uppy.use(Transloadit, {
params: '{"auth":{"key":"some auth key string"},"template_id":"some template id string"}'
})
}, /The `params\.auth\.key` option is required/)
t.end()
})
test('Transloadit: Validates response from getAssemblyOptions()', (t) => {
const uppy = new Core({ autoProceed: false })
uppy.use(Transloadit, {
getAssemblyOptions: (file) => {
t.equal(file.name, 'testfile')
return {
params: '{"some":"json"}'
}
}
})
const data = Buffer.alloc(4000)
data.size = data.byteLength
uppy.addFile({
name: 'testfile',
data
}).then(() => {
uppy.upload().then(t.fail, (err) => {
t.ok(/The `params\.auth\.key` option is required/.test(err.message), 'should reject invalid dynamic params')
t.end()
})
}, t.fail)
})
test('Transloadit: Uses different assemblies for different params', (t) => {
t.plan(5)
const uppy = new Core({ autoProceed: false })
uppy.use(Transloadit, {
getAssemblyOptions: (file) => ({
params: {
auth: { key: 'fake key' },
steps: {
fake_step: { data: file.name }
}
}
})
})
const tl = uppy.getPlugin('Transloadit')
const files = ['a.png', 'b.png', 'c.png', 'd.png']
let i = 0
tl.client.createAssembly = (opts) => {
t.equal(opts.params.steps.fake_step.data, files[i], `assembly for file ${files[i]}`)
i++
// Short-circuit upload
return Promise.reject('short-circuit')
}
const data = Buffer.alloc(10)
data.size = data.byteLength
Promise.all([
uppy.addFile({ name: 'a.png', data }),
uppy.addFile({ name: 'b.png', data }),
uppy.addFile({ name: 'c.png', data }),
uppy.addFile({ name: 'd.png', data })
]).then(() => {
uppy.upload().then(t.fail, () => {
t.equal(i, 4, 'created 4 assemblies')
t.end()
})
}, t.fail)
})
test('Transloadit: Should merge files with same parameters into one assembly', (t) => {
t.plan(3)
const uppy = new Core({ autoProceed: false })
uppy.use(Transloadit, {
getAssemblyOptions: (file) => ({
params: {
auth: { key: 'fake key' },
steps: {
fake_step: { data: file.size }
}
}
})
})
const tl = uppy.getPlugin('Transloadit')
const assemblies = [
{ data: 10, files: ['a.png', 'b.png', 'c.png'] },
{ data: 20, files: ['d.png'] }
]
let i = 0
tl.client.createAssembly = (opts) => {
const assembly = assemblies[i]
t.equal(opts.params.steps.fake_step.data, assembly.data, `assembly for files ${assembly.files.join(',')}`)
i++
// Short-circuit upload
return Promise.reject('short-circuit')
}
const data = Buffer.alloc(10)
data.size = data.byteLength
const data2 = Buffer.alloc(20)
data2.size = data2.byteLength
Promise.all([
uppy.addFile({ name: 'a.png', data }),
uppy.addFile({ name: 'b.png', data }),
uppy.addFile({ name: 'c.png', data }),
uppy.addFile({ name: 'd.png', data: data2 })
]).then(() => {
uppy.upload().then(t.fail, () => {
t.equal(i, 2, 'created two assemblies')
t.end()
})
}, t.fail)
})
test('Does not create an assembly if no files are being uploaded', (t) => {
t.plan(0)
const uppy = new Core()
uppy.use(Transloadit, {
getAssemblyOptions () {
t.fail('should not create assembly')
}
})
uppy.run()
uppy.upload().then(() => {
t.end()
}).catch(t.fail)
})
test('Creates an assembly if no files are being uploaded but `alwaysRunAssembly` is enabled', (t) => {
t.plan(1)
const uppy = new Core()
uppy.use(Transloadit, {
alwaysRunAssembly: true,
getAssemblyOptions (file) {
t.equal(file, null, 'should call getAssemblyOptions with `null`')
return Promise.reject()
}
})
uppy.upload().then(() => {
t.fail('should be rejected by `getAssemblyOptions`')
}, () => {
t.end()
})
})

View file

@ -1,47 +0,0 @@
import test from 'tape'
import Uppy from '../../src/core/Core.js'
import Acquirer1Plugin from './mocks/plugin-acquirer1.js'
// import Acquirer2Plugin from './mocks/plugin-acquirer2.js'
test('core', function (t) {
t.equal(typeof new Uppy(), 'object', '`new Uppy()` should return an `object`')
t.equal(typeof Uppy(), 'object', '`Uppy()` without `new` should also return an `object`')
// t.ok(uppy instanceof Uppy, '`uppy` should be an instance of `Core` core')
t.end()
})
test('use plugins', function (t) {
const uppy = new Uppy()
uppy
.use(Acquirer1Plugin)
t.equal(Object.keys(uppy.plugins).length, 1, 'should add a plugin to the plugins stack')
t.end()
})
test('noDuplicates', function (t) {
const uppyTwoAcquirers = new Uppy()
uppyTwoAcquirers.use(Acquirer1Plugin)
const fn = uppyTwoAcquirers.use.bind(uppyTwoAcquirers, Acquirer1Plugin)
t.throws(fn, new RegExp('Uppy is currently limited to running one of every plugin'))
t.end()
})
// test('autoProceed', function (t) {
// const uppyOneAcquirer = new Core()
// uppyOneAcquirer
// .use(Acquirer1Plugin)
// .run()
//
// const uppyTwoAcquirers = new Core()
// uppyTwoAcquirers
// .use(Acquirer1Plugin)
// .use(Acquirer2Plugin)
// .run()
//
// t.equal(uppyOneAcquirer.opts.autoProceed, true, 'should autoProceed if only one acquirer is used')
// t.equal(uppyTwoAcquirers.opts.autoProceed, false, 'should not autoProceed if more than one acquirer is used')
// t.end()
// })

View file

@ -1,8 +0,0 @@
require('babel-register')
require('isomorphic-fetch')
require('./core.spec.js')
require('./translator.spec.js')
require('./Transloadit.spec.js')
require('./plugins/FileInput.spec.js')
// TODO: enable once getFile error is fixed
// require('./GoogleDrive.spec.js')

View file

@ -1,12 +0,0 @@
import test from 'tape'
import FileInput from '../../../src/plugins/FileInput'
test('FileInput plugin: options', function (t) {
let fi = new FileInput()
t.equal(fi.opts.inputName, 'files[]', 'inputName defaults to files[]')
fi = new FileInput(null, { inputName: 'upload' })
t.equal(fi.opts.inputName, 'upload', 'inputName can be overridden')
t.end()
})

View file

@ -1,17 +0,0 @@
/*
* A fake blob used in tests since not every browser supports Blob yet.
*
* @param {Array} blob
*/
function FakeBlob (blob) {
this._blob = blob
this.size = blob.length
}
FakeBlob.prototype.slice = function (start, end) {
return new FakeBlob(this._blob.slice(start, end))
}
FakeBlob.prototype.stringify = function () {
return this._blob.join('')
}

View file

@ -1,7 +0,0 @@
if (typeof require === 'function') {
require('../lib/mock-ajax.js')
}
// describe('transloadit-client-js', function () {
//
// })

View file

@ -1,40 +0,0 @@
import test from 'tape'
import Core from '../../src/core/index.js'
import russian from '../../src/locales/ru_RU.js'
import english from '../../src/locales/en_US.js'
test('translation', function (t) {
const core = new Core({locale: russian})
t.equal(
core.translator.translate('chooseFile'),
'Выберите файл',
'should return translated string'
)
t.end()
})
test('interpolation', function (t) {
const core = new Core({locale: english})
t.equal(
core.translator.translate('youHaveChosen', {'fileName': 'img.jpg'}),
'You have chosen: img.jpg',
'should return interpolated string'
)
t.end()
})
test('pluralization', function (t) {
const core = new Core({locale: russian})
t.equal(
core.translator.translate('filesChosen', {'smart_count': '18'}),
'Выбрано 18 файлов',
'should return interpolated & pluralized string'
)
t.end()
})