UNPKG

13.5 kBJavaScriptView Raw
1import test from 'ava'
2import Chance from '../chance.js'
3import _ from 'lodash'
4
5const chance = new Chance()
6
7// chance.capitalize()
8test('capitalize() works as expected', t => {
9 _.times(1000, () => {
10 let word = chance.capitalize(chance.word())
11 t.true(_.isString(word))
12 t.true(/[A-Z]/.test(word))
13 })
14})
15
16// chance.n()
17test('n() gives an array of n terms for the given function', t => {
18 let arr = chance.n(chance.email, 25, { domain: 'example.com' })
19 t.true(_.isArray(arr))
20 t.is(arr.length, 25)
21 arr.map((email) => {
22 t.true(/example\.com$/.test(email))
23 })
24})
25
26test('n() gives an array of 1 when n not given', t => {
27 let arr = chance.n(chance.email)
28 t.true(_.isArray(arr))
29 t.is(arr.length, 1)
30})
31
32test('n() throws when first argument is not a function', t => {
33 let testFns = [
34 () => chance.n(chance.character({ pool: 'abcde' }), 10),
35 () => chance.n('foo', 10),
36 () => chance.n({}, 10),
37 () => chance.n(null, 10),
38 () => chance.n(undefined, 10),
39 () => chance.n(21, 10),
40 ]
41 testFns.map((fn) => {
42 t.throws(fn, 'Chance: The first argument must be a function.')
43 })
44})
45
46test('n() gives an empty array when n is set to 0', t => {
47 let arr = chance.n(chance.email, 0)
48 t.true(_.isArray(arr))
49 t.is(arr.length, 0)
50})
51
52// chance.pick()
53test('pick() returns a single element when called without a count argument', t => {
54 let arr = ['a', 'b', 'c', 'd']
55 _.times(1000, () => {
56 let picked = chance.pick(arr)
57 t.is(picked.length, 1)
58 })
59})
60
61test('pick() returns a multiple elements when called with a count argument', t => {
62 let arr = ['a', 'b', 'c', 'd']
63 _.times(1000, () => {
64 let picked = chance.pick(arr, 3)
65 t.is(picked.length, 3)
66 })
67})
68
69test('pick() does not destroy the original array', t => {
70 let arr = ['a', 'b', 'c', 'd', 'e', 'f'];
71 _.times(1000, () => {
72 let cloned = _.clone(arr)
73 let picked = chance.pick(cloned, 3)
74 t.is(cloned.length, 6)
75 t.deepEqual(arr, cloned)
76 })
77})
78
79test('pick() throws if zero elements in array', t => {
80 const fn = () => chance.pick([])
81 t.throws(fn, 'Chance: Cannot pick() from an empty array')
82})
83
84// chance.pickone()
85test('pickone() returns a single element', t => {
86 let arr = ['a', 'b', 'c', 'd']
87 _.times(1000, () => {
88 let picked = chance.pickone(arr)
89 t.is(picked.length, 1)
90 t.false(_.isArray(picked))
91 })
92})
93
94test('pickone() throws if zero elements in array', t => {
95 const fn = () => chance.pickone([])
96 t.throws(fn, 'Chance: Cannot pickone() from an empty array')
97})
98
99// chance.pickset()
100test('pickset() returns empty array when count 0', t => {
101 let arr = ['a', 'b', 'c', 'd']
102 _.times(1000, () => {
103 let picked = chance.pickset(arr, 0)
104 t.is(picked.length, 0)
105 t.true(_.isArray(picked))
106 })
107})
108
109test('pickset() throws if zero elements in array', t => {
110 const fn = () => chance.pickset([])
111 t.throws(fn, 'Chance: Cannot pickset() from an empty array')
112})
113
114test('pickset() returns single element array if no count provided', t => {
115 let arr = ['a', 'b', 'c', 'd']
116 _.times(1000, () => {
117 let picked = chance.pickset(arr)
118 t.is(picked.length, 1)
119 t.true(_.isArray(picked))
120 })
121})
122
123test('pickset() throws if count is not positive number', t => {
124 let arr = ['a', 'b', 'c', 'd']
125 const fn = () => chance.pickset(arr, -1)
126 t.throws(fn, 'Chance: Count must be a positive number')
127})
128
129test('pickset() returns single element array when called with count of 1', t => {
130 let arr = ['a', 'b', 'c', 'd']
131 _.times(1000, () => {
132 let picked = chance.pickset(arr, 1)
133 t.is(picked.length, 1)
134 t.true(_.isArray(picked))
135 })
136})
137
138test('pickset() returns multiple elements when called with count > 1', t => {
139 let arr = ['a', 'b', 'c', 'd']
140 _.times(1000, () => {
141 let picked = chance.pickset(arr, 3)
142 t.is(picked.length, 3)
143 t.true(_.isArray(picked))
144 })
145})
146
147test('pickset() does not destroy the original array', t => {
148 let arr = ['a', 'b', 'c', 'd', 'e', 'f'];
149 _.times(1000, () => {
150 let cloned = _.clone(arr)
151 let picked = chance.pickset(cloned, 3)
152 t.is(cloned.length, 6)
153 t.deepEqual(arr, cloned)
154 })
155})
156
157test('pickset() returns unique values', t => {
158 let arr = ['a', 'b', 'c', 'd']
159 _.times(1000, () => {
160 let picked = chance.pickset(arr, 4)
161 t.not(picked.indexOf('a'), -1)
162 t.not(picked.indexOf('b'), -1)
163 t.not(picked.indexOf('c'), -1)
164 t.not(picked.indexOf('d'), -1)
165 })
166})
167
168// chance.shuffle()
169test('shuffle() returns an array of the same size', t => {
170 let arr = ['a', 'b', 'c', 'd', 'e']
171 _.times(1000, () => {
172 let shuffled = chance.shuffle(_.clone(arr))
173 t.is(shuffled.length, 5)
174 t.not(shuffled.indexOf('a'), -1)
175 })
176})
177
178test('shuffle() returns a well shuffled array', t => {
179 // See http://vq.io/1lEhbim checking it isn't doing that!
180 let arr = ['a', 'b', 'c', 'd', 'e'];
181 let positions = {
182 a: [0, 0, 0, 0, 0],
183 b: [0, 0, 0, 0, 0],
184 c: [0, 0, 0, 0, 0],
185 d: [0, 0, 0, 0, 0],
186 e: [0, 0, 0, 0, 0]
187 };
188
189 let shuffled = _.clone(arr)
190 _.times(10000, () => {
191 shuffled = chance.shuffle(shuffled)
192 shuffled.map((item, index) => {
193 // Accumulate the position of the item each time
194 positions[item][index]++
195 })
196 })
197
198 Object.keys(positions).map((index) => {
199 let position = positions[index]
200 position.map((item) => {
201 // This should be around 20% give or take a bit since there are
202 // 5 elements and they should be evenly distributed
203 t.true(item >= 1800)
204 t.true(item <= 2200)
205 })
206 })
207})
208
209test('shuffle() does not destroy original array', t => {
210 let arr = ['a', 'b', 'c', 'd', 'e']
211 _.times(1000, () => {
212 let cloned = _.clone(arr)
213 let shuffled = chance.shuffle(cloned)
214 t.is(shuffled.length, 5)
215 t.deepEqual(arr, cloned)
216 })
217})
218
219// chance.unique()
220test('unique() gives a unique array of the selected function', t => {
221 _.times(500, () => {
222 let arr = chance.unique(chance.character, 25, { pool: "abcdefghijklmnopqrstuvwxyz" })
223 t.true(_.isArray(arr))
224 t.is(_.uniq(arr).length, 25)
225 })
226})
227
228test('unique() works properly with options', t => {
229 _.times(500, () => {
230 let arr = chance.unique(chance.date, 20, { year: 2016 })
231 t.true(_.isArray(arr))
232 t.is(_.uniq(arr).length, 20)
233 })
234})
235
236test('unique() throws when num is likely out of range', t => {
237 const fn = () => chance.unique(chance.character, 10, { pool: 'abcde' })
238 t.throws(fn, 'Chance: num is likely too large for sample set')
239})
240
241test('unique() throws when first argument is not a function', t => {
242 const fn = () => chance.unique(chance.character({ pool: 'abcde' }), 10)
243 t.throws(fn, 'Chance: The first argument must be a function.')
244})
245
246test('unique() will take a custom comparator for comparing complex objects', t => {
247 const comparator = (arr, val) => {
248 // If this is the first element, we know it doesn't exist
249 if (arr.length === 0) {
250 return false
251 } else {
252 // If a match has been found, short circuit check and just return
253 return arr.reduce((acc, item) => acc ? acc : item.name === val.name, false)
254 }
255 }
256 let arr = chance.unique(chance.currency, 25, { comparator: comparator })
257 t.is(_.uniq(arr).length, 25)
258})
259
260test('unique() works without a third argument', t => {
261 _.times(200, () => {
262 t.true(_.isArray(chance.unique(chance.character, 10)))
263 })
264})
265
266// chance.weighted()
267test('weighted() returns an element', t => {
268 _.times(1000, () => {
269 let picked = chance.weighted(['a', 'b', 'c', 'd'], [1, 1, 1, 1])
270 t.true(_.isString(picked))
271 t.is(picked.length, 1)
272 })
273})
274
275test('weighted() works with just 2 items', t => {
276 // Use Math.random as the random function rather than our Mersenne twister
277 // just tospeed things up here because this test takes awhile to gather
278 // enough data to have a large enough sample size to adequately test. This
279 // increases speed by a few orders of magnitude at the cost of
280 // repeatability (which we aren't using here)
281 let chance = new Chance(Math.random)
282 var picked = { a: 0, b: 0 }
283
284 // This makes it a tad slow, but we need a large enough sample size to
285 // adequately test
286 _.times(50000, () => {
287 picked[chance.weighted(['a', 'b'], [1, 100])]++
288 })
289
290 // This range is somewhat arbitrary, but good enough to test our constraints
291 let ratio = picked.b / picked.a
292 t.true(ratio > 80)
293 t.true(ratio < 120)
294})
295
296test('weighted() works with trim', t => {
297 _.times(1000, () => {
298 let picked = chance.weighted(['a', 'b', 'c', 'd'], [1, 1, 1, 1], true)
299 t.true(_.isString(picked))
300 t.is(picked.length, 1)
301 })
302})
303
304test('weighted() throws error if called with an array of weights different from options', t => {
305 const fn = () => chance.weighted(['a', 'b', 'c', 'd'], [1, 2, 3])
306 t.throws(fn, 'Chance: Length of array and weights must match')
307})
308
309test('weighted() does not throw error if called with good weights', t => {
310 const fn = () => chance.weighted(['a', 'b', 'c', 'd'], [1, 2, 3, 4])
311 t.notThrows(fn)
312})
313
314test('weighted() throws error if weights invalid', t => {
315 const fn = () => chance.weighted(['a', 'b', 'c', 'd'], [0, 0, 0, 0])
316 t.throws(fn, 'Chance: No valid entries in array weights')
317})
318
319test('weighted() throws error if called with an array of weights different from options 2', t => {
320 const fn = () => chance.weighted(['a', 'b', 'c', 'd'], [1, 2, 3, 4, 5])
321 t.throws(fn, 'Chance: Length of array and weights must match')
322})
323
324test('weighted() throws error if weights contains NaN', t => {
325 const fn = () => chance.weighted(['a', 'b', 'c', 'd'], [1, NaN, 1, 1])
326 t.throws(fn, 'Chance: All weights must be numbers')
327})
328
329test('weighted() returns results properly weighted', t => {
330 // Use Math.random as the random function rather than our Mersenne twister
331 // just tospeed things up here because this test takes awhile to gather
332 // enough data to have a large enough sample size to adequately test. This
333 // increases speed by a few orders of magnitude at the cost of
334 // repeatability (which we aren't using here)
335 let chance = new Chance(Math.random)
336 let picked = { a: 0, b: 0, c: 0, d: 0 }
337 _.times(50000, () => {
338 picked[chance.weighted(['a', 'b', 'c', 'd'], [1, 100, 100, 1])]++
339 })
340
341 // This range is somewhat arbitrary, but good enough to test our constraints
342 let baRatio = picked.b / picked.a
343 t.true(baRatio > 60)
344 t.true(baRatio < 140)
345
346 let cdRatio = picked.c / picked.d
347 t.true(cdRatio > 60)
348 t.true(cdRatio < 140)
349
350 let cbRatio = (picked.c / picked.b) * 100
351 t.true(cbRatio > 50)
352 t.true(cbRatio < 150)
353})
354
355test('weighted() works with fractional weights', t => {
356 // Use Math.random as the random function rather than our Mersenne twister
357 // just tospeed things up here because this test takes awhile to gather
358 // enough data to have a large enough sample size to adequately test. This
359 // increases speed by a few orders of magnitude at the cost of
360 // repeatability (which we aren't using here)
361 let chance = new Chance(Math.random)
362 let picked = { a: 0, b: 0, c: 0, d: 0 }
363 _.times(50000, () => {
364 picked[chance.weighted(['a', 'b', 'c', 'd'], [0.001, 0.1, 0.1, 0.001])]++
365 })
366
367 // This range is somewhat arbitrary, but good enough to test our constraints
368 let baRatio = picked.b / picked.a
369 t.true(baRatio > 60)
370 t.true(baRatio < 140)
371
372 let cdRatio = picked.c / picked.d
373 t.true(cdRatio > 60)
374 t.true(cdRatio < 140)
375
376 let cbRatio = (picked.c / picked.b) * 100
377 t.true(cbRatio > 50)
378 t.true(cbRatio < 150)
379})
380
381test('weighted() works with weight of 0', t => {
382 _.times(1000, () => {
383 let picked = chance.weighted(['a', 'b', 'c'], [1, 0, 1])
384 t.true(_.isString(picked))
385 t.true(picked !== 'b')
386 })
387})
388
389test('weighted() works with negative weight', t => {
390 _.times(1000, () => {
391 let picked = chance.weighted(['a', 'b', 'c'], [1, -2, 1])
392 t.true(_.isString(picked))
393 t.true(picked !== 'b')
394 })
395})
396
397// chance.pad()
398test('pad() always returns same number when width same as length of number', t => {
399 _.times(1000, () => {
400 let num = chance.natural({ min: 10000, max: 99999 })
401 let padded = chance.pad(num, 5)
402 t.true(_.isString(padded))
403 t.is(padded.length, 5)
404 })
405})
406
407test('pad() will pad smaller number to the right width', t => {
408 _.times(1000, () => {
409 let num = chance.natural({ max: 99999 })
410 let padded = chance.pad(num, 10)
411 t.true(_.isString(padded))
412 t.is(padded.length, 10)
413 t.not(padded.indexOf('00000'), -1)
414 })
415})
416
417test('pad() can take a pad e.lement', t => {
418 _.times(1000, () => {
419 let num = chance.natural({ max: 99999 })
420 let padded = chance.pad(num, 10, 'V')
421 t.true(_.isString(padded))
422 t.is(padded.length, 10)
423 t.not(padded.indexOf('VVVVV'), -1)
424 })
425})