2 // $ phantomjs tests.js
5 var page
= require('webpage').create();
6 var url
= 'src/index.html';
7 var testMaxTime
= 20000;
14 page
.onResourceError = function(e
) {
15 console
.log("Error loading " + e
.url
);
20 console
.log("Failed");
24 function waitForGenerate(fn
, maxTime
) {
26 maxTime
= testMaxTime
;
28 var start
= new Date().getTime();
29 var prevAddressCount
= -1;
30 var wait
= function keepWaiting() {
31 var now
= new Date().getTime();
32 var hasTimedOut
= now
- start
> maxTime
;
33 var addressCount
= page
.evaluate(function() {
34 return $(".address").length
;
36 var hasFinished
= addressCount
> 0 && addressCount
== prevAddressCount
;
37 prevAddressCount
= addressCount
;
41 else if (hasTimedOut
) {
42 console
.log("Test timed out");
46 setTimeout(keepWaiting
, 100);
52 function waitForFeedback(fn
, maxTime
) {
54 maxTime
= testMaxTime
;
56 var start
= new Date().getTime();
57 var wait
= function keepWaiting() {
58 var now
= new Date().getTime();
59 var hasTimedOut
= now
- start
> maxTime
;
61 console
.log("Test timed out");
65 var feedback
= page
.evaluate(function() {
66 var feedback
= $(".feedback");
67 if (feedback
.css("display") == "none") {
70 return feedback
.text();
72 var hasFinished
= feedback
.length
> 0 && feedback
!= "Calculating...";
77 setTimeout(keepWaiting
, 100);
83 function waitForEntropyFeedback(fn
, maxTime
) {
85 maxTime
= testMaxTime
;
87 var origFeedback
= page
.evaluate(function() {
88 return $(".entropy-container").text();
90 var start
= new Date().getTime();
91 var wait
= function keepWaiting() {
92 var now
= new Date().getTime();
93 var hasTimedOut
= now
- start
> maxTime
;
95 console
.log("Test timed out");
99 var feedback
= page
.evaluate(function() {
100 return $(".entropy-container").text();
102 var hasFinished
= feedback
!= origFeedback
;
107 setTimeout(keepWaiting
, 100);
114 if (tests
.length
> 0) {
115 var testsStr
= tests
.length
== 1 ? "test" : "tests";
116 console
.log(tests
.length
+ " " + testsStr
+ " remaining");
120 console
.log("Finished with 0 failures");
126 * Randomize array element order in-place.
127 * Using Durstenfeld shuffle algorithm.
128 * See http://stackoverflow.com/a/12646864
130 function shuffle(array
) {
131 for (var i
= array
.length
- 1; i
> 0; i
--) {
132 var j
= Math
.floor(Math
.random() * (i
+ 1));
142 // Page loads with status of 'success'
144 page
.open(url
, function(status
) {
145 if (status
!= "success") {
146 console
.log("Page did not load with status 'success'");
155 page
.open(url
, function(status
) {
156 var content
= page
.evaluate(function() {
157 return document
.body
.textContent
.trim();
160 console
.log("Page does not have text");
167 // Entering mnemonic generates addresses
169 page
.open(url
, function(status
) {
171 page
.evaluate(function() {
172 $(".phrase").val("abandon abandon ability").trigger("input");
175 waitForGenerate(function() {
176 var addressCount
= page
.evaluate(function() {
177 return $(".address").length
;
179 if (addressCount
!= 20) {
180 console
.log("Mnemonic did not generate addresses");
181 console
.log("Expected: " + expected
);
182 console
.log("Got: " + actual
);
190 // Random button generates random mnemonic
192 page
.open(url
, function(status
) {
193 // check initial phrase is empty
194 var phrase
= page
.evaluate(function() {
195 return $(".phrase").text();
198 console
.log("Initial phrase is not blank");
201 // press the 'generate' button
202 page
.evaluate(function() {
203 $(".generate").click();
205 // get the new phrase
206 waitForGenerate(function() {
207 var phrase
= page
.evaluate(function() {
208 return $(".phrase").val();
210 if (phrase
.length
<= 0) {
211 console
.log("Phrase not generated by pressing button");
219 // Mnemonic length can be customized
221 page
.open(url
, function(status
) {
222 // set the length to 6
223 var expectedLength
= "6";
224 page
.evaluate(function() {
225 $(".strength option[selected]").removeAttr("selected");
226 $(".strength option[value=6]").prop("selected", true);
228 // press the 'generate' button
229 page
.evaluate(function() {
230 $(".generate").click();
232 // check the new phrase is six words long
233 waitForGenerate(function() {
234 var actualLength
= page
.evaluate(function() {
235 var words
= $(".phrase").val().split(" ");
238 if (actualLength
!= expectedLength
) {
239 console
.log("Phrase not generated with correct length");
240 console
.log("Expected: " + expectedLength
);
241 console
.log("Actual: " + actualLength
);
249 // Passphrase can be set
251 page
.open(url
, function(status
) {
252 // set the phrase and passphrase
253 var expected
= "15pJzUWPGzR7avffV9nY5by4PSgSKG9rba";
254 page
.evaluate(function() {
255 $(".phrase").val("abandon abandon ability");
256 $(".passphrase").val("secure_passphrase").trigger("input");
258 // check the address is generated correctly
259 waitForGenerate(function() {
260 var actual
= page
.evaluate(function() {
261 return $(".address:first").text();
263 if (actual
!= expected
) {
264 console
.log("Passphrase results in wrong address");
265 console
.log("Expected: " + expected
);
266 console
.log("Actual: " + actual
);
274 // Network can be set to bitcoin testnet
276 page
.open(url
, function(status
) {
277 // set the phrase and coin
278 var expected
= "mucaU5iiDaJDb69BHLeDv8JFfGiyg2nJKi";
279 page
.evaluate(function() {
280 $(".phrase").val("abandon abandon ability");
281 $(".phrase").trigger("input");
282 $(".network option[selected]").removeAttr("selected");
283 $(".network option").filter(function() {
284 return $(this).html() == "BTC - Bitcoin Testnet";
285 }).prop("selected", true);
286 $(".network").trigger("change");
288 // check the address is generated correctly
289 waitForGenerate(function() {
290 var actual
= page
.evaluate(function() {
291 return $(".address:first").text();
293 if (actual
!= expected
) {
294 console
.log("Bitcoin testnet address is incorrect");
295 console
.log("Expected: " + expected
);
296 console
.log("Actual: " + actual
);
304 // Network can be set to litecoin
306 page
.open(url
, function(status
) {
307 // set the phrase and coin
308 var expected
= "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn";
309 page
.evaluate(function() {
310 $(".phrase").val("abandon abandon ability");
311 $(".phrase").trigger("input");
312 $(".network option[selected]").removeAttr("selected");
313 $(".network option").filter(function() {
314 return $(this).html() == "LTC - Litecoin";
315 }).prop("selected", true);
316 $(".network").trigger("change");
318 // check the address is generated correctly
319 waitForGenerate(function() {
320 var actual
= page
.evaluate(function() {
321 return $(".address:first").text();
323 if (actual
!= expected
) {
324 console
.log("Litecoin address is incorrect");
325 console
.log("Expected: " + expected
);
326 console
.log("Actual: " + actual
);
334 // Network can be set to ripple
336 page
.open(url
, function(status
) {
337 // set the phrase and coin
338 var expected
= "rLTFnqbmCVPGx6VfaygdtuKWJgcN4v1zRS";
339 page
.evaluate(function() {
340 $(".phrase").val("ill clump only blind unit burden thing track silver cloth review awake useful craft whale all satisfy else trophy sunset walk vanish hope valve");
341 $(".phrase").trigger("input");
342 $(".network option[selected]").removeAttr("selected");
343 $(".network option").filter(function() {
344 return $(this).html() == "XRP - Ripple";
345 }).prop("selected", true);
346 $(".network").trigger("change");
348 // check the address is generated correctly
349 waitForGenerate(function() {
350 var actual
= page
.evaluate(function() {
351 return $(".address:first").text();
353 if (actual
!= expected
) {
354 console
.log("Ripple address is incorrect");
355 console
.log("Expected: " + expected
);
356 console
.log("Actual: " + actual
);
364 // Network can be set to dogecoin
366 page
.open(url
, function(status
) {
367 // set the phrase and coin
368 var expected
= "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA";
369 page
.evaluate(function() {
370 $(".phrase").val("abandon abandon ability");
371 $(".phrase").trigger("input");
372 $(".network option[selected]").removeAttr("selected");
373 $(".network option").filter(function() {
374 return $(this).html() == "DOGE - Dogecoin";
375 }).prop("selected", true);
376 $(".network").trigger("change");
378 // check the address is generated correctly
379 waitForGenerate(function() {
380 var actual
= page
.evaluate(function() {
381 return $(".address:first").text();
383 if (actual
!= expected
) {
384 console
.log("Dogecoin address is incorrect");
385 console
.log("Expected: " + expected
);
386 console
.log("Actual: " + actual
);
394 // Network can be set to shadowcash
396 page
.open(url
, function(status
) {
397 // set the phrase and coin
398 var expected
= "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG";
399 page
.evaluate(function() {
400 $(".phrase").val("abandon abandon ability");
401 $(".phrase").trigger("input");
402 $(".network option[selected]").removeAttr("selected");
403 $(".network option").filter(function() {
404 return $(this).html() == "SDC - ShadowCash";
405 }).prop("selected", true);
406 $(".network").trigger("change");
408 // check the address is generated correctly
409 waitForGenerate(function() {
410 var actual
= page
.evaluate(function() {
411 return $(".address:first").text();
413 if (actual
!= expected
) {
414 console
.log("Shadowcash address is incorrect");
415 console
.log("Expected: " + expected
);
416 console
.log("Actual: " + actual
);
424 // Network can be set to shadowcash testnet
426 page
.open(url
, function(status
) {
427 // set the phrase and coin
428 var expected
= "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
429 page
.evaluate(function() {
430 $(".phrase").val("abandon abandon ability");
431 $(".phrase").trigger("input");
432 $(".network option[selected]").removeAttr("selected");
433 $(".network option").filter(function() {
434 return $(this).html() == "SDC - ShadowCash Testnet";
435 }).prop("selected", true);
436 $(".network").trigger("change");
438 // check the address is generated correctly
439 waitForGenerate(function() {
440 var actual
= page
.evaluate(function() {
441 return $(".address:first").text();
443 if (actual
!= expected
) {
444 console
.log("Shadowcash testnet address is incorrect");
445 console
.log("Expected: " + expected
);
446 console
.log("Actual: " + actual
);
454 // Network can be set to viacoin
456 page
.open(url
, function(status
) {
457 // set the phrase and coin
458 var expected
= "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
459 page
.evaluate(function() {
460 $(".phrase").val("abandon abandon ability");
461 $(".phrase").trigger("input");
462 $(".network option[selected]").removeAttr("selected");
463 $(".network option").filter(function() {
464 return $(this).html() == "VIA - Viacoin";
465 }).prop("selected", true);
466 $(".network").trigger("change");
468 // check the address is generated correctly
469 waitForGenerate(function() {
470 var actual
= page
.evaluate(function() {
471 return $(".address:first").text();
473 if (actual
!= expected
) {
474 console
.log("Viacoin address is incorrect");
475 console
.log("Expected: " + expected
);
476 console
.log("Actual: " + actual
);
484 // Network can be set to viacoin testnet
486 page
.open(url
, function(status
) {
487 // set the phrase and coin
488 var expected
= "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
489 page
.evaluate(function() {
490 $(".phrase").val("abandon abandon ability");
491 $(".phrase").trigger("input");
492 $(".network option[selected]").removeAttr("selected");
493 $(".network option").filter(function() {
494 return $(this).html() == "VIA - Viacoin Testnet";
495 }).prop("selected", true);
496 $(".network").trigger("change");
498 // check the address is generated correctly
499 waitForGenerate(function() {
500 var actual
= page
.evaluate(function() {
501 return $(".address:first").text();
503 if (actual
!= expected
) {
504 console
.log("Viacoin testnet address is incorrect");
505 console
.log("Expected: " + expected
);
506 console
.log("Actual: " + actual
);
514 // Network can be set to jumbucks
516 page
.open(url
, function(status
) {
517 // set the phrase and coin
518 var expected
= "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew";
519 page
.evaluate(function() {
520 $(".phrase").val("abandon abandon ability");
521 $(".phrase").trigger("input");
522 $(".network option[selected]").removeAttr("selected");
523 $(".network option").filter(function() {
524 return $(this).html() == "JBS - Jumbucks";
525 }).prop("selected", true);
526 $(".network").trigger("change");
528 // check the address is generated correctly
529 waitForGenerate(function() {
530 var actual
= page
.evaluate(function() {
531 return $(".address:first").text();
533 if (actual
!= expected
) {
534 console
.log("Jumbucks address is incorrect");
535 console
.log("Expected: " + expected
);
536 console
.log("Actual: " + actual
);
544 // Network can be set to clam
546 page
.open(url
, function(status
) {
547 // set the phrase and coin
548 var expected
= "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y";
549 page
.evaluate(function() {
550 $(".phrase").val("abandon abandon ability");
551 $(".phrase").trigger("input");
552 $(".network option[selected]").removeAttr("selected");
553 $(".network option").filter(function() {
554 return $(this).html() == "CLAM - Clams";
555 }).prop("selected", true);
556 $(".network").trigger("change");
558 // check the address is generated correctly
559 waitForGenerate(function() {
560 var actual
= page
.evaluate(function() {
561 return $(".address:first").text();
563 if (actual
!= expected
) {
564 console
.log("CLAM address is incorrect");
565 console
.log("Expected: " + expected
);
566 console
.log("Actual: " + actual
);
574 // Network can be set to crown
576 page
.open(url
, function(status
) {
577 // set the phrase and coin
578 var expected
= "18pWSwSUAQdiwMHUfFZB1fM2xue9X1FqE5";
579 page
.evaluate(function() {
580 $(".phrase").val("abandon abandon ability");
581 $(".phrase").trigger("input");
582 $(".network option[selected]").removeAttr("selected");
583 $(".network option").filter(function() {
584 return $(this).html() == "CRW - Crown";
585 }).prop("selected", true);
586 $(".network").trigger("change");
588 // check the address is generated correctly
589 waitForGenerate(function() {
590 var actual
= page
.evaluate(function() {
591 return $(".address:first").text();
593 if (actual
!= expected
) {
594 console
.log("CRW address is incorrect");
595 console
.log("Expected: " + expected
);
596 console
.log("Actual: " + actual
);
604 // Network can be set to dash
606 page
.open(url
, function(status
) {
607 // set the phrase and coin
608 var expected
= "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT";
609 page
.evaluate(function() {
610 $(".phrase").val("abandon abandon ability");
611 $(".phrase").trigger("input");
612 $(".network option[selected]").removeAttr("selected");
613 $(".network option").filter(function() {
614 return $(this).html() == "DASH - Dash";
615 }).prop("selected", true);
616 $(".network").trigger("change");
618 // check the address is generated correctly
619 waitForGenerate(function() {
620 var actual
= page
.evaluate(function() {
621 return $(".address:first").text();
623 if (actual
!= expected
) {
624 console
.log("DASH address is incorrect");
625 console
.log("Expected: " + expected
);
626 console
.log("Actual: " + actual
);
635 page
.open(url
, function(status
) {
636 // set the phrase and coin
637 var expected
= "yaR52EN4oojdJfBgzWJTymC4uuCLPT29Gw";
638 page
.evaluate(function() {
639 $(".phrase").val("abandon abandon ability");
640 $(".phrase").trigger("input");
641 $(".network option[selected]").removeAttr("selected");
642 $(".network option").filter(function() {
643 return $(this).html() == "DASH - Dash Testnet";
644 }).prop("selected", true);
645 $(".network").trigger("change");
647 // check the address is generated correctly
648 waitForGenerate(function() {
649 var actual
= page
.evaluate(function() {
650 return $(".address:first").text();
652 if (actual
!= expected
) {
653 console
.log("DASH Testnet address is incorrect");
654 console
.log("Expected: " + expected
);
655 console
.log("Actual: " + actual
);
663 // Network can be set to game
665 page
.open(url
, function(status
) {
666 // set the phrase and coin
667 var expected
= "GSMY9bAp36cMR4zyT4uGVS7GFjpdXbao5Q";
668 page
.evaluate(function() {
669 $(".phrase").val("abandon abandon ability");
670 $(".phrase").trigger("input");
671 $(".network option[selected]").removeAttr("selected");
672 $(".network option").filter(function() {
673 return $(this).html() == "GAME - GameCredits";
674 }).prop("selected", true);
675 $(".network").trigger("change");
677 // check the address is generated correctly
678 waitForGenerate(function() {
679 var actual
= page
.evaluate(function() {
680 return $(".address:first").text();
682 if (actual
!= expected
) {
683 console
.log("GAME address is incorrect");
684 console
.log("Expected: " + expected
);
685 console
.log("Actual: " + actual
);
693 // Network can be set to namecoin
695 page
.open(url
, function(status
) {
696 // set the phrase and coin
697 var expected
= "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2";
698 page
.evaluate(function() {
699 $(".phrase").val("abandon abandon ability");
700 $(".phrase").trigger("input");
701 $(".network option[selected]").removeAttr("selected");
702 $(".network option").filter(function() {
703 return $(this).html() == "NMC - Namecoin";
704 }).prop("selected", true);
705 $(".network").trigger("change");
707 // check the address is generated correctly
708 waitForGenerate(function() {
709 var actual
= page
.evaluate(function() {
710 return $(".address:first").text();
712 if (actual
!= expected
) {
713 console
.log("Namecoin address is incorrect");
714 console
.log("Expected: " + expected
);
715 console
.log("Actual: " + actual
);
723 // Network can be set to peercoin
725 page
.open(url
, function(status
) {
726 // set the phrase and coin
727 var expected
= "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm";
728 page
.evaluate(function() {
729 $(".phrase").val("abandon abandon ability");
730 $(".phrase").trigger("input");
731 $(".network option[selected]").removeAttr("selected");
732 $(".network option").filter(function() {
733 return $(this).html() == "PPC - Peercoin";
734 }).prop("selected", true);
735 $(".network").trigger("change");
737 // check the address is generated correctly
738 waitForGenerate(function() {
739 var actual
= page
.evaluate(function() {
740 return $(".address:first").text();
742 if (actual
!= expected
) {
743 console
.log("Peercoin address is incorrect");
744 console
.log("Expected: " + expected
);
745 console
.log("Actual: " + actual
);
753 // Network can be set to ethereum
756 page
.open(url
, function(status
) {
758 // set the phrase and coin
759 page
.evaluate(function() {
760 $(".phrase").val("abandon abandon ability");
761 $(".phrase").trigger("input");
762 $(".network option[selected]").removeAttr("selected");
763 $(".network option").filter(function() {
764 return $(this).html() == "ETH - Ethereum";
765 }).prop("selected", true);
766 $(".network").trigger("change");
768 waitForGenerate(function() {
769 // check the address is generated correctly
770 // this value comes from
771 // https://www.myetherwallet.com/#view-wallet-info
772 // Unusual capitalization is due to checksum
773 var expected
= "0xe5815d5902Ad612d49283DEdEc02100Bd44C2772";
774 var actual
= page
.evaluate(function() {
775 return $(".address:first").text();
777 if (actual
!= expected
) {
778 console
.log("Ethereum address is incorrect");
779 console
.log("Expected: " + expected
);
780 console
.log("Actual: " + actual
);
783 // check the private key is correct
784 // this private key can be imported into
785 // https://www.myetherwallet.com/#view-wallet-info
786 // and it should correlate to the address above
787 var expected
= "0x8f253078b73d7498302bb78c171b23ce7a8fb511987d2b2702b731638a4a15e7";
788 var actual
= page
.evaluate(function() {
789 return $(".privkey:first").text();
791 if (actual
!= expected
) {
792 console
.log("Ethereum privkey is incorrect");
793 console
.log("Expected: " + expected
);
794 console
.log("Actual: " + actual
);
797 // check the public key is correct
799 // don't have any third-party source to generate the expected value
800 //var expected = "?";
801 //var actual = page.evaluate(function() {
802 // return $(".pubkey:first").text();
804 //if (actual != expected) {
805 // console.log("Ethereum privkey is incorrect");
806 // console.log("Expected: " + expected);
807 // console.log("Actual: " + actual);
815 // Network can be set to Slimcoin
817 page
.open(url
, function(status
) {
818 // set the phrase and coin
819 var expected
= "SNzPi1CafHFm3WWjRo43aMgiaEEj3ogjww";
820 page
.evaluate(function() {
821 $(".phrase").val("abandon abandon ability");
822 $(".phrase").trigger("input");
823 $(".network option[selected]").removeAttr("selected");
824 $(".network option").filter(function() {
825 return $(this).html() == "SLM - Slimcoin";
826 }).prop("selected", true);
827 $(".network").trigger("change");
829 // check the address is generated correctly
830 waitForGenerate(function() {
831 var actual
= page
.evaluate(function() {
832 return $(".address:first").text();
834 if (actual
!= expected
) {
835 console
.log("Slimcoin address is incorrect");
836 console
.log("Expected: " + expected
);
837 console
.log("Actual: " + actual
);
845 // Network can be set to Slimcointn
847 page
.open(url
, function(status
) {
848 // set the phrase and coin
849 var expected
= "n3nMgWufTek5QQAr6uwMhg5xbzj8xqc4Dq";
850 page
.evaluate(function() {
851 $(".phrase").val("abandon abandon ability");
852 $(".phrase").trigger("input");
853 $(".network option[selected]").removeAttr("selected");
854 $(".network option").filter(function() {
855 return $(this).html() == "SLM - Slimcoin Testnet";
856 }).prop("selected", true);
857 $(".network").trigger("change");
859 // check the address is generated correctly
860 waitForGenerate(function() {
861 var actual
= page
.evaluate(function() {
862 return $(".address:first").text();
864 if (actual
!= expected
) {
865 console
.log("Slimcoin testnet address is incorrect");
866 console
.log("Expected: " + expected
);
867 console
.log("Actual: " + actual
);
875 // BIP39 seed is set from phrase
877 page
.open(url
, function(status
) {
879 var expected
= "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
880 page
.evaluate(function() {
881 $(".phrase").val("abandon abandon ability");
882 $(".phrase").trigger("input");
884 // check the address is generated correctly
885 waitForGenerate(function() {
886 var actual
= page
.evaluate(function() {
887 return $(".seed").val();
889 if (actual
!= expected
) {
890 console
.log("BIP39 seed is incorrectly generated from mnemonic");
891 console
.log("Expected: " + expected
);
892 console
.log("Actual: " + actual
);
900 // BIP32 root key is set from phrase
902 page
.open(url
, function(status
) {
904 var expected
= "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi";
905 page
.evaluate(function() {
906 $(".phrase").val("abandon abandon ability");
907 $(".phrase").trigger("input");
909 // check the address is generated correctly
910 waitForGenerate(function() {
911 var actual
= page
.evaluate(function() {
912 return $(".root-key").val();
914 if (actual
!= expected
) {
915 console
.log("Root key is incorrectly generated from mnemonic");
916 console
.log("Expected: " + expected
);
917 console
.log("Actual: " + actual
);
925 // Tabs show correct addresses when changed
927 page
.open(url
, function(status
) {
929 var expected
= "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz";
930 page
.evaluate(function() {
931 $(".phrase").val("abandon abandon ability");
932 $(".phrase").trigger("input");
935 waitForGenerate(function() {
936 page
.evaluate(function() {
937 $("#bip32-tab a").click();
939 // check the address is generated correctly
940 waitForGenerate(function() {
941 var actual
= page
.evaluate(function() {
942 return $(".address:first").text();
944 if (actual
!= expected
) {
945 console
.log("Clicking tab generates incorrect address");
946 console
.log("Expected: " + expected
);
947 console
.log("Actual: " + actual
);
956 // BIP44 derivation path is shown
958 page
.open(url
, function(status
) {
960 var expected
= "m/44'/0'/0'/0";
961 page
.evaluate(function() {
962 $(".phrase").val("abandon abandon ability");
963 $(".phrase").trigger("input");
965 // check the derivation path of the first address
966 waitForGenerate(function() {
967 var actual
= page
.evaluate(function() {
968 return $("#bip44 .path").val();
970 if (actual
!= expected
) {
971 console
.log("BIP44 derivation path is incorrect");
972 console
.log("Expected: " + expected
);
973 console
.log("Actual: " + actual
);
981 // BIP44 extended private key is shown
983 page
.open(url
, function(status
) {
985 var expected
= "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG";
986 page
.evaluate(function() {
987 $(".phrase").val("abandon abandon ability");
988 $(".phrase").trigger("input");
990 // check the BIP44 extended private key
991 waitForGenerate(function() {
992 var actual
= page
.evaluate(function() {
993 return $(".extended-priv-key").val();
995 if (actual
!= expected
) {
996 console
.log("BIP44 extended private key is incorrect");
997 console
.log("Expected: " + expected
);
998 console
.log("Actual: " + actual
);
1006 // BIP44 extended public key is shown
1008 page
.open(url
, function(status
) {
1010 var expected
= "xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM";
1011 page
.evaluate(function() {
1012 $(".phrase").val("abandon abandon ability");
1013 $(".phrase").trigger("input");
1015 // check the BIP44 extended public key
1016 waitForGenerate(function() {
1017 var actual
= page
.evaluate(function() {
1018 return $(".extended-pub-key").val();
1020 if (actual
!= expected
) {
1021 console
.log("BIP44 extended public key is incorrect");
1022 console
.log("Expected: " + expected
);
1023 console
.log("Actual: " + actual
);
1031 // BIP44 purpose field changes address list
1033 page
.open(url
, function(status
) {
1035 var expected
= "1JbDzRJ2cDT8aat2xwKd6Pb2zzavow5MhF";
1036 page
.evaluate(function() {
1037 $(".phrase").val("abandon abandon ability");
1038 $(".phrase").trigger("input");
1040 waitForGenerate(function() {
1041 // change the bip44 purpose field to 45
1042 page
.evaluate(function() {
1043 $("#bip44 .purpose").val("45");
1044 $("#bip44 .purpose").trigger("input");
1046 waitForGenerate(function() {
1047 // check the address for the new derivation path
1048 var actual
= page
.evaluate(function() {
1049 return $(".address:first").text();
1051 if (actual
!= expected
) {
1052 console
.log("BIP44 purpose field generates incorrect address");
1053 console
.log("Expected: " + expected
);
1054 console
.log("Actual: " + actual
);
1063 // BIP44 coin field changes address list
1065 page
.open(url
, function(status
) {
1067 var expected
= "1F6dB2djQYrxoyfZZmfr6D5voH8GkJTghk";
1068 page
.evaluate(function() {
1069 $(".phrase").val("abandon abandon ability");
1070 $(".phrase").trigger("input");
1072 waitForGenerate(function() {
1073 // change the bip44 purpose field to 45
1074 page
.evaluate(function() {
1075 $("#bip44 .coin").val("1");
1076 $("#bip44 .coin").trigger("input");
1078 waitForGenerate(function() {
1079 // check the address for the new derivation path
1080 var actual
= page
.evaluate(function() {
1081 return $(".address:first").text();
1083 if (actual
!= expected
) {
1084 console
.log("BIP44 coin field generates incorrect address");
1085 console
.log("Expected: " + expected
);
1086 console
.log("Actual: " + actual
);
1095 // BIP44 account field changes address list
1097 page
.open(url
, function(status
) {
1099 var expected
= "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1100 page
.evaluate(function() {
1101 $(".phrase").val("abandon abandon ability");
1102 $(".phrase").trigger("input");
1104 waitForGenerate(function() {
1105 // change the bip44 purpose field to 45
1106 page
.evaluate(function() {
1107 $("#bip44 .account").val("1");
1108 $("#bip44 .account").trigger("input");
1110 waitForGenerate(function() {
1111 // check the address for the new derivation path
1112 var actual
= page
.evaluate(function() {
1113 return $(".address:first").text();
1115 if (actual
!= expected
) {
1116 console
.log("BIP44 account field generates incorrect address");
1117 console
.log("Expected: " + expected
);
1118 console
.log("Actual: " + actual
);
1127 // BIP44 change field changes address list
1129 page
.open(url
, function(status
) {
1131 var expected
= "1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo";
1132 page
.evaluate(function() {
1133 $(".phrase").val("abandon abandon ability");
1134 $(".phrase").trigger("input");
1136 waitForGenerate(function() {
1137 // change the bip44 purpose field to 45
1138 page
.evaluate(function() {
1139 $("#bip44 .change").val("1");
1140 $("#bip44 .change").trigger("input");
1142 waitForGenerate(function() {
1143 // check the address for the new derivation path
1144 var actual
= page
.evaluate(function() {
1145 return $(".address:first").text();
1147 if (actual
!= expected
) {
1148 console
.log("BIP44 change field generates incorrect address");
1149 console
.log("Expected: " + expected
);
1150 console
.log("Actual: " + actual
);
1159 // BIP32 derivation path can be set
1161 page
.open(url
, function(status
) {
1163 var expected
= "16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L";
1164 page
.evaluate(function() {
1165 $(".phrase").val("abandon abandon ability");
1166 $(".phrase").trigger("input");
1169 waitForGenerate(function() {
1170 page
.evaluate(function() {
1171 $("#bip32-tab a").click();
1173 // set the derivation path to m/1
1174 waitForGenerate(function() {
1175 page
.evaluate(function() {
1176 $("#bip32 .path").val("m/1");
1177 $("#bip32 .path").trigger("input");
1179 // check the address is generated correctly
1180 waitForGenerate(function() {
1181 var actual
= page
.evaluate(function() {
1182 return $(".address:first").text();
1184 if (actual
!= expected
) {
1185 console
.log("Custom BIP32 path generates incorrect address");
1186 console
.log("Expected: " + expected
);
1187 console
.log("Actual: " + actual
);
1197 // BIP32 can use hardened derivation paths
1199 page
.open(url
, function(status
) {
1201 var expected
= "14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4";
1202 page
.evaluate(function() {
1203 $(".phrase").val("abandon abandon ability");
1204 $(".phrase").trigger("input");
1207 waitForGenerate(function() {
1208 page
.evaluate(function() {
1209 $("#bip32-tab a").click();
1211 // set the derivation path to m/0'
1212 waitForGenerate(function() {
1213 page
.evaluate(function() {
1214 $("#bip32 .path").val("m/0'");
1215 $("#bip32 .path").trigger("input");
1217 // check the address is generated correctly
1218 waitForGenerate(function() {
1219 var actual
= page
.evaluate(function() {
1220 return $(".address:first").text();
1222 if (actual
!= expected
) {
1223 console
.log("Hardened BIP32 path generates incorrect address");
1224 console
.log("Expected: " + expected
);
1225 console
.log("Actual: " + actual
);
1235 // BIP32 extended private key is shown
1237 page
.open(url
, function(status
) {
1239 var expected
= "xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe";
1240 page
.evaluate(function() {
1241 $(".phrase").val("abandon abandon ability");
1242 $(".phrase").trigger("input");
1245 waitForGenerate(function() {
1246 page
.evaluate(function() {
1247 $("#bip32-tab a").click();
1249 // check the extended private key is generated correctly
1250 waitForGenerate(function() {
1251 var actual
= page
.evaluate(function() {
1252 return $(".extended-priv-key").val();
1254 if (actual
!= expected
) {
1255 console
.log("BIP32 extended private key is incorrect");
1256 console
.log("Expected: " + expected
);
1257 console
.log("Actual: " + actual
);
1266 // BIP32 extended public key is shown
1268 page
.open(url
, function(status
) {
1270 var expected
= "xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P";
1271 page
.evaluate(function() {
1272 $(".phrase").val("abandon abandon ability");
1273 $(".phrase").trigger("input");
1276 waitForGenerate(function() {
1277 page
.evaluate(function() {
1278 $("#bip32-tab a").click();
1280 // check the extended public key is generated correctly
1281 waitForGenerate(function() {
1282 var actual
= page
.evaluate(function() {
1283 return $(".extended-pub-key").val();
1285 if (actual
!= expected
) {
1286 console
.log("BIP32 extended public key is incorrect");
1287 console
.log("Expected: " + expected
);
1288 console
.log("Actual: " + actual
);
1297 // Derivation path is shown in table
1299 page
.open(url
, function(status
) {
1301 var expected
= "m/44'/0'/0'/0/0";
1302 page
.evaluate(function() {
1303 $(".phrase").val("abandon abandon ability");
1304 $(".phrase").trigger("input");
1306 // check for derivation path in table
1307 waitForGenerate(function() {
1308 var actual
= page
.evaluate(function() {
1309 return $(".index:first").text();
1311 if (actual
!= expected
) {
1312 console
.log("Derivation path shown incorrectly in table");
1313 console
.log("Expected: " + expected
);
1314 console
.log("Actual: " + actual
);
1322 // Derivation path for address can be hardened
1324 page
.open(url
, function(status
) {
1326 var expected
= "18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd";
1327 page
.evaluate(function() {
1328 $(".phrase").val("abandon abandon ability");
1329 $(".phrase").trigger("input");
1332 waitForGenerate(function() {
1333 page
.evaluate(function() {
1334 $("#bip32-tab a").click();
1336 waitForGenerate(function() {
1337 // select the hardened addresses option
1338 page
.evaluate(function() {
1339 $(".hardened-addresses").prop("checked", true);
1340 $(".hardened-addresses").trigger("change");
1342 waitForGenerate(function() {
1343 // check the generated address is hardened
1344 var actual
= page
.evaluate(function() {
1345 return $(".address:first").text();
1347 if (actual
!= expected
) {
1348 console
.log("Hardened address is incorrect");
1349 console
.log("Expected: " + expected
);
1350 console
.log("Actual: " + actual
);
1360 // Derivation path visibility can be toggled
1362 page
.open(url
, function(status
) {
1364 page
.evaluate(function() {
1365 $(".phrase").val("abandon abandon ability");
1366 $(".phrase").trigger("input");
1368 waitForGenerate(function() {
1369 // toggle path visibility
1370 page
.evaluate(function() {
1371 $(".index-toggle").click();
1373 // check the path is not visible
1374 var isInvisible
= page
.evaluate(function() {
1375 return $(".index:first span").hasClass("invisible");
1378 console
.log("Toggled derivation path is visible");
1388 page
.open(url
, function(status
) {
1389 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1391 page
.evaluate(function() {
1392 $(".phrase").val("abandon abandon ability").trigger("input");
1395 waitForGenerate(function() {
1396 var actual
= page
.evaluate(function() {
1397 return $(".address:first").text();
1399 if (actual
!= expected
) {
1400 console
.log("Address is not shown");
1401 console
.log("Expected: " + expected
);
1402 console
.log("Got: " + actual
);
1410 // Addresses are shown in order of derivation path
1412 page
.open(url
, function(status
) {
1414 page
.evaluate(function() {
1415 $(".phrase").val("abandon abandon ability").trigger("input");
1417 // get the derivation paths
1418 waitForGenerate(function() {
1419 var paths
= page
.evaluate(function() {
1420 return $(".index").map(function(i
, e
) {
1424 if (paths
.length
!= 20) {
1425 console
.log("Total paths is less than expected: " + paths
.length
);
1428 for (var i
=0; i
<paths
.length
; i
++) {
1429 var expected
= "m/44'/0'/0'/0/" + i
;
1430 var actual
= paths
[i
];
1431 if (actual
!= expected
) {
1432 console
.log("Path " + i
+ " is incorrect");
1433 console
.log("Expected: " + expected
);
1434 console
.log("Actual: " + actual
);
1443 // Address visibility can be toggled
1445 page
.open(url
, function(status
) {
1447 page
.evaluate(function() {
1448 $(".phrase").val("abandon abandon ability");
1449 $(".phrase").trigger("input");
1451 waitForGenerate(function() {
1452 // toggle address visibility
1453 page
.evaluate(function() {
1454 $(".address-toggle").click();
1456 // check the address is not visible
1457 var isInvisible
= page
.evaluate(function() {
1458 return $(".address:first span").hasClass("invisible");
1461 console
.log("Toggled address is visible");
1469 // Public key is shown
1471 page
.open(url
, function(status
) {
1472 var expected
= "033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3";
1474 page
.evaluate(function() {
1475 $(".phrase").val("abandon abandon ability").trigger("input");
1478 waitForGenerate(function() {
1479 var actual
= page
.evaluate(function() {
1480 return $(".pubkey:first").text();
1482 if (actual
!= expected
) {
1483 console
.log("Public key is not shown");
1484 console
.log("Expected: " + expected
);
1485 console
.log("Got: " + actual
);
1493 // Public key visibility can be toggled
1495 page
.open(url
, function(status
) {
1497 page
.evaluate(function() {
1498 $(".phrase").val("abandon abandon ability");
1499 $(".phrase").trigger("input");
1501 waitForGenerate(function() {
1502 // toggle public key visibility
1503 page
.evaluate(function() {
1504 $(".public-key-toggle").click();
1506 // check the public key is not visible
1507 var isInvisible
= page
.evaluate(function() {
1508 return $(".pubkey:first span").hasClass("invisible");
1511 console
.log("Toggled public key is visible");
1519 // Private key is shown
1521 page
.open(url
, function(status
) {
1522 var expected
= "L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE";
1524 page
.evaluate(function() {
1525 $(".phrase").val("abandon abandon ability").trigger("input");
1528 waitForGenerate(function() {
1529 var actual
= page
.evaluate(function() {
1530 return $(".privkey:first").text();
1532 if (actual
!= expected
) {
1533 console
.log("Private key is not shown");
1534 console
.log("Expected: " + expected
);
1535 console
.log("Got: " + actual
);
1543 // Private key visibility can be toggled
1545 page
.open(url
, function(status
) {
1547 page
.evaluate(function() {
1548 $(".phrase").val("abandon abandon ability");
1549 $(".phrase").trigger("input");
1551 waitForGenerate(function() {
1552 // toggle private key visibility
1553 page
.evaluate(function() {
1554 $(".private-key-toggle").click();
1556 // check the private key is not visible
1557 var isInvisible
= page
.evaluate(function() {
1558 return $(".privkey:first span").hasClass("invisible");
1561 console
.log("Toggled private key is visible");
1569 // More addresses can be generated
1571 page
.open(url
, function(status
) {
1573 page
.evaluate(function() {
1574 $(".phrase").val("abandon abandon ability");
1575 $(".phrase").trigger("input");
1577 waitForGenerate(function() {
1578 // generate more addresses
1579 page
.evaluate(function() {
1582 waitForGenerate(function() {
1583 // check there are more addresses
1584 var addressCount
= page
.evaluate(function() {
1585 return $(".address").length
;
1587 if (addressCount
!= 40) {
1588 console
.log("More addresses cannot be generated");
1597 // A custom number of additional addresses can be generated
1599 page
.open(url
, function(status
) {
1601 page
.evaluate(function() {
1602 $(".phrase").val("abandon abandon ability");
1603 $(".phrase").trigger("input");
1605 waitForGenerate(function() {
1606 // get the current number of addresses
1607 var oldAddressCount
= page
.evaluate(function() {
1608 return $(".address").length
;
1610 // set a custom number of additional addresses
1611 page
.evaluate(function() {
1612 $(".rows-to-add").val(1);
1614 // generate more addresses
1615 page
.evaluate(function() {
1618 waitForGenerate(function() {
1619 // check there are the correct number of addresses
1620 var newAddressCount
= page
.evaluate(function() {
1621 return $(".address").length
;
1623 if (newAddressCount
- oldAddressCount
!= 1) {
1624 console
.log("Number of additional addresses cannot be customized");
1625 console
.log(newAddressCount
)
1626 console
.log(oldAddressCount
)
1635 // Additional addresses are shown in order of derivation path
1637 page
.open(url
, function(status
) {
1639 page
.evaluate(function() {
1640 $(".phrase").val("abandon abandon ability").trigger("input");
1642 waitForGenerate(function() {
1643 // generate more addresses
1644 page
.evaluate(function() {
1647 // get the derivation paths
1648 waitForGenerate(function() {
1649 var paths
= page
.evaluate(function() {
1650 return $(".index").map(function(i
, e
) {
1654 if (paths
.length
!= 40) {
1655 console
.log("Total additional paths is less than expected: " + paths
.length
);
1658 for (var i
=0; i
<paths
.length
; i
++) {
1659 var expected
= "m/44'/0'/0'/0/" + i
;
1660 var actual
= paths
[i
];
1661 if (actual
!= expected
) {
1662 console
.log("Path " + i
+ " is not in correct order");
1663 console
.log("Expected: " + expected
);
1664 console
.log("Actual: " + actual
);
1674 // BIP32 root key can be set by the user
1676 page
.open(url
, function(status
) {
1677 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1679 page
.evaluate(function() {
1680 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1682 waitForGenerate(function() {
1683 var actual
= page
.evaluate(function() {
1684 return $(".address:first").text();
1686 if (actual
!= expected
) {
1687 console
.log("Setting BIP32 root key results in wrong address");
1688 console
.log("Expected: " + expected
);
1689 console
.log("Actual: " + actual
);
1697 // Setting BIP32 root key clears the existing phrase, passphrase and seed
1699 page
.open(url
, function(status
) {
1702 page
.evaluate(function() {
1703 $(".phrase").val("A non-blank but invalid value");
1705 // Accept any confirm dialogs
1706 page
.onConfirm = function() {
1710 page
.evaluate(function() {
1711 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1713 waitForGenerate(function() {
1714 var actual
= page
.evaluate(function() {
1715 return $(".phrase").val();
1717 if (actual
!= expected
) {
1718 console
.log("Phrase not cleared when setting BIP32 root key");
1719 console
.log("Expected: " + expected
);
1720 console
.log("Actual: " + actual
);
1728 // Clearing of phrase, passphrase and seed can be cancelled by user
1730 page
.open(url
, function(status
) {
1731 var expected
= "abandon abandon ability";
1733 page
.evaluate(function() {
1734 $(".phrase").val("abandon abandon ability");
1736 // Cancel any confirm dialogs
1737 page
.onConfirm = function() {
1741 page
.evaluate(function() {
1742 $(".root-key").val("xprv9s21ZrQH143K3d3vzEDD3KpSKmxsZ3y7CqhAL1tinwtP6wqK4TKEKjpBuo6P2hUhB6ZENo7TTSRytiP857hBZVpBdk8PooFuRspE1eywwNZ").trigger("input");
1744 var actual
= page
.evaluate(function() {
1745 return $(".phrase").val();
1747 if (actual
!= expected
) {
1748 console
.log("Phrase not retained when cancelling changes to BIP32 root key");
1749 console
.log("Expected: " + expected
);
1750 console
.log("Actual: " + actual
);
1757 // Custom BIP32 root key is used when changing the derivation path
1759 page
.open(url
, function(status
) {
1760 var expected
= "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1762 page
.evaluate(function() {
1763 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1765 waitForGenerate(function() {
1766 // change the derivation path
1767 page
.evaluate(function() {
1768 $("#account").val("1").trigger("input");
1770 // check the bip32 root key is used for derivation, not the blank phrase
1771 waitForGenerate(function() {
1772 var actual
= page
.evaluate(function() {
1773 return $(".address:first").text();
1775 if (actual
!= expected
) {
1776 console
.log("Changing the derivation path does not use BIP32 root key");
1777 console
.log("Expected: " + expected
);
1778 console
.log("Actual: " + actual
);
1787 // Incorrect mnemonic shows error
1789 page
.open(url
, function(status
) {
1791 page
.evaluate(function() {
1792 $(".phrase").val("abandon abandon abandon").trigger("input");
1794 waitForFeedback(function() {
1795 // check there is an error shown
1796 var feedback
= page
.evaluate(function() {
1797 return $(".feedback").text();
1799 if (feedback
.length
<= 0) {
1800 console
.log("Invalid mnemonic does not show error");
1808 // Incorrect word shows suggested replacement
1810 page
.open(url
, function(status
) {
1812 page
.evaluate(function() {
1813 $(".phrase").val("abandon abandon abiliti").trigger("input");
1815 // check there is a suggestion shown
1816 waitForFeedback(function() {
1817 var feedback
= page
.evaluate(function() {
1818 return $(".feedback").text();
1820 if (feedback
.indexOf("did you mean ability?") < 0) {
1821 console
.log("Incorrect word does not show suggested replacement");
1822 console
.log("Error: " + error
);
1830 // Github pull request 48
1831 // First four letters of word shows that word, not closest
1832 // since first four letters gives unique word in BIP39 wordlist
1833 // eg ille should show illegal, not idle
1835 page
.open(url
, function(status
) {
1836 // set the incomplete word
1837 page
.evaluate(function() {
1838 $(".phrase").val("ille").trigger("input");
1840 // check there is a suggestion shown
1841 waitForFeedback(function() {
1842 var feedback
= page
.evaluate(function() {
1843 return $(".feedback").text();
1845 if (feedback
.indexOf("did you mean illegal?") < 0) {
1846 console
.log("Start of word does not show correct suggestion");
1847 console
.log("Error: " + error
);
1855 // Incorrect BIP32 root key shows error
1857 page
.open(url
, function(status
) {
1859 page
.evaluate(function() {
1860 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj").trigger("input");
1862 // check there is an error shown
1863 waitForFeedback(function() {
1864 var feedback
= page
.evaluate(function() {
1865 return $(".feedback").text();
1867 if (feedback
!= "Invalid root key") {
1868 console
.log("Invalid root key does not show error");
1869 console
.log("Error: " + error
);
1877 // Derivation path not starting with m shows error
1879 page
.open(url
, function(status
) {
1880 // set the mnemonic phrase
1881 page
.evaluate(function() {
1882 $(".phrase").val("abandon abandon ability").trigger("input");
1884 waitForGenerate(function() {
1885 // select the bip32 tab so custom derivation path can be set
1886 page
.evaluate(function() {
1887 $("#bip32-tab a").click();
1889 waitForGenerate(function() {
1890 // set the incorrect derivation path
1891 page
.evaluate(function() {
1892 $("#bip32 .path").val("n/0").trigger("input");
1894 waitForFeedback(function() {
1895 var feedback
= page
.evaluate(function() {
1896 return $(".feedback").text();
1898 if (feedback
!= "First character must be 'm'") {
1899 console
.log("Derivation path not starting with m should show error");
1900 console
.log("Error: " + error
);
1910 // Derivation path containing invalid characters shows useful error
1912 page
.open(url
, function(status
) {
1913 // set the mnemonic phrase
1914 page
.evaluate(function() {
1915 $(".phrase").val("abandon abandon ability").trigger("input");
1917 waitForGenerate(function() {
1918 // select the bip32 tab so custom derivation path can be set
1919 page
.evaluate(function() {
1920 $("#bip32-tab a").click();
1922 waitForGenerate(function() {
1923 // set the incorrect derivation path
1924 page
.evaluate(function() {
1925 $("#bip32 .path").val("m/1/0wrong1/1").trigger("input");
1927 waitForFeedback(function() {
1928 var feedback
= page
.evaluate(function() {
1929 return $(".feedback").text();
1931 if (feedback
!= "Invalid characters 0wrong1 found at depth 2") {
1932 console
.log("Derivation path with invalid characters should show error");
1933 console
.log("Error: " + error
);
1943 // Github Issue 11: Default word length is 15
1944 // https://github.com/iancoleman/bip39/issues/11
1946 page
.open(url
, function(status
) {
1947 // get the word length
1948 var defaultLength
= page
.evaluate(function() {
1949 return $(".strength").val();
1951 if (defaultLength
!= 15) {
1952 console
.log("Default word length is not 15");
1960 // Github Issue 12: Generate more rows with private keys hidden
1961 // https://github.com/iancoleman/bip39/issues/12
1963 page
.open(url
, function(status
) {
1965 page
.evaluate(function() {
1966 $(".phrase").val("abandon abandon ability");
1967 $(".phrase").trigger("input");
1969 waitForGenerate(function() {
1970 // toggle private keys hidden, then generate more addresses
1971 page
.evaluate(function() {
1972 $(".private-key-toggle").click();
1975 waitForGenerate(function() {
1976 // check more have been generated
1978 var numPrivKeys
= page
.evaluate(function() {
1979 return $(".privkey").length
;
1981 if (numPrivKeys
!= expected
) {
1982 console
.log("Wrong number of addresses when clicking 'more' with hidden privkeys");
1983 console
.log("Expected: " + expected
);
1984 console
.log("Actual: " + numPrivKeys
);
1987 // check no private keys are shown
1988 var numHiddenPrivKeys
= page
.evaluate(function() {
1989 return $(".privkey span[class=invisible]").length
;
1991 if (numHiddenPrivKeys
!= expected
) {
1992 console
.log("Generating more does not retain hidden state of privkeys");
1993 console
.log("Expected: " + expected
);
1994 console
.log("Actual: " + numHiddenPrivKeys
);
2003 // Github Issue 19: Mnemonic is not sensitive to whitespace
2004 // https://github.com/iancoleman/bip39/issues/19
2006 page
.open(url
, function(status
) {
2008 var expected
= "xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC";
2009 page
.evaluate(function() {
2010 var doubleSpace
= " ";
2011 $(".phrase").val("urge cat" + doubleSpace
+ "bid");
2012 $(".phrase").trigger("input");
2014 waitForGenerate(function() {
2015 // Check the bip32 root key is correct
2016 var actual
= page
.evaluate(function() {
2017 return $(".root-key").val();
2019 if (actual
!= expected
) {
2020 console
.log("Mnemonic is sensitive to whitespace");
2021 console
.log("Expected: " + expected
);
2022 console
.log("Actual: " + actual
);
2030 // Github Issue 23: Part 1: Use correct derivation path when changing tabs
2031 // https://github.com/iancoleman/bip39/issues/23
2033 page
.open(url
, function(status
) {
2034 // 1) and 2) set the phrase
2035 page
.evaluate(function() {
2036 $(".phrase").val("abandon abandon ability").trigger("input");
2038 waitForGenerate(function() {
2039 // 3) select bip32 tab
2040 page
.evaluate(function() {
2041 $("#bip32-tab a").click();
2043 waitForGenerate(function() {
2044 // 4) switch from bitcoin to litecoin
2045 page
.evaluate(function() {
2046 $(".network option").filter(function() {
2047 return $(this).html() == "LTC - Litecoin";
2048 }).prop("selected", true);
2049 $(".network").trigger("change");
2051 waitForGenerate(function() {
2052 // 5) Check derivation path is displayed correctly
2053 var expected
= "m/0/0";
2054 var actual
= page
.evaluate(function() {
2055 return $(".index:first").text();
2057 if (actual
!= expected
) {
2058 console
.log("Github Issue 23 Part 1: derivation path display error");
2059 console
.log("Expected: " + expected
);
2060 console
.log("Actual: " + actual
);
2063 // 5) Check address is displayed correctly
2064 var expected
= "LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5";
2065 var actual
= page
.evaluate(function() {
2066 return $(".address:first").text();
2068 if (actual
!= expected
) {
2069 console
.log("Github Issue 23 Part 1: address display error");
2070 console
.log("Expected: " + expected
);
2071 console
.log("Actual: " + actual
);
2081 // Github Issue 23 Part 2: Coin selection in derivation path
2082 // https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
2084 page
.open(url
, function(status
) {
2086 page
.evaluate(function() {
2087 $(".phrase").val("abandon abandon ability").trigger("input");
2089 waitForGenerate(function() {
2090 // switch from bitcoin to clam
2091 page
.evaluate(function() {
2092 $(".network option").filter(function() {
2093 return $(this).html() == "CLAM - Clams";
2094 }).prop("selected", true);
2095 $(".network").trigger("change");
2097 waitForGenerate(function() {
2098 // check derivation path is displayed correctly
2099 var expected
= "m/44'/23'/0'/0/0";
2100 var actual
= page
.evaluate(function() {
2101 return $(".index:first").text();
2103 if (actual
!= expected
) {
2104 console
.log("Github Issue 23 Part 2: Coin in BIP44 derivation path is incorrect");
2105 console
.log("Expected: " + expected
);
2106 console
.log("Actual: " + actual
);
2115 // Github Issue 26: When using a Root key derrived altcoins are incorrect
2116 // https://github.com/iancoleman/bip39/issues/26
2118 page
.open(url
, function(status
) {
2119 // 1) 2) and 3) set the root key
2120 page
.evaluate(function() {
2121 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
2123 waitForGenerate(function() {
2124 // 4) switch from bitcoin to viacoin
2125 page
.evaluate(function() {
2126 $(".network option").filter(function() {
2127 return $(this).html() == "VIA - Viacoin";
2128 }).prop("selected", true);
2129 $(".network").trigger("change");
2131 waitForGenerate(function() {
2132 // 5) ensure the derived address is correct
2133 var expected
= "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
2134 var actual
= page
.evaluate(function() {
2135 return $(".address:first").text();
2137 if (actual
!= expected
) {
2138 console
.log("Github Issue 26: address is incorrect when changing networks and using root-key to derive");
2139 console
.log("Expected: " + expected
);
2140 console
.log("Actual: " + actual
);
2149 // Selecting a language with no existing phrase should generate a phrase in
2152 page
.open(url
, function(status
) {
2153 // Select a language
2154 // Need to manually simulate hash being set due to quirk between
2155 // 'click' event triggered by javascript vs triggered by mouse.
2156 // Perhaps look into page.sendEvent
2157 // http://phantomjs.org/api/webpage/method/send-event.html
2158 page
.evaluate(function() {
2159 window
.location
.hash
= "#japanese";
2160 $("a[href='#japanese']").trigger("click");
2162 waitForGenerate(function() {
2163 // Check the mnemonic is in Japanese
2164 var phrase
= page
.evaluate(function() {
2165 return $(".phrase").val();
2167 if (phrase
.length
<= 0) {
2168 console
.log("No Japanese phrase generated");
2171 if (phrase
.charCodeAt(0) < 128) {
2172 console
.log("First character of Japanese phrase is ascii");
2173 console
.log("Phrase: " + phrase
);
2181 // Selecting a language with existing phrase should update the phrase to use
2184 page
.open(url
, function(status
) {
2185 // Set the phrase to an English phrase.
2186 page
.evaluate(function() {
2187 $(".phrase").val("abandon abandon ability").trigger("input");
2189 waitForGenerate(function() {
2190 // Change to Italian
2191 // Need to manually simulate hash being set due to quirk between
2192 // 'click' event triggered by javascript vs triggered by mouse.
2193 // Perhaps look into page.sendEvent
2194 // http://phantomjs.org/api/webpage/method/send-event.html
2195 page
.evaluate(function() {
2196 window
.location
.hash
= "#italian";
2197 $("a[href='#italian']").trigger("click");
2199 waitForGenerate(function() {
2200 // Check only the language changes, not the phrase
2201 var expected
= "abaco abaco abbaglio";
2202 var actual
= page
.evaluate(function() {
2203 return $(".phrase").val();
2205 if (actual
!= expected
) {
2206 console
.log("Changing language with existing phrase");
2207 console
.log("Expected: " + expected
);
2208 console
.log("Actual: " + actual
);
2211 // Check the address is correct
2212 var expected
= "1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV";
2213 var actual
= page
.evaluate(function() {
2214 return $(".address:first").text();
2216 if (actual
!= expected
) {
2217 console
.log("Changing language generates incorrect address");
2218 console
.log("Expected: " + expected
);
2219 console
.log("Actual: " + actual
);
2228 // Suggested replacement for erroneous word in non-English language
2230 page
.open(url
, function(status
) {
2231 // Set an incorrect phrase in Italian
2232 page
.evaluate(function() {
2233 $(".phrase").val("abaco abaco zbbaglio").trigger("input");
2235 waitForFeedback(function() {
2236 // Check the suggestion is correct
2237 var feedback
= page
.evaluate(function() {
2238 return $(".feedback").text();
2240 if (feedback
.indexOf("did you mean abbaglio?") < 0) {
2241 console
.log("Incorrect Italian word does not show suggested replacement");
2242 console
.log("Error: " + error
);
2251 // Japanese word does not break across lines.
2253 // https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
2255 page
.open(url
, function(status
) {
2256 hasWordBreakCss
= page
.content
.indexOf("word-break: keep-all;") > -1;
2257 if (!hasWordBreakCss
) {
2258 console
.log("Japanese words can break across lines mid-word");
2259 console
.log("Check CSS for '.phrase { word-break: keep-all; }'");
2262 // Run the next test
2267 // Language can be specified at page load using hash value in url
2269 page
.open(url
, function(status
) {
2270 // Set the page hash as if it were on a fresh page load
2271 page
.evaluate(function() {
2272 window
.location
.hash
= "#japanese";
2274 // Generate a random phrase
2275 page
.evaluate(function() {
2276 $(".generate").trigger("click");
2278 waitForGenerate(function() {
2279 // Check the phrase is in Japanese
2280 var phrase
= page
.evaluate(function() {
2281 return $(".phrase").val();
2283 if (phrase
.length
<= 0) {
2284 console
.log("No phrase generated using url hash");
2287 if (phrase
.charCodeAt(0) < 128) {
2288 console
.log("Language not detected from url hash on page load.");
2289 console
.log("Phrase: " + phrase
);
2297 // Entropy unit tests
2299 page
.open(url
, function(status
) {
2300 var response
= page
.evaluate(function() {
2302 // binary entropy is detected
2304 e
= Entropy
.fromString("01010101");
2305 if (e
.base
.str
!= "binary") {
2306 return "Binary entropy not detected correctly";
2312 // base6 entropy is detected
2314 e
= Entropy
.fromString("012345012345");
2315 if (e
.base
.str
!= "base 6") {
2316 return "base6 entropy not detected correctly";
2322 // dice entropy is detected
2324 e
= Entropy
.fromString("123456123456");
2325 if (e
.base
.str
!= "base 6 (dice)") {
2326 return "dice entropy not detected correctly";
2332 // base10 entropy is detected
2334 e
= Entropy
.fromString("0123456789");
2335 if (e
.base
.str
!= "base 10") {
2336 return "base10 entropy not detected correctly";
2342 // hex entropy is detected
2344 e
= Entropy
.fromString("0123456789ABCDEF");
2345 if (e
.base
.str
!= "hexadecimal") {
2346 return "hexadecimal entropy not detected correctly";
2352 // card entropy is detected
2354 e
= Entropy
.fromString("AC4DTHKS");
2355 if (e
.base
.str
!= "card") {
2356 return "card entropy not detected correctly";
2362 // entropy is case insensitive
2364 e
= Entropy
.fromString("aBcDeF");
2365 if (e
.cleanStr
!= "aBcDeF") {
2366 return "Entropy should not be case sensitive";
2372 // dice entropy is converted to base6
2374 e
= Entropy
.fromString("123456");
2375 if (e
.cleanStr
!= "123450") {
2376 return "Dice entropy is not automatically converted to base6";
2382 // dice entropy is preferred to base6 if ambiguous
2384 e
= Entropy
.fromString("12345");
2385 if (e
.base
.str
!= "base 6 (dice)") {
2386 return "dice not used as default over base 6";
2392 // unused characters are ignored
2394 e
= Entropy
.fromString("fghijkl");
2395 if (e
.cleanStr
!= "f") {
2396 return "additional characters are not ignored";
2402 // the lowest base is used by default
2403 // 7 could be decimal or hexadecimal, but should be detected as decimal
2405 e
= Entropy
.fromString("7");
2406 if (e
.base
.str
!= "base 10") {
2407 return "lowest base is not used";
2413 // Leading zeros are retained
2415 e
= Entropy
.fromString("000A");
2416 if (e
.cleanStr
!= "000A") {
2417 return "Leading zeros are not retained";
2423 // Leading zeros are correctly preserved for hex in binary string
2425 e
= Entropy
.fromString("2A");
2426 if (e
.binaryStr
!= "00101010") {
2427 return "Hex leading zeros are not correct in binary";
2433 // Leading zeros for base 6 as binary string
2434 // 20 = 2 events at 2.58 bits per event = 5 bits
2435 // 20 in base 6 = 12 in base 10 = 1100 in base 2
2436 // so it needs 1 bit of padding to be the right bit length
2438 e
= Entropy
.fromString("20");
2439 if (e
.binaryStr
!= "01100") {
2440 return "Base 6 as binary has leading zeros";
2446 // Leading zeros for base 10 as binary string
2448 e
= Entropy
.fromString("17");
2449 if (e
.binaryStr
!= "010001") {
2450 return "Base 10 as binary has leading zeros";
2456 // Leading zeros for card entropy as binary string.
2457 // Card entropy is hashed so 2c does not necessarily produce leading zeros.
2459 e
= Entropy
.fromString("2c");
2460 if (e
.binaryStr
!= "0010") {
2461 return "Card entropy as binary has leading zeros";
2467 // Keyboard mashing results in weak entropy
2468 // Despite being a long string, it's less than 30 bits of entropy
2470 e
= Entropy
.fromString("aj;se ifj; ask,dfv js;ifj");
2471 if (e
.binaryStr
.length
>= 30) {
2472 return "Keyboard mashing should produce weak entropy";
2478 // Card entropy is used if every pair could be a card
2480 e
= Entropy
.fromString("4c3c2c");
2481 if (e
.base
.str
!= "card") {
2482 return "Card entropy not used if all pairs are cards";
2488 // Card entropy uses base 52
2489 // [ cards, binary ]
2493 [ "acqs", "11011100" ],
2494 [ "acks", "01011100" ],
2495 [ "2cac", "11111000" ],
2508 [ "ks2c", "01010100" ],
2509 [ "KS2C", "01010100" ],
2511 for (var i
=0; i
<cards
.length
; i
++) {
2512 var card
= cards
[i
][0];
2513 var result
= cards
[i
][1];
2514 e
= Entropy
.fromString(card
);
2515 console
.log(e
.binary
+ " " + result
);
2516 if (e
.binaryStr
!== result
) {
2517 return "card entropy " + card
+ " not parsed correctly: " + result
+ " != " + e
.binaryStr
;
2526 if (response
!= "PASS") {
2527 console
.log("Entropy unit tests");
2528 console
.log(response
);
2535 // Entropy can be entered by the user
2537 page
.open(url
, function(status
) {
2539 mnemonic: "abandon abandon ability",
2540 address: "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug",
2543 page
.evaluate(function() {
2544 $(".use-entropy").prop("checked", true).trigger("change");
2545 $(".entropy").val("00000000 00000000 00000000 00000000").trigger("input");
2547 // check the mnemonic is set and address is correct
2548 waitForGenerate(function() {
2549 var actual
= page
.evaluate(function() {
2551 address: $(".address:first").text(),
2552 mnemonic: $(".phrase").val(),
2555 if (actual
.mnemonic
!= expected
.mnemonic
) {
2556 console
.log("Entropy does not generate correct mnemonic");
2557 console
.log("Expected: " + expected
.mnemonic
);
2558 console
.log("Got: " + actual
.mnemonic
);
2561 if (actual
.address
!= expected
.address
) {
2562 console
.log("Entropy does not generate correct address");
2563 console
.log("Expected: " + expected
.address
);
2564 console
.log("Got: " + actual
.address
);
2572 // A warning about entropy is shown to the user, with additional information
2574 page
.open(url
, function(status
) {
2575 // get text content from entropy sections of page
2576 var hasWarning
= page
.evaluate(function() {
2577 var entropyText
= $(".entropy-container").text();
2578 var warning
= "mnemonic may be insecure";
2579 if (entropyText
.indexOf(warning
) == -1) {
2582 var readMoreText
= $("#entropy-notes").parent().text();
2583 var goodSources
= "flipping a fair coin, rolling a fair dice, noise measurements etc";
2584 if (readMoreText
.indexOf(goodSources
) == -1) {
2589 // check the warnings and information are shown
2591 console
.log("Page does not contain warning about using own entropy");
2598 // The types of entropy available are described to the user
2600 page
.open(url
, function(status
) {
2601 // get placeholder text for entropy field
2602 var placeholder
= page
.evaluate(function() {
2603 return $(".entropy").attr("placeholder");
2613 for (var i
=0; i
<options
.length
; i
++) {
2614 var option
= options
[i
];
2615 if (placeholder
.indexOf(option
) == -1) {
2616 console
.log("Available entropy type is not shown to user: " + option
);
2624 // The actual entropy used is shown to the user
2626 page
.open(url
, function(status
) {
2628 var badEntropySource
= page
.evaluate(function() {
2629 var entropy
= "Not A Very Good Entropy Source At All";
2630 $(".use-entropy").prop("checked", true).trigger("change");
2631 $(".entropy").val(entropy
).trigger("input");
2633 // check the actual entropy being used is shown
2634 waitForEntropyFeedback(function() {
2635 var expectedText
= "AedEceAA";
2636 var entropyText
= page
.evaluate(function() {
2637 return $(".entropy-container").text();
2639 if (entropyText
.indexOf(expectedText
) == -1) {
2640 console
.log("Actual entropy used is not shown");
2648 // Binary entropy can be entered
2650 page
.open(url
, function(status
) {
2652 page
.evaluate(function() {
2653 $(".use-entropy").prop("checked", true).trigger("change");
2654 $(".entropy").val("01").trigger("input");
2656 // check the entropy is shown to be the correct type
2657 waitForEntropyFeedback(function() {
2658 var entropyText
= page
.evaluate(function() {
2659 return $(".entropy-container").text();
2661 if (entropyText
.indexOf("binary") == -1) {
2662 console
.log("Binary entropy is not detected and presented to user");
2670 // Base 6 entropy can be entered
2672 page
.open(url
, function(status
) {
2674 page
.evaluate(function() {
2675 $(".use-entropy").prop("checked", true).trigger("change");
2676 $(".entropy").val("012345").trigger("input");
2678 // check the entropy is shown to be the correct type
2679 waitForEntropyFeedback(function() {
2680 var entropyText
= page
.evaluate(function() {
2681 return $(".entropy-container").text();
2683 if (entropyText
.indexOf("base 6") == -1) {
2684 console
.log("Base 6 entropy is not detected and presented to user");
2692 // Base 6 dice entropy can be entered
2694 page
.open(url
, function(status
) {
2696 page
.evaluate(function() {
2697 $(".use-entropy").prop("checked", true).trigger("change");
2698 $(".entropy").val("123456").trigger("input");
2700 // check the entropy is shown to be the correct type
2701 waitForEntropyFeedback(function() {
2702 var entropyText
= page
.evaluate(function() {
2703 return $(".entropy-container").text();
2705 if (entropyText
.indexOf("dice") == -1) {
2706 console
.log("Dice entropy is not detected and presented to user");
2714 // Base 10 entropy can be entered
2716 page
.open(url
, function(status
) {
2718 page
.evaluate(function() {
2719 $(".use-entropy").prop("checked", true).trigger("change");
2720 $(".entropy").val("789").trigger("input");
2722 // check the entropy is shown to be the correct type
2723 waitForEntropyFeedback(function() {
2724 var entropyText
= page
.evaluate(function() {
2725 return $(".entropy-container").text();
2727 if (entropyText
.indexOf("base 10") == -1) {
2728 console
.log("Base 10 entropy is not detected and presented to user");
2736 // Hexadecimal entropy can be entered
2738 page
.open(url
, function(status
) {
2740 page
.evaluate(function() {
2741 $(".use-entropy").prop("checked", true).trigger("change");
2742 $(".entropy").val("abcdef").trigger("input");
2744 // check the entropy is shown to be the correct type
2745 waitForEntropyFeedback(function() {
2746 var entropyText
= page
.evaluate(function() {
2747 return $(".entropy-container").text();
2749 if (entropyText
.indexOf("hexadecimal") == -1) {
2750 console
.log("Hexadecimal entropy is not detected and presented to user");
2758 // Dice entropy value is shown as the converted base 6 value
2760 page
.open(url
, function(status
) {
2762 page
.evaluate(function() {
2763 $(".use-entropy").prop("checked", true).trigger("change");
2764 $(".entropy").val("123456").trigger("input");
2766 // check the entropy is shown as base 6, not as the original dice value
2767 waitForEntropyFeedback(function() {
2768 var entropyText
= page
.evaluate(function() {
2769 return $(".entropy-container").text();
2771 if (entropyText
.indexOf("123450") == -1) {
2772 console
.log("Dice entropy is not shown to user as base 6 value");
2775 if (entropyText
.indexOf("123456") > -1) {
2776 console
.log("Dice entropy value is shown instead of true base 6 value");
2784 // The number of bits of entropy accumulated is shown
2786 page
.open(url
, function(status
) {
2789 [ "0000 0000 0000 0000 0000", "20" ],
2792 [ "6", "2" ], // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
2793 [ "7", "3" ], // 7 in base 10 is 111 in base 2, no leading zeros
2798 [ "1A", "8" ], // hex is always multiple of 4 bits of entropy
2805 [ "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)
2806 [ "2227", "13" ], // Uses base 10, which is 4 lots of 3.32 bits, which is 13.3 bits (rounded down to 13)
2809 [ "0000101017", "33" ], // 10 events at 3.32 bits per event
2810 [ "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
2813 page
.evaluate(function(e
) {
2814 $(".use-entropy").prop("checked", true).trigger("change");
2817 var nextTest
= function runNextTest(i
) {
2818 var entropy
= tests
[i
][0];
2819 var expected
= tests
[i
][1];
2821 page
.evaluate(function(e
) {
2822 $(".entropy").val(e
).trigger("input");
2824 // check the number of bits of entropy is shown
2825 waitForEntropyFeedback(function() {
2826 var entropyText
= page
.evaluate(function() {
2827 return $(".entropy-container").text();
2829 if (entropyText
.replace(/\s/g,"").indexOf("Bits" + expected
) == -1) {
2830 console
.log("Accumulated entropy is not shown correctly for " + entropy
);
2833 var isLastTest
= i
== tests
.length
- 1;
2846 // There is feedback provided about the supplied entropy
2848 page
.open(url
, function(status
) {
2853 type: "hexadecimal",
2857 strength: "less than a second",
2860 entropy: "AAAAAAAA",
2861 filtered: "AAAAAAAA",
2862 type: "hexadecimal",
2866 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
2869 entropy: "AAAAAAAA B",
2870 filtered: "AAAAAAAAB",
2871 type: "hexadecimal",
2875 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
2878 entropy: "AAAAAAAA BBBBBBBB",
2879 filtered: "AAAAAAAABBBBBBBB",
2880 type: "hexadecimal",
2884 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
2887 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
2888 filtered: "AAAAAAAABBBBBBBBCCCCCCCC",
2889 type: "hexadecimal",
2893 strength: "less than a second",
2896 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
2897 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD",
2898 type: "hexadecimal",
2902 strength: "2 minutes",
2905 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA",
2906 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA",
2907 type: "hexadecimal",
2914 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE",
2915 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE",
2916 type: "hexadecimal",
2920 strength: "3 years",
2923 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF",
2924 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF",
2925 type: "hexadecimal",
2929 strength: "centuries",
2937 strength: "less than a second",
2940 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2941 type: "card (full deck)",
2945 strength: "centuries",
2948 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d",
2949 type: "card (full deck, 1 duplicate: 3d)",
2953 strength: "centuries",
2956 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d",
2957 type: "card (2 duplicates: 3d 4d, 1 missing: KS)",
2961 strength: "centuries",
2964 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d",
2965 type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)",
2969 strength: "centuries",
2971 // Next test was throwing uncaught error in zxcvbn
2972 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
2974 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2975 type: "card (full deck, 52 duplicates: ac 2c 3c...)",
2979 strength: "centuries",
2981 // Case insensitivity to duplicate cards
2984 type: "card (1 duplicate: AS)",
2988 strength: "less than a second",
2992 type: "card (1 duplicate: as)",
2996 strength: "less than a second",
2998 // Missing cards are detected
3000 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
3001 type: "card (1 missing: 9C)",
3005 strength: "centuries",
3008 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
3009 type: "card (2 missing: 9C 5D)",
3013 strength: "centuries",
3016 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
3017 type: "card (4 missing: 9C 5D QD...)",
3021 strength: "centuries",
3023 // More than six missing cards does not show message
3025 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks",
3030 strength: "centuries",
3032 // Multiple decks of cards increases bits per event
3052 entropy: "3d3d3d3d",
3058 entropy: "3d3d3d3d3d",
3064 entropy: "3d3d3d3d3d3d",
3070 entropy: "3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d",
3074 strength: 'less than a second - Repeats like "abcabcabc" are only slightly harder to guess than "abc"',
3078 page
.evaluate(function() {
3079 $(".use-entropy").prop("checked", true).trigger("change");
3081 var nextTest
= function runNextTest(i
) {
3082 function getFeedbackError(expected
, actual
) {
3083 if ("filtered" in expected
&& actual
.indexOf(expected
.filtered
) == -1) {
3084 return "Filtered value not in feedback";
3086 if ("type" in expected
&& actual
.indexOf(expected
.type
) == -1) {
3087 return "Entropy type not in feedback";
3089 if ("events" in expected
&& actual
.indexOf(expected
.events
) == -1) {
3090 return "Event count not in feedback";
3092 if ("bits" in expected
&& actual
.indexOf(expected
.bits
) == -1) {
3093 return "Bit count not in feedback";
3095 if ("strength" in expected
&& actual
.indexOf(expected
.strength
) == -1) {
3096 return "Strength not in feedback";
3098 if ("bitsPerEvent" in expected
&& actual
.indexOf(expected
.bitsPerEvent
) == -1) {
3099 return "bitsPerEvent not in feedback";
3104 page
.evaluate(function(e
) {
3105 $(".addresses").empty();
3106 $(".phrase").val("");
3107 $(".entropy").val(e
).trigger("input");
3109 waitForEntropyFeedback(function() {
3110 var mnemonic
= page
.evaluate(function() {
3111 return $(".phrase").val();
3113 // Check mnemonic length
3114 if ("words" in test
&& test
.words
== 0) {
3115 if (mnemonic
.length
> 0) {
3116 console
.log("Mnemonic length for " + test
.strength
+ " strength is not " + test
.words
);
3117 console
.log("Entropy: " + test
.entropy
);
3118 console
.log("Mnemonic: " + mnemonic
);
3122 else if ("words" in test
) {
3123 if (mnemonic
.split(" ").length
!= test
.words
) {
3124 console
.log("Mnemonic length for " + test
.strength
+ " strength is not " + test
.words
);
3125 console
.log("Entropy: " + test
.entropy
);
3126 console
.log("Mnemonic: " + mnemonic
);
3131 var feedback
= page
.evaluate(function() {
3132 return $(".entropy-container").text();
3134 var feedbackError
= getFeedbackError(test
, feedback
);
3135 if (feedbackError
) {
3136 console
.log("Entropy feedback for " + test
.entropy
+ " returned error");
3137 console
.log(feedbackError
);
3141 var isLastTest
= i
== tests
.length
- 1;
3154 // Entropy is truncated from the left
3156 page
.open(url
, function(status
) {
3157 var expected
= "avocado zoo zone";
3159 page
.evaluate(function() {
3160 $(".use-entropy").prop("checked", true).trigger("change");
3161 var entropy
= "00000000 00000000 00000000 00000000";
3162 entropy
+= "11111111 11111111 11111111 1111"; // Missing last byte
3163 $(".entropy").val(entropy
).trigger("input");
3165 // check the entropy is truncated from the right
3166 waitForGenerate(function() {
3167 var actual
= page
.evaluate(function() {
3168 return $(".phrase").val();
3170 if (actual
!= expected
) {
3171 console
.log("Entropy is not truncated from the right");
3172 console
.log("Expected: " + expected
);
3173 console
.log("Got: " + actual
);
3181 // Very large entropy results in very long mnemonics
3183 page
.open(url
, function(status
) {
3185 page
.evaluate(function() {
3186 $(".use-entropy").prop("checked", true).trigger("change");
3188 // Generate a very long entropy string
3189 for (var i
=0; i
<33; i
++) {
3190 entropy
+= "AAAAAAAA"; // 3 words * 33 iterations = 99 words
3192 $(".entropy").val(entropy
).trigger("input");
3194 // check the mnemonic is very long
3195 waitForGenerate(function() {
3196 var wordCount
= page
.evaluate(function() {
3197 return $(".phrase").val().split(" ").length
;
3199 if (wordCount
!= 99) {
3200 console
.log("Large entropy does not generate long mnemonic");
3201 console
.log("Expected 99 words, got " + wordCount
);
3209 // Is compatible with bip32jp entropy
3210 // https://bip32jp.github.io/english/index.html
3212 // Is incompatible with:
3215 page
.open(url
, function(status
) {
3216 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";
3218 page
.evaluate(function() {
3219 $(".use-entropy").prop("checked", true).trigger("change");
3220 var entropy
= "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543";
3221 $(".entropy").val(entropy
).trigger("input");
3223 // check the mnemonic matches the expected value from bip32jp
3224 waitForGenerate(function() {
3225 var actual
= page
.evaluate(function() {
3226 return $(".phrase").val();
3228 if (actual
!= expected
) {
3229 console
.log("Mnemonic does not match bip32jp for base 6 entropy");
3230 console
.log("Expected: " + expected
);
3231 console
.log("Got: " + actual
);
3239 // Blank entropy does not generate mnemonic or addresses
3241 page
.open(url
, function(status
) {
3243 page
.evaluate(function() {
3244 $(".use-entropy").prop("checked", true).trigger("change");
3245 $(".entropy").val("").trigger("input");
3247 waitForFeedback(function() {
3248 // check there is no mnemonic
3249 var phrase
= page
.evaluate(function() {
3250 return $(".phrase").val();
3253 console
.log("Blank entropy does not result in blank mnemonic");
3254 console
.log("Got: " + phrase
);
3257 // check there are no addresses displayed
3258 var addresses
= page
.evaluate(function() {
3259 return $(".address").length
;
3261 if (addresses
!= 0) {
3262 console
.log("Blank entropy does not result in zero addresses");
3265 // Check the feedback says 'blank entropy'
3266 var feedback
= page
.evaluate(function() {
3267 return $(".feedback").text();
3269 if (feedback
!= "Blank entropy") {
3270 console
.log("Blank entropy does not show feedback message");
3278 // Mnemonic length can be selected even for weak entropy
3280 page
.open(url
, function(status
) {
3282 page
.evaluate(function() {
3283 $(".use-entropy").prop("checked", true).trigger("change");
3284 $(".entropy").val("012345");
3285 $(".mnemonic-length").val("18").trigger("change");
3287 // check the mnemonic is the correct length
3288 waitForGenerate(function() {
3289 var phrase
= page
.evaluate(function() {
3290 return $(".phrase").val();
3292 var numberOfWords
= phrase
.split(/\s/g).length
;
3293 if (numberOfWords
!= 18) {
3294 console
.log("Weak entropy cannot be overridden to give 18 word mnemonic");
3295 console
.log(phrase
);
3304 // https://github.com/iancoleman/bip39/issues/33
3305 // Final cards should contribute entropy
3307 page
.open(url
, function(status
) {
3309 page
.evaluate(function() {
3310 $(".use-entropy").prop("checked", true).trigger("change");
3311 $(".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");
3314 waitForGenerate(function() {
3315 var originalPhrase
= page
.evaluate(function() {
3316 return $(".phrase").val();
3318 // Set the last 12 cards to be AS
3319 page
.evaluate(function() {
3320 $(".addresses").empty();
3321 $(".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");
3323 // get the new mnemonic
3324 waitForGenerate(function() {
3325 var newPhrase
= page
.evaluate(function() {
3326 return $(".phrase").val();
3328 // check the phrase has changed
3329 if (newPhrase
== originalPhrase
) {
3330 console
.log("Changing last 12 cards does not change mnemonic");
3331 console
.log("Original:");
3332 console
.log(originalPhrase
);
3333 console
.log("New:");
3334 console
.log(newPhrase
);
3344 // https://github.com/iancoleman/bip39/issues/35
3347 page
.open(url
, function(status
) {
3349 page
.evaluate(function() {
3350 $(".generate").click();
3352 waitForGenerate(function() {
3353 var p
= page
.evaluate(function() {
3354 // get position of mnemonic element
3355 return $(".phrase").offset();
3357 p
.top
= Math
.ceil(p
.top
);
3358 p
.left
= Math
.ceil(p
.left
);
3359 // check the qr code shows
3360 page
.sendEvent("mousemove", p
.left
+4, p
.top
+4);
3361 var qrShowing
= page
.evaluate(function() {
3362 return $(".qr-container").find("canvas").length
> 0;
3365 console
.log("QR Code does not show");
3368 // check the qr code hides
3369 page
.sendEvent("mousemove", p
.left
-4, p
.top
-4);
3370 var qrHidden
= page
.evaluate(function() {
3371 return $(".qr-container").find("canvas").length
== 0;
3374 console
.log("QR Code does not hide");
3382 // BIP44 account extendend private key is shown
3383 // github issue 37 - compatibility with electrum
3385 page
.open(url
, function(status
) {
3387 var expected
= "xprv9yzrnt4zWVJUr1k2VxSPy9ettKz5PpeDMgaVG7UKedhqnw1tDkxP2UyYNhuNSumk2sLE5ctwKZs9vwjsq3e1vo9egCK6CzP87H2cVYXpfwQ";
3388 page
.evaluate(function() {
3389 $(".phrase").val("abandon abandon ability");
3390 $(".phrase").trigger("input");
3392 // check the BIP44 account extended private key
3393 waitForGenerate(function() {
3394 var actual
= page
.evaluate(function() {
3395 return $(".account-xprv").val();
3397 if (actual
!= expected
) {
3398 console
.log("BIP44 account extended private key is incorrect");
3399 console
.log("Expected: " + expected
);
3400 console
.log("Actual: " + actual
);
3408 // BIP44 account extendend public key is shown
3409 // github issue 37 - compatibility with electrum
3411 page
.open(url
, function(status
) {
3413 var expected
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3414 page
.evaluate(function() {
3415 $(".phrase").val("abandon abandon ability");
3416 $(".phrase").trigger("input");
3418 // check the BIP44 account extended public key
3419 waitForGenerate(function() {
3420 var actual
= page
.evaluate(function() {
3421 return $(".account-xpub").val();
3423 if (actual
!= expected
) {
3424 console
.log("BIP44 account extended public key is incorrect");
3425 console
.log("Expected: " + expected
);
3426 console
.log("Actual: " + actual
);
3435 // BIP32 root key can be set as an xpub
3437 page
.open(url
, function(status
) {
3439 page
.evaluate(function() {
3440 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3441 var bip44AccountXpub
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3442 $("#root-key").val(bip44AccountXpub
);
3443 $("#root-key").trigger("input");
3445 waitForFeedback(function() {
3446 page
.evaluate(function() {
3448 $("#bip32-tab a").click();
3450 waitForGenerate(function() {
3451 page
.evaluate(function() {
3452 // derive external addresses for this xpub
3453 var firstAccountDerivationPath
= "m/0";
3454 $("#bip32-path").val(firstAccountDerivationPath
);
3455 $("#bip32-path").trigger("input");
3457 waitForGenerate(function() {
3458 // check the addresses are generated
3459 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3460 var actual
= page
.evaluate(function() {
3461 return $(".address:first").text();
3463 if (actual
!= expected
) {
3464 console
.log("xpub key does not generate addresses in table");
3465 console
.log("Expected: " + expected
);
3466 console
.log("Actual: " + actual
);
3469 // check the xprv key is not set
3470 var expected
= "NA";
3471 var actual
= page
.evaluate(function() {
3472 return $(".extended-priv-key").val();
3474 if (actual
!= expected
) {
3475 console
.log("xpub key as root shows derived bip32 xprv key");
3476 console
.log("Expected: " + expected
);
3477 console
.log("Actual: " + actual
);
3480 // check the private key is not set
3481 var expected
= "NA";
3482 var actual
= page
.evaluate(function() {
3483 return $(".privkey:first").text();
3485 if (actual
!= expected
) {
3486 console
.log("xpub key generates private key in addresses table");
3487 console
.log("Expected: " + expected
);
3488 console
.log("Actual: " + actual
);
3499 // xpub for bip32 root key will not work with hardened derivation paths
3501 page
.open(url
, function(status
) {
3503 page
.evaluate(function() {
3504 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3505 var bip44AccountXpub
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3506 $("#root-key").val(bip44AccountXpub
);
3507 $("#root-key").trigger("input");
3509 waitForFeedback(function() {
3510 // Check feedback is correct
3511 var expected
= "Hardened derivation path is invalid with xpub key";
3512 var actual
= page
.evaluate(function() {
3513 return $(".feedback").text();
3515 if (actual
!= expected
) {
3516 console
.log("xpub key with hardened derivation path does not show feedback");
3517 console
.log("Expected: " + expected
);
3518 console
.log("Actual: " + actual
);
3521 // Check no addresses are shown
3523 var actual
= page
.evaluate(function() {
3524 return $(".addresses tr").length
;
3526 if (actual
!= expected
) {
3527 console
.log("addresses still show after setting xpub key with hardened derivation path");
3528 console
.log("Expected: " + expected
);
3529 console
.log("Actual: " + actual
);
3538 // no root key shows feedback
3540 page
.open(url
, function(status
) {
3541 // click the bip32 tab on fresh page
3542 page
.evaluate(function() {
3543 $("#bip32-tab a").click();
3545 waitForFeedback(function() {
3546 // Check feedback is correct
3547 var expected
= "No root key";
3548 var actual
= page
.evaluate(function() {
3549 return $(".feedback").text();
3551 if (actual
!= expected
) {
3552 console
.log("Blank root key not detected");
3553 console
.log("Expected: " + expected
);
3554 console
.log("Actual: " + actual
);
3563 // display error switching tabs while addresses are generating
3565 page
.open(url
, function(status
) {
3567 page
.evaluate(function() {
3568 $(".phrase").val("abandon abandon ability").trigger("input");
3570 waitForGenerate(function() {
3571 // set to generate 500 more addresses
3572 // generate more addresses
3573 // change tabs which should cancel the previous generating
3574 page
.evaluate(function() {
3575 $(".rows-to-add").val("100");
3577 $("#bip32-tab a").click();
3579 // check the derivation paths are in order and of the right quantity
3580 waitForGenerate(function() {
3581 var paths
= page
.evaluate(function() {
3582 return $(".index").map(function(i
, e
) {
3586 for (var i
=0; i
<paths
.length
; i
++) {
3587 var expected
= "m/0/" + i
;
3588 var actual
= paths
[i
];
3589 if (actual
!= expected
) {
3590 console
.log("Path " + i
+ " is not in correct order");
3591 console
.log("Expected: " + expected
);
3592 console
.log("Actual: " + actual
);
3596 if (paths
.length
!= 20) {
3597 console
.log("Generation was not cancelled by new action");
3607 // padding for binary should give length with multiple of 256
3608 // hashed entropy 1111 is length 252, so requires 4 leading zeros
3609 // prior to issue 49 it would only generate 2 leading zeros, ie missing 2
3611 page
.open(url
, function(status
) {
3612 expected
= "avocado valid quantum cross link predict excuse edit street able flame large galaxy ginger nuclear"
3614 page
.evaluate(function() {
3615 $(".use-entropy").prop("checked", true).trigger("change");
3616 $(".mnemonic-length").val("15");
3617 $(".entropy").val("1111").trigger("input");
3619 waitForGenerate(function() {
3621 var actual
= page
.evaluate(function() {
3622 return $(".phrase").val();
3624 // check the mnemonic is correct
3625 if (actual
!= expected
) {
3626 console
.log("Left padding error for entropy");
3627 console
.log("Expected: " + expected
);
3628 console
.log("Actual: " + actual
);
3636 // Github pull request 55
3637 // https://github.com/iancoleman/bip39/pull/55
3640 page
.open(url
, function(status
) {
3641 // set mnemonic and select bip32 tab
3642 page
.evaluate(function() {
3643 $("#bip32-tab a").click();
3644 $(".phrase").val("abandon abandon ability").trigger("input");
3646 waitForGenerate(function() {
3648 // set bip32 client to bitcoin core
3649 page
.evaluate(function() {
3650 var bitcoinCoreIndex
= "0";
3651 $("#bip32-client").val(bitcoinCoreIndex
).trigger("change");
3653 waitForGenerate(function() {
3654 // get the derivation path
3655 var actual
= page
.evaluate(function() {
3656 return $("#bip32-path").val();
3658 // check the derivation path is correct
3659 expected
= "m/0'/0'"
3660 if (actual
!= expected
) {
3661 console
.log("Selecting Bitcoin Core client does not set correct derivation path");
3662 console
.log("Expected: " + expected
);
3663 console
.log("Actual: " + actual
);
3666 // get hardened addresses
3667 var usesHardenedAddresses
= page
.evaluate(function() {
3668 return $(".hardened-addresses").prop("checked");
3670 // check hardened addresses is selected
3671 if(!usesHardenedAddresses
) {
3672 console
.log("Selecting Bitcoin Core client does not use hardened addresses");
3675 // check input is readonly
3676 var pathIsReadonly
= page
.evaluate(function() {
3677 return $("#bip32-path").prop("readonly");
3679 if (!pathIsReadonly
) {
3680 console
.log("Selecting Bitcoin Core client does not set derivation path to readonly");
3684 // set bip32 client to multibit
3685 page
.evaluate(function() {
3686 var multibitIndex
= "2";
3687 $("#bip32-client").val(multibitIndex
).trigger("change");
3689 waitForGenerate(function() {
3690 // get the derivation path
3691 var actual
= page
.evaluate(function() {
3692 return $("#bip32-path").val();
3694 // check the derivation path is correct
3696 if (actual
!= expected
) {
3697 console
.log("Selecting Multibit client does not set correct derivation path");
3698 console
.log("Expected: " + expected
);
3699 console
.log("Actual: " + actual
);
3702 // get hardened addresses
3703 var usesHardenedAddresses
= page
.evaluate(function() {
3704 return $(".hardened-addresses").prop("checked");
3706 // check hardened addresses is selected
3707 if(usesHardenedAddresses
) {
3708 console
.log("Selecting Multibit client does not uncheck hardened addresses");
3711 // CUSTOM DERIVATION PATH
3712 // check input is not readonly
3713 page
.evaluate(function() {
3714 $("#bip32-client").val("custom").trigger("change");
3716 // do not wait for generate, since there is no change to the
3717 // derivation path there is no new generation performed
3718 var pathIsReadonly
= page
.evaluate(function() {
3719 return $("#bip32-path").prop("readonly");
3721 if (pathIsReadonly
) {
3722 console
.log("Selecting Custom Derivation Path does not allow derivation path input");
3733 // https://github.com/iancoleman/bip39/issues/58
3734 // bip32 derivation is correct, does not drop leading zeros
3736 // https://medium.com/@alexberegszaszi/why-do-my-bip32-wallets-disagree-6f3254cc5846
3738 page
.open(url
, function(status
) {
3739 // set the phrase and passphrase
3740 var expected
= "17rxURoF96VhmkcEGCj5LNQkmN9HVhWb7F";
3741 // Note that bitcore generates an incorrect address
3742 // 13EuKhffWkBE2KUwcbkbELZb1MpzbimJ3Y
3743 // see the medium.com link above for more details
3744 page
.evaluate(function() {
3745 $(".phrase").val("fruit wave dwarf banana earth journey tattoo true farm silk olive fence");
3746 $(".passphrase").val("banana").trigger("input");
3748 // check the address is generated correctly
3749 waitForGenerate(function() {
3750 var actual
= page
.evaluate(function() {
3751 return $(".address:first").text();
3753 if (actual
!= expected
) {
3754 console
.log("BIP32 derivation is incorrect");
3755 console
.log("Expected: " + expected
);
3756 console
.log("Actual: " + actual
);
3766 // Japanese mnemonics generate incorrect bip32 seed
3767 // BIP39 seed is set from phrase
3769 page
.open(url
, function(status
) {
3771 var expected
= "a262d6fb6122ecf45be09c50492b31f92e9beb7d9a845987a02cefda57a15f9c467a17872029a9e92299b5cbdf306e3a0ee620245cbd508959b6cb7ca637bd55";
3772 page
.evaluate(function() {
3773 $(".phrase").val("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら");
3774 $("#passphrase").val("メートルガバヴァぱばぐゞちぢ十人十色");
3775 $("#passphrase").trigger("input");
3777 // check the seed is generated correctly
3778 waitForGenerate(function() {
3779 var actual
= page
.evaluate(function() {
3780 return $(".seed").val();
3782 if (actual
!= expected
) {
3783 console
.log("BIP39 seed is incorrectly generated from Japanese mnemonic");
3784 console
.log("Expected: " + expected
);
3785 console
.log("Actual: " + actual
);
3793 // If you wish to add more tests, do so here...
3795 // Here is a blank test template
3799 page.open(url, function(status) {
3800 // Do something on the page
3801 page.evaluate(function() {
3802 $(".phrase").val("abandon abandon ability").trigger("input");
3804 waitForGenerate(function() {
3805 // Check the result of doing the thing
3806 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3807 var actual = page.evaluate(function() {
3808 return $(".address:first").text();
3810 if (actual != expected) {
3811 console.log("A specific message about what failed");
3812 console.log("Expected: " + expected);
3813 console.log("Actual: " + actual);
3816 // Run the next test
3826 console
.log("Running tests...");
3827 tests
= shuffle(tests
);