UNPKG

13.7 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() returns no more values than the size of the array', t => {
148 let arr = ['a', 'b', 'c', 'd']
149 _.times(1000, () => {
150 let picked = chance.pickset(arr, 5)
151 t.is(picked.length, 4)
152 })
153})
154
155test('pickset() does not destroy the original array', t => {
156 let arr = ['a', 'b', 'c', 'd', 'e', 'f'];
157 _.times(1000, () => {
158 let cloned = _.clone(arr)
159 let picked = chance.pickset(cloned, 3)
160 t.is(cloned.length, 6)
161 t.deepEqual(arr, cloned)
162 })
163})
164
165test('pickset() returns unique values', t => {
166 let arr = ['a', 'b', 'c', 'd']
167 _.times(1000, () => {
168 let picked = chance.pickset(arr, 4)
169 t.not(picked.indexOf('a'), -1)
170 t.not(picked.indexOf('b'), -1)
171 t.not(picked.indexOf('c'), -1)
172 t.not(picked.indexOf('d'), -1)
173 })
174})
175
176// chance.shuffle()
177test('shuffle() returns an array of the same size', t => {
178 let arr = ['a', 'b', 'c', 'd', 'e']
179 _.times(1000, () => {
180 let shuffled = chance.shuffle(_.clone(arr))
181 t.is(shuffled.length, 5)
182 t.not(shuffled.indexOf('a'), -1)
183 })
184})
185
186test('shuffle() returns a well shuffled array', t => {
187 // See http://vq.io/1lEhbim checking it isn't doing that!
188 let arr = ['a', 'b', 'c', 'd', 'e'];
189 let positions = {
190 a: [0, 0, 0, 0, 0],
191 b: [0, 0, 0, 0, 0],
192 c: [0, 0, 0, 0, 0],
193 d: [0, 0, 0, 0, 0],
194 e: [0, 0, 0, 0, 0]
195 };
196
197 let shuffled = _.clone(arr)
198 _.times(10000, () => {
199 shuffled = chance.shuffle(shuffled)
200 shuffled.map((item, index) => {
201 // Accumulate the position of the item each time
202 positions[item][index]++
203 })
204 })
205
206 Object.keys(positions).map((index) => {
207 let position = positions[index]
208 position.map((item) => {
209 // This should be around 20% give or take a bit since there are
210 // 5 elements and they should be evenly distributed
211 t.true(item >= 1800)
212 t.true(item <= 2200)
213 })
214 })
215})
216
217test('shuffle() does not destroy original array', t => {
218 let arr = ['a', 'b', 'c', 'd', 'e']
219 _.times(1000, () => {
220 let cloned = _.clone(arr)
221 let shuffled = chance.shuffle(cloned)
222 t.is(shuffled.length, 5)
223 t.deepEqual(arr, cloned)
224 })
225})
226
227// chance.unique()
228test('unique() gives a unique array of the selected function', t => {
229 _.times(500, () => {
230 let arr = chance.unique(chance.character, 25, { pool: "abcdefghijklmnopqrstuvwxyz" })
231 t.true(_.isArray(arr))
232 t.is(_.uniq(arr).length, 25)
233 })
234})
235
236test('unique() works properly with options', t => {
237 _.times(500, () => {
238 let arr = chance.unique(chance.date, 20, { year: 2016 })
239 t.true(_.isArray(arr))
240 t.is(_.uniq(arr).length, 20)
241 })
242})
243
244test('unique() throws when num is likely out of range', t => {
245 const fn = () => chance.unique(chance.character, 10, { pool: 'abcde' })
246 t.throws(fn, 'Chance: num is likely too large for sample set')
247})
248
249test('unique() throws when first argument is not a function', t => {
250 const fn = () => chance.unique(chance.character({ pool: 'abcde' }), 10)
251 t.throws(fn, 'Chance: The first argument must be a function.')
252})
253
254test('unique() will take a custom comparator for comparing complex objects', t => {
255 const comparator = (arr, val) => {
256 // If this is the first element, we know it doesn't exist
257 if (arr.length === 0) {
258 return false
259 } else {
260 // If a match has been found, short circuit check and just return
261 return arr.reduce((acc, item) => acc ? acc : item.name === val.name, false)
262 }
263 }
264 let arr = chance.unique(chance.currency, 25, { comparator: comparator })
265 t.is(_.uniq(arr).length, 25)
266})
267
268test('unique() works without a third argument', t => {
269 _.times(200, () => {
270 t.true(_.isArray(chance.unique(chance.character, 10)))
271 })
272})
273
274// chance.weighted()
275test('weighted() returns an element', t => {
276 _.times(1000, () => {
277 let picked = chance.weighted(['a', 'b', 'c', 'd'], [1, 1, 1, 1])
278 t.true(_.isString(picked))
279 t.is(picked.length, 1)
280 })
281})
282
283test('weighted() works with just 2 items', t => {
284 // Use Math.random as the random function rather than our Mersenne twister
285 // just tospeed things up here because this test takes awhile to gather
286 // enough data to have a large enough sample size to adequately test. This
287 // increases speed by a few orders of magnitude at the cost of
288 // repeatability (which we aren't using here)
289 let chance = new Chance(Math.random)
290 var picked = { a: 0, b: 0 }
291
292 // This makes it a tad slow, but we need a large enough sample size to
293 // adequately test
294 _.times(50000, () => {
295 picked[chance.weighted(['a', 'b'], [1, 100])]++
296 })
297
298 // This range is somewhat arbitrary, but good enough to test our constraints
299 let ratio = picked.b / picked.a
300 t.true(ratio > 80)
301 t.true(ratio < 120)
302})
303
304test('weighted() works with trim', t => {
305 _.times(1000, () => {
306 let picked = chance.weighted(['a', 'b', 'c', 'd'], [1, 1, 1, 1], true)
307 t.true(_.isString(picked))
308 t.is(picked.length, 1)
309 })
310})
311
312test('weighted() throws error if called with an array of weights different from options', t => {
313 const fn = () => chance.weighted(['a', 'b', 'c', 'd'], [1, 2, 3])
314 t.throws(fn, 'Chance: Length of array and weights must match')
315})
316
317test('weighted() does not throw error if called with good weights', t => {
318 const fn = () => chance.weighted(['a', 'b', 'c', 'd'], [1, 2, 3, 4])
319 t.notThrows(fn)
320})
321
322test('weighted() throws error if weights invalid', t => {
323 const fn = () => chance.weighted(['a', 'b', 'c', 'd'], [0, 0, 0, 0])
324 t.throws(fn, 'Chance: No valid entries in array weights')
325})
326
327test('weighted() throws error if called with an array of weights different from options 2', t => {
328 const fn = () => chance.weighted(['a', 'b', 'c', 'd'], [1, 2, 3, 4, 5])
329 t.throws(fn, 'Chance: Length of array and weights must match')
330})
331
332test('weighted() throws error if weights contains NaN', t => {
333 const fn = () => chance.weighted(['a', 'b', 'c', 'd'], [1, NaN, 1, 1])
334 t.throws(fn, 'Chance: All weights must be numbers')
335})
336
337test('weighted() returns results properly weighted', t => {
338 // Use Math.random as the random function rather than our Mersenne twister
339 // just tospeed things up here because this test takes awhile to gather
340 // enough data to have a large enough sample size to adequately test. This
341 // increases speed by a few orders of magnitude at the cost of
342 // repeatability (which we aren't using here)
343 let chance = new Chance(Math.random)
344 let picked = { a: 0, b: 0, c: 0, d: 0 }
345 _.times(50000, () => {
346 picked[chance.weighted(['a', 'b', 'c', 'd'], [1, 100, 100, 1])]++
347 })
348
349 // This range is somewhat arbitrary, but good enough to test our constraints
350 let baRatio = picked.b / picked.a
351 t.true(baRatio > 60)
352 t.true(baRatio < 140)
353
354 let cdRatio = picked.c / picked.d
355 t.true(cdRatio > 60)
356 t.true(cdRatio < 140)
357
358 let cbRatio = (picked.c / picked.b) * 100
359 t.true(cbRatio > 50)
360 t.true(cbRatio < 150)
361})
362
363test('weighted() works with fractional weights', t => {
364 // Use Math.random as the random function rather than our Mersenne twister
365 // just tospeed things up here because this test takes awhile to gather
366 // enough data to have a large enough sample size to adequately test. This
367 // increases speed by a few orders of magnitude at the cost of
368 // repeatability (which we aren't using here)
369 let chance = new Chance(Math.random)
370 let picked = { a: 0, b: 0, c: 0, d: 0 }
371 _.times(50000, () => {
372 picked[chance.weighted(['a', 'b', 'c', 'd'], [0.001, 0.1, 0.1, 0.001])]++
373 })
374
375 // This range is somewhat arbitrary, but good enough to test our constraints
376 let baRatio = picked.b / picked.a
377 t.true(baRatio > 60)
378 t.true(baRatio < 140)
379
380 let cdRatio = picked.c / picked.d
381 t.true(cdRatio > 60)
382 t.true(cdRatio < 140)
383
384 let cbRatio = (picked.c / picked.b) * 100
385 t.true(cbRatio > 50)
386 t.true(cbRatio < 150)
387})
388
389test('weighted() works with weight of 0', t => {
390 _.times(1000, () => {
391 let picked = chance.weighted(['a', 'b', 'c'], [1, 0, 1])
392 t.true(_.isString(picked))
393 t.true(picked !== 'b')
394 })
395})
396
397test('weighted() works with negative weight', t => {
398 _.times(1000, () => {
399 let picked = chance.weighted(['a', 'b', 'c'], [1, -2, 1])
400 t.true(_.isString(picked))
401 t.true(picked !== 'b')
402 })
403})
404
405// chance.pad()
406test('pad() always returns same number when width same as length of number', t => {
407 _.times(1000, () => {
408 let num = chance.natural({ min: 10000, max: 99999 })
409 let padded = chance.pad(num, 5)
410 t.true(_.isString(padded))
411 t.is(padded.length, 5)
412 })
413})
414
415test('pad() will pad smaller number to the right width', t => {
416 _.times(1000, () => {
417 let num = chance.natural({ max: 99999 })
418 let padded = chance.pad(num, 10)
419 t.true(_.isString(padded))
420 t.is(padded.length, 10)
421 t.not(padded.indexOf('00000'), -1)
422 })
423})
424
425test('pad() can take a pad e.lement', t => {
426 _.times(1000, () => {
427 let num = chance.natural({ max: 99999 })
428 let padded = chance.pad(num, 10, 'V')
429 t.true(_.isString(padded))
430 t.is(padded.length, 10)
431 t.not(padded.indexOf('VVVVV'), -1)
432 })
433})