]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blame - tests.js
Entropy with more than 4 decks can be calculated
[perso/Immae/Projets/Cryptomonnaies/BIP39.git] / tests.js
CommitLineData
88e2cdaa
IC
1// Usage:
2// $ phantomjs tests.js
3
4
5var page = require('webpage').create();
6var url = 'src/index.html';
0c6777ee 7var testMaxTime = 10000;
88e2cdaa
IC
8
9page.onResourceError = function(e) {
10 console.log("Error loading " + e.url);
11 phantom.exit();
12}
13
14function fail() {
15 console.log("Failed");
16 phantom.exit();
17}
18
3eef9d0d
IC
19function waitForGenerate(fn, maxTime) {
20 if (!maxTime) {
21 maxTime = testMaxTime;
22 }
23 var start = new Date().getTime();
24 var prevAddressCount = -1;
25 var wait = function keepWaiting() {
26 var now = new Date().getTime();
27 var hasTimedOut = now - start > maxTime;
28 var addressCount = page.evaluate(function() {
29 return $(".address").length;
30 });
31 var hasFinished = addressCount > 0 && addressCount == prevAddressCount;
32 prevAddressCount = addressCount;
33 if (hasFinished) {
34 fn();
35 }
36 else if (hasTimedOut) {
37 console.log("Test timed out");
38 fn();
39 }
40 else {
41 setTimeout(keepWaiting, 100);
42 }
43 }
44 wait();
45}
46
c3719b00
IC
47function waitForFeedback(fn, maxTime) {
48 if (!maxTime) {
49 maxTime = testMaxTime;
50 }
51 var start = new Date().getTime();
52 var wait = function keepWaiting() {
53 var now = new Date().getTime();
54 var hasTimedOut = now - start > maxTime;
55 if (hasTimedOut) {
56 console.log("Test timed out");
57 fn();
58 return;
59 }
60 var feedback = page.evaluate(function() {
61 var feedback = $(".feedback");
62 if (feedback.css("display") == "none") {
63 return "";
64 }
65 return feedback.text();
66 });
67 var hasFinished = feedback.length > 0 && feedback != "Calculating...";
68 if (hasFinished) {
69 fn();
70 }
71 else {
72 setTimeout(keepWaiting, 100);
73 }
74 }
75 wait();
76}
77
057722b0
IC
78function waitForEntropyFeedback(fn, maxTime) {
79 if (!maxTime) {
80 maxTime = testMaxTime;
81 }
82 var origFeedback = page.evaluate(function() {
dd944906 83 return $(".entropy-container").text();
057722b0
IC
84 });
85 var start = new Date().getTime();
86 var wait = function keepWaiting() {
87 var now = new Date().getTime();
88 var hasTimedOut = now - start > maxTime;
89 if (hasTimedOut) {
90 console.log("Test timed out");
91 fn();
92 return;
93 }
94 var feedback = page.evaluate(function() {
dd944906 95 return $(".entropy-container").text();
057722b0
IC
96 });
97 var hasFinished = feedback != origFeedback;
98 if (hasFinished) {
99 fn();
100 }
101 else {
102 setTimeout(keepWaiting, 100);
103 }
104 }
105 wait();
106}
107
88e2cdaa
IC
108function next() {
109 if (tests.length > 0) {
110 var testsStr = tests.length == 1 ? "test" : "tests";
111 console.log(tests.length + " " + testsStr + " remaining");
112 tests.shift()();
113 }
114 else {
115 console.log("Finished with 0 failures");
116 phantom.exit();
117 }
118}
119
fb372687
IC
120/**
121 * Randomize array element order in-place.
122 * Using Durstenfeld shuffle algorithm.
123 * See http://stackoverflow.com/a/12646864
124 */
125function shuffle(array) {
126 for (var i = array.length - 1; i > 0; i--) {
127 var j = Math.floor(Math.random() * (i + 1));
128 var temp = array[i];
129 array[i] = array[j];
130 array[j] = temp;
131 }
132 return array;
133}
134
88e2cdaa
IC
135tests = [
136
137// Page loads with status of 'success'
138function() {
139page.open(url, function(status) {
140 if (status != "success") {
141 console.log("Page did not load with status 'success'");
142 fail();
143 }
144 next();
145});
146},
147
148// Page has text
149function() {
150page.open(url, function(status) {
151 var content = page.evaluate(function() {
152 return document.body.textContent.trim();
153 });
154 if (!content) {
155 console.log("Page does not have text");
156 fail();
157 }
158 next();
159});
160},
161
162// Entering mnemonic generates addresses
163function() {
164page.open(url, function(status) {
88e2cdaa
IC
165 // set the phrase
166 page.evaluate(function() {
167 $(".phrase").val("abandon abandon ability").trigger("input");
168 });
169 // get the address
3eef9d0d 170 waitForGenerate(function() {
06c4c6e3
IC
171 var addressCount = page.evaluate(function() {
172 return $(".address").length;
88e2cdaa 173 });
06c4c6e3
IC
174 if (addressCount != 20) {
175 console.log("Mnemonic did not generate addresses");
88e2cdaa
IC
176 console.log("Expected: " + expected);
177 console.log("Got: " + actual);
178 fail();
179 }
180 next();
3eef9d0d 181 });
88e2cdaa
IC
182});
183},
184
185// Random button generates random mnemonic
186function() {
187page.open(url, function(status) {
188 // check initial phrase is empty
189 var phrase = page.evaluate(function() {
190 return $(".phrase").text();
191 });
192 if (phrase != "") {
193 console.log("Initial phrase is not blank");
194 fail();
195 }
196 // press the 'generate' button
197 page.evaluate(function() {
198 $(".generate").click();
199 });
200 // get the new phrase
3eef9d0d 201 waitForGenerate(function() {
88e2cdaa
IC
202 var phrase = page.evaluate(function() {
203 return $(".phrase").val();
204 });
205 if (phrase.length <= 0) {
206 console.log("Phrase not generated by pressing button");
207 fail();
208 }
209 next();
3eef9d0d 210 });
88e2cdaa
IC
211});
212},
213
214// Mnemonic length can be customized
215function() {
216page.open(url, function(status) {
217 // set the length to 6
54563907 218 var expectedLength = "6";
88e2cdaa 219 page.evaluate(function() {
54563907
IC
220 $(".strength option[selected]").removeAttr("selected");
221 $(".strength option[value=6]").prop("selected", true);
88e2cdaa
IC
222 });
223 // press the 'generate' button
224 page.evaluate(function() {
225 $(".generate").click();
226 });
227 // check the new phrase is six words long
3eef9d0d 228 waitForGenerate(function() {
88e2cdaa
IC
229 var actualLength = page.evaluate(function() {
230 var words = $(".phrase").val().split(" ");
231 return words.length;
232 });
233 if (actualLength != expectedLength) {
234 console.log("Phrase not generated with correct length");
235 console.log("Expected: " + expectedLength);
236 console.log("Actual: " + actualLength);
237 fail();
238 }
54563907 239 next();
3eef9d0d 240 });
88e2cdaa
IC
241});
242},
243
88e2cdaa 244// Passphrase can be set
54563907
IC
245function() {
246page.open(url, function(status) {
247 // set the phrase and passphrase
248 var expected = "15pJzUWPGzR7avffV9nY5by4PSgSKG9rba";
249 page.evaluate(function() {
250 $(".phrase").val("abandon abandon ability");
251 $(".passphrase").val("secure_passphrase").trigger("input");
252 });
253 // check the address is generated correctly
3eef9d0d 254 waitForGenerate(function() {
54563907
IC
255 var actual = page.evaluate(function() {
256 return $(".address:first").text();
257 });
258 if (actual != expected) {
259 console.log("Passphrase results in wrong address");
260 console.log("Expected: " + expected);
261 console.log("Actual: " + actual);
262 fail();
263 }
264 next();
3eef9d0d 265 });
54563907
IC
266});
267},
268
88e2cdaa 269// Network can be set to bitcoin testnet
54563907
IC
270function() {
271page.open(url, function(status) {
59193779 272 // set the phrase and coin
54563907
IC
273 var expected = "mucaU5iiDaJDb69BHLeDv8JFfGiyg2nJKi";
274 page.evaluate(function() {
275 $(".phrase").val("abandon abandon ability");
276 $(".phrase").trigger("input");
277 $(".network option[selected]").removeAttr("selected");
278 $(".network option[value=1]").prop("selected", true);
279 $(".network").trigger("change");
280 });
281 // check the address is generated correctly
3eef9d0d 282 waitForGenerate(function() {
54563907
IC
283 var actual = page.evaluate(function() {
284 return $(".address:first").text();
285 });
286 if (actual != expected) {
287 console.log("Bitcoin testnet address is incorrect");
288 console.log("Expected: " + expected);
289 console.log("Actual: " + actual);
290 fail();
291 }
292 next();
3eef9d0d 293 });
54563907
IC
294});
295},
296
88e2cdaa 297// Network can be set to litecoin
59193779
IC
298function() {
299page.open(url, function(status) {
300 // set the phrase and coin
301 var expected = "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn";
302 page.evaluate(function() {
303 $(".phrase").val("abandon abandon ability");
304 $(".phrase").trigger("input");
305 $(".network option[selected]").removeAttr("selected");
306 $(".network option[value=2]").prop("selected", true);
307 $(".network").trigger("change");
308 });
309 // check the address is generated correctly
3eef9d0d 310 waitForGenerate(function() {
59193779
IC
311 var actual = page.evaluate(function() {
312 return $(".address:first").text();
313 });
314 if (actual != expected) {
315 console.log("Litecoin address is incorrect");
316 console.log("Expected: " + expected);
317 console.log("Actual: " + actual);
318 fail();
319 }
320 next();
3eef9d0d 321 });
59193779
IC
322});
323},
324
88e2cdaa 325// Network can be set to dogecoin
59193779
IC
326function() {
327page.open(url, function(status) {
328 // set the phrase and coin
329 var expected = "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA";
330 page.evaluate(function() {
331 $(".phrase").val("abandon abandon ability");
332 $(".phrase").trigger("input");
333 $(".network option[selected]").removeAttr("selected");
334 $(".network option[value=3]").prop("selected", true);
335 $(".network").trigger("change");
336 });
337 // check the address is generated correctly
3eef9d0d 338 waitForGenerate(function() {
59193779
IC
339 var actual = page.evaluate(function() {
340 return $(".address:first").text();
341 });
342 if (actual != expected) {
343 console.log("Dogecoin address is incorrect");
344 console.log("Expected: " + expected);
345 console.log("Actual: " + actual);
346 fail();
347 }
348 next();
3eef9d0d 349 });
59193779
IC
350});
351},
352
88e2cdaa 353// Network can be set to shadowcash
59193779
IC
354function() {
355page.open(url, function(status) {
356 // set the phrase and coin
357 var expected = "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG";
358 page.evaluate(function() {
359 $(".phrase").val("abandon abandon ability");
360 $(".phrase").trigger("input");
361 $(".network option[selected]").removeAttr("selected");
362 $(".network option[value=4]").prop("selected", true);
363 $(".network").trigger("change");
364 });
365 // check the address is generated correctly
3eef9d0d 366 waitForGenerate(function() {
59193779
IC
367 var actual = page.evaluate(function() {
368 return $(".address:first").text();
369 });
370 if (actual != expected) {
371 console.log("Shadowcash address is incorrect");
372 console.log("Expected: " + expected);
373 console.log("Actual: " + actual);
374 fail();
375 }
376 next();
3eef9d0d 377 });
59193779
IC
378});
379},
380
88e2cdaa 381// Network can be set to shadowcash testnet
59193779
IC
382function() {
383page.open(url, function(status) {
384 // set the phrase and coin
385 var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
386 page.evaluate(function() {
387 $(".phrase").val("abandon abandon ability");
388 $(".phrase").trigger("input");
389 $(".network option[selected]").removeAttr("selected");
390 $(".network option[value=5]").prop("selected", true);
391 $(".network").trigger("change");
392 });
393 // check the address is generated correctly
3eef9d0d 394 waitForGenerate(function() {
59193779
IC
395 var actual = page.evaluate(function() {
396 return $(".address:first").text();
397 });
398 if (actual != expected) {
399 console.log("Shadowcash testnet address is incorrect");
400 console.log("Expected: " + expected);
401 console.log("Actual: " + actual);
402 fail();
403 }
404 next();
3eef9d0d 405 });
59193779
IC
406});
407},
408
88e2cdaa 409// Network can be set to viacoin
59193779
IC
410function() {
411page.open(url, function(status) {
412 // set the phrase and coin
413 var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
414 page.evaluate(function() {
415 $(".phrase").val("abandon abandon ability");
416 $(".phrase").trigger("input");
417 $(".network option[selected]").removeAttr("selected");
418 $(".network option[value=6]").prop("selected", true);
419 $(".network").trigger("change");
420 });
421 // check the address is generated correctly
3eef9d0d 422 waitForGenerate(function() {
59193779
IC
423 var actual = page.evaluate(function() {
424 return $(".address:first").text();
425 });
426 if (actual != expected) {
427 console.log("Viacoin address is incorrect");
428 console.log("Expected: " + expected);
429 console.log("Actual: " + actual);
430 fail();
431 }
432 next();
3eef9d0d 433 });
59193779
IC
434});
435},
436
88e2cdaa 437// Network can be set to viacoin testnet
59193779
IC
438function() {
439page.open(url, function(status) {
440 // set the phrase and coin
441 var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
442 page.evaluate(function() {
443 $(".phrase").val("abandon abandon ability");
444 $(".phrase").trigger("input");
445 $(".network option[selected]").removeAttr("selected");
446 $(".network option[value=7]").prop("selected", true);
447 $(".network").trigger("change");
448 });
449 // check the address is generated correctly
3eef9d0d 450 waitForGenerate(function() {
59193779
IC
451 var actual = page.evaluate(function() {
452 return $(".address:first").text();
453 });
454 if (actual != expected) {
455 console.log("Viacoin testnet address is incorrect");
456 console.log("Expected: " + expected);
457 console.log("Actual: " + actual);
458 fail();
459 }
460 next();
3eef9d0d 461 });
59193779
IC
462});
463},
464
88e2cdaa 465// Network can be set to jumbucks
59193779
IC
466function() {
467page.open(url, function(status) {
468 // set the phrase and coin
469 var expected = "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew";
470 page.evaluate(function() {
471 $(".phrase").val("abandon abandon ability");
472 $(".phrase").trigger("input");
473 $(".network option[selected]").removeAttr("selected");
474 $(".network option[value=8]").prop("selected", true);
475 $(".network").trigger("change");
476 });
477 // check the address is generated correctly
3eef9d0d 478 waitForGenerate(function() {
59193779
IC
479 var actual = page.evaluate(function() {
480 return $(".address:first").text();
481 });
482 if (actual != expected) {
483 console.log("Jumbucks address is incorrect");
484 console.log("Expected: " + expected);
485 console.log("Actual: " + actual);
486 fail();
487 }
488 next();
3eef9d0d 489 });
59193779
IC
490});
491},
492
88e2cdaa 493// Network can be set to clam
59193779
IC
494function() {
495page.open(url, function(status) {
496 // set the phrase and coin
497 var expected = "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y";
498 page.evaluate(function() {
499 $(".phrase").val("abandon abandon ability");
500 $(".phrase").trigger("input");
501 $(".network option[selected]").removeAttr("selected");
502 $(".network option[value=9]").prop("selected", true);
503 $(".network").trigger("change");
504 });
505 // check the address is generated correctly
3eef9d0d 506 waitForGenerate(function() {
59193779
IC
507 var actual = page.evaluate(function() {
508 return $(".address:first").text();
509 });
510 if (actual != expected) {
511 console.log("CLAM address is incorrect");
512 console.log("Expected: " + expected);
513 console.log("Actual: " + actual);
514 fail();
f3fad1b5
IC
515 }
516 next();
517 });
518});
519},
520
521// Network can be set to dash
522function() {
523page.open(url, function(status) {
524 // set the phrase and coin
525 var expected = "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT";
526 page.evaluate(function() {
527 $(".phrase").val("abandon abandon ability");
528 $(".phrase").trigger("input");
529 $(".network option[selected]").removeAttr("selected");
530 $(".network option[value=10]").prop("selected", true);
531 $(".network").trigger("change");
532 });
533 // check the address is generated correctly
534 waitForGenerate(function() {
535 var actual = page.evaluate(function() {
536 return $(".address:first").text();
537 });
538 if (actual != expected) {
539 console.log("DASH address is incorrect");
540 console.log("Expected: " + expected);
541 console.log("Actual: " + actual);
542 fail();
59193779 543 }
bfb3dab6
IC
544 next();
545 });
546});
547},
548
549// Network can be set to namecoin
550function() {
551page.open(url, function(status) {
552 // set the phrase and coin
553 var expected = "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2";
554 page.evaluate(function() {
555 $(".phrase").val("abandon abandon ability");
556 $(".phrase").trigger("input");
557 $(".network option[selected]").removeAttr("selected");
558 $(".network option[value=11]").prop("selected", true);
559 $(".network").trigger("change");
560 });
561 // check the address is generated correctly
562 waitForGenerate(function() {
563 var actual = page.evaluate(function() {
564 return $(".address:first").text();
565 });
566 if (actual != expected) {
567 console.log("Namecoin address is incorrect");
568 console.log("Expected: " + expected);
569 console.log("Actual: " + actual);
570 fail();
571 }
572 next();
573 });
574});
575},
576
577// Network can be set to peercoin
578function() {
579page.open(url, function(status) {
580 // set the phrase and coin
581 var expected = "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm";
582 page.evaluate(function() {
583 $(".phrase").val("abandon abandon ability");
584 $(".phrase").trigger("input");
585 $(".network option[selected]").removeAttr("selected");
586 $(".network option[value=12]").prop("selected", true);
587 $(".network").trigger("change");
588 });
589 // check the address is generated correctly
590 waitForGenerate(function() {
591 var actual = page.evaluate(function() {
592 return $(".address:first").text();
593 });
594 if (actual != expected) {
595 console.log("Peercoin address is incorrect");
596 console.log("Expected: " + expected);
597 console.log("Actual: " + actual);
598 fail();
599 }
59193779 600 next();
3eef9d0d 601 });
59193779
IC
602});
603},
604
88e2cdaa 605// BIP39 seed is set from phrase
c196ad55
IC
606function() {
607page.open(url, function(status) {
608 // set the phrase
609 var expected = "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
610 page.evaluate(function() {
611 $(".phrase").val("abandon abandon ability");
612 $(".phrase").trigger("input");
613 });
614 // check the address is generated correctly
3eef9d0d 615 waitForGenerate(function() {
c196ad55
IC
616 var actual = page.evaluate(function() {
617 return $(".seed").val();
618 });
619 if (actual != expected) {
620 console.log("BIP39 seed is incorrectly generated from mnemonic");
621 console.log("Expected: " + expected);
622 console.log("Actual: " + actual);
623 fail();
624 }
625 next();
3eef9d0d 626 });
c196ad55
IC
627});
628},
629
88e2cdaa 630// BIP32 root key is set from phrase
ec60b662
IC
631function() {
632page.open(url, function(status) {
633 // set the phrase
634 var expected = "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi";
635 page.evaluate(function() {
636 $(".phrase").val("abandon abandon ability");
637 $(".phrase").trigger("input");
638 });
639 // check the address is generated correctly
3eef9d0d 640 waitForGenerate(function() {
ec60b662
IC
641 var actual = page.evaluate(function() {
642 return $(".root-key").val();
643 });
644 if (actual != expected) {
645 console.log("Root key is incorrectly generated from mnemonic");
646 console.log("Expected: " + expected);
647 console.log("Actual: " + actual);
648 fail();
649 }
650 next();
3eef9d0d 651 });
ec60b662
IC
652});
653},
654
88e2cdaa 655// Tabs show correct addresses when changed
cf7258fd
IC
656function() {
657page.open(url, function(status) {
658 // set the phrase
659 var expected = "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz";
660 page.evaluate(function() {
661 $(".phrase").val("abandon abandon ability");
662 $(".phrase").trigger("input");
663 });
664 // change tabs
665 waitForGenerate(function() {
666 page.evaluate(function() {
667 $("#bip32-tab a").click();
668 });
669 // check the address is generated correctly
670 waitForGenerate(function() {
671 var actual = page.evaluate(function() {
672 return $(".address:first").text();
673 });
674 if (actual != expected) {
675 console.log("Clicking tab generates incorrect address");
676 console.log("Expected: " + expected);
677 console.log("Actual: " + actual);
678 fail();
679 }
680 next();
681 });
682 });
683});
684},
88e2cdaa
IC
685
686// BIP44 derivation path is shown
d077e1e7
IC
687function() {
688page.open(url, function(status) {
689 // set the phrase
690 var expected = "m/44'/0'/0'/0";
691 page.evaluate(function() {
692 $(".phrase").val("abandon abandon ability");
693 $(".phrase").trigger("input");
694 });
695 // check the derivation path of the first address
696 waitForGenerate(function() {
697 var actual = page.evaluate(function() {
698 return $("#bip44 .path").val();
699 });
700 if (actual != expected) {
701 console.log("BIP44 derivation path is incorrect");
702 console.log("Expected: " + expected);
703 console.log("Actual: " + actual);
704 fail();
705 }
706 next();
707 });
708});
709},
710
88e2cdaa 711// BIP44 extended private key is shown
4fd2925d
IC
712function() {
713page.open(url, function(status) {
714 // set the phrase
715 var expected = "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG";
716 page.evaluate(function() {
717 $(".phrase").val("abandon abandon ability");
718 $(".phrase").trigger("input");
719 });
06286adb 720 // check the BIP44 extended private key
4fd2925d
IC
721 waitForGenerate(function() {
722 var actual = page.evaluate(function() {
723 return $(".extended-priv-key").val();
724 });
725 if (actual != expected) {
726 console.log("BIP44 extended private key is incorrect");
727 console.log("Expected: " + expected);
728 console.log("Actual: " + actual);
729 fail();
730 }
731 next();
732 });
733});
734},
735
88e2cdaa 736// BIP44 extended public key is shown
39fd45bb
IC
737function() {
738page.open(url, function(status) {
739 // set the phrase
740 var expected = "xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM";
741 page.evaluate(function() {
742 $(".phrase").val("abandon abandon ability");
743 $(".phrase").trigger("input");
744 });
06286adb 745 // check the BIP44 extended public key
39fd45bb
IC
746 waitForGenerate(function() {
747 var actual = page.evaluate(function() {
748 return $(".extended-pub-key").val();
749 });
750 if (actual != expected) {
751 console.log("BIP44 extended public key is incorrect");
752 console.log("Expected: " + expected);
753 console.log("Actual: " + actual);
754 fail();
755 }
756 next();
757 });
758});
759},
760
06286adb
IC
761// BIP44 purpose field changes address list
762function() {
763page.open(url, function(status) {
764 // set the phrase
765 var expected = "1JbDzRJ2cDT8aat2xwKd6Pb2zzavow5MhF";
766 page.evaluate(function() {
767 $(".phrase").val("abandon abandon ability");
768 $(".phrase").trigger("input");
769 });
770 waitForGenerate(function() {
771 // change the bip44 purpose field to 45
772 page.evaluate(function() {
773 $("#bip44 .purpose").val("45");
774 $("#bip44 .purpose").trigger("input");
775 });
776 waitForGenerate(function() {
777 // check the address for the new derivation path
778 var actual = page.evaluate(function() {
779 return $(".address:first").text();
780 });
781 if (actual != expected) {
782 console.log("BIP44 purpose field generates incorrect address");
783 console.log("Expected: " + expected);
784 console.log("Actual: " + actual);
785 fail();
786 }
787 next();
788 });
789 });
790});
791},
792
88e2cdaa 793// BIP44 coin field changes address list
9eb72cdf
IC
794function() {
795page.open(url, function(status) {
796 // set the phrase
797 var expected = "1F6dB2djQYrxoyfZZmfr6D5voH8GkJTghk";
798 page.evaluate(function() {
799 $(".phrase").val("abandon abandon ability");
800 $(".phrase").trigger("input");
801 });
802 waitForGenerate(function() {
803 // change the bip44 purpose field to 45
804 page.evaluate(function() {
805 $("#bip44 .coin").val("1");
806 $("#bip44 .coin").trigger("input");
807 });
808 waitForGenerate(function() {
809 // check the address for the new derivation path
810 var actual = page.evaluate(function() {
811 return $(".address:first").text();
812 });
813 if (actual != expected) {
814 console.log("BIP44 coin field generates incorrect address");
815 console.log("Expected: " + expected);
816 console.log("Actual: " + actual);
817 fail();
818 }
819 next();
820 });
821 });
822});
823},
824
88e2cdaa 825// BIP44 account field changes address list
048bc3e0
IC
826function() {
827page.open(url, function(status) {
828 // set the phrase
829 var expected = "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
830 page.evaluate(function() {
831 $(".phrase").val("abandon abandon ability");
832 $(".phrase").trigger("input");
833 });
834 waitForGenerate(function() {
835 // change the bip44 purpose field to 45
836 page.evaluate(function() {
837 $("#bip44 .account").val("1");
838 $("#bip44 .account").trigger("input");
839 });
840 waitForGenerate(function() {
841 // check the address for the new derivation path
842 var actual = page.evaluate(function() {
843 return $(".address:first").text();
844 });
845 if (actual != expected) {
846 console.log("BIP44 account field generates incorrect address");
847 console.log("Expected: " + expected);
848 console.log("Actual: " + actual);
849 fail();
850 }
851 next();
852 });
853 });
854});
855},
856
fa4da086
IC
857// BIP44 change field changes address list
858function() {
859page.open(url, function(status) {
860 // set the phrase
861 var expected = "1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo";
862 page.evaluate(function() {
863 $(".phrase").val("abandon abandon ability");
864 $(".phrase").trigger("input");
865 });
866 waitForGenerate(function() {
867 // change the bip44 purpose field to 45
868 page.evaluate(function() {
869 $("#bip44 .change").val("1");
870 $("#bip44 .change").trigger("input");
871 });
872 waitForGenerate(function() {
873 // check the address for the new derivation path
874 var actual = page.evaluate(function() {
875 return $(".address:first").text();
876 });
877 if (actual != expected) {
878 console.log("BIP44 change field generates incorrect address");
879 console.log("Expected: " + expected);
880 console.log("Actual: " + actual);
881 fail();
882 }
883 next();
884 });
885 });
886});
887},
048bc3e0 888
88e2cdaa 889// BIP32 derivation path can be set
651382a3
IC
890function() {
891page.open(url, function(status) {
892 // set the phrase
893 var expected = "16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L";
894 page.evaluate(function() {
895 $(".phrase").val("abandon abandon ability");
896 $(".phrase").trigger("input");
897 });
898 // change tabs
899 waitForGenerate(function() {
900 page.evaluate(function() {
901 $("#bip32-tab a").click();
902 });
903 // set the derivation path to m/1
904 waitForGenerate(function() {
905 page.evaluate(function() {
906 $("#bip32 .path").val("m/1");
907 $("#bip32 .path").trigger("input");
908 });
909 // check the address is generated correctly
910 waitForGenerate(function() {
911 var actual = page.evaluate(function() {
912 return $(".address:first").text();
913 });
914 if (actual != expected) {
915 console.log("Custom BIP32 path generates incorrect address");
916 console.log("Expected: " + expected);
917 console.log("Actual: " + actual);
918 fail();
919 }
920 next();
921 });
922 });
923 });
924});
925},
926
88e2cdaa 927// BIP32 can use hardened derivation paths
651382a3
IC
928function() {
929page.open(url, function(status) {
930 // set the phrase
931 var expected = "14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4";
932 page.evaluate(function() {
933 $(".phrase").val("abandon abandon ability");
934 $(".phrase").trigger("input");
935 });
936 // change tabs
937 waitForGenerate(function() {
938 page.evaluate(function() {
939 $("#bip32-tab a").click();
940 });
941 // set the derivation path to m/0'
942 waitForGenerate(function() {
943 page.evaluate(function() {
944 $("#bip32 .path").val("m/0'");
945 $("#bip32 .path").trigger("input");
946 });
947 // check the address is generated correctly
948 waitForGenerate(function() {
949 var actual = page.evaluate(function() {
950 return $(".address:first").text();
951 });
952 if (actual != expected) {
953 console.log("Hardened BIP32 path generates incorrect address");
954 console.log("Expected: " + expected);
955 console.log("Actual: " + actual);
956 fail();
957 }
958 next();
959 });
960 });
961 });
962});
963},
964
88e2cdaa 965// BIP32 extended private key is shown
9e9dcfda
IC
966function() {
967page.open(url, function(status) {
968 // set the phrase
969 var expected = "xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe";
970 page.evaluate(function() {
971 $(".phrase").val("abandon abandon ability");
972 $(".phrase").trigger("input");
973 });
974 // change tabs
975 waitForGenerate(function() {
976 page.evaluate(function() {
977 $("#bip32-tab a").click();
978 });
979 // check the extended private key is generated correctly
980 waitForGenerate(function() {
981 var actual = page.evaluate(function() {
982 return $(".extended-priv-key").val();
983 });
984 if (actual != expected) {
985 console.log("BIP32 extended private key is incorrect");
986 console.log("Expected: " + expected);
987 console.log("Actual: " + actual);
988 fail();
989 }
990 next();
991 });
992 });
993});
994},
995
88e2cdaa 996// BIP32 extended public key is shown
9e9dcfda
IC
997function() {
998page.open(url, function(status) {
999 // set the phrase
1000 var expected = "xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P";
1001 page.evaluate(function() {
1002 $(".phrase").val("abandon abandon ability");
1003 $(".phrase").trigger("input");
1004 });
1005 // change tabs
1006 waitForGenerate(function() {
1007 page.evaluate(function() {
1008 $("#bip32-tab a").click();
1009 });
1010 // check the extended public key is generated correctly
1011 waitForGenerate(function() {
1012 var actual = page.evaluate(function() {
1013 return $(".extended-pub-key").val();
1014 });
1015 if (actual != expected) {
1016 console.log("BIP32 extended public key is incorrect");
1017 console.log("Expected: " + expected);
1018 console.log("Actual: " + actual);
1019 fail();
1020 }
1021 next();
1022 });
1023 });
1024});
1025},
88e2cdaa
IC
1026
1027// Derivation path is shown in table
5f844c62
IC
1028function() {
1029page.open(url, function(status) {
1030 // set the phrase
1031 var expected = "m/44'/0'/0'/0/0";
1032 page.evaluate(function() {
1033 $(".phrase").val("abandon abandon ability");
1034 $(".phrase").trigger("input");
1035 });
1036 // check for derivation path in table
1037 waitForGenerate(function() {
1038 var actual = page.evaluate(function() {
1039 return $(".index:first").text();
1040 });
1041 if (actual != expected) {
1042 console.log("Derivation path shown incorrectly in table");
1043 console.log("Expected: " + expected);
1044 console.log("Actual: " + actual);
1045 fail();
1046 }
1047 next();
1048 });
1049});
1050},
1051
88e2cdaa 1052// Derivation path for address can be hardened
4974fd7f
IC
1053function() {
1054page.open(url, function(status) {
1055 // set the phrase
1056 var expected = "18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd";
1057 page.evaluate(function() {
1058 $(".phrase").val("abandon abandon ability");
1059 $(".phrase").trigger("input");
1060 });
1061 // change tabs
1062 waitForGenerate(function() {
1063 page.evaluate(function() {
1064 $("#bip32-tab a").click();
1065 });
1066 waitForGenerate(function() {
1067 // select the hardened addresses option
1068 page.evaluate(function() {
1069 $(".hardened-addresses").prop("checked", true);
1070 $(".hardened-addresses").trigger("change");
1071 });
1072 waitForGenerate(function() {
1073 // check the generated address is hardened
1074 var actual = page.evaluate(function() {
1075 return $(".address:first").text();
1076 });
1077 if (actual != expected) {
1078 console.log("Hardened address is incorrect");
1079 console.log("Expected: " + expected);
1080 console.log("Actual: " + actual);
1081 fail();
1082 }
1083 next();
1084 });
1085 });
1086 });
1087});
1088},
1089
88e2cdaa 1090// Derivation path visibility can be toggled
a775b5c6
IC
1091function() {
1092page.open(url, function(status) {
1093 // set the phrase
1094 page.evaluate(function() {
1095 $(".phrase").val("abandon abandon ability");
1096 $(".phrase").trigger("input");
1097 });
a775b5c6
IC
1098 waitForGenerate(function() {
1099 // toggle path visibility
1100 page.evaluate(function() {
1101 $(".index-toggle").click();
1102 });
1103 // check the path is not visible
1104 var isInvisible = page.evaluate(function() {
1105 return $(".index:first span").hasClass("invisible");
1106 });
1107 if (!isInvisible) {
1108 console.log("Toggled derivation path is visible");
1109 fail();
1110 }
1111 next();
1112 });
1113});
1114},
1115
88e2cdaa 1116// Address is shown
06c4c6e3
IC
1117function() {
1118page.open(url, function(status) {
1119 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1120 // set the phrase
1121 page.evaluate(function() {
1122 $(".phrase").val("abandon abandon ability").trigger("input");
1123 });
1124 // get the address
1125 waitForGenerate(function() {
1126 var actual = page.evaluate(function() {
1127 return $(".address:first").text();
1128 });
1129 if (actual != expected) {
1130 console.log("Address is not shown");
1131 console.log("Expected: " + expected);
1132 console.log("Got: " + actual);
1133 fail();
1134 }
1135 next();
1136 });
1137});
1138},
1139
88e2cdaa 1140// Addresses are shown in order of derivation path
3f1faa4d
IC
1141function() {
1142page.open(url, function(status) {
1143 // set the phrase
1144 page.evaluate(function() {
1145 $(".phrase").val("abandon abandon ability").trigger("input");
1146 });
1147 // get the derivation paths
1148 waitForGenerate(function() {
1149 var paths = page.evaluate(function() {
1150 return $(".index").map(function(i, e) {
1151 return $(e).text();
1152 });
1153 });
1154 if (paths.length != 20) {
1155 console.log("Total paths is less than expected: " + paths.length);
1156 fail();
1157 }
1158 for (var i=0; i<paths.length; i++) {
1159 var expected = "m/44'/0'/0'/0/" + i;
1160 var actual = paths[i];
1161 if (actual != expected) {
1162 console.log("Path " + i + " is incorrect");
1163 console.log("Expected: " + expected);
1164 console.log("Actual: " + actual);
1165 fail();
1166 }
1167 }
1168 next();
1169 });
1170});
1171},
21372fab 1172
88e2cdaa 1173// Address visibility can be toggled
21372fab
IC
1174function() {
1175page.open(url, function(status) {
1176 // set the phrase
1177 page.evaluate(function() {
1178 $(".phrase").val("abandon abandon ability");
1179 $(".phrase").trigger("input");
1180 });
1181 waitForGenerate(function() {
1182 // toggle address visibility
1183 page.evaluate(function() {
1184 $(".address-toggle").click();
1185 });
1186 // check the address is not visible
1187 var isInvisible = page.evaluate(function() {
1188 return $(".address:first span").hasClass("invisible");
1189 });
1190 if (!isInvisible) {
1191 console.log("Toggled address is visible");
1192 fail();
1193 }
1194 next();
1195 });
1196});
1197},
1198
1b12b2f5
IC
1199// Public key is shown
1200function() {
1201page.open(url, function(status) {
1202 var expected = "033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3";
1203 // set the phrase
1204 page.evaluate(function() {
1205 $(".phrase").val("abandon abandon ability").trigger("input");
1206 });
1207 // get the address
1208 waitForGenerate(function() {
1209 var actual = page.evaluate(function() {
1210 return $(".pubkey:first").text();
1211 });
1212 if (actual != expected) {
1213 console.log("Public key is not shown");
1214 console.log("Expected: " + expected);
1215 console.log("Got: " + actual);
1216 fail();
1217 }
1218 next();
1219 });
1220});
1221},
1222
1223// Public key visibility can be toggled
1224function() {
1225page.open(url, function(status) {
1226 // set the phrase
1227 page.evaluate(function() {
1228 $(".phrase").val("abandon abandon ability");
1229 $(".phrase").trigger("input");
1230 });
1231 waitForGenerate(function() {
1232 // toggle public key visibility
1233 page.evaluate(function() {
1234 $(".public-key-toggle").click();
1235 });
1236 // check the public key is not visible
1237 var isInvisible = page.evaluate(function() {
1238 return $(".pubkey:first span").hasClass("invisible");
1239 });
1240 if (!isInvisible) {
1241 console.log("Toggled public key is visible");
1242 fail();
1243 }
1244 next();
1245 });
1246});
1247},
1248
88e2cdaa 1249// Private key is shown
8cd5e231
IC
1250function() {
1251page.open(url, function(status) {
1252 var expected = "L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE";
1253 // set the phrase
1254 page.evaluate(function() {
1255 $(".phrase").val("abandon abandon ability").trigger("input");
1256 });
1257 // get the address
1258 waitForGenerate(function() {
1259 var actual = page.evaluate(function() {
1260 return $(".privkey:first").text();
1261 });
1262 if (actual != expected) {
1263 console.log("Private key is not shown");
1264 console.log("Expected: " + expected);
1265 console.log("Got: " + actual);
1266 fail();
1267 }
1268 next();
1269 });
1270});
1271},
1272
88e2cdaa 1273// Private key visibility can be toggled
6848d03b
IC
1274function() {
1275page.open(url, function(status) {
1276 // set the phrase
1277 page.evaluate(function() {
1278 $(".phrase").val("abandon abandon ability");
1279 $(".phrase").trigger("input");
1280 });
1281 waitForGenerate(function() {
1282 // toggle private key visibility
1283 page.evaluate(function() {
1284 $(".private-key-toggle").click();
1285 });
1286 // check the private key is not visible
1287 var isInvisible = page.evaluate(function() {
1288 return $(".privkey:first span").hasClass("invisible");
1289 });
1290 if (!isInvisible) {
1291 console.log("Toggled private key is visible");
1292 fail();
1293 }
1294 next();
1295 });
1296});
1297},
88e2cdaa
IC
1298
1299// More addresses can be generated
35631659
IC
1300function() {
1301page.open(url, function(status) {
1302 // set the phrase
1303 page.evaluate(function() {
1304 $(".phrase").val("abandon abandon ability");
1305 $(".phrase").trigger("input");
1306 });
1307 waitForGenerate(function() {
1308 // generate more addresses
1309 page.evaluate(function() {
1310 $(".more").click();
1311 });
1312 waitForGenerate(function() {
1313 // check there are more addresses
1314 var addressCount = page.evaluate(function() {
1315 return $(".address").length;
1316 });
1317 if (addressCount != 40) {
1318 console.log("More addresses cannot be generated");
1319 fail();
1320 }
1321 next();
1322 });
1323 });
1324});
1325},
1326
88e2cdaa 1327// A custom number of additional addresses can be generated
8a89b9da
IC
1328function() {
1329page.open(url, function(status) {
1330 // set the phrase
1331 page.evaluate(function() {
1332 $(".phrase").val("abandon abandon ability");
1333 $(".phrase").trigger("input");
1334 });
1335 waitForGenerate(function() {
1336 // get the current number of addresses
1337 var oldAddressCount = page.evaluate(function() {
1338 return $(".address").length;
1339 });
1340 // set a custom number of additional addresses
1341 page.evaluate(function() {
1342 $(".rows-to-add").val(1);
1343 });
1344 // generate more addresses
1345 page.evaluate(function() {
1346 $(".more").click();
1347 });
1348 waitForGenerate(function() {
1349 // check there are the correct number of addresses
1350 var newAddressCount = page.evaluate(function() {
1351 return $(".address").length;
1352 });
1353 if (newAddressCount - oldAddressCount != 1) {
1354 console.log("Number of additional addresses cannot be customized");
1355 console.log(newAddressCount)
1356 console.log(oldAddressCount)
1357 fail();
1358 }
1359 next();
1360 });
1361 });
1362});
1363},
1364
88e2cdaa 1365// Additional addresses are shown in order of derivation path
4d387bf5
IC
1366function() {
1367page.open(url, function(status) {
1368 // set the phrase
1369 page.evaluate(function() {
1370 $(".phrase").val("abandon abandon ability").trigger("input");
1371 });
1372 waitForGenerate(function() {
1373 // generate more addresses
1374 page.evaluate(function() {
1375 $(".more").click();
1376 });
1377 // get the derivation paths
1378 waitForGenerate(function() {
1379 var paths = page.evaluate(function() {
1380 return $(".index").map(function(i, e) {
1381 return $(e).text();
1382 });
1383 });
1384 if (paths.length != 40) {
1385 console.log("Total additional paths is less than expected: " + paths.length);
1386 fail();
1387 }
1388 for (var i=0; i<paths.length; i++) {
1389 var expected = "m/44'/0'/0'/0/" + i;
1390 var actual = paths[i];
1391 if (actual != expected) {
1392 console.log("Path " + i + " is not in correct order");
1393 console.log("Expected: " + expected);
1394 console.log("Actual: " + actual);
1395 fail();
1396 }
1397 }
1398 next();
1399 });
1400 });
1401});
1402},
88e2cdaa
IC
1403
1404// BIP32 root key can be set by the user
61ed16a9
IC
1405function() {
1406page.open(url, function(status) {
1407 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1408 // set the root key
1409 page.evaluate(function() {
1410 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1411 });
1412 waitForGenerate(function() {
1413 var actual = page.evaluate(function() {
1414 return $(".address:first").text();
1415 });
1416 if (actual != expected) {
bc324fd2 1417 console.log("Setting BIP32 root key results in wrong address");
61ed16a9
IC
1418 console.log("Expected: " + expected);
1419 console.log("Actual: " + actual);
1420 fail();
1421 }
1422 next();
1423 });
1424});
1425},
1426
54563907 1427// Setting BIP32 root key clears the existing phrase, passphrase and seed
bc324fd2
IC
1428function() {
1429page.open(url, function(status) {
1430 var expected = "";
1431 // set a mnemonic
1432 page.evaluate(function() {
1433 $(".phrase").val("A non-blank but invalid value");
1434 });
1435 // Accept any confirm dialogs
1436 page.onConfirm = function() {
1437 return true;
1438 };
1439 // set the root key
1440 page.evaluate(function() {
1441 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1442 });
1443 waitForGenerate(function() {
1444 var actual = page.evaluate(function() {
1445 return $(".phrase").val();
1446 });
1447 if (actual != expected) {
1448 console.log("Phrase not cleared when setting BIP32 root key");
1449 console.log("Expected: " + expected);
1450 console.log("Actual: " + actual);
1451 fail();
1452 }
1453 next();
1454 });
1455});
1456},
1457
54563907 1458// Clearing of phrase, passphrase and seed can be cancelled by user
abfbe450
IC
1459function() {
1460page.open(url, function(status) {
1461 var expected = "abandon abandon ability";
1462 // set a mnemonic
1463 page.evaluate(function() {
1464 $(".phrase").val("abandon abandon ability");
1465 });
1466 // Cancel any confirm dialogs
1467 page.onConfirm = function() {
1468 return false;
1469 };
1470 // set the root key
1471 page.evaluate(function() {
1472 $(".root-key").val("xprv9s21ZrQH143K3d3vzEDD3KpSKmxsZ3y7CqhAL1tinwtP6wqK4TKEKjpBuo6P2hUhB6ZENo7TTSRytiP857hBZVpBdk8PooFuRspE1eywwNZ").trigger("input");
1473 });
1474 var actual = page.evaluate(function() {
1475 return $(".phrase").val();
1476 });
1477 if (actual != expected) {
1478 console.log("Phrase not retained when cancelling changes to BIP32 root key");
1479 console.log("Expected: " + expected);
1480 console.log("Actual: " + actual);
1481 fail();
1482 }
1483 next();
1484});
1485},
7ff86d4c 1486
88e2cdaa 1487// Custom BIP32 root key is used when changing the derivation path
7ff86d4c
IC
1488function() {
1489page.open(url, function(status) {
1490 var expected = "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1491 // set the root key
1492 page.evaluate(function() {
1493 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1494 });
1495 waitForGenerate(function() {
1496 // change the derivation path
1497 page.evaluate(function() {
1498 $("#account").val("1").trigger("input");
1499 });
1500 // check the bip32 root key is used for derivation, not the blank phrase
1501 waitForGenerate(function() {
1502 var actual = page.evaluate(function() {
1503 return $(".address:first").text();
1504 });
1505 if (actual != expected) {
1506 console.log("Changing the derivation path does not use BIP32 root key");
1507 console.log("Expected: " + expected);
1508 console.log("Actual: " + actual);
1509 fail();
1510 }
1511 next();
1512 });
1513 });
1514});
1515},
88e2cdaa
IC
1516
1517// Incorrect mnemonic shows error
c3719b00
IC
1518function() {
1519page.open(url, function(status) {
1520 // set the root key
1521 page.evaluate(function() {
1522 $(".phrase").val("abandon abandon abandon").trigger("input");
1523 });
1524 waitForFeedback(function() {
1525 // check there is an error shown
1526 var feedback = page.evaluate(function() {
1527 return $(".feedback").text();
1528 });
1529 if (feedback.length <= 0) {
1530 console.log("Invalid mnemonic does not show error");
1531 fail();
1532 }
1533 next();
1534 });
1535});
1536},
1537
88e2cdaa 1538// Incorrect word shows suggested replacement
20f80cfa
IC
1539function() {
1540page.open(url, function(status) {
1541 // set the root key
1542 page.evaluate(function() {
1543 $(".phrase").val("abandon abandon abiliti").trigger("input");
1544 });
1545 // check there is a suggestion shown
1546 waitForFeedback(function() {
1547 var feedback = page.evaluate(function() {
1548 return $(".feedback").text();
1549 });
1550 if (feedback.indexOf("did you mean ability?") < 0) {
1551 console.log("Incorrect word does not show suggested replacement");
1552 console.log("Error: " + error);
1553 fail();
1554 }
1555 next();
1556 });
1557});
1558},
1559
88e2cdaa 1560// Incorrect BIP32 root key shows error
02f4a90e
IC
1561function() {
1562page.open(url, function(status) {
1563 // set the root key
1564 page.evaluate(function() {
1565 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj").trigger("input");
1566 });
1567 // check there is an error shown
1568 waitForFeedback(function() {
1569 var feedback = page.evaluate(function() {
1570 return $(".feedback").text();
1571 });
1572 if (feedback != "Invalid root key") {
1573 console.log("Invalid root key does not show error");
1574 console.log("Error: " + error);
1575 fail();
1576 }
1577 next();
1578 });
1579});
1580},
1581
88e2cdaa 1582// Derivation path not starting with m shows error
f976b541
IC
1583function() {
1584page.open(url, function(status) {
1585 // set the mnemonic phrase
1586 page.evaluate(function() {
1587 $(".phrase").val("abandon abandon ability").trigger("input");
1588 });
1589 waitForGenerate(function() {
1590 // select the bip32 tab so custom derivation path can be set
1591 page.evaluate(function() {
1592 $("#bip32-tab a").click();
1593 });
1594 waitForGenerate(function() {
1595 // set the incorrect derivation path
1596 page.evaluate(function() {
1597 $("#bip32 .path").val("n/0").trigger("input");
1598 });
1599 waitForFeedback(function() {
1600 var feedback = page.evaluate(function() {
1601 return $(".feedback").text();
1602 });
1603 if (feedback != "First character must be 'm'") {
1604 console.log("Derivation path not starting with m should show error");
1605 console.log("Error: " + error);
1606 fail();
1607 }
1608 next();
1609 });
1610 });
1611 });
1612});
1613},
1614
88e2cdaa 1615// Derivation path containing invalid characters shows useful error
55c0ffcb
IC
1616function() {
1617page.open(url, function(status) {
1618 // set the mnemonic phrase
1619 page.evaluate(function() {
1620 $(".phrase").val("abandon abandon ability").trigger("input");
1621 });
1622 waitForGenerate(function() {
1623 // select the bip32 tab so custom derivation path can be set
1624 page.evaluate(function() {
1625 $("#bip32-tab a").click();
1626 });
1627 waitForGenerate(function() {
1628 // set the incorrect derivation path
1629 page.evaluate(function() {
1630 $("#bip32 .path").val("m/1/0wrong1/1").trigger("input");
1631 });
1632 waitForFeedback(function() {
1633 var feedback = page.evaluate(function() {
1634 return $(".feedback").text();
1635 });
1636 if (feedback != "Invalid characters 0wrong1 found at depth 2") {
1637 console.log("Derivation path with invalid characters should show error");
1638 console.log("Error: " + error);
1639 fail();
1640 }
1641 next();
1642 });
1643 });
1644 });
1645});
1646},
88e2cdaa
IC
1647
1648// Github Issue 11: Default word length is 15
b630f83d 1649// https://github.com/iancoleman/bip39/issues/11
ca976aa9
IC
1650function() {
1651page.open(url, function(status) {
1652 // get the word length
1653 var defaultLength = page.evaluate(function() {
1654 return $(".strength").val();
1655 });
1656 if (defaultLength != 15) {
1657 console.log("Default word length is not 15");
1658 fail();
1659 }
1660 next();
1661});
1662},
1663
88e2cdaa
IC
1664
1665// Github Issue 12: Generate more rows with private keys hidden
b630f83d 1666// https://github.com/iancoleman/bip39/issues/12
c97627fa
IC
1667function() {
1668page.open(url, function(status) {
1669 // set the phrase
1670 page.evaluate(function() {
1671 $(".phrase").val("abandon abandon ability");
1672 $(".phrase").trigger("input");
1673 });
1674 waitForGenerate(function() {
1675 // toggle private keys hidden, then generate more addresses
1676 page.evaluate(function() {
1677 $(".private-key-toggle").click();
1678 $(".more").click();
1679 });
1680 waitForGenerate(function() {
1681 // check more have been generated
1682 var expected = 40;
1683 var numPrivKeys = page.evaluate(function() {
1684 return $(".privkey").length;
1685 });
1686 if (numPrivKeys != expected) {
1687 console.log("Wrong number of addresses when clicking 'more' with hidden privkeys");
1688 console.log("Expected: " + expected);
1689 console.log("Actual: " + numPrivKeys);
1690 fail();
1691 }
1692 // check no private keys are shown
1693 var numHiddenPrivKeys = page.evaluate(function() {
1694 return $(".privkey span[class=invisible]").length;
1695 });
1696 if (numHiddenPrivKeys != expected) {
1697 console.log("Generating more does not retain hidden state of privkeys");
1698 console.log("Expected: " + expected);
1699 console.log("Actual: " + numHiddenPrivKeys);
1700 fail();
1701 }
1702 next();
1703 });
1704 });
1705});
1706},
88e2cdaa
IC
1707
1708// Github Issue 19: Mnemonic is not sensitive to whitespace
b630f83d 1709// https://github.com/iancoleman/bip39/issues/19
a7becc43
IC
1710function() {
1711page.open(url, function(status) {
1712 // set the phrase
1713 var expected = "xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC";
1714 page.evaluate(function() {
1715 var doubleSpace = " ";
1716 $(".phrase").val("urge cat" + doubleSpace + "bid");
1717 $(".phrase").trigger("input");
1718 });
1719 waitForGenerate(function() {
1720 // Check the bip32 root key is correct
1721 var actual = page.evaluate(function() {
1722 return $(".root-key").val();
1723 });
1724 if (actual != expected) {
1725 console.log("Mnemonic is sensitive to whitespace");
1726 console.log("Expected: " + expected);
1727 console.log("Actual: " + actual);
1728 fail();
1729 }
1730 next();
1731 });
1732});
1733},
88e2cdaa 1734
e3001539 1735// Github Issue 23: Part 1: Use correct derivation path when changing tabs
b630f83d 1736// https://github.com/iancoleman/bip39/issues/23
e3001539
IC
1737function() {
1738page.open(url, function(status) {
1739 // 1) and 2) set the phrase
1740 page.evaluate(function() {
1741 $(".phrase").val("abandon abandon ability").trigger("input");
1742 });
1743 waitForGenerate(function() {
1744 // 3) select bip32 tab
1745 page.evaluate(function() {
1746 $("#bip32-tab a").click();
1747 });
1748 waitForGenerate(function() {
1749 // 4) switch from bitcoin to litecoin
1750 page.evaluate(function() {
1751 $(".network").val("2").trigger("change");
1752 });
1753 waitForGenerate(function() {
1754 // 5) Check derivation path is displayed correctly
1755 var expected = "m/0/0";
1756 var actual = page.evaluate(function() {
1757 return $(".index:first").text();
1758 });
1759 if (actual != expected) {
1760 console.log("Github Issue 23 Part 1: derivation path display error");
1761 console.log("Expected: " + expected);
1762 console.log("Actual: " + actual);
1763 fail();
1764 }
1765 // 5) Check address is displayed correctly
1766 var expected = "LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5";
1767 var actual = page.evaluate(function() {
1768 return $(".address:first").text();
1769 });
1770 if (actual != expected) {
1771 console.log("Github Issue 23 Part 1: address display error");
1772 console.log("Expected: " + expected);
1773 console.log("Actual: " + actual);
1774 fail();
1775 }
1776 next();
1777 });
1778 });
1779 });
1780});
1781},
1782
1783// Github Issue 23 Part 2: Coin selection in derivation path
b630f83d 1784// https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
af4fd3a2
IC
1785function() {
1786page.open(url, function(status) {
1787 // set the phrase
1788 page.evaluate(function() {
1789 $(".phrase").val("abandon abandon ability").trigger("input");
1790 });
1791 waitForGenerate(function() {
1792 // switch from bitcoin to clam
1793 page.evaluate(function() {
1794 $(".network").val("9").trigger("change");
1795 });
1796 waitForGenerate(function() {
1797 // check derivation path is displayed correctly
1798 var expected = "m/44'/23'/0'/0/0";
1799 var actual = page.evaluate(function() {
1800 return $(".index:first").text();
1801 });
1802 if (actual != expected) {
1803 console.log("Github Issue 23 Part 2: Coin in BIP44 derivation path is incorrect");
1804 console.log("Expected: " + expected);
1805 console.log("Actual: " + actual);
1806 fail();
1807 }
1808 next();
1809 });
1810 });
1811});
1812},
88e2cdaa 1813
f3d0aca1 1814// Github Issue 26: When using a Root key derrived altcoins are incorrect
b630f83d 1815// https://github.com/iancoleman/bip39/issues/26
558ef9ac
IC
1816function() {
1817page.open(url, function(status) {
1818 // 1) 2) and 3) set the root key
1819 page.evaluate(function() {
1820 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1821 });
1822 waitForGenerate(function() {
1823 // 4) switch from bitcoin to viacoin
1824 page.evaluate(function() {
1825 $(".network").val("6").trigger("change");
1826 });
1827 waitForGenerate(function() {
1828 // 5) ensure the derived address is correct
1829 var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
1830 var actual = page.evaluate(function() {
1831 return $(".address:first").text();
1832 });
1833 if (actual != expected) {
1834 console.log("Github Issue 26: address is incorrect when changing networks and using root-key to derive");
1835 console.log("Expected: " + expected);
1836 console.log("Actual: " + actual);
1837 fail();
1838 }
1839 next();
1840 });
1841 });
1842});
1843},
f3d0aca1 1844
e1bae843
IC
1845// Selecting a language with no existing phrase should generate a phrase in
1846// that language.
1847function() {
1848page.open(url, function(status) {
1849 // Select a language
1850 // Need to manually simulate hash being set due to quirk between
1851 // 'click' event triggered by javascript vs triggered by mouse.
1852 // Perhaps look into page.sendEvent
1853 // http://phantomjs.org/api/webpage/method/send-event.html
1854 page.evaluate(function() {
1855 window.location.hash = "#japanese";
1856 $("a[href='#japanese']").trigger("click");
1857 });
1858 waitForGenerate(function() {
1859 // Check the mnemonic is in Japanese
1860 var phrase = page.evaluate(function() {
1861 return $(".phrase").val();
1862 });
1863 if (phrase.length <= 0) {
1864 console.log("No Japanese phrase generated");
1865 fail();
1866 }
1867 if (phrase.charCodeAt(0) < 128) {
1868 console.log("First character of Japanese phrase is ascii");
1869 console.log("Phrase: " + phrase);
1870 fail();
1871 }
1872 next();
1873 });
1874});
1875},
1876
1877// Selecting a language with existing phrase should update the phrase to use
1878// that language.
1879function() {
1880page.open(url, function(status) {
1881 // Set the phrase to an English phrase.
1882 page.evaluate(function() {
1883 $(".phrase").val("abandon abandon ability").trigger("input");
1884 });
1885 waitForGenerate(function() {
1886 // Change to Italian
1887 // Need to manually simulate hash being set due to quirk between
1888 // 'click' event triggered by javascript vs triggered by mouse.
1889 // Perhaps look into page.sendEvent
1890 // http://phantomjs.org/api/webpage/method/send-event.html
1891 page.evaluate(function() {
1892 window.location.hash = "#italian";
1893 $("a[href='#italian']").trigger("click");
1894 });
1895 waitForGenerate(function() {
1896 // Check only the language changes, not the phrase
1897 var expected = "abaco abaco abbaglio";
1898 var actual = page.evaluate(function() {
1899 return $(".phrase").val();
1900 });
1901 if (actual != expected) {
1902 console.log("Changing language with existing phrase");
1903 console.log("Expected: " + expected);
1904 console.log("Actual: " + actual);
1905 fail();
1906 }
1907 // Check the address is correct
1908 var expected = "1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV";
1909 var actual = page.evaluate(function() {
1910 return $(".address:first").text();
1911 });
1912 if (actual != expected) {
1913 console.log("Changing language generates incorrect address");
1914 console.log("Expected: " + expected);
1915 console.log("Actual: " + actual);
1916 fail();
1917 }
1918 next();
1919 });
1920 });
1921});
1922},
1923
1924// Suggested replacement for erroneous word in non-English language
1925function() {
1926page.open(url, function(status) {
1927 // Set an incorrect phrase in Italian
1928 page.evaluate(function() {
1929 $(".phrase").val("abaco abaco zbbaglio").trigger("input");
1930 });
1931 waitForFeedback(function() {
1932 // Check the suggestion is correct
1933 var feedback = page.evaluate(function() {
1934 return $(".feedback").text();
1935 });
1936 if (feedback.indexOf("did you mean abbaglio?") < 0) {
1937 console.log("Incorrect Italian word does not show suggested replacement");
1938 console.log("Error: " + error);
1939 fail();
1940 }
1941 next();
1942 });
1943});
1944},
1945
1946
1947// Japanese word does not break across lines.
1948// Point 2 from
1949// https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
1950function() {
1951page.open(url, function(status) {
1952 hasWordBreakCss = page.content.indexOf("word-break: keep-all;") > -1;
1953 if (!hasWordBreakCss) {
1954 console.log("Japanese words can break across lines mid-word");
1955 console.log("Check CSS for '.phrase { word-break: keep-all; }'");
1956 fail();
1957 }
1958 // Run the next test
1959 next();
1960});
1961},
1962
1963// Language can be specified at page load using hash value in url
1964function() {
1965page.open(url, function(status) {
1966 // Set the page hash as if it were on a fresh page load
1967 page.evaluate(function() {
1968 window.location.hash = "#japanese";
1969 });
1970 // Generate a random phrase
1971 page.evaluate(function() {
1972 $(".generate").trigger("click");
1973 });
1974 waitForGenerate(function() {
1975 // Check the phrase is in Japanese
1976 var phrase = page.evaluate(function() {
1977 return $(".phrase").val();
1978 });
1979 if (phrase.length <= 0) {
1980 console.log("No phrase generated using url hash");
1981 fail();
1982 }
1983 if (phrase.charCodeAt(0) < 128) {
1984 console.log("Language not detected from url hash on page load.");
1985 console.log("Phrase: " + phrase);
1986 fail();
1987 }
1988 next();
1989 });
1990});
1991},
1992
c6624d51
IC
1993// Entropy unit tests
1994function() {
1995page.open(url, function(status) {
adc8ce12 1996 var response = page.evaluate(function() {
c6624d51
IC
1997 var e;
1998 // binary entropy is detected
adc8ce12
IC
1999 try {
2000 e = Entropy.fromString("01010101");
2001 if (e.base.str != "binary") {
2002 return "Binary entropy not detected correctly";
2003 }
2004 }
2005 catch (e) {
2006 return e.message;
c6624d51
IC
2007 }
2008 // base6 entropy is detected
adc8ce12
IC
2009 try {
2010 e = Entropy.fromString("012345012345");
2011 if (e.base.str != "base 6") {
2012 return "base6 entropy not detected correctly";
2013 }
2014 }
2015 catch (e) {
2016 return e.message;
c6624d51
IC
2017 }
2018 // dice entropy is detected
adc8ce12
IC
2019 try {
2020 e = Entropy.fromString("123456123456");
2021 if (e.base.str != "base 6 (dice)") {
2022 return "dice entropy not detected correctly";
2023 }
2024 }
2025 catch (e) {
2026 return e.message;
c6624d51
IC
2027 }
2028 // base10 entropy is detected
adc8ce12
IC
2029 try {
2030 e = Entropy.fromString("0123456789");
2031 if (e.base.str != "base 10") {
2032 return "base10 entropy not detected correctly";
2033 }
2034 }
2035 catch (e) {
2036 return e.message;
c6624d51
IC
2037 }
2038 // hex entropy is detected
adc8ce12
IC
2039 try {
2040 e = Entropy.fromString("0123456789ABCDEF");
2041 if (e.base.str != "hexadecimal") {
2042 return "hexadecimal entropy not detected correctly";
2043 }
2044 }
2045 catch (e) {
2046 return e.message;
2047 }
2048 // card entropy is detected
2049 try {
2050 e = Entropy.fromString("AC4DTHKS");
2051 if (e.base.str != "card") {
2052 return "card entropy not detected correctly";
2053 }
2054 }
2055 catch (e) {
2056 return e.message;
c6624d51
IC
2057 }
2058 // entropy is case insensitive
adc8ce12
IC
2059 try {
2060 e = Entropy.fromString("aBcDeF");
2061 if (e.cleanStr != "aBcDeF") {
2062 return "Entropy should not be case sensitive";
2063 }
2064 }
2065 catch (e) {
2066 return e.message;
c6624d51
IC
2067 }
2068 // dice entropy is converted to base6
adc8ce12
IC
2069 try {
2070 e = Entropy.fromString("123456");
425b75a9 2071 if (e.cleanStr != "123450") {
adc8ce12
IC
2072 return "Dice entropy is not automatically converted to base6";
2073 }
2074 }
2075 catch (e) {
2076 return e.message;
c6624d51
IC
2077 }
2078 // dice entropy is preferred to base6 if ambiguous
adc8ce12
IC
2079 try {
2080 e = Entropy.fromString("12345");
2081 if (e.base.str != "base 6 (dice)") {
2082 return "dice not used as default over base 6";
2083 }
2084 }
2085 catch (e) {
2086 return e.message;
c6624d51
IC
2087 }
2088 // unused characters are ignored
adc8ce12
IC
2089 try {
2090 e = Entropy.fromString("fghijkl");
2091 if (e.cleanStr != "f") {
2092 return "additional characters are not ignored";
2093 }
2094 }
2095 catch (e) {
2096 return e.message;
c6624d51
IC
2097 }
2098 // the lowest base is used by default
2099 // 7 could be decimal or hexadecimal, but should be detected as decimal
adc8ce12
IC
2100 try {
2101 e = Entropy.fromString("7");
2102 if (e.base.str != "base 10") {
2103 return "lowest base is not used";
2104 }
c6624d51 2105 }
adc8ce12
IC
2106 catch (e) {
2107 return e.message;
c6624d51
IC
2108 }
2109 // Leading zeros are retained
adc8ce12
IC
2110 try {
2111 e = Entropy.fromString("000A");
2112 if (e.cleanStr != "000A") {
2113 return "Leading zeros are not retained";
2114 }
2115 }
2116 catch (e) {
2117 return e.message;
c6624d51
IC
2118 }
2119 // Leading zeros are correctly preserved for hex in binary string
adc8ce12
IC
2120 try {
2121 e = Entropy.fromString("2A");
2122 if (e.binaryStr != "00101010") {
2123 return "Hex leading zeros are not correct in binary";
2124 }
2125 }
2126 catch (e) {
2127 return e.message;
2128 }
1cf1bbaf
IC
2129 // Leading zeros for base 6 as binary string
2130 // 20 = 2 events at 2.58 bits per event = 5 bits
2131 // 20 in base 6 = 12 in base 10 = 1100 in base 2
2132 // so it needs 1 bit of padding to be the right bit length
adc8ce12 2133 try {
1cf1bbaf
IC
2134 e = Entropy.fromString("20");
2135 if (e.binaryStr != "01100") {
0d0f07f9
IC
2136 return "Base 6 as binary has leading zeros";
2137 }
2138 }
2139 catch (e) {
2140 return e.message;
2141 }
1cf1bbaf 2142 // Leading zeros for base 10 as binary string
0d0f07f9 2143 try {
1cf1bbaf
IC
2144 e = Entropy.fromString("17");
2145 if (e.binaryStr != "010001") {
0d0f07f9
IC
2146 return "Base 10 as binary has leading zeros";
2147 }
2148 }
2149 catch (e) {
2150 return e.message;
2151 }
87ad2c6e
IC
2152 // Leading zeros for card entropy as binary string.
2153 // Card entropy is hashed so 2c does not produce leading zeros.
0d0f07f9 2154 try {
87ad2c6e
IC
2155 e = Entropy.fromString("4c");
2156 if (e.binaryStr != "0001") {
0d0f07f9 2157 return "Card entropy as binary has leading zeros";
adc8ce12
IC
2158 }
2159 }
2160 catch (e) {
2161 return e.message;
c6624d51
IC
2162 }
2163 // Keyboard mashing results in weak entropy
2164 // Despite being a long string, it's less than 30 bits of entropy
adc8ce12
IC
2165 try {
2166 e = Entropy.fromString("aj;se ifj; ask,dfv js;ifj");
2167 if (e.binaryStr.length >= 30) {
2168 return "Keyboard mashing should produce weak entropy";
2169 }
c6624d51 2170 }
adc8ce12
IC
2171 catch (e) {
2172 return e.message;
2173 }
2174 // Card entropy is used if every pair could be a card
2175 try {
2176 e = Entropy.fromString("4c3c2c");
2177 if (e.base.str != "card") {
2178 return "Card entropy not used if all pairs are cards";
2179 }
2180 }
2181 catch (e) {
2182 return e.message;
2183 }
2184 // Card entropy uses base 52
2185 // [ cards, binary ]
2186 try {
2187 var cards = [
87ad2c6e
IC
2188 [ "ac", "0100" ],
2189 [ "acqs", "10111101" ],
2190 [ "acks", "11110000" ],
2191 [ "2cac", "11000010" ],
2192 [ "2c", "1000" ],
2193 [ "3d", "1111" ],
2194 [ "4h", "0011" ],
2195 [ "5s", "1001" ],
2196 [ "6c", "1011" ],
2197 [ "7d", "1101" ],
2198 [ "8h", "1011" ],
2199 [ "9s", "1010" ],
2200 [ "tc", "1101" ],
2201 [ "jd", "1101" ],
2202 [ "qh", "1100" ],
2203 [ "ks", "1111" ],
2204 [ "ks2c", "10000001" ],
2205 [ "KS2C", "10000001" ],
adc8ce12
IC
2206 ];
2207 for (var i=0; i<cards.length; i++) {
2208 var card = cards[i][0];
2209 var result = cards[i][1];
2210 e = Entropy.fromString(card);
2211 console.log(e.binary + " " + result);
2212 if (e.binaryStr !== result) {
886f06ee 2213 return "card entropy " + card + " not parsed correctly: " + result + " != " + e.binaryStr;
adc8ce12
IC
2214 }
2215 }
2216 }
2217 catch (e) {
2218 return e.message;
2219 }
2220 return "PASS";
c6624d51 2221 });
adc8ce12 2222 if (response != "PASS") {
c6624d51 2223 console.log("Entropy unit tests");
adc8ce12 2224 console.log(response);
c6624d51
IC
2225 fail();
2226 };
2227 next();
2228});
2229},
2230
2231// Entropy can be entered by the user
2232function() {
2233page.open(url, function(status) {
2234 expected = {
2235 mnemonic: "abandon abandon ability",
2236 address: "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug",
2237 }
2238 // use entropy
2239 page.evaluate(function() {
2240 $(".use-entropy").prop("checked", true).trigger("change");
55b592b4 2241 $(".mnemonic-length").val("raw");
c6624d51
IC
2242 $(".entropy").val("00000000 00000000 00000000 00000000").trigger("input");
2243 });
2244 // check the mnemonic is set and address is correct
2245 waitForGenerate(function() {
2246 var actual = page.evaluate(function() {
2247 return {
2248 address: $(".address:first").text(),
2249 mnemonic: $(".phrase").val(),
2250 }
2251 });
2252 if (actual.mnemonic != expected.mnemonic) {
2253 console.log("Entropy does not generate correct mnemonic");
2254 console.log("Expected: " + expected.mnemonic);
2255 console.log("Got: " + actual.mnemonic);
2256 fail();
2257 }
2258 if (actual.address != expected.address) {
2259 console.log("Entropy does not generate correct address");
2260 console.log("Expected: " + expected.address);
2261 console.log("Got: " + actual.address);
2262 fail();
2263 }
2264 next();
2265 });
2266});
2267},
2268
2269// A warning about entropy is shown to the user, with additional information
2270function() {
2271page.open(url, function(status) {
2272 // get text content from entropy sections of page
2273 var hasWarning = page.evaluate(function() {
2274 var entropyText = $(".entropy-container").text();
2275 var warning = "mnemonic may be insecure";
2276 if (entropyText.indexOf(warning) == -1) {
2277 return false;
2278 }
2279 var readMoreText = $("#entropy-notes").parent().text();
2280 var goodSources = "flipping a fair coin, rolling a fair dice, noise measurements etc";
2281 if (readMoreText.indexOf(goodSources) == -1) {
2282 return false;
2283 }
2284 return true;
2285 });
2286 // check the warnings and information are shown
2287 if (!hasWarning) {
2288 console.log("Page does not contain warning about using own entropy");
2289 fail();
2290 }
2291 next();
2292});
2293},
2294
2295// The types of entropy available are described to the user
2296function() {
2297page.open(url, function(status) {
2298 // get placeholder text for entropy field
2299 var placeholder = page.evaluate(function() {
2300 return $(".entropy").attr("placeholder");
2301 });
2302 var options = [
2303 "binary",
2304 "base 6",
2305 "dice",
2306 "base 10",
2307 "hexadecimal",
439f0e25 2308 "cards",
c6624d51
IC
2309 ];
2310 for (var i=0; i<options.length; i++) {
2311 var option = options[i];
2312 if (placeholder.indexOf(option) == -1) {
2313 console.log("Available entropy type is not shown to user: " + option);
2314 fail();
2315 }
2316 }
2317 next();
2318});
2319},
2320
2321// The actual entropy used is shown to the user
2322function() {
2323page.open(url, function(status) {
2324 // use entropy
2325 var badEntropySource = page.evaluate(function() {
2326 var entropy = "Not A Very Good Entropy Source At All";
2327 $(".use-entropy").prop("checked", true).trigger("change");
2328 $(".entropy").val(entropy).trigger("input");
2329 });
2330 // check the actual entropy being used is shown
057722b0 2331 waitForEntropyFeedback(function() {
c6624d51
IC
2332 var expectedText = "AedEceAA";
2333 var entropyText = page.evaluate(function() {
2334 return $(".entropy-container").text();
2335 });
2336 if (entropyText.indexOf(expectedText) == -1) {
2337 console.log("Actual entropy used is not shown");
2338 fail();
2339 }
2340 next();
2341 });
2342});
2343},
2344
2345// Binary entropy can be entered
2346function() {
2347page.open(url, function(status) {
2348 // use entropy
2349 page.evaluate(function() {
2350 $(".use-entropy").prop("checked", true).trigger("change");
2351 $(".entropy").val("01").trigger("input");
2352 });
2353 // check the entropy is shown to be the correct type
057722b0 2354 waitForEntropyFeedback(function() {
c6624d51
IC
2355 var entropyText = page.evaluate(function() {
2356 return $(".entropy-container").text();
2357 });
2358 if (entropyText.indexOf("binary") == -1) {
2359 console.log("Binary entropy is not detected and presented to user");
2360 fail();
2361 }
2362 next();
2363 });
2364});
2365},
2366
2367// Base 6 entropy can be entered
2368function() {
2369page.open(url, function(status) {
2370 // use entropy
2371 page.evaluate(function() {
2372 $(".use-entropy").prop("checked", true).trigger("change");
2373 $(".entropy").val("012345").trigger("input");
2374 });
2375 // check the entropy is shown to be the correct type
057722b0 2376 waitForEntropyFeedback(function() {
c6624d51
IC
2377 var entropyText = page.evaluate(function() {
2378 return $(".entropy-container").text();
2379 });
2380 if (entropyText.indexOf("base 6") == -1) {
2381 console.log("Base 6 entropy is not detected and presented to user");
2382 fail();
2383 }
2384 next();
2385 });
2386});
2387},
2388
2389// Base 6 dice entropy can be entered
2390function() {
2391page.open(url, function(status) {
2392 // use entropy
2393 page.evaluate(function() {
2394 $(".use-entropy").prop("checked", true).trigger("change");
2395 $(".entropy").val("123456").trigger("input");
2396 });
2397 // check the entropy is shown to be the correct type
057722b0 2398 waitForEntropyFeedback(function() {
c6624d51
IC
2399 var entropyText = page.evaluate(function() {
2400 return $(".entropy-container").text();
2401 });
2402 if (entropyText.indexOf("dice") == -1) {
2403 console.log("Dice entropy is not detected and presented to user");
2404 fail();
2405 }
2406 next();
2407 });
2408});
2409},
2410
2411// Base 10 entropy can be entered
2412function() {
2413page.open(url, function(status) {
2414 // use entropy
2415 page.evaluate(function() {
2416 $(".use-entropy").prop("checked", true).trigger("change");
2417 $(".entropy").val("789").trigger("input");
2418 });
2419 // check the entropy is shown to be the correct type
057722b0 2420 waitForEntropyFeedback(function() {
c6624d51
IC
2421 var entropyText = page.evaluate(function() {
2422 return $(".entropy-container").text();
2423 });
2424 if (entropyText.indexOf("base 10") == -1) {
2425 console.log("Base 10 entropy is not detected and presented to user");
2426 fail();
2427 }
2428 next();
2429 });
2430});
2431},
2432
2433// Hexadecimal entropy can be entered
2434function() {
2435page.open(url, function(status) {
2436 // use entropy
2437 page.evaluate(function() {
2438 $(".use-entropy").prop("checked", true).trigger("change");
2439 $(".entropy").val("abcdef").trigger("input");
2440 });
2441 // check the entropy is shown to be the correct type
057722b0 2442 waitForEntropyFeedback(function() {
c6624d51
IC
2443 var entropyText = page.evaluate(function() {
2444 return $(".entropy-container").text();
2445 });
2446 if (entropyText.indexOf("hexadecimal") == -1) {
2447 console.log("Hexadecimal entropy is not detected and presented to user");
2448 fail();
2449 }
2450 next();
2451 });
2452});
2453},
2454
2455// Dice entropy value is shown as the converted base 6 value
2456function() {
2457page.open(url, function(status) {
2458 // use entropy
2459 page.evaluate(function() {
2460 $(".use-entropy").prop("checked", true).trigger("change");
2461 $(".entropy").val("123456").trigger("input");
2462 });
2463 // check the entropy is shown as base 6, not as the original dice value
057722b0 2464 waitForEntropyFeedback(function() {
c6624d51
IC
2465 var entropyText = page.evaluate(function() {
2466 return $(".entropy-container").text();
2467 });
425b75a9 2468 if (entropyText.indexOf("123450") == -1) {
c6624d51
IC
2469 console.log("Dice entropy is not shown to user as base 6 value");
2470 fail();
2471 }
2472 if (entropyText.indexOf("123456") > -1) {
2473 console.log("Dice entropy value is shown instead of true base 6 value");
2474 fail();
2475 }
2476 next();
2477 });
2478});
2479},
2480
2481// The number of bits of entropy accumulated is shown
2482function() {
2483page.open(url, function(status) {
057722b0
IC
2484 //[ entropy, bits ]
2485 var tests = [
2486 [ "0000 0000 0000 0000 0000", "20" ],
2487 [ "0", "1" ],
2488 [ "0000", "4" ],
0d0f07f9
IC
2489 [ "6", "2" ], // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
2490 [ "7", "3" ], // 7 in base 10 is 111 in base 2, no leading zeros
057722b0
IC
2491 [ "8", "4" ],
2492 [ "F", "4" ],
1cf1bbaf 2493 [ "29", "6" ],
057722b0
IC
2494 [ "0A", "8" ],
2495 [ "1A", "8" ], // hex is always multiple of 4 bits of entropy
2496 [ "2A", "8" ],
2497 [ "4A", "8" ],
2498 [ "8A", "8" ],
2499 [ "FA", "8" ],
2500 [ "000A", "16" ],
0d0f07f9
IC
2501 [ "5555", "11" ],
2502 [ "6666", "10" ], // uses dice, so entropy is actually 0000 in base 6, which is 4 lots of 2.58 bits, which is 10.32 bits (rounded down to 10 bits)
1cf1bbaf 2503 [ "2227", "13" ], // Uses base 10, which is 4 lots of 3.32 bits, which is 13.3 bits (rounded down to 13)
057722b0
IC
2504 [ "222F", "16" ],
2505 [ "FFFF", "16" ],
1cf1bbaf 2506 [ "0000101017", "33" ], // 10 events at 3.32 bits per event
6422c1cd 2507 [ "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", "225" ], // cards are not replaced, so a full deck is not 52^52 entropy which is 296 bits, it's 52!, which is 225 bits
057722b0 2508 ]
c6624d51
IC
2509 // use entropy
2510 page.evaluate(function(e) {
2511 $(".use-entropy").prop("checked", true).trigger("change");
2512 });
2513 // Run each test
2514 var nextTest = function runNextTest(i) {
057722b0
IC
2515 var entropy = tests[i][0];
2516 var expected = tests[i][1];
c6624d51
IC
2517 // set entropy
2518 page.evaluate(function(e) {
c6624d51
IC
2519 $(".entropy").val(e).trigger("input");
2520 }, entropy);
2521 // check the number of bits of entropy is shown
057722b0 2522 waitForEntropyFeedback(function() {
c6624d51 2523 var entropyText = page.evaluate(function() {
dd944906 2524 return $(".entropy-container").text();
c6624d51 2525 });
1cf1bbaf 2526 if (entropyText.replace(/\s/g,"").indexOf("Bits" + expected) == -1) {
c6624d51
IC
2527 console.log("Accumulated entropy is not shown correctly for " + entropy);
2528 fail();
2529 }
057722b0 2530 var isLastTest = i == tests.length - 1;
c6624d51
IC
2531 if (isLastTest) {
2532 next();
2533 }
2534 else {
2535 runNextTest(i+1);
2536 }
2537 });
2538 }
2539 nextTest(0);
2540});
2541},
2542
fb353f9d 2543// There is feedback provided about the supplied entropy
c6624d51
IC
2544function() {
2545page.open(url, function(status) {
2546 var tests = [
2547 {
2548 entropy: "A",
fb353f9d
IC
2549 filtered: "A",
2550 type: "hexadecimal",
2551 events: 1,
2552 bits: 4,
c6624d51 2553 words: 0,
1cf1bbaf 2554 strength: "extremely weak",
c6624d51
IC
2555 },
2556 {
2557 entropy: "AAAAAAAA",
fb353f9d
IC
2558 filtered: "AAAAAAAA",
2559 type: "hexadecimal",
2560 events: 8,
2561 bits: 32,
c6624d51 2562 words: 3,
1cf1bbaf 2563 strength: "extremely weak",
c6624d51
IC
2564 },
2565 {
2566 entropy: "AAAAAAAA B",
fb353f9d
IC
2567 filtered: "AAAAAAAAB",
2568 type: "hexadecimal",
2569 events: 9,
2570 bits: 36,
c6624d51 2571 words: 3,
1cf1bbaf 2572 strength: "extremely weak",
c6624d51
IC
2573 },
2574 {
2575 entropy: "AAAAAAAA BBBBBBBB",
fb353f9d
IC
2576 filtered: "AAAAAAAABBBBBBBB",
2577 type: "hexadecimal",
2578 events: 16,
2579 bits: 64,
c6624d51 2580 words: 6,
1cf1bbaf 2581 strength: "very weak",
c6624d51
IC
2582 },
2583 {
2584 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
fb353f9d
IC
2585 filtered: "AAAAAAAABBBBBBBBCCCCCCCC",
2586 type: "hexadecimal",
2587 events: 24,
2588 bits: 96,
c6624d51 2589 words: 9,
1cf1bbaf 2590 strength: "weak",
c6624d51
IC
2591 },
2592 {
2593 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
fb353f9d
IC
2594 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD",
2595 type: "hexadecimal",
2596 events: 32,
2597 bits: 128,
c6624d51 2598 words: 12,
e6a799cc
IC
2599 strength: "easily cracked",
2600 },
2601 {
2602 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA",
2603 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA",
2604 type: "hexadecimal",
2605 events: 32,
2606 bits: 128,
2607 words: 12,
1cf1bbaf 2608 strength: "strong",
c6624d51
IC
2609 },
2610 {
e6a799cc
IC
2611 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE",
2612 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE",
fb353f9d
IC
2613 type: "hexadecimal",
2614 events: 40,
2615 bits: 160,
c6624d51 2616 words: 15,
1cf1bbaf
IC
2617 strength: "very strong",
2618 },
2619 {
e6a799cc
IC
2620 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF",
2621 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF",
fb353f9d
IC
2622 type: "hexadecimal",
2623 events: 48,
2624 bits: 192,
1cf1bbaf
IC
2625 words: 18,
2626 strength: "extremely strong",
fb353f9d 2627 },
391c7f26
IC
2628 {
2629 entropy: "7d",
2630 type: "card",
2631 events: 1,
2632 bits: 5,
2633 words: 0,
2634 strength: "extremely weak",
2635 },
2636 {
2637 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2638 type: "card (full deck)",
2639 events: 52,
6422c1cd
IC
2640 bits: 225,
2641 words: 21,
391c7f26
IC
2642 strength: "extremely strong",
2643 },
2644 {
2645 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d",
2646 type: "card (full deck, 1 duplicate: 3d)",
2647 events: 53,
87ad2c6e 2648 bits: 254,
6422c1cd 2649 words: 21,
391c7f26
IC
2650 strength: "extremely strong",
2651 },
2652 {
2653 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d",
bbc29c80 2654 type: "card (2 duplicates: 3d 4d, 1 missing: KS)",
391c7f26 2655 events: 53,
87ad2c6e 2656 bits: 254,
6422c1cd 2657 words: 21,
391c7f26
IC
2658 strength: "extremely strong",
2659 },
2660 {
2661 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d",
bbc29c80 2662 type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)",
391c7f26 2663 events: 53,
87ad2c6e
IC
2664 bits: 264,
2665 words: 24,
391c7f26
IC
2666 strength: "extremely strong",
2667 },
9bc39377 2668 // Next test was throwing uncaught error in zxcvbn
6422c1cd 2669 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
9bc39377
IC
2670 {
2671 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2672 type: "card (full deck, 52 duplicates: ac 2c 3c...)",
2673 events: 104,
87ad2c6e
IC
2674 bits: 499,
2675 words: 45,
9bc39377
IC
2676 strength: "extremely strong",
2677 },
5c653a12
IC
2678 // Case insensitivity to duplicate cards
2679 {
2680 entropy: "asAS",
2681 type: "card (1 duplicate: AS)",
2682 events: 2,
87ad2c6e 2683 bits: 9,
5c653a12
IC
2684 words: 0,
2685 strength: "extremely weak",
2686 },
2687 {
2688 entropy: "ASas",
2689 type: "card (1 duplicate: as)",
2690 events: 2,
87ad2c6e 2691 bits: 9,
5c653a12
IC
2692 words: 0,
2693 strength: "extremely weak",
2694 },
bbc29c80
IC
2695 // Missing cards are detected
2696 {
2697 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2698 type: "card (1 missing: 9C)",
2699 events: 51,
87ad2c6e
IC
2700 bits: 221,
2701 words: 18,
bbc29c80
IC
2702 strength: "extremely strong",
2703 },
2704 {
2705 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2706 type: "card (2 missing: 9C 5D)",
2707 events: 50,
87ad2c6e
IC
2708 bits: 216,
2709 words: 18,
bbc29c80
IC
2710 strength: "extremely strong",
2711 },
2712 {
2713 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2714 type: "card (4 missing: 9C 5D QD...)",
2715 events: 48,
87ad2c6e 2716 bits: 208,
6422c1cd 2717 words: 18,
bbc29c80
IC
2718 strength: "extremely strong",
2719 },
2720 // More than six missing cards does not show message
2721 {
2722 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks",
2723 type: "card",
2724 events: 45,
87ad2c6e 2725 bits: 195,
6422c1cd 2726 words: 18,
bbc29c80
IC
2727 strength: "extremely strong",
2728 },
fc7c248f
IC
2729 // Multiple decks of cards increases bits per event
2730 {
2731 entropy: "3d",
2732 events: 1,
2733 bits: 4,
2734 bitsPerEvent: 4.34,
2735 },
2736 {
2737 entropy: "3d3d",
2738 events: 2,
2739 bits: 9,
2740 bitsPerEvent: 4.80,
2741 },
2742 {
2743 entropy: "3d3d3d",
2744 events: 3,
2745 bits: 15,
2746 bitsPerEvent: 5.01,
2747 },
2748 {
2749 entropy: "3d3d3d3d",
2750 events: 4,
2751 bits: 20,
2752 bitsPerEvent: 5.14,
2753 },
2754 {
2755 entropy: "3d3d3d3d3d",
2756 events: 5,
2757 bits: 26,
2758 bitsPerEvent: 5.22,
2759 },
2760 {
2761 entropy: "3d3d3d3d3d3d",
2762 events: 6,
2763 bits: 31,
2764 bitsPerEvent: 5.28,
2765 },
2766 {
2767 entropy: "3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d",
2768 events: 33,
2769 bits: 184,
2770 bitsPerEvent: 5.59,
2771 strength: 'easily cracked - Repeats like "abcabcabc" are only slightly harder to guess than "abc"',
2772 },
c6624d51
IC
2773 ];
2774 // use entropy
2775 page.evaluate(function() {
2776 $(".use-entropy").prop("checked", true).trigger("change");
55b592b4 2777 $(".mnemonic-length").val("raw");
c6624d51
IC
2778 });
2779 var nextTest = function runNextTest(i) {
fb353f9d 2780 function getFeedbackError(expected, actual) {
391c7f26 2781 if ("filtered" in expected && actual.indexOf(expected.filtered) == -1) {
fb353f9d
IC
2782 return "Filtered value not in feedback";
2783 }
fc7c248f 2784 if ("type" in expected && actual.indexOf(expected.type) == -1) {
fb353f9d
IC
2785 return "Entropy type not in feedback";
2786 }
fc7c248f 2787 if ("events" in expected && actual.indexOf(expected.events) == -1) {
fb353f9d
IC
2788 return "Event count not in feedback";
2789 }
fc7c248f 2790 if ("bits" in expected && actual.indexOf(expected.bits) == -1) {
fb353f9d
IC
2791 return "Bit count not in feedback";
2792 }
fc7c248f 2793 if ("strength" in expected && actual.indexOf(expected.strength) == -1) {
fb353f9d
IC
2794 return "Strength not in feedback";
2795 }
fc7c248f
IC
2796 if ("bitsPerEvent" in expected && actual.indexOf(expected.bitsPerEvent) == -1) {
2797 return "bitsPerEvent not in feedback";
2798 }
fb353f9d
IC
2799 return false;
2800 }
c6624d51
IC
2801 test = tests[i];
2802 page.evaluate(function(e) {
2803 $(".addresses").empty();
057722b0 2804 $(".phrase").val("");
c6624d51
IC
2805 $(".entropy").val(e).trigger("input");
2806 }, test.entropy);
fb353f9d 2807 waitForEntropyFeedback(function() {
c6624d51
IC
2808 var mnemonic = page.evaluate(function() {
2809 return $(".phrase").val();
2810 });
fb353f9d 2811 // Check mnemonic length
fc7c248f 2812 if ("words" in test && test.words == 0) {
fb353f9d
IC
2813 if (mnemonic.length > 0) {
2814 console.log("Mnemonic length for " + test.strength + " strength is not " + test.words);
87ad2c6e 2815 console.log("Entropy: " + test.entropy);
fb353f9d
IC
2816 console.log("Mnemonic: " + mnemonic);
2817 fail();
2818 }
2819 }
fc7c248f 2820 else if ("words" in test) {
fb353f9d
IC
2821 if (mnemonic.split(" ").length != test.words) {
2822 console.log("Mnemonic length for " + test.strength + " strength is not " + test.words);
87ad2c6e 2823 console.log("Entropy: " + test.entropy);
fb353f9d
IC
2824 console.log("Mnemonic: " + mnemonic);
2825 fail();
2826 }
2827 }
2828 // check feedback
2829 var feedback = page.evaluate(function() {
dd944906 2830 return $(".entropy-container").text();
fb353f9d
IC
2831 });
2832 var feedbackError = getFeedbackError(test, feedback);
2833 if (feedbackError) {
2834 console.log("Entropy feedback for " + test.entropy + " returned error");
2835 console.log(feedbackError);
c6624d51
IC
2836 fail();
2837 }
fb353f9d 2838 // Run next test
c6624d51
IC
2839 var isLastTest = i == tests.length - 1;
2840 if (isLastTest) {
2841 next();
2842 }
2843 else {
2844 runNextTest(i+1);
2845 }
fb353f9d 2846 });
c6624d51
IC
2847 }
2848 nextTest(0);
2849});
2850},
2851
d6fd8ebf 2852// Entropy is truncated from the left
c6624d51
IC
2853function() {
2854page.open(url, function(status) {
d6fd8ebf 2855 var expected = "avocado zoo zone";
c6624d51
IC
2856 // use entropy
2857 page.evaluate(function() {
2858 $(".use-entropy").prop("checked", true).trigger("change");
55b592b4 2859 $(".mnemonic-length").val("raw");
c6624d51 2860 var entropy = "00000000 00000000 00000000 00000000";
d6fd8ebf 2861 entropy += "11111111 11111111 11111111 1111"; // Missing last byte
c6624d51
IC
2862 $(".entropy").val(entropy).trigger("input");
2863 });
2864 // check the entropy is truncated from the right
2865 waitForGenerate(function() {
2866 var actual = page.evaluate(function() {
2867 return $(".phrase").val();
2868 });
2869 if (actual != expected) {
2870 console.log("Entropy is not truncated from the right");
2871 console.log("Expected: " + expected);
2872 console.log("Got: " + actual);
2873 fail();
2874 }
2875 next();
2876 });
2877});
2878},
2879
2880// Very large entropy results in very long mnemonics
2881function() {
2882page.open(url, function(status) {
2883 // use entropy
2884 page.evaluate(function() {
2885 $(".use-entropy").prop("checked", true).trigger("change");
55b592b4 2886 $(".mnemonic-length").val("raw");
c6624d51
IC
2887 var entropy = "";
2888 // Generate a very long entropy string
2889 for (var i=0; i<33; i++) {
2890 entropy += "AAAAAAAA"; // 3 words * 33 iterations = 99 words
2891 }
2892 $(".entropy").val(entropy).trigger("input");
2893 });
2894 // check the mnemonic is very long
2895 waitForGenerate(function() {
2896 var wordCount = page.evaluate(function() {
2897 return $(".phrase").val().split(" ").length;
2898 });
2899 if (wordCount != 99) {
2900 console.log("Large entropy does not generate long mnemonic");
2901 console.log("Expected 99 words, got " + wordCount);
2902 fail();
2903 }
2904 next();
2905 });
2906});
2907},
2908
2909// Is compatible with bip32jp entropy
2910// https://bip32jp.github.io/english/index.html
2911// NOTES:
2912// Is incompatible with:
c6624d51
IC
2913// base 20
2914function() {
2915page.open(url, function(status) {
0d0f07f9 2916 var expected = "train then jungle barely whip fiber purpose puppy eagle cloud clump hospital robot brave balcony utility detect estate old green desk skill multiply virus";
c6624d51
IC
2917 // use entropy
2918 page.evaluate(function() {
2919 $(".use-entropy").prop("checked", true).trigger("change");
55b592b4 2920 $(".mnemonic-length").val("raw");
0d0f07f9 2921 var entropy = "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543";
c6624d51
IC
2922 $(".entropy").val(entropy).trigger("input");
2923 });
2924 // check the mnemonic matches the expected value from bip32jp
2925 waitForGenerate(function() {
2926 var actual = page.evaluate(function() {
2927 return $(".phrase").val();
2928 });
2929 if (actual != expected) {
2930 console.log("Mnemonic does not match bip32jp for base 6 entropy");
2931 console.log("Expected: " + expected);
2932 console.log("Got: " + actual);
2933 fail();
2934 }
2935 next();
2936 });
2937});
2938},
2939
057722b0
IC
2940// Blank entropy does not generate mnemonic or addresses
2941function() {
2942page.open(url, function(status) {
2943 // use entropy
2944 page.evaluate(function() {
2945 $(".use-entropy").prop("checked", true).trigger("change");
2946 $(".entropy").val("").trigger("input");
2947 });
2948 waitForFeedback(function() {
2949 // check there is no mnemonic
2950 var phrase = page.evaluate(function() {
2951 return $(".phrase").val();
2952 });
2953 if (phrase != "") {
2954 console.log("Blank entropy does not result in blank mnemonic");
2955 console.log("Got: " + phrase);
2956 fail();
2957 }
2958 // check there are no addresses displayed
2959 var addresses = page.evaluate(function() {
2960 return $(".address").length;
2961 });
2962 if (addresses != 0) {
2963 console.log("Blank entropy does not result in zero addresses");
2964 fail();
2965 }
2966 // Check the feedback says 'blank entropy'
2967 var feedback = page.evaluate(function() {
2968 return $(".feedback").text();
2969 });
2970 if (feedback != "Blank entropy") {
2971 console.log("Blank entropy does not show feedback message");
2972 fail();
2973 }
2974 next();
2975 });
2976});
2977},
2978
3599674d
IC
2979// Mnemonic length can be selected even for weak entropy
2980function() {
2981page.open(url, function(status) {
2982 // use entropy
2983 page.evaluate(function() {
2984 $(".use-entropy").prop("checked", true).trigger("change");
2985 $(".entropy").val("012345");
2986 $(".mnemonic-length").val("18").trigger("change");
2987 });
2988 // check the mnemonic is the correct length
2989 waitForGenerate(function() {
2990 var phrase = page.evaluate(function() {
2991 return $(".phrase").val();
2992 });
2993 var numberOfWords = phrase.split(/\s/g).length;
2994 if (numberOfWords != 18) {
2995 console.log("Weak entropy cannot be overridden to give 18 word mnemonic");
2996 console.log(phrase);
2997 fail();
2998 }
2999 next();
3000 });
3001});
3002},
3003
886f06ee
IC
3004// Github issue 33
3005// https://github.com/iancoleman/bip39/issues/33
3006// Final cards should contribute entropy
3007function() {
3008page.open(url, function(status) {
3009 // use entropy
3010 page.evaluate(function() {
3011 $(".use-entropy").prop("checked", true).trigger("change");
92e73fd9 3012 $(".mnemonic-length").val("raw");
886f06ee
IC
3013 $(".entropy").val("7S 9H 9S QH 8C KS AS 7D 7C QD 4S 4D TC 2D 5S JS 3D 8S 8H 4C 3C AC 3S QC 9C JC 7H AD TD JD 6D KH 5C QS 2S 6S 6H JH KD 9D-6C TS TH 4H KC 5H 2H AH 2C 8D 3H 5D").trigger("input");
3014 });
3015 // get the mnemonic
3016 waitForGenerate(function() {
3017 var originalPhrase = page.evaluate(function() {
3018 return $(".phrase").val();
3019 });
3020 // Set the last 12 cards to be AS
3021 page.evaluate(function() {
3022 $(".addresses").empty();
3023 $(".entropy").val("7S 9H 9S QH 8C KS AS 7D 7C QD 4S 4D TC 2D 5S JS 3D 8S 8H 4C 3C AC 3S QC 9C JC 7H AD TD JD 6D KH 5C QS 2S 6S 6H JH KD 9D-AS AS AS AS AS AS AS AS AS AS AS AS").trigger("input");
3024 });
3025 // get the new mnemonic
3026 waitForGenerate(function() {
3027 var newPhrase = page.evaluate(function() {
3028 return $(".phrase").val();
3029 });
92e73fd9 3030 // check the phrase has changed
886f06ee
IC
3031 if (newPhrase == originalPhrase) {
3032 console.log("Changing last 12 cards does not change mnemonic");
3033 console.log("Original:");
3034 console.log(originalPhrase);
3035 console.log("New:");
3036 console.log(newPhrase);
3037 fail();
3038 }
3039 next();
3040 });
3041 });
3042});
3043},
3044
3045
b0fb45b9
IC
3046// If you wish to add more tests, do so here...
3047
3048// Here is a blank test template
3049/*
3050
3051function() {
3052page.open(url, function(status) {
3053 // Do something on the page
3054 page.evaluate(function() {
3055 $(".phrase").val("abandon abandon ability").trigger("input");
3056 });
3057 waitForGenerate(function() {
3058 // Check the result of doing the thing
3059 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3060 var actual = page.evaluate(function() {
3061 return $(".address:first").text();
3062 });
3063 if (actual != expected) {
3064 console.log("A specific message about what failed");
3065 console.log("Expected: " + expected);
3066 console.log("Actual: " + actual);
3067 fail();
3068 }
3069 // Run the next test
3070 next();
3071 });
3072});
3073},
3074
3075*/
3076
88e2cdaa
IC
3077];
3078
3079console.log("Running tests...");
fb372687 3080tests = shuffle(tests);
88e2cdaa 3081next();