UNPKG

16 kBJavaScriptView Raw
1import test from 'ava'
2import Chance from '../chance.js'
3import _ from 'lodash'
4
5const chance = new Chance()
6
7const timeout = (seconds) => {
8 new Promise((resolve, reject) => {
9 setTimeout(() => resolve(), seconds)
10 })
11}
12
13test('bool() returns a random boolean', t => {
14 let bool = chance.bool()
15 t.is(typeof bool, 'boolean')
16})
17
18test('bool() is within the bounds of what we would call random', t => {
19 let trueCount = 0;
20 _.times(1000, () => {
21 if (chance.bool()) {
22 trueCount++
23 }
24 })
25
26 // The probability of this test failing is approximately 4.09e-86.
27 // So, in theory, it could give a false negative, but the sun will
28 // probably die long before that happens.
29
30 t.true((trueCount > 200) && (trueCount < 800))
31})
32
33test('bool() takes and obeys likelihood', t => {
34 let trueCount = 0
35 _.times(1000, () => {
36 if (chance.bool({ likelihood: 30 })) {
37 trueCount++
38 }
39 })
40
41 // Expect it to average around 300
42 t.true((trueCount > 200) && (trueCount < 400))
43
44 trueCount = 0
45 _.times(1000, () => {
46 if (chance.bool({ likelihood: 99 })) {
47 trueCount++
48 }
49 })
50
51 // Expect it to average at 990
52 t.true(trueCount > 900)
53})
54
55test('bool() throws an error if likelihood < 0 or > 100', t => {
56 const fn1 = () => chance.bool({likelihood: -23})
57 t.throws(fn1, RangeError)
58 const fn2 = () => chance.bool({likelihood: 7933})
59 t.throws(fn2, RangeError)
60})
61
62test('Chance() null works', async t => {
63 t.plan(1)
64
65 let chance1 = Chance(null)
66 // Wait 5 ms before creating chance2 else sometimes they happen on the same
67 // tick and end up with the same seed!
68 await timeout(5)
69 let chance2 = Chance(null)
70 t.not(chance1.random(), chance2.random())
71})
72
73test('Chance() does return differing results if differing seeds provided', t => {
74 let chance1 = new Chance(12345)
75 let chance2 = new Chance(54321)
76 t.not(chance1.random(), chance2.random())
77})
78
79test('Chance() does not return repeatable results if no seed provided', async t => {
80 t.plan(1000)
81 let chance1 = new Chance()
82 await timeout(5)
83 let chance2 = new Chance()
84 _.times(1000, () => {
85 t.not(chance1.random(), chance2.random())
86 })
87})
88
89test('Chance() returns repeatable results if seed provided on the Chance object', t => {
90 let seed = new Date().getTime()
91 let chance1 = new Chance(seed)
92 let chance2 = new Chance(seed)
93
94 _.times(1000, () => {
95 t.is(chance1.random(), chance2.random())
96 })
97})
98
99test('Chance() returns repeatable results if a string is provided as a seed', t => {
100 let seed = "foo"
101 let chance1 = new Chance(seed)
102 let chance2 = new Chance(seed)
103
104 _.times(1000, () => {
105 t.is(chance1.random(), chance2.random())
106 })
107})
108
109test('Chance() returns different results if two different strings are provided', t => {
110 let chance1 = new Chance("foo")
111 let chance2 = new Chance("baz")
112
113 _.times(1000, () => {
114 t.not(chance1.random(), chance2.random())
115 })
116})
117
118test('Chance() returns different results if two different strings are provided redux', t => {
119 // Credit to @dan-tilley for noticing this flaw in the old seed
120 let chance1 = new Chance("abe")
121 let chance2 = new Chance("acc")
122
123 _.times(1000, () => {
124 t.not(chance1.random(), chance2.random())
125 })
126})
127
128test('Chance() returns different results if multiple arguments are provided', t => {
129 let seed = new Date().getTime()
130 let chance1 = new Chance(seed, "foo")
131 let chance2 = new Chance(seed, "bar")
132
133 _.times(1000, () => {
134 t.not(chance1.random(), chance2.random())
135 })
136})
137
138test('Chance() will take an arbitrary function for the seed and use it', t => {
139 let chance = new Chance(() => 123)
140
141 _.times(1000, () => {
142 t.is(chance.random(), 123)
143 })
144})
145
146test('character() returns a character', t => {
147 let char = chance.character()
148 t.is(typeof char, 'string')
149 t.is(char.length, 1)
150})
151
152test('character() pulls only from pool, when specified', t => {
153 _.times(1000, () => {
154 let char = chance.character({ pool: 'abcde' })
155 t.true(/[abcde]/.test(char))
156 })
157})
158
159test('character() allows only alpha', t => {
160 _.times(1000, () => {
161 let char = chance.character({ alpha: true })
162 t.true(/[a-zA-Z]/.test(char))
163 })
164})
165
166test('character() allows only alphanumeric', t => {
167 _.times(1000, () => {
168 let char = chance.character({ alpha: true, numeric: true })
169 t.true(/[a-zA-Z0-9]/.test(char))
170 })
171})
172
173test('character() obeys upper case', t => {
174 _.times(1000, () => {
175 let char = chance.character({ alpha: true, casing: 'upper' })
176 t.true(/[A-Z]/.test(char))
177 })
178})
179
180test('character() obeys lower case', t => {
181 _.times(1000, () => {
182 let char = chance.character({ alpha: true, casing: 'lower' })
183 t.true(/[a-z]/.test(char))
184 })
185})
186
187test('floating() returns a random floating', t => {
188 t.is(typeof chance.floating(), 'number')
189})
190
191test('floating() can take both a max and min and obey them both', t => {
192 _.times(1000, () => {
193 let floating = chance.floating({ min: 90, max: 100 })
194 t.true(floating > 89)
195 t.true(floating < 101)
196 })
197})
198
199test('floating() will not take fixed + min that would be out of range', t => {
200 const fn = () => chance.floating({ fixed: 13, min: -9007199254740992 })
201 t.throws(fn, "Chance: Min specified is out of range with fixed. Min should be, at least, -900.7199254740992")
202})
203
204test('floating() will not take fixed + max that would be out of range', t => {
205 const fn = () => chance.floating({ fixed: 13, max: 9007199254740992 })
206 t.throws(fn, "Chance: Max specified is out of range with fixed. Max should be, at most, 900.7199254740992")
207})
208
209test('floating() obeys the fixed parameter, when present', t => {
210 _.times(1000, () => {
211 let floating = chance.floating({ fixed: 4 })
212 let decimals = floating.toString().split('.')[1] ? floating.toString().split('.')[1] : ''
213 t.true(decimals.length < 5)
214 })
215})
216
217test('floating() can take fixed and obey it', t => {
218 _.times(1000, () => {
219 let floating = chance.floating({ fixed: 3 })
220 let parsed = parseFloat(floating.toFixed(3))
221 t.is(floating, parsed)
222 })
223})
224
225test('floating() will not take both fixed and precision', t => {
226 const fn = () => chance.floating({fixed: 2, precision: 8})
227 t.throws(fn, 'Chance: Cannot specify both fixed and precision.')
228})
229
230test('get() works as expected', t => {
231 let data = chance.get('lastNames')
232 t.true(typeof data === 'object')
233})
234
235test('hex() works as expected', t => {
236 _.times(1000, () => {
237 t.true(/[0-9a-f]/.test(chance.hex()))
238 })
239})
240
241test('hex() can take Upper and obey it', t => {
242 _.times(1000, () => {
243 t.true(/[0-9A-F]/.test(chance.hex({ casing: 'upper' })))
244 })
245})
246
247test('integer() returns a random integer', t => {
248 t.is(typeof chance.integer(), 'number')
249})
250
251test('integer() is sometimes negative, sometimes positive', t => {
252 let positiveCount = 0
253 _.times(1000, () => {
254 if (chance.integer() > 0) {
255 positiveCount++
256 }
257 })
258
259 // Note: In very extreme circumstances this test may fail as, by its
260 // nature it's random. But it's a low enough percentage that I'm
261 // willing to accept it.
262 t.true((positiveCount > 200) && (positiveCount < 800))
263})
264
265test('integer() can take a zero min and obey it', t => {
266 _.times(1000, () => {
267 t.true(chance.integer({ min: 0 }) > 0)
268 })
269})
270
271test('integer() can take a negative min and obey it', t => {
272 _.times(1000, () => {
273 t.true(chance.integer({ min: -25 }) > -26)
274 })
275})
276
277test('integer() can take a negative min and max and obey both', t => {
278 _.times(1000, () => {
279 let integer = chance.integer({ min: -25, max: -1 })
280 t.true((integer > -26) && integer < 0)
281 })
282})
283
284test('integer() can take a min with absolute value less than max and return in range above', t => {
285 let count = 0
286 _.times(1000, () => {
287 // With a range this large we'd expect most values to be
288 // greater than 1 if this works correctly.
289 if (Math.abs(chance.integer({ min: -1, max: 1000000 })) < 2) {
290 count++
291 }
292 })
293 t.true(count < 900)
294})
295
296test('integer() throws an error when min > max', t => {
297 const fn = () => chance.integer({ min: 1000, max: 500 })
298 t.throws(fn, 'Chance: Min cannot be greater than Max.')
299})
300
301test('letter() returns a letter', t => {
302 _.times(1000, () => {
303 let letter = chance.letter()
304 t.is(typeof letter, 'string')
305 t.is(letter.length, 1)
306 t.true(letter.match(/[a-z]/) !== null)
307 })
308})
309
310test('letter() can take upper case', t => {
311 _.times(1000, () => {
312 let letter = chance.letter({ casing: 'upper' })
313 t.is(typeof letter, 'string')
314 t.is(letter.length, 1)
315 t.true(letter.match(/[A-Z]/) !== null)
316 })
317})
318
319test('natural() returns a random natural', t => {
320 t.is(typeof chance.natural(), 'number')
321})
322
323test('natural() throws an error if min < 0', t => {
324 const fn = () => chance.natural({ min: -23 })
325 t.throws(fn, 'Chance: Min cannot be less than zero.')
326})
327
328test('natural() is always positive or zero', t => {
329 let positiveCount = 0
330 _.times(1000, () => {
331 if (chance.natural() >= 0) {
332 positiveCount++
333 }
334 })
335 t.is(positiveCount, 1000)
336})
337
338test('natural() can take just a min and obey it', t => {
339 _.times(1000, () => {
340 t.true(chance.natural({ min: 9007199254740991 }) > 9007199254740990)
341 })
342})
343
344test('natural() can take just a max and obey it', t => {
345 _.times(1000, () => {
346 t.true(chance.natural({ max: 100 }) < 101)
347 })
348})
349
350test('natural() can take both a max and min and obey them both', t => {
351 _.times(1000, () => {
352 let natural = chance.natural({ min: 90, max: 100 })
353 t.true(natural > 89)
354 t.true(natural < 101)
355 })
356})
357
358test('natural() works with both bounds 0', t => {
359 _.times(1000, () => {
360 t.is(chance.natural({ min: 0, max: 0 }), 0)
361 })
362})
363
364test('natural() respects numerals', t => {
365 _.times(1000, () => {
366 let natural = chance.natural({ numerals: 2 })
367 t.true(natural <= 99)
368 t.true(natural >= 10)
369 })
370})
371
372test('natural() works with excluded numbers', t => {
373 _.times(1000, () => {
374 let natural = chance.natural({ min: 1, max: 5, exclude: [1, 3] })
375 t.true(natural <= 5)
376 t.true(natural >= 1)
377 t.true(natural !== 1)
378 t.true(natural !== 3)
379 })
380})
381
382test('natural() works within empty exclude option', t => {
383 _.times(1000, () => {
384 let natural = chance.natural({ min: 1, max: 5, exclude: [] })
385 t.true(natural <= 5)
386 t.true(natural >= 1)
387 })
388})
389
390test('natural() throws an error if exclude is not an array', t => {
391 const fn = () => chance.natural({ min: 1, max: 5, exclude: "foo" })
392 t.throws(fn, 'Chance: exclude must be an array.')
393})
394
395test('natural() throws an error if exclude is not an array', t => {
396 const fn = () => chance.natural({ min: 1, max: 5, exclude: ["puppies", 1] })
397 t.throws(fn, 'Chance: exclude must be numbers.')
398})
399
400test('natural() throws an error if min > max', t => {
401 const fn = () => chance.natural({ min: 1000, max: 500 })
402 t.throws(fn, 'Chance: Min cannot be greater than Max.')
403})
404
405test('natural() throws an error if numerals is less than 1', t => {
406 const fn = () => chance.natural({ numerals: 0 })
407 t.throws(fn, 'Chance: Numerals cannot be less than one.')
408})
409
410test('prime() returns a number', t => {
411 t.is(typeof chance.prime(), 'number')
412})
413
414test('prime() throws an error if min < 0', t => {
415 const fn = () => chance.prime({ min: -23 })
416 t.throws(fn, 'Chance: Min cannot be less than zero.')
417})
418
419test('prime() throws an error if min > max', t => {
420 const fn = () => chance.prime({ min: 1000, max: 500 })
421 t.throws(fn, 'Chance: Min cannot be greater than Max.')
422})
423
424test('prime() is always positive and odd (or 2)', t => {
425 let positiveCount = 0
426 _.times(1000, () => {
427 const prime = chance.prime()
428 if (prime > 0 && (prime % 2 === 1 || prime === 2)) {
429 positiveCount++
430 }
431 })
432 t.is(positiveCount, 1000)
433})
434
435test('prime() can take just a min and obey it', t => {
436 _.times(1000, () => {
437 t.true(chance.prime({ min: 5000 }) >= 5000)
438 })
439})
440
441test('prime() can take just a max and obey it', t => {
442 _.times(1000, () => {
443 t.true(chance.prime({ max: 20000 }) <= 20000)
444 })
445})
446
447test('prime() can take both a max and min and obey them both', t => {
448 _.times(1000, () => {
449 const prime = chance.prime({ min: 90, max: 100 })
450 t.true(prime >= 90)
451 t.true(prime <= 100)
452 })
453})
454
455test('set() works as expected', t => {
456 let cData = { lastNames: ['customName', 'testLast'] }
457 chance.set(cData)
458 let data = chance.get('lastNames')
459 t.true(_.isArray(data))
460 t.is(data.length, 2)
461})
462
463test('string() returns a string', t => {
464 t.is(typeof chance.string(), 'string')
465})
466
467test('string() obeys length, when specified', t => {
468 _.times(1000, () => {
469 let length = chance.natural({ min: 1, max: 25 })
470 t.is(chance.string({ length: length }).length, length)
471 })
472})
473
474test('string() throws error if length < 0', t => {
475 const fn = () => chance.string({ length: -23 })
476 t.throws(fn, 'Chance: Length cannot be less than zero.')
477})
478
479test('string() returns only letters with alpha', t => {
480 _.times(1000, () => {
481 let str = chance.string({ alpha: true })
482 t.true(/[a-zA-Z]+/.test(str))
483 })
484})
485
486test('string() obeys upper case', t => {
487 _.times(1000, () => {
488 let str = chance.string({ alpha: true, casing: 'upper' })
489 t.true(/[A-Z]+/.test(str))
490 })
491})
492
493test('string() obeys lower case', t => {
494 _.times(1000, () => {
495 let str = chance.string({ alpha: true, casing: 'lower' })
496 t.true(/[a-z]+/.test(str))
497 })
498})
499
500test('string() obeys symbol', t => {
501 _.times(1000, () => {
502 let str = chance.string({ symbols: true })
503 t.true(/[\!\@\#\$\%\^\&\*\(\)\[\]]+/.test(str))
504 })
505})
506
507test('string() can take just a min and obey it', t => {
508 _.times(1000, () => {
509 t.true(chance.string({ min: 6 }).length >= 6)
510 })
511})
512
513test('string() can take just a max and obey it', t => {
514 _.times(1000, () => {
515 t.true(chance.string({ max: 20 }).length <= 20)
516 })
517})
518
519test('string() returns an empty string with zero length', t => {
520 t.is(chance.string({length: 0}), '')
521})
522
523test('falsy() should return a falsy value', t => {
524 _.times(1000, () => {
525 const value = chance.falsy()
526 t.falsy(value)
527 })
528})
529
530test('falsy() should return a falsy value using a pool data', t => {
531 _.times(1000, () => {
532 const value = chance.falsy({pool: [null, undefined]})
533 t.falsy(value)
534 })
535})
536
537test('template() returns alpha numeric substituted', t => {
538 _.times(1000, () => {
539 let str = chance.template('ID-{Aa}-{##}')
540 t.regex(str, /^ID-[A-Z][a-z]-[0-9][0-9]$/)
541 })
542})
543
544test('template() rejects unknown tokens', t => {
545 t.throws(() => chance.template('{Aa-}'), 'Invalid replacement character: "-".')
546 t.throws(() => chance.template('{Aa{}'), 'Invalid replacement character: "{".')
547 t.throws(() => chance.template('{Aab}'), 'Invalid replacement character: "b".')
548})
549
550test('template() allows escape sequnce', t => {
551 t.is(chance.template('\\\\ID-\\{Aa\\}'), '\\ID-{Aa}')
552})
553
554test('template() rejects invalid escape sequnce', t => {
555 t.throws(() => chance.template('ID-\\Aa'), 'Invalid escape sequence: "\\A".')
556})
557
558test('template() cannot be undefined', t => {
559 t.throws(() => chance.template(), 'Template string is required')
560})
561
562test('template() cannot be empty', t => {
563 t.throws(() => chance.template(''), 'Template string is required')
564})