1 | import test from 'ava'
|
2 | import Chance from '../chance.js'
|
3 | import _ from 'lodash'
|
4 |
|
5 | const chance = new Chance()
|
6 |
|
7 |
|
8 | test('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 |
|
17 | test('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 |
|
26 | test('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 |
|
32 | test('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 |
|
46 | test('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 |
|
53 | test('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 |
|
61 | test('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 |
|
69 | test('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 |
|
79 | test('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 |
|
85 | test('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 |
|
94 | test('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 |
|
100 | test('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 |
|
109 | test('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 |
|
114 | test('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 |
|
123 | test('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 |
|
129 | test('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 |
|
138 | test('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 |
|
147 | test('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 |
|
155 | test('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 |
|
165 | test('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 |
|
177 | test('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 |
|
186 | test('shuffle() returns a well shuffled array', t => {
|
187 |
|
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 |
|
202 | positions[item][index]++
|
203 | })
|
204 | })
|
205 |
|
206 | Object.keys(positions).map((index) => {
|
207 | let position = positions[index]
|
208 | position.map((item) => {
|
209 |
|
210 |
|
211 | t.true(item >= 1800)
|
212 | t.true(item <= 2200)
|
213 | })
|
214 | })
|
215 | })
|
216 |
|
217 | test('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 |
|
228 | test('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 |
|
236 | test('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 |
|
244 | test('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 |
|
249 | test('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 |
|
254 | test('unique() will take a custom comparator for comparing complex objects', t => {
|
255 | const comparator = (arr, val) => {
|
256 |
|
257 | if (arr.length === 0) {
|
258 | return false
|
259 | } else {
|
260 |
|
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 |
|
268 | test('unique() works without a third argument', t => {
|
269 | _.times(200, () => {
|
270 | t.true(_.isArray(chance.unique(chance.character, 10)))
|
271 | })
|
272 | })
|
273 |
|
274 |
|
275 | test('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 |
|
283 | test('weighted() works with just 2 items', t => {
|
284 |
|
285 |
|
286 |
|
287 |
|
288 |
|
289 | let chance = new Chance(Math.random)
|
290 | var picked = { a: 0, b: 0 }
|
291 |
|
292 |
|
293 |
|
294 | _.times(50000, () => {
|
295 | picked[chance.weighted(['a', 'b'], [1, 100])]++
|
296 | })
|
297 |
|
298 |
|
299 | let ratio = picked.b / picked.a
|
300 | t.true(ratio > 80)
|
301 | t.true(ratio < 120)
|
302 | })
|
303 |
|
304 | test('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 |
|
312 | test('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 |
|
317 | test('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 |
|
322 | test('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 |
|
327 | test('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 |
|
332 | test('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 |
|
337 | test('weighted() returns results properly weighted', t => {
|
338 |
|
339 |
|
340 |
|
341 |
|
342 |
|
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 |
|
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 |
|
363 | test('weighted() works with fractional weights', t => {
|
364 |
|
365 |
|
366 |
|
367 |
|
368 |
|
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 |
|
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 |
|
389 | test('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 |
|
397 | test('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 |
|
406 | test('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 |
|
415 | test('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 |
|
425 | test('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 | })
|