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() == "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() == "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() == "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("Litecoin 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() == "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() == "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() == "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() == "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() == "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() == "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";
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 dash
576 page
.open(url
, function(status
) {
577 // set the phrase and coin
578 var expected
= "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT";
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() == "DASH";
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("DASH address is incorrect");
595 console
.log("Expected: " + expected
);
596 console
.log("Actual: " + actual
);
604 // Network can be set to game
606 page
.open(url
, function(status
) {
607 // set the phrase and coin
608 var expected
= "GSMY9bAp36cMR4zyT4uGVS7GFjpdXbao5Q";
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() == "GAME";
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("GAME address is incorrect");
625 console
.log("Expected: " + expected
);
626 console
.log("Actual: " + actual
);
634 // Network can be set to namecoin
636 page
.open(url
, function(status
) {
637 // set the phrase and coin
638 var expected
= "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2";
639 page
.evaluate(function() {
640 $(".phrase").val("abandon abandon ability");
641 $(".phrase").trigger("input");
642 $(".network option[selected]").removeAttr("selected");
643 $(".network option").filter(function() {
644 return $(this).html() == "Namecoin";
645 }).prop("selected", true);
646 $(".network").trigger("change");
648 // check the address is generated correctly
649 waitForGenerate(function() {
650 var actual
= page
.evaluate(function() {
651 return $(".address:first").text();
653 if (actual
!= expected
) {
654 console
.log("Namecoin address is incorrect");
655 console
.log("Expected: " + expected
);
656 console
.log("Actual: " + actual
);
664 // Network can be set to peercoin
666 page
.open(url
, function(status
) {
667 // set the phrase and coin
668 var expected
= "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm";
669 page
.evaluate(function() {
670 $(".phrase").val("abandon abandon ability");
671 $(".phrase").trigger("input");
672 $(".network option[selected]").removeAttr("selected");
673 $(".network option").filter(function() {
674 return $(this).html() == "Peercoin";
675 }).prop("selected", true);
676 $(".network").trigger("change");
678 // check the address is generated correctly
679 waitForGenerate(function() {
680 var actual
= page
.evaluate(function() {
681 return $(".address:first").text();
683 if (actual
!= expected
) {
684 console
.log("Peercoin address is incorrect");
685 console
.log("Expected: " + expected
);
686 console
.log("Actual: " + actual
);
694 // Network can be set to ethereum
697 page
.open(url
, function(status
) {
699 // set the phrase and coin
700 page
.evaluate(function() {
701 $(".phrase").val("abandon abandon ability");
702 $(".phrase").trigger("input");
703 $(".network option[selected]").removeAttr("selected");
704 $(".network option").filter(function() {
705 return $(this).html() == "Ethereum";
706 }).prop("selected", true);
707 $(".network").trigger("change");
709 waitForGenerate(function() {
710 // check the address is generated correctly
711 // this value comes from
712 // https://www.myetherwallet.com/#view-wallet-info
713 // Unusual capitalization is due to checksum
714 var expected
= "0xe5815d5902Ad612d49283DEdEc02100Bd44C2772";
715 var actual
= page
.evaluate(function() {
716 return $(".address:first").text();
718 if (actual
!= expected
) {
719 console
.log("Ethereum address is incorrect");
720 console
.log("Expected: " + expected
);
721 console
.log("Actual: " + actual
);
724 // check the private key is correct
725 // this private key can be imported into
726 // https://www.myetherwallet.com/#view-wallet-info
727 // and it should correlate to the address above
728 var expected
= "8f253078b73d7498302bb78c171b23ce7a8fb511987d2b2702b731638a4a15e7";
729 var actual
= page
.evaluate(function() {
730 return $(".privkey:first").text();
732 if (actual
!= expected
) {
733 console
.log("Ethereum privkey is incorrect");
734 console
.log("Expected: " + expected
);
735 console
.log("Actual: " + actual
);
738 // check the public key is correct
740 // don't have any third-party source to generate the expected value
741 //var expected = "?";
742 //var actual = page.evaluate(function() {
743 // return $(".pubkey:first").text();
745 //if (actual != expected) {
746 // console.log("Ethereum privkey is incorrect");
747 // console.log("Expected: " + expected);
748 // console.log("Actual: " + actual);
756 // BIP39 seed is set from phrase
758 page
.open(url
, function(status
) {
760 var expected
= "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
761 page
.evaluate(function() {
762 $(".phrase").val("abandon abandon ability");
763 $(".phrase").trigger("input");
765 // check the address is generated correctly
766 waitForGenerate(function() {
767 var actual
= page
.evaluate(function() {
768 return $(".seed").val();
770 if (actual
!= expected
) {
771 console
.log("BIP39 seed is incorrectly generated from mnemonic");
772 console
.log("Expected: " + expected
);
773 console
.log("Actual: " + actual
);
781 // BIP32 root key is set from phrase
783 page
.open(url
, function(status
) {
785 var expected
= "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi";
786 page
.evaluate(function() {
787 $(".phrase").val("abandon abandon ability");
788 $(".phrase").trigger("input");
790 // check the address is generated correctly
791 waitForGenerate(function() {
792 var actual
= page
.evaluate(function() {
793 return $(".root-key").val();
795 if (actual
!= expected
) {
796 console
.log("Root key is incorrectly generated from mnemonic");
797 console
.log("Expected: " + expected
);
798 console
.log("Actual: " + actual
);
806 // Tabs show correct addresses when changed
808 page
.open(url
, function(status
) {
810 var expected
= "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz";
811 page
.evaluate(function() {
812 $(".phrase").val("abandon abandon ability");
813 $(".phrase").trigger("input");
816 waitForGenerate(function() {
817 page
.evaluate(function() {
818 $("#bip32-tab a").click();
820 // check the address is generated correctly
821 waitForGenerate(function() {
822 var actual
= page
.evaluate(function() {
823 return $(".address:first").text();
825 if (actual
!= expected
) {
826 console
.log("Clicking tab generates incorrect address");
827 console
.log("Expected: " + expected
);
828 console
.log("Actual: " + actual
);
837 // BIP44 derivation path is shown
839 page
.open(url
, function(status
) {
841 var expected
= "m/44'/0'/0'/0";
842 page
.evaluate(function() {
843 $(".phrase").val("abandon abandon ability");
844 $(".phrase").trigger("input");
846 // check the derivation path of the first address
847 waitForGenerate(function() {
848 var actual
= page
.evaluate(function() {
849 return $("#bip44 .path").val();
851 if (actual
!= expected
) {
852 console
.log("BIP44 derivation path is incorrect");
853 console
.log("Expected: " + expected
);
854 console
.log("Actual: " + actual
);
862 // BIP44 extended private key is shown
864 page
.open(url
, function(status
) {
866 var expected
= "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG";
867 page
.evaluate(function() {
868 $(".phrase").val("abandon abandon ability");
869 $(".phrase").trigger("input");
871 // check the BIP44 extended private key
872 waitForGenerate(function() {
873 var actual
= page
.evaluate(function() {
874 return $(".extended-priv-key").val();
876 if (actual
!= expected
) {
877 console
.log("BIP44 extended private key is incorrect");
878 console
.log("Expected: " + expected
);
879 console
.log("Actual: " + actual
);
887 // BIP44 extended public key is shown
889 page
.open(url
, function(status
) {
891 var expected
= "xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM";
892 page
.evaluate(function() {
893 $(".phrase").val("abandon abandon ability");
894 $(".phrase").trigger("input");
896 // check the BIP44 extended public key
897 waitForGenerate(function() {
898 var actual
= page
.evaluate(function() {
899 return $(".extended-pub-key").val();
901 if (actual
!= expected
) {
902 console
.log("BIP44 extended public key is incorrect");
903 console
.log("Expected: " + expected
);
904 console
.log("Actual: " + actual
);
912 // BIP44 purpose field changes address list
914 page
.open(url
, function(status
) {
916 var expected
= "1JbDzRJ2cDT8aat2xwKd6Pb2zzavow5MhF";
917 page
.evaluate(function() {
918 $(".phrase").val("abandon abandon ability");
919 $(".phrase").trigger("input");
921 waitForGenerate(function() {
922 // change the bip44 purpose field to 45
923 page
.evaluate(function() {
924 $("#bip44 .purpose").val("45");
925 $("#bip44 .purpose").trigger("input");
927 waitForGenerate(function() {
928 // check the address for the new derivation path
929 var actual
= page
.evaluate(function() {
930 return $(".address:first").text();
932 if (actual
!= expected
) {
933 console
.log("BIP44 purpose field generates incorrect address");
934 console
.log("Expected: " + expected
);
935 console
.log("Actual: " + actual
);
944 // BIP44 coin field changes address list
946 page
.open(url
, function(status
) {
948 var expected
= "1F6dB2djQYrxoyfZZmfr6D5voH8GkJTghk";
949 page
.evaluate(function() {
950 $(".phrase").val("abandon abandon ability");
951 $(".phrase").trigger("input");
953 waitForGenerate(function() {
954 // change the bip44 purpose field to 45
955 page
.evaluate(function() {
956 $("#bip44 .coin").val("1");
957 $("#bip44 .coin").trigger("input");
959 waitForGenerate(function() {
960 // check the address for the new derivation path
961 var actual
= page
.evaluate(function() {
962 return $(".address:first").text();
964 if (actual
!= expected
) {
965 console
.log("BIP44 coin field generates incorrect address");
966 console
.log("Expected: " + expected
);
967 console
.log("Actual: " + actual
);
976 // BIP44 account field changes address list
978 page
.open(url
, function(status
) {
980 var expected
= "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
981 page
.evaluate(function() {
982 $(".phrase").val("abandon abandon ability");
983 $(".phrase").trigger("input");
985 waitForGenerate(function() {
986 // change the bip44 purpose field to 45
987 page
.evaluate(function() {
988 $("#bip44 .account").val("1");
989 $("#bip44 .account").trigger("input");
991 waitForGenerate(function() {
992 // check the address for the new derivation path
993 var actual
= page
.evaluate(function() {
994 return $(".address:first").text();
996 if (actual
!= expected
) {
997 console
.log("BIP44 account field generates incorrect address");
998 console
.log("Expected: " + expected
);
999 console
.log("Actual: " + actual
);
1008 // BIP44 change field changes address list
1010 page
.open(url
, function(status
) {
1012 var expected
= "1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo";
1013 page
.evaluate(function() {
1014 $(".phrase").val("abandon abandon ability");
1015 $(".phrase").trigger("input");
1017 waitForGenerate(function() {
1018 // change the bip44 purpose field to 45
1019 page
.evaluate(function() {
1020 $("#bip44 .change").val("1");
1021 $("#bip44 .change").trigger("input");
1023 waitForGenerate(function() {
1024 // check the address for the new derivation path
1025 var actual
= page
.evaluate(function() {
1026 return $(".address:first").text();
1028 if (actual
!= expected
) {
1029 console
.log("BIP44 change field generates incorrect address");
1030 console
.log("Expected: " + expected
);
1031 console
.log("Actual: " + actual
);
1040 // BIP32 derivation path can be set
1042 page
.open(url
, function(status
) {
1044 var expected
= "16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L";
1045 page
.evaluate(function() {
1046 $(".phrase").val("abandon abandon ability");
1047 $(".phrase").trigger("input");
1050 waitForGenerate(function() {
1051 page
.evaluate(function() {
1052 $("#bip32-tab a").click();
1054 // set the derivation path to m/1
1055 waitForGenerate(function() {
1056 page
.evaluate(function() {
1057 $("#bip32 .path").val("m/1");
1058 $("#bip32 .path").trigger("input");
1060 // check the address is generated correctly
1061 waitForGenerate(function() {
1062 var actual
= page
.evaluate(function() {
1063 return $(".address:first").text();
1065 if (actual
!= expected
) {
1066 console
.log("Custom BIP32 path generates incorrect address");
1067 console
.log("Expected: " + expected
);
1068 console
.log("Actual: " + actual
);
1078 // BIP32 can use hardened derivation paths
1080 page
.open(url
, function(status
) {
1082 var expected
= "14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4";
1083 page
.evaluate(function() {
1084 $(".phrase").val("abandon abandon ability");
1085 $(".phrase").trigger("input");
1088 waitForGenerate(function() {
1089 page
.evaluate(function() {
1090 $("#bip32-tab a").click();
1092 // set the derivation path to m/0'
1093 waitForGenerate(function() {
1094 page
.evaluate(function() {
1095 $("#bip32 .path").val("m/0'");
1096 $("#bip32 .path").trigger("input");
1098 // check the address is generated correctly
1099 waitForGenerate(function() {
1100 var actual
= page
.evaluate(function() {
1101 return $(".address:first").text();
1103 if (actual
!= expected
) {
1104 console
.log("Hardened BIP32 path generates incorrect address");
1105 console
.log("Expected: " + expected
);
1106 console
.log("Actual: " + actual
);
1116 // BIP32 extended private key is shown
1118 page
.open(url
, function(status
) {
1120 var expected
= "xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe";
1121 page
.evaluate(function() {
1122 $(".phrase").val("abandon abandon ability");
1123 $(".phrase").trigger("input");
1126 waitForGenerate(function() {
1127 page
.evaluate(function() {
1128 $("#bip32-tab a").click();
1130 // check the extended private key is generated correctly
1131 waitForGenerate(function() {
1132 var actual
= page
.evaluate(function() {
1133 return $(".extended-priv-key").val();
1135 if (actual
!= expected
) {
1136 console
.log("BIP32 extended private key is incorrect");
1137 console
.log("Expected: " + expected
);
1138 console
.log("Actual: " + actual
);
1147 // BIP32 extended public key is shown
1149 page
.open(url
, function(status
) {
1151 var expected
= "xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P";
1152 page
.evaluate(function() {
1153 $(".phrase").val("abandon abandon ability");
1154 $(".phrase").trigger("input");
1157 waitForGenerate(function() {
1158 page
.evaluate(function() {
1159 $("#bip32-tab a").click();
1161 // check the extended public key is generated correctly
1162 waitForGenerate(function() {
1163 var actual
= page
.evaluate(function() {
1164 return $(".extended-pub-key").val();
1166 if (actual
!= expected
) {
1167 console
.log("BIP32 extended public key is incorrect");
1168 console
.log("Expected: " + expected
);
1169 console
.log("Actual: " + actual
);
1178 // Derivation path is shown in table
1180 page
.open(url
, function(status
) {
1182 var expected
= "m/44'/0'/0'/0/0";
1183 page
.evaluate(function() {
1184 $(".phrase").val("abandon abandon ability");
1185 $(".phrase").trigger("input");
1187 // check for derivation path in table
1188 waitForGenerate(function() {
1189 var actual
= page
.evaluate(function() {
1190 return $(".index:first").text();
1192 if (actual
!= expected
) {
1193 console
.log("Derivation path shown incorrectly in table");
1194 console
.log("Expected: " + expected
);
1195 console
.log("Actual: " + actual
);
1203 // Derivation path for address can be hardened
1205 page
.open(url
, function(status
) {
1207 var expected
= "18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd";
1208 page
.evaluate(function() {
1209 $(".phrase").val("abandon abandon ability");
1210 $(".phrase").trigger("input");
1213 waitForGenerate(function() {
1214 page
.evaluate(function() {
1215 $("#bip32-tab a").click();
1217 waitForGenerate(function() {
1218 // select the hardened addresses option
1219 page
.evaluate(function() {
1220 $(".hardened-addresses").prop("checked", true);
1221 $(".hardened-addresses").trigger("change");
1223 waitForGenerate(function() {
1224 // check the generated address is hardened
1225 var actual
= page
.evaluate(function() {
1226 return $(".address:first").text();
1228 if (actual
!= expected
) {
1229 console
.log("Hardened address is incorrect");
1230 console
.log("Expected: " + expected
);
1231 console
.log("Actual: " + actual
);
1241 // Derivation path visibility can be toggled
1243 page
.open(url
, function(status
) {
1245 page
.evaluate(function() {
1246 $(".phrase").val("abandon abandon ability");
1247 $(".phrase").trigger("input");
1249 waitForGenerate(function() {
1250 // toggle path visibility
1251 page
.evaluate(function() {
1252 $(".index-toggle").click();
1254 // check the path is not visible
1255 var isInvisible
= page
.evaluate(function() {
1256 return $(".index:first span").hasClass("invisible");
1259 console
.log("Toggled derivation path is visible");
1269 page
.open(url
, function(status
) {
1270 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1272 page
.evaluate(function() {
1273 $(".phrase").val("abandon abandon ability").trigger("input");
1276 waitForGenerate(function() {
1277 var actual
= page
.evaluate(function() {
1278 return $(".address:first").text();
1280 if (actual
!= expected
) {
1281 console
.log("Address is not shown");
1282 console
.log("Expected: " + expected
);
1283 console
.log("Got: " + actual
);
1291 // Addresses are shown in order of derivation path
1293 page
.open(url
, function(status
) {
1295 page
.evaluate(function() {
1296 $(".phrase").val("abandon abandon ability").trigger("input");
1298 // get the derivation paths
1299 waitForGenerate(function() {
1300 var paths
= page
.evaluate(function() {
1301 return $(".index").map(function(i
, e
) {
1305 if (paths
.length
!= 20) {
1306 console
.log("Total paths is less than expected: " + paths
.length
);
1309 for (var i
=0; i
<paths
.length
; i
++) {
1310 var expected
= "m/44'/0'/0'/0/" + i
;
1311 var actual
= paths
[i
];
1312 if (actual
!= expected
) {
1313 console
.log("Path " + i
+ " is incorrect");
1314 console
.log("Expected: " + expected
);
1315 console
.log("Actual: " + actual
);
1324 // Address visibility can be toggled
1326 page
.open(url
, function(status
) {
1328 page
.evaluate(function() {
1329 $(".phrase").val("abandon abandon ability");
1330 $(".phrase").trigger("input");
1332 waitForGenerate(function() {
1333 // toggle address visibility
1334 page
.evaluate(function() {
1335 $(".address-toggle").click();
1337 // check the address is not visible
1338 var isInvisible
= page
.evaluate(function() {
1339 return $(".address:first span").hasClass("invisible");
1342 console
.log("Toggled address is visible");
1350 // Public key is shown
1352 page
.open(url
, function(status
) {
1353 var expected
= "033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3";
1355 page
.evaluate(function() {
1356 $(".phrase").val("abandon abandon ability").trigger("input");
1359 waitForGenerate(function() {
1360 var actual
= page
.evaluate(function() {
1361 return $(".pubkey:first").text();
1363 if (actual
!= expected
) {
1364 console
.log("Public key is not shown");
1365 console
.log("Expected: " + expected
);
1366 console
.log("Got: " + actual
);
1374 // Public key visibility can be toggled
1376 page
.open(url
, function(status
) {
1378 page
.evaluate(function() {
1379 $(".phrase").val("abandon abandon ability");
1380 $(".phrase").trigger("input");
1382 waitForGenerate(function() {
1383 // toggle public key visibility
1384 page
.evaluate(function() {
1385 $(".public-key-toggle").click();
1387 // check the public key is not visible
1388 var isInvisible
= page
.evaluate(function() {
1389 return $(".pubkey:first span").hasClass("invisible");
1392 console
.log("Toggled public key is visible");
1400 // Private key is shown
1402 page
.open(url
, function(status
) {
1403 var expected
= "L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE";
1405 page
.evaluate(function() {
1406 $(".phrase").val("abandon abandon ability").trigger("input");
1409 waitForGenerate(function() {
1410 var actual
= page
.evaluate(function() {
1411 return $(".privkey:first").text();
1413 if (actual
!= expected
) {
1414 console
.log("Private key is not shown");
1415 console
.log("Expected: " + expected
);
1416 console
.log("Got: " + actual
);
1424 // Private key visibility can be toggled
1426 page
.open(url
, function(status
) {
1428 page
.evaluate(function() {
1429 $(".phrase").val("abandon abandon ability");
1430 $(".phrase").trigger("input");
1432 waitForGenerate(function() {
1433 // toggle private key visibility
1434 page
.evaluate(function() {
1435 $(".private-key-toggle").click();
1437 // check the private key is not visible
1438 var isInvisible
= page
.evaluate(function() {
1439 return $(".privkey:first span").hasClass("invisible");
1442 console
.log("Toggled private key is visible");
1450 // More addresses can be generated
1452 page
.open(url
, function(status
) {
1454 page
.evaluate(function() {
1455 $(".phrase").val("abandon abandon ability");
1456 $(".phrase").trigger("input");
1458 waitForGenerate(function() {
1459 // generate more addresses
1460 page
.evaluate(function() {
1463 waitForGenerate(function() {
1464 // check there are more addresses
1465 var addressCount
= page
.evaluate(function() {
1466 return $(".address").length
;
1468 if (addressCount
!= 40) {
1469 console
.log("More addresses cannot be generated");
1478 // A custom number of additional addresses can be generated
1480 page
.open(url
, function(status
) {
1482 page
.evaluate(function() {
1483 $(".phrase").val("abandon abandon ability");
1484 $(".phrase").trigger("input");
1486 waitForGenerate(function() {
1487 // get the current number of addresses
1488 var oldAddressCount
= page
.evaluate(function() {
1489 return $(".address").length
;
1491 // set a custom number of additional addresses
1492 page
.evaluate(function() {
1493 $(".rows-to-add").val(1);
1495 // generate more addresses
1496 page
.evaluate(function() {
1499 waitForGenerate(function() {
1500 // check there are the correct number of addresses
1501 var newAddressCount
= page
.evaluate(function() {
1502 return $(".address").length
;
1504 if (newAddressCount
- oldAddressCount
!= 1) {
1505 console
.log("Number of additional addresses cannot be customized");
1506 console
.log(newAddressCount
)
1507 console
.log(oldAddressCount
)
1516 // Additional addresses are shown in order of derivation path
1518 page
.open(url
, function(status
) {
1520 page
.evaluate(function() {
1521 $(".phrase").val("abandon abandon ability").trigger("input");
1523 waitForGenerate(function() {
1524 // generate more addresses
1525 page
.evaluate(function() {
1528 // get the derivation paths
1529 waitForGenerate(function() {
1530 var paths
= page
.evaluate(function() {
1531 return $(".index").map(function(i
, e
) {
1535 if (paths
.length
!= 40) {
1536 console
.log("Total additional paths is less than expected: " + paths
.length
);
1539 for (var i
=0; i
<paths
.length
; i
++) {
1540 var expected
= "m/44'/0'/0'/0/" + i
;
1541 var actual
= paths
[i
];
1542 if (actual
!= expected
) {
1543 console
.log("Path " + i
+ " is not in correct order");
1544 console
.log("Expected: " + expected
);
1545 console
.log("Actual: " + actual
);
1555 // BIP32 root key can be set by the user
1557 page
.open(url
, function(status
) {
1558 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1560 page
.evaluate(function() {
1561 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1563 waitForGenerate(function() {
1564 var actual
= page
.evaluate(function() {
1565 return $(".address:first").text();
1567 if (actual
!= expected
) {
1568 console
.log("Setting BIP32 root key results in wrong address");
1569 console
.log("Expected: " + expected
);
1570 console
.log("Actual: " + actual
);
1578 // Setting BIP32 root key clears the existing phrase, passphrase and seed
1580 page
.open(url
, function(status
) {
1583 page
.evaluate(function() {
1584 $(".phrase").val("A non-blank but invalid value");
1586 // Accept any confirm dialogs
1587 page
.onConfirm = function() {
1591 page
.evaluate(function() {
1592 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1594 waitForGenerate(function() {
1595 var actual
= page
.evaluate(function() {
1596 return $(".phrase").val();
1598 if (actual
!= expected
) {
1599 console
.log("Phrase not cleared when setting BIP32 root key");
1600 console
.log("Expected: " + expected
);
1601 console
.log("Actual: " + actual
);
1609 // Clearing of phrase, passphrase and seed can be cancelled by user
1611 page
.open(url
, function(status
) {
1612 var expected
= "abandon abandon ability";
1614 page
.evaluate(function() {
1615 $(".phrase").val("abandon abandon ability");
1617 // Cancel any confirm dialogs
1618 page
.onConfirm = function() {
1622 page
.evaluate(function() {
1623 $(".root-key").val("xprv9s21ZrQH143K3d3vzEDD3KpSKmxsZ3y7CqhAL1tinwtP6wqK4TKEKjpBuo6P2hUhB6ZENo7TTSRytiP857hBZVpBdk8PooFuRspE1eywwNZ").trigger("input");
1625 var actual
= page
.evaluate(function() {
1626 return $(".phrase").val();
1628 if (actual
!= expected
) {
1629 console
.log("Phrase not retained when cancelling changes to BIP32 root key");
1630 console
.log("Expected: " + expected
);
1631 console
.log("Actual: " + actual
);
1638 // Custom BIP32 root key is used when changing the derivation path
1640 page
.open(url
, function(status
) {
1641 var expected
= "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1643 page
.evaluate(function() {
1644 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1646 waitForGenerate(function() {
1647 // change the derivation path
1648 page
.evaluate(function() {
1649 $("#account").val("1").trigger("input");
1651 // check the bip32 root key is used for derivation, not the blank phrase
1652 waitForGenerate(function() {
1653 var actual
= page
.evaluate(function() {
1654 return $(".address:first").text();
1656 if (actual
!= expected
) {
1657 console
.log("Changing the derivation path does not use BIP32 root key");
1658 console
.log("Expected: " + expected
);
1659 console
.log("Actual: " + actual
);
1668 // Incorrect mnemonic shows error
1670 page
.open(url
, function(status
) {
1672 page
.evaluate(function() {
1673 $(".phrase").val("abandon abandon abandon").trigger("input");
1675 waitForFeedback(function() {
1676 // check there is an error shown
1677 var feedback
= page
.evaluate(function() {
1678 return $(".feedback").text();
1680 if (feedback
.length
<= 0) {
1681 console
.log("Invalid mnemonic does not show error");
1689 // Incorrect word shows suggested replacement
1691 page
.open(url
, function(status
) {
1693 page
.evaluate(function() {
1694 $(".phrase").val("abandon abandon abiliti").trigger("input");
1696 // check there is a suggestion shown
1697 waitForFeedback(function() {
1698 var feedback
= page
.evaluate(function() {
1699 return $(".feedback").text();
1701 if (feedback
.indexOf("did you mean ability?") < 0) {
1702 console
.log("Incorrect word does not show suggested replacement");
1703 console
.log("Error: " + error
);
1711 // Github pull request 48
1712 // First four letters of word shows that word, not closest
1713 // since first four letters gives unique word in BIP39 wordlist
1714 // eg ille should show illegal, not idle
1716 page
.open(url
, function(status
) {
1717 // set the incomplete word
1718 page
.evaluate(function() {
1719 $(".phrase").val("ille").trigger("input");
1721 // check there is a suggestion shown
1722 waitForFeedback(function() {
1723 var feedback
= page
.evaluate(function() {
1724 return $(".feedback").text();
1726 if (feedback
.indexOf("did you mean illegal?") < 0) {
1727 console
.log("Start of word does not show correct suggestion");
1728 console
.log("Error: " + error
);
1736 // Incorrect BIP32 root key shows error
1738 page
.open(url
, function(status
) {
1740 page
.evaluate(function() {
1741 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj").trigger("input");
1743 // check there is an error shown
1744 waitForFeedback(function() {
1745 var feedback
= page
.evaluate(function() {
1746 return $(".feedback").text();
1748 if (feedback
!= "Invalid root key") {
1749 console
.log("Invalid root key does not show error");
1750 console
.log("Error: " + error
);
1758 // Derivation path not starting with m shows error
1760 page
.open(url
, function(status
) {
1761 // set the mnemonic phrase
1762 page
.evaluate(function() {
1763 $(".phrase").val("abandon abandon ability").trigger("input");
1765 waitForGenerate(function() {
1766 // select the bip32 tab so custom derivation path can be set
1767 page
.evaluate(function() {
1768 $("#bip32-tab a").click();
1770 waitForGenerate(function() {
1771 // set the incorrect derivation path
1772 page
.evaluate(function() {
1773 $("#bip32 .path").val("n/0").trigger("input");
1775 waitForFeedback(function() {
1776 var feedback
= page
.evaluate(function() {
1777 return $(".feedback").text();
1779 if (feedback
!= "First character must be 'm'") {
1780 console
.log("Derivation path not starting with m should show error");
1781 console
.log("Error: " + error
);
1791 // Derivation path containing invalid characters shows useful error
1793 page
.open(url
, function(status
) {
1794 // set the mnemonic phrase
1795 page
.evaluate(function() {
1796 $(".phrase").val("abandon abandon ability").trigger("input");
1798 waitForGenerate(function() {
1799 // select the bip32 tab so custom derivation path can be set
1800 page
.evaluate(function() {
1801 $("#bip32-tab a").click();
1803 waitForGenerate(function() {
1804 // set the incorrect derivation path
1805 page
.evaluate(function() {
1806 $("#bip32 .path").val("m/1/0wrong1/1").trigger("input");
1808 waitForFeedback(function() {
1809 var feedback
= page
.evaluate(function() {
1810 return $(".feedback").text();
1812 if (feedback
!= "Invalid characters 0wrong1 found at depth 2") {
1813 console
.log("Derivation path with invalid characters should show error");
1814 console
.log("Error: " + error
);
1824 // Github Issue 11: Default word length is 15
1825 // https://github.com/iancoleman/bip39/issues/11
1827 page
.open(url
, function(status
) {
1828 // get the word length
1829 var defaultLength
= page
.evaluate(function() {
1830 return $(".strength").val();
1832 if (defaultLength
!= 15) {
1833 console
.log("Default word length is not 15");
1841 // Github Issue 12: Generate more rows with private keys hidden
1842 // https://github.com/iancoleman/bip39/issues/12
1844 page
.open(url
, function(status
) {
1846 page
.evaluate(function() {
1847 $(".phrase").val("abandon abandon ability");
1848 $(".phrase").trigger("input");
1850 waitForGenerate(function() {
1851 // toggle private keys hidden, then generate more addresses
1852 page
.evaluate(function() {
1853 $(".private-key-toggle").click();
1856 waitForGenerate(function() {
1857 // check more have been generated
1859 var numPrivKeys
= page
.evaluate(function() {
1860 return $(".privkey").length
;
1862 if (numPrivKeys
!= expected
) {
1863 console
.log("Wrong number of addresses when clicking 'more' with hidden privkeys");
1864 console
.log("Expected: " + expected
);
1865 console
.log("Actual: " + numPrivKeys
);
1868 // check no private keys are shown
1869 var numHiddenPrivKeys
= page
.evaluate(function() {
1870 return $(".privkey span[class=invisible]").length
;
1872 if (numHiddenPrivKeys
!= expected
) {
1873 console
.log("Generating more does not retain hidden state of privkeys");
1874 console
.log("Expected: " + expected
);
1875 console
.log("Actual: " + numHiddenPrivKeys
);
1884 // Github Issue 19: Mnemonic is not sensitive to whitespace
1885 // https://github.com/iancoleman/bip39/issues/19
1887 page
.open(url
, function(status
) {
1889 var expected
= "xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC";
1890 page
.evaluate(function() {
1891 var doubleSpace
= " ";
1892 $(".phrase").val("urge cat" + doubleSpace
+ "bid");
1893 $(".phrase").trigger("input");
1895 waitForGenerate(function() {
1896 // Check the bip32 root key is correct
1897 var actual
= page
.evaluate(function() {
1898 return $(".root-key").val();
1900 if (actual
!= expected
) {
1901 console
.log("Mnemonic is sensitive to whitespace");
1902 console
.log("Expected: " + expected
);
1903 console
.log("Actual: " + actual
);
1911 // Github Issue 23: Part 1: Use correct derivation path when changing tabs
1912 // https://github.com/iancoleman/bip39/issues/23
1914 page
.open(url
, function(status
) {
1915 // 1) and 2) set the phrase
1916 page
.evaluate(function() {
1917 $(".phrase").val("abandon abandon ability").trigger("input");
1919 waitForGenerate(function() {
1920 // 3) select bip32 tab
1921 page
.evaluate(function() {
1922 $("#bip32-tab a").click();
1924 waitForGenerate(function() {
1925 // 4) switch from bitcoin to litecoin
1926 page
.evaluate(function() {
1927 $(".network option").filter(function() {
1928 return $(this).html() == "Litecoin";
1929 }).prop("selected", true);
1930 $(".network").trigger("change");
1932 waitForGenerate(function() {
1933 // 5) Check derivation path is displayed correctly
1934 var expected
= "m/0/0";
1935 var actual
= page
.evaluate(function() {
1936 return $(".index:first").text();
1938 if (actual
!= expected
) {
1939 console
.log("Github Issue 23 Part 1: derivation path display error");
1940 console
.log("Expected: " + expected
);
1941 console
.log("Actual: " + actual
);
1944 // 5) Check address is displayed correctly
1945 var expected
= "LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5";
1946 var actual
= page
.evaluate(function() {
1947 return $(".address:first").text();
1949 if (actual
!= expected
) {
1950 console
.log("Github Issue 23 Part 1: address display error");
1951 console
.log("Expected: " + expected
);
1952 console
.log("Actual: " + actual
);
1962 // Github Issue 23 Part 2: Coin selection in derivation path
1963 // https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
1965 page
.open(url
, function(status
) {
1967 page
.evaluate(function() {
1968 $(".phrase").val("abandon abandon ability").trigger("input");
1970 waitForGenerate(function() {
1971 // switch from bitcoin to clam
1972 page
.evaluate(function() {
1973 $(".network option").filter(function() {
1974 return $(this).html() == "CLAM";
1975 }).prop("selected", true);
1976 $(".network").trigger("change");
1978 waitForGenerate(function() {
1979 // check derivation path is displayed correctly
1980 var expected
= "m/44'/23'/0'/0/0";
1981 var actual
= page
.evaluate(function() {
1982 return $(".index:first").text();
1984 if (actual
!= expected
) {
1985 console
.log("Github Issue 23 Part 2: Coin in BIP44 derivation path is incorrect");
1986 console
.log("Expected: " + expected
);
1987 console
.log("Actual: " + actual
);
1996 // Github Issue 26: When using a Root key derrived altcoins are incorrect
1997 // https://github.com/iancoleman/bip39/issues/26
1999 page
.open(url
, function(status
) {
2000 // 1) 2) and 3) set the root key
2001 page
.evaluate(function() {
2002 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
2004 waitForGenerate(function() {
2005 // 4) switch from bitcoin to viacoin
2006 page
.evaluate(function() {
2007 $(".network option").filter(function() {
2008 return $(this).html() == "Viacoin";
2009 }).prop("selected", true);
2010 $(".network").trigger("change");
2012 waitForGenerate(function() {
2013 // 5) ensure the derived address is correct
2014 var expected
= "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
2015 var actual
= page
.evaluate(function() {
2016 return $(".address:first").text();
2018 if (actual
!= expected
) {
2019 console
.log("Github Issue 26: address is incorrect when changing networks and using root-key to derive");
2020 console
.log("Expected: " + expected
);
2021 console
.log("Actual: " + actual
);
2030 // Selecting a language with no existing phrase should generate a phrase in
2033 page
.open(url
, function(status
) {
2034 // Select a language
2035 // Need to manually simulate hash being set due to quirk between
2036 // 'click' event triggered by javascript vs triggered by mouse.
2037 // Perhaps look into page.sendEvent
2038 // http://phantomjs.org/api/webpage/method/send-event.html
2039 page
.evaluate(function() {
2040 window
.location
.hash
= "#japanese";
2041 $("a[href='#japanese']").trigger("click");
2043 waitForGenerate(function() {
2044 // Check the mnemonic is in Japanese
2045 var phrase
= page
.evaluate(function() {
2046 return $(".phrase").val();
2048 if (phrase
.length
<= 0) {
2049 console
.log("No Japanese phrase generated");
2052 if (phrase
.charCodeAt(0) < 128) {
2053 console
.log("First character of Japanese phrase is ascii");
2054 console
.log("Phrase: " + phrase
);
2062 // Selecting a language with existing phrase should update the phrase to use
2065 page
.open(url
, function(status
) {
2066 // Set the phrase to an English phrase.
2067 page
.evaluate(function() {
2068 $(".phrase").val("abandon abandon ability").trigger("input");
2070 waitForGenerate(function() {
2071 // Change to Italian
2072 // Need to manually simulate hash being set due to quirk between
2073 // 'click' event triggered by javascript vs triggered by mouse.
2074 // Perhaps look into page.sendEvent
2075 // http://phantomjs.org/api/webpage/method/send-event.html
2076 page
.evaluate(function() {
2077 window
.location
.hash
= "#italian";
2078 $("a[href='#italian']").trigger("click");
2080 waitForGenerate(function() {
2081 // Check only the language changes, not the phrase
2082 var expected
= "abaco abaco abbaglio";
2083 var actual
= page
.evaluate(function() {
2084 return $(".phrase").val();
2086 if (actual
!= expected
) {
2087 console
.log("Changing language with existing phrase");
2088 console
.log("Expected: " + expected
);
2089 console
.log("Actual: " + actual
);
2092 // Check the address is correct
2093 var expected
= "1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV";
2094 var actual
= page
.evaluate(function() {
2095 return $(".address:first").text();
2097 if (actual
!= expected
) {
2098 console
.log("Changing language generates incorrect address");
2099 console
.log("Expected: " + expected
);
2100 console
.log("Actual: " + actual
);
2109 // Suggested replacement for erroneous word in non-English language
2111 page
.open(url
, function(status
) {
2112 // Set an incorrect phrase in Italian
2113 page
.evaluate(function() {
2114 $(".phrase").val("abaco abaco zbbaglio").trigger("input");
2116 waitForFeedback(function() {
2117 // Check the suggestion is correct
2118 var feedback
= page
.evaluate(function() {
2119 return $(".feedback").text();
2121 if (feedback
.indexOf("did you mean abbaglio?") < 0) {
2122 console
.log("Incorrect Italian word does not show suggested replacement");
2123 console
.log("Error: " + error
);
2132 // Japanese word does not break across lines.
2134 // https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
2136 page
.open(url
, function(status
) {
2137 hasWordBreakCss
= page
.content
.indexOf("word-break: keep-all;") > -1;
2138 if (!hasWordBreakCss
) {
2139 console
.log("Japanese words can break across lines mid-word");
2140 console
.log("Check CSS for '.phrase { word-break: keep-all; }'");
2143 // Run the next test
2148 // Language can be specified at page load using hash value in url
2150 page
.open(url
, function(status
) {
2151 // Set the page hash as if it were on a fresh page load
2152 page
.evaluate(function() {
2153 window
.location
.hash
= "#japanese";
2155 // Generate a random phrase
2156 page
.evaluate(function() {
2157 $(".generate").trigger("click");
2159 waitForGenerate(function() {
2160 // Check the phrase is in Japanese
2161 var phrase
= page
.evaluate(function() {
2162 return $(".phrase").val();
2164 if (phrase
.length
<= 0) {
2165 console
.log("No phrase generated using url hash");
2168 if (phrase
.charCodeAt(0) < 128) {
2169 console
.log("Language not detected from url hash on page load.");
2170 console
.log("Phrase: " + phrase
);
2178 // Entropy unit tests
2180 page
.open(url
, function(status
) {
2181 var response
= page
.evaluate(function() {
2183 // binary entropy is detected
2185 e
= Entropy
.fromString("01010101");
2186 if (e
.base
.str
!= "binary") {
2187 return "Binary entropy not detected correctly";
2193 // base6 entropy is detected
2195 e
= Entropy
.fromString("012345012345");
2196 if (e
.base
.str
!= "base 6") {
2197 return "base6 entropy not detected correctly";
2203 // dice entropy is detected
2205 e
= Entropy
.fromString("123456123456");
2206 if (e
.base
.str
!= "base 6 (dice)") {
2207 return "dice entropy not detected correctly";
2213 // base10 entropy is detected
2215 e
= Entropy
.fromString("0123456789");
2216 if (e
.base
.str
!= "base 10") {
2217 return "base10 entropy not detected correctly";
2223 // hex entropy is detected
2225 e
= Entropy
.fromString("0123456789ABCDEF");
2226 if (e
.base
.str
!= "hexadecimal") {
2227 return "hexadecimal entropy not detected correctly";
2233 // card entropy is detected
2235 e
= Entropy
.fromString("AC4DTHKS");
2236 if (e
.base
.str
!= "card") {
2237 return "card entropy not detected correctly";
2243 // entropy is case insensitive
2245 e
= Entropy
.fromString("aBcDeF");
2246 if (e
.cleanStr
!= "aBcDeF") {
2247 return "Entropy should not be case sensitive";
2253 // dice entropy is converted to base6
2255 e
= Entropy
.fromString("123456");
2256 if (e
.cleanStr
!= "123450") {
2257 return "Dice entropy is not automatically converted to base6";
2263 // dice entropy is preferred to base6 if ambiguous
2265 e
= Entropy
.fromString("12345");
2266 if (e
.base
.str
!= "base 6 (dice)") {
2267 return "dice not used as default over base 6";
2273 // unused characters are ignored
2275 e
= Entropy
.fromString("fghijkl");
2276 if (e
.cleanStr
!= "f") {
2277 return "additional characters are not ignored";
2283 // the lowest base is used by default
2284 // 7 could be decimal or hexadecimal, but should be detected as decimal
2286 e
= Entropy
.fromString("7");
2287 if (e
.base
.str
!= "base 10") {
2288 return "lowest base is not used";
2294 // Leading zeros are retained
2296 e
= Entropy
.fromString("000A");
2297 if (e
.cleanStr
!= "000A") {
2298 return "Leading zeros are not retained";
2304 // Leading zeros are correctly preserved for hex in binary string
2306 e
= Entropy
.fromString("2A");
2307 if (e
.binaryStr
!= "00101010") {
2308 return "Hex leading zeros are not correct in binary";
2314 // Leading zeros for base 6 as binary string
2315 // 20 = 2 events at 2.58 bits per event = 5 bits
2316 // 20 in base 6 = 12 in base 10 = 1100 in base 2
2317 // so it needs 1 bit of padding to be the right bit length
2319 e
= Entropy
.fromString("20");
2320 if (e
.binaryStr
!= "01100") {
2321 return "Base 6 as binary has leading zeros";
2327 // Leading zeros for base 10 as binary string
2329 e
= Entropy
.fromString("17");
2330 if (e
.binaryStr
!= "010001") {
2331 return "Base 10 as binary has leading zeros";
2337 // Leading zeros for card entropy as binary string.
2338 // Card entropy is hashed so 2c does not necessarily produce leading zeros.
2340 e
= Entropy
.fromString("2c");
2341 if (e
.binaryStr
!= "0010") {
2342 return "Card entropy as binary has leading zeros";
2348 // Keyboard mashing results in weak entropy
2349 // Despite being a long string, it's less than 30 bits of entropy
2351 e
= Entropy
.fromString("aj;se ifj; ask,dfv js;ifj");
2352 if (e
.binaryStr
.length
>= 30) {
2353 return "Keyboard mashing should produce weak entropy";
2359 // Card entropy is used if every pair could be a card
2361 e
= Entropy
.fromString("4c3c2c");
2362 if (e
.base
.str
!= "card") {
2363 return "Card entropy not used if all pairs are cards";
2369 // Card entropy uses base 52
2370 // [ cards, binary ]
2374 [ "acqs", "11011100" ],
2375 [ "acks", "01011100" ],
2376 [ "2cac", "11111000" ],
2389 [ "ks2c", "01010100" ],
2390 [ "KS2C", "01010100" ],
2392 for (var i
=0; i
<cards
.length
; i
++) {
2393 var card
= cards
[i
][0];
2394 var result
= cards
[i
][1];
2395 e
= Entropy
.fromString(card
);
2396 console
.log(e
.binary
+ " " + result
);
2397 if (e
.binaryStr
!== result
) {
2398 return "card entropy " + card
+ " not parsed correctly: " + result
+ " != " + e
.binaryStr
;
2407 if (response
!= "PASS") {
2408 console
.log("Entropy unit tests");
2409 console
.log(response
);
2416 // Entropy can be entered by the user
2418 page
.open(url
, function(status
) {
2420 mnemonic: "abandon abandon ability",
2421 address: "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug",
2424 page
.evaluate(function() {
2425 $(".use-entropy").prop("checked", true).trigger("change");
2426 $(".entropy").val("00000000 00000000 00000000 00000000").trigger("input");
2428 // check the mnemonic is set and address is correct
2429 waitForGenerate(function() {
2430 var actual
= page
.evaluate(function() {
2432 address: $(".address:first").text(),
2433 mnemonic: $(".phrase").val(),
2436 if (actual
.mnemonic
!= expected
.mnemonic
) {
2437 console
.log("Entropy does not generate correct mnemonic");
2438 console
.log("Expected: " + expected
.mnemonic
);
2439 console
.log("Got: " + actual
.mnemonic
);
2442 if (actual
.address
!= expected
.address
) {
2443 console
.log("Entropy does not generate correct address");
2444 console
.log("Expected: " + expected
.address
);
2445 console
.log("Got: " + actual
.address
);
2453 // A warning about entropy is shown to the user, with additional information
2455 page
.open(url
, function(status
) {
2456 // get text content from entropy sections of page
2457 var hasWarning
= page
.evaluate(function() {
2458 var entropyText
= $(".entropy-container").text();
2459 var warning
= "mnemonic may be insecure";
2460 if (entropyText
.indexOf(warning
) == -1) {
2463 var readMoreText
= $("#entropy-notes").parent().text();
2464 var goodSources
= "flipping a fair coin, rolling a fair dice, noise measurements etc";
2465 if (readMoreText
.indexOf(goodSources
) == -1) {
2470 // check the warnings and information are shown
2472 console
.log("Page does not contain warning about using own entropy");
2479 // The types of entropy available are described to the user
2481 page
.open(url
, function(status
) {
2482 // get placeholder text for entropy field
2483 var placeholder
= page
.evaluate(function() {
2484 return $(".entropy").attr("placeholder");
2494 for (var i
=0; i
<options
.length
; i
++) {
2495 var option
= options
[i
];
2496 if (placeholder
.indexOf(option
) == -1) {
2497 console
.log("Available entropy type is not shown to user: " + option
);
2505 // The actual entropy used is shown to the user
2507 page
.open(url
, function(status
) {
2509 var badEntropySource
= page
.evaluate(function() {
2510 var entropy
= "Not A Very Good Entropy Source At All";
2511 $(".use-entropy").prop("checked", true).trigger("change");
2512 $(".entropy").val(entropy
).trigger("input");
2514 // check the actual entropy being used is shown
2515 waitForEntropyFeedback(function() {
2516 var expectedText
= "AedEceAA";
2517 var entropyText
= page
.evaluate(function() {
2518 return $(".entropy-container").text();
2520 if (entropyText
.indexOf(expectedText
) == -1) {
2521 console
.log("Actual entropy used is not shown");
2529 // Binary entropy can be entered
2531 page
.open(url
, function(status
) {
2533 page
.evaluate(function() {
2534 $(".use-entropy").prop("checked", true).trigger("change");
2535 $(".entropy").val("01").trigger("input");
2537 // check the entropy is shown to be the correct type
2538 waitForEntropyFeedback(function() {
2539 var entropyText
= page
.evaluate(function() {
2540 return $(".entropy-container").text();
2542 if (entropyText
.indexOf("binary") == -1) {
2543 console
.log("Binary entropy is not detected and presented to user");
2551 // Base 6 entropy can be entered
2553 page
.open(url
, function(status
) {
2555 page
.evaluate(function() {
2556 $(".use-entropy").prop("checked", true).trigger("change");
2557 $(".entropy").val("012345").trigger("input");
2559 // check the entropy is shown to be the correct type
2560 waitForEntropyFeedback(function() {
2561 var entropyText
= page
.evaluate(function() {
2562 return $(".entropy-container").text();
2564 if (entropyText
.indexOf("base 6") == -1) {
2565 console
.log("Base 6 entropy is not detected and presented to user");
2573 // Base 6 dice entropy can be entered
2575 page
.open(url
, function(status
) {
2577 page
.evaluate(function() {
2578 $(".use-entropy").prop("checked", true).trigger("change");
2579 $(".entropy").val("123456").trigger("input");
2581 // check the entropy is shown to be the correct type
2582 waitForEntropyFeedback(function() {
2583 var entropyText
= page
.evaluate(function() {
2584 return $(".entropy-container").text();
2586 if (entropyText
.indexOf("dice") == -1) {
2587 console
.log("Dice entropy is not detected and presented to user");
2595 // Base 10 entropy can be entered
2597 page
.open(url
, function(status
) {
2599 page
.evaluate(function() {
2600 $(".use-entropy").prop("checked", true).trigger("change");
2601 $(".entropy").val("789").trigger("input");
2603 // check the entropy is shown to be the correct type
2604 waitForEntropyFeedback(function() {
2605 var entropyText
= page
.evaluate(function() {
2606 return $(".entropy-container").text();
2608 if (entropyText
.indexOf("base 10") == -1) {
2609 console
.log("Base 10 entropy is not detected and presented to user");
2617 // Hexadecimal entropy can be entered
2619 page
.open(url
, function(status
) {
2621 page
.evaluate(function() {
2622 $(".use-entropy").prop("checked", true).trigger("change");
2623 $(".entropy").val("abcdef").trigger("input");
2625 // check the entropy is shown to be the correct type
2626 waitForEntropyFeedback(function() {
2627 var entropyText
= page
.evaluate(function() {
2628 return $(".entropy-container").text();
2630 if (entropyText
.indexOf("hexadecimal") == -1) {
2631 console
.log("Hexadecimal entropy is not detected and presented to user");
2639 // Dice entropy value is shown as the converted base 6 value
2641 page
.open(url
, function(status
) {
2643 page
.evaluate(function() {
2644 $(".use-entropy").prop("checked", true).trigger("change");
2645 $(".entropy").val("123456").trigger("input");
2647 // check the entropy is shown as base 6, not as the original dice value
2648 waitForEntropyFeedback(function() {
2649 var entropyText
= page
.evaluate(function() {
2650 return $(".entropy-container").text();
2652 if (entropyText
.indexOf("123450") == -1) {
2653 console
.log("Dice entropy is not shown to user as base 6 value");
2656 if (entropyText
.indexOf("123456") > -1) {
2657 console
.log("Dice entropy value is shown instead of true base 6 value");
2665 // The number of bits of entropy accumulated is shown
2667 page
.open(url
, function(status
) {
2670 [ "0000 0000 0000 0000 0000", "20" ],
2673 [ "6", "2" ], // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
2674 [ "7", "3" ], // 7 in base 10 is 111 in base 2, no leading zeros
2679 [ "1A", "8" ], // hex is always multiple of 4 bits of entropy
2686 [ "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)
2687 [ "2227", "13" ], // Uses base 10, which is 4 lots of 3.32 bits, which is 13.3 bits (rounded down to 13)
2690 [ "0000101017", "33" ], // 10 events at 3.32 bits per event
2691 [ "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
2694 page
.evaluate(function(e
) {
2695 $(".use-entropy").prop("checked", true).trigger("change");
2698 var nextTest
= function runNextTest(i
) {
2699 var entropy
= tests
[i
][0];
2700 var expected
= tests
[i
][1];
2702 page
.evaluate(function(e
) {
2703 $(".entropy").val(e
).trigger("input");
2705 // check the number of bits of entropy is shown
2706 waitForEntropyFeedback(function() {
2707 var entropyText
= page
.evaluate(function() {
2708 return $(".entropy-container").text();
2710 if (entropyText
.replace(/\s/g,"").indexOf("Bits" + expected
) == -1) {
2711 console
.log("Accumulated entropy is not shown correctly for " + entropy
);
2714 var isLastTest
= i
== tests
.length
- 1;
2727 // There is feedback provided about the supplied entropy
2729 page
.open(url
, function(status
) {
2734 type: "hexadecimal",
2738 strength: "extremely weak",
2741 entropy: "AAAAAAAA",
2742 filtered: "AAAAAAAA",
2743 type: "hexadecimal",
2747 strength: "extremely weak",
2750 entropy: "AAAAAAAA B",
2751 filtered: "AAAAAAAAB",
2752 type: "hexadecimal",
2756 strength: "extremely weak",
2759 entropy: "AAAAAAAA BBBBBBBB",
2760 filtered: "AAAAAAAABBBBBBBB",
2761 type: "hexadecimal",
2765 strength: "very weak",
2768 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
2769 filtered: "AAAAAAAABBBBBBBBCCCCCCCC",
2770 type: "hexadecimal",
2777 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
2778 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD",
2779 type: "hexadecimal",
2783 strength: "easily cracked",
2786 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA",
2787 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA",
2788 type: "hexadecimal",
2795 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE",
2796 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE",
2797 type: "hexadecimal",
2801 strength: "very strong",
2804 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF",
2805 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF",
2806 type: "hexadecimal",
2810 strength: "extremely strong",
2818 strength: "extremely weak",
2821 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2822 type: "card (full deck)",
2826 strength: "extremely strong",
2829 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d",
2830 type: "card (full deck, 1 duplicate: 3d)",
2834 strength: "extremely strong",
2837 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d",
2838 type: "card (2 duplicates: 3d 4d, 1 missing: KS)",
2842 strength: "extremely strong",
2845 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d",
2846 type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)",
2850 strength: "extremely strong",
2852 // Next test was throwing uncaught error in zxcvbn
2853 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
2855 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2856 type: "card (full deck, 52 duplicates: ac 2c 3c...)",
2860 strength: "extremely strong",
2862 // Case insensitivity to duplicate cards
2865 type: "card (1 duplicate: AS)",
2869 strength: "extremely weak",
2873 type: "card (1 duplicate: as)",
2877 strength: "extremely weak",
2879 // Missing cards are detected
2881 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2882 type: "card (1 missing: 9C)",
2886 strength: "extremely strong",
2889 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2890 type: "card (2 missing: 9C 5D)",
2894 strength: "extremely strong",
2897 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2898 type: "card (4 missing: 9C 5D QD...)",
2902 strength: "extremely strong",
2904 // More than six missing cards does not show message
2906 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks",
2911 strength: "extremely strong",
2913 // Multiple decks of cards increases bits per event
2933 entropy: "3d3d3d3d",
2939 entropy: "3d3d3d3d3d",
2945 entropy: "3d3d3d3d3d3d",
2951 entropy: "3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d",
2955 strength: 'easily cracked - Repeats like "abcabcabc" are only slightly harder to guess than "abc"',
2959 page
.evaluate(function() {
2960 $(".use-entropy").prop("checked", true).trigger("change");
2962 var nextTest
= function runNextTest(i
) {
2963 function getFeedbackError(expected
, actual
) {
2964 if ("filtered" in expected
&& actual
.indexOf(expected
.filtered
) == -1) {
2965 return "Filtered value not in feedback";
2967 if ("type" in expected
&& actual
.indexOf(expected
.type
) == -1) {
2968 return "Entropy type not in feedback";
2970 if ("events" in expected
&& actual
.indexOf(expected
.events
) == -1) {
2971 return "Event count not in feedback";
2973 if ("bits" in expected
&& actual
.indexOf(expected
.bits
) == -1) {
2974 return "Bit count not in feedback";
2976 if ("strength" in expected
&& actual
.indexOf(expected
.strength
) == -1) {
2977 return "Strength not in feedback";
2979 if ("bitsPerEvent" in expected
&& actual
.indexOf(expected
.bitsPerEvent
) == -1) {
2980 return "bitsPerEvent not in feedback";
2985 page
.evaluate(function(e
) {
2986 $(".addresses").empty();
2987 $(".phrase").val("");
2988 $(".entropy").val(e
).trigger("input");
2990 waitForEntropyFeedback(function() {
2991 var mnemonic
= page
.evaluate(function() {
2992 return $(".phrase").val();
2994 // Check mnemonic length
2995 if ("words" in test
&& test
.words
== 0) {
2996 if (mnemonic
.length
> 0) {
2997 console
.log("Mnemonic length for " + test
.strength
+ " strength is not " + test
.words
);
2998 console
.log("Entropy: " + test
.entropy
);
2999 console
.log("Mnemonic: " + mnemonic
);
3003 else if ("words" in test
) {
3004 if (mnemonic
.split(" ").length
!= test
.words
) {
3005 console
.log("Mnemonic length for " + test
.strength
+ " strength is not " + test
.words
);
3006 console
.log("Entropy: " + test
.entropy
);
3007 console
.log("Mnemonic: " + mnemonic
);
3012 var feedback
= page
.evaluate(function() {
3013 return $(".entropy-container").text();
3015 var feedbackError
= getFeedbackError(test
, feedback
);
3016 if (feedbackError
) {
3017 console
.log("Entropy feedback for " + test
.entropy
+ " returned error");
3018 console
.log(feedbackError
);
3022 var isLastTest
= i
== tests
.length
- 1;
3035 // Entropy is truncated from the left
3037 page
.open(url
, function(status
) {
3038 var expected
= "avocado zoo zone";
3040 page
.evaluate(function() {
3041 $(".use-entropy").prop("checked", true).trigger("change");
3042 var entropy
= "00000000 00000000 00000000 00000000";
3043 entropy
+= "11111111 11111111 11111111 1111"; // Missing last byte
3044 $(".entropy").val(entropy
).trigger("input");
3046 // check the entropy is truncated from the right
3047 waitForGenerate(function() {
3048 var actual
= page
.evaluate(function() {
3049 return $(".phrase").val();
3051 if (actual
!= expected
) {
3052 console
.log("Entropy is not truncated from the right");
3053 console
.log("Expected: " + expected
);
3054 console
.log("Got: " + actual
);
3062 // Very large entropy results in very long mnemonics
3064 page
.open(url
, function(status
) {
3066 page
.evaluate(function() {
3067 $(".use-entropy").prop("checked", true).trigger("change");
3069 // Generate a very long entropy string
3070 for (var i
=0; i
<33; i
++) {
3071 entropy
+= "AAAAAAAA"; // 3 words * 33 iterations = 99 words
3073 $(".entropy").val(entropy
).trigger("input");
3075 // check the mnemonic is very long
3076 waitForGenerate(function() {
3077 var wordCount
= page
.evaluate(function() {
3078 return $(".phrase").val().split(" ").length
;
3080 if (wordCount
!= 99) {
3081 console
.log("Large entropy does not generate long mnemonic");
3082 console
.log("Expected 99 words, got " + wordCount
);
3090 // Is compatible with bip32jp entropy
3091 // https://bip32jp.github.io/english/index.html
3093 // Is incompatible with:
3096 page
.open(url
, function(status
) {
3097 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";
3099 page
.evaluate(function() {
3100 $(".use-entropy").prop("checked", true).trigger("change");
3101 var entropy
= "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543";
3102 $(".entropy").val(entropy
).trigger("input");
3104 // check the mnemonic matches the expected value from bip32jp
3105 waitForGenerate(function() {
3106 var actual
= page
.evaluate(function() {
3107 return $(".phrase").val();
3109 if (actual
!= expected
) {
3110 console
.log("Mnemonic does not match bip32jp for base 6 entropy");
3111 console
.log("Expected: " + expected
);
3112 console
.log("Got: " + actual
);
3120 // Blank entropy does not generate mnemonic or addresses
3122 page
.open(url
, function(status
) {
3124 page
.evaluate(function() {
3125 $(".use-entropy").prop("checked", true).trigger("change");
3126 $(".entropy").val("").trigger("input");
3128 waitForFeedback(function() {
3129 // check there is no mnemonic
3130 var phrase
= page
.evaluate(function() {
3131 return $(".phrase").val();
3134 console
.log("Blank entropy does not result in blank mnemonic");
3135 console
.log("Got: " + phrase
);
3138 // check there are no addresses displayed
3139 var addresses
= page
.evaluate(function() {
3140 return $(".address").length
;
3142 if (addresses
!= 0) {
3143 console
.log("Blank entropy does not result in zero addresses");
3146 // Check the feedback says 'blank entropy'
3147 var feedback
= page
.evaluate(function() {
3148 return $(".feedback").text();
3150 if (feedback
!= "Blank entropy") {
3151 console
.log("Blank entropy does not show feedback message");
3159 // Mnemonic length can be selected even for weak entropy
3161 page
.open(url
, function(status
) {
3163 page
.evaluate(function() {
3164 $(".use-entropy").prop("checked", true).trigger("change");
3165 $(".entropy").val("012345");
3166 $(".mnemonic-length").val("18").trigger("change");
3168 // check the mnemonic is the correct length
3169 waitForGenerate(function() {
3170 var phrase
= page
.evaluate(function() {
3171 return $(".phrase").val();
3173 var numberOfWords
= phrase
.split(/\s/g).length
;
3174 if (numberOfWords
!= 18) {
3175 console
.log("Weak entropy cannot be overridden to give 18 word mnemonic");
3176 console
.log(phrase
);
3185 // https://github.com/iancoleman/bip39/issues/33
3186 // Final cards should contribute entropy
3188 page
.open(url
, function(status
) {
3190 page
.evaluate(function() {
3191 $(".use-entropy").prop("checked", true).trigger("change");
3192 $(".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");
3195 waitForGenerate(function() {
3196 var originalPhrase
= page
.evaluate(function() {
3197 return $(".phrase").val();
3199 // Set the last 12 cards to be AS
3200 page
.evaluate(function() {
3201 $(".addresses").empty();
3202 $(".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");
3204 // get the new mnemonic
3205 waitForGenerate(function() {
3206 var newPhrase
= page
.evaluate(function() {
3207 return $(".phrase").val();
3209 // check the phrase has changed
3210 if (newPhrase
== originalPhrase
) {
3211 console
.log("Changing last 12 cards does not change mnemonic");
3212 console
.log("Original:");
3213 console
.log(originalPhrase
);
3214 console
.log("New:");
3215 console
.log(newPhrase
);
3225 // https://github.com/iancoleman/bip39/issues/35
3228 page
.open(url
, function(status
) {
3230 page
.evaluate(function() {
3231 $(".generate").click();
3233 waitForGenerate(function() {
3234 var p
= page
.evaluate(function() {
3235 // get position of mnemonic element
3236 return $(".phrase").offset();
3238 p
.top
= Math
.ceil(p
.top
);
3239 p
.left
= Math
.ceil(p
.left
);
3240 // check the qr code shows
3241 page
.sendEvent("mousemove", p
.left
+4, p
.top
+4);
3242 var qrShowing
= page
.evaluate(function() {
3243 return $(".qr-container").find("canvas").length
> 0;
3246 console
.log("QR Code does not show");
3249 // check the qr code hides
3250 page
.sendEvent("mousemove", p
.left
-4, p
.top
-4);
3251 var qrHidden
= page
.evaluate(function() {
3252 return $(".qr-container").find("canvas").length
== 0;
3255 console
.log("QR Code does not hide");
3263 // BIP44 account extendend private key is shown
3264 // github issue 37 - compatibility with electrum
3266 page
.open(url
, function(status
) {
3268 var expected
= "xprv9yzrnt4zWVJUr1k2VxSPy9ettKz5PpeDMgaVG7UKedhqnw1tDkxP2UyYNhuNSumk2sLE5ctwKZs9vwjsq3e1vo9egCK6CzP87H2cVYXpfwQ";
3269 page
.evaluate(function() {
3270 $(".phrase").val("abandon abandon ability");
3271 $(".phrase").trigger("input");
3273 // check the BIP44 account extended private key
3274 waitForGenerate(function() {
3275 var actual
= page
.evaluate(function() {
3276 return $(".account-xprv").val();
3278 if (actual
!= expected
) {
3279 console
.log("BIP44 account extended private key is incorrect");
3280 console
.log("Expected: " + expected
);
3281 console
.log("Actual: " + actual
);
3289 // BIP44 account extendend public key is shown
3290 // github issue 37 - compatibility with electrum
3292 page
.open(url
, function(status
) {
3294 var expected
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3295 page
.evaluate(function() {
3296 $(".phrase").val("abandon abandon ability");
3297 $(".phrase").trigger("input");
3299 // check the BIP44 account extended public key
3300 waitForGenerate(function() {
3301 var actual
= page
.evaluate(function() {
3302 return $(".account-xpub").val();
3304 if (actual
!= expected
) {
3305 console
.log("BIP44 account extended public key is incorrect");
3306 console
.log("Expected: " + expected
);
3307 console
.log("Actual: " + actual
);
3316 // BIP32 root key can be set as an xpub
3318 page
.open(url
, function(status
) {
3320 page
.evaluate(function() {
3321 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3322 var bip44AccountXpub
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3323 $("#root-key").val(bip44AccountXpub
);
3324 $("#root-key").trigger("input");
3326 waitForFeedback(function() {
3327 page
.evaluate(function() {
3329 $("#bip32-tab a").click();
3331 waitForGenerate(function() {
3332 page
.evaluate(function() {
3333 // derive external addresses for this xpub
3334 var firstAccountDerivationPath
= "m/0";
3335 $("#bip32-path").val(firstAccountDerivationPath
);
3336 $("#bip32-path").trigger("input");
3338 waitForGenerate(function() {
3339 // check the addresses are generated
3340 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3341 var actual
= page
.evaluate(function() {
3342 return $(".address:first").text();
3344 if (actual
!= expected
) {
3345 console
.log("xpub key does not generate addresses in table");
3346 console
.log("Expected: " + expected
);
3347 console
.log("Actual: " + actual
);
3350 // check the xprv key is not set
3351 var expected
= "NA";
3352 var actual
= page
.evaluate(function() {
3353 return $(".extended-priv-key").val();
3355 if (actual
!= expected
) {
3356 console
.log("xpub key as root shows derived bip32 xprv key");
3357 console
.log("Expected: " + expected
);
3358 console
.log("Actual: " + actual
);
3361 // check the private key is not set
3362 var expected
= "NA";
3363 var actual
= page
.evaluate(function() {
3364 return $(".privkey:first").text();
3366 if (actual
!= expected
) {
3367 console
.log("xpub key generates private key in addresses table");
3368 console
.log("Expected: " + expected
);
3369 console
.log("Actual: " + actual
);
3380 // xpub for bip32 root key will not work with hardened derivation paths
3382 page
.open(url
, function(status
) {
3384 page
.evaluate(function() {
3385 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3386 var bip44AccountXpub
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3387 $("#root-key").val(bip44AccountXpub
);
3388 $("#root-key").trigger("input");
3390 waitForFeedback(function() {
3391 // Check feedback is correct
3392 var expected
= "Hardened derivation path is invalid with xpub key";
3393 var actual
= page
.evaluate(function() {
3394 return $(".feedback").text();
3396 if (actual
!= expected
) {
3397 console
.log("xpub key with hardened derivation path does not show feedback");
3398 console
.log("Expected: " + expected
);
3399 console
.log("Actual: " + actual
);
3402 // Check no addresses are shown
3404 var actual
= page
.evaluate(function() {
3405 return $(".addresses tr").length
;
3407 if (actual
!= expected
) {
3408 console
.log("addresses still show after setting xpub key with hardened derivation path");
3409 console
.log("Expected: " + expected
);
3410 console
.log("Actual: " + actual
);
3419 // no root key shows feedback
3421 page
.open(url
, function(status
) {
3422 // click the bip32 tab on fresh page
3423 page
.evaluate(function() {
3424 $("#bip32-tab a").click();
3426 waitForFeedback(function() {
3427 // Check feedback is correct
3428 var expected
= "No root key";
3429 var actual
= page
.evaluate(function() {
3430 return $(".feedback").text();
3432 if (actual
!= expected
) {
3433 console
.log("Blank root key not detected");
3434 console
.log("Expected: " + expected
);
3435 console
.log("Actual: " + actual
);
3444 // display error switching tabs while addresses are generating
3446 page
.open(url
, function(status
) {
3448 page
.evaluate(function() {
3449 $(".phrase").val("abandon abandon ability").trigger("input");
3451 waitForGenerate(function() {
3452 // set to generate 500 more addresses
3453 // generate more addresses
3454 // change tabs which should cancel the previous generating
3455 page
.evaluate(function() {
3456 $(".rows-to-add").val("100");
3458 $("#bip32-tab a").click();
3460 // check the derivation paths are in order and of the right quantity
3461 waitForGenerate(function() {
3462 var paths
= page
.evaluate(function() {
3463 return $(".index").map(function(i
, e
) {
3467 for (var i
=0; i
<paths
.length
; i
++) {
3468 var expected
= "m/0/" + i
;
3469 var actual
= paths
[i
];
3470 if (actual
!= expected
) {
3471 console
.log("Path " + i
+ " is not in correct order");
3472 console
.log("Expected: " + expected
);
3473 console
.log("Actual: " + actual
);
3477 if (paths
.length
!= 20) {
3478 console
.log("Generation was not cancelled by new action");
3488 // padding for binary should give length with multiple of 256
3489 // hashed entropy 1111 is length 252, so requires 4 leading zeros
3490 // prior to issue 49 it would only generate 2 leading zeros, ie missing 2
3492 page
.open(url
, function(status
) {
3493 expected
= "avocado valid quantum cross link predict excuse edit street able flame large galaxy ginger nuclear"
3495 page
.evaluate(function() {
3496 $(".use-entropy").prop("checked", true).trigger("change");
3497 $(".mnemonic-length").val("15");
3498 $(".entropy").val("1111").trigger("input");
3500 waitForGenerate(function() {
3502 var actual
= page
.evaluate(function() {
3503 return $(".phrase").val();
3505 // check the mnemonic is correct
3506 if (actual
!= expected
) {
3507 console
.log("Left padding error for entropy");
3508 console
.log("Expected: " + expected
);
3509 console
.log("Actual: " + actual
);
3517 // Github pull request 55
3518 // https://github.com/iancoleman/bip39/pull/55
3521 page
.open(url
, function(status
) {
3522 // set mnemonic and select bip32 tab
3523 page
.evaluate(function() {
3524 $("#bip32-tab a").click();
3525 $(".phrase").val("abandon abandon ability").trigger("input");
3527 waitForGenerate(function() {
3529 // set bip32 client to bitcoin core
3530 page
.evaluate(function() {
3531 var bitcoinCoreIndex
= "0";
3532 $("#bip32-client").val(bitcoinCoreIndex
).trigger("change");
3534 waitForGenerate(function() {
3535 // get the derivation path
3536 var actual
= page
.evaluate(function() {
3537 return $("#bip32-path").val();
3539 // check the derivation path is correct
3540 expected
= "m/0'/0'"
3541 if (actual
!= expected
) {
3542 console
.log("Selecting Bitcoin Core client does not set correct derivation path");
3543 console
.log("Expected: " + expected
);
3544 console
.log("Actual: " + actual
);
3547 // get hardened addresses
3548 var usesHardenedAddresses
= page
.evaluate(function() {
3549 return $(".hardened-addresses").prop("checked");
3551 // check hardened addresses is selected
3552 if(!usesHardenedAddresses
) {
3553 console
.log("Selecting Bitcoin Core client does not use hardened addresses");
3556 // check input is readonly
3557 var pathIsReadonly
= page
.evaluate(function() {
3558 return $("#bip32-path").prop("readonly");
3560 if (!pathIsReadonly
) {
3561 console
.log("Selecting Bitcoin Core client does not set derivation path to readonly");
3565 // set bip32 client to multibit
3566 page
.evaluate(function() {
3567 var multibitIndex
= "2";
3568 $("#bip32-client").val(multibitIndex
).trigger("change");
3570 waitForGenerate(function() {
3571 // get the derivation path
3572 var actual
= page
.evaluate(function() {
3573 return $("#bip32-path").val();
3575 // check the derivation path is correct
3577 if (actual
!= expected
) {
3578 console
.log("Selecting Multibit client does not set correct derivation path");
3579 console
.log("Expected: " + expected
);
3580 console
.log("Actual: " + actual
);
3583 // get hardened addresses
3584 var usesHardenedAddresses
= page
.evaluate(function() {
3585 return $(".hardened-addresses").prop("checked");
3587 // check hardened addresses is selected
3588 if(usesHardenedAddresses
) {
3589 console
.log("Selecting Multibit client does not uncheck hardened addresses");
3592 // CUSTOM DERIVATION PATH
3593 // check input is not readonly
3594 page
.evaluate(function() {
3595 $("#bip32-client").val("custom").trigger("change");
3597 // do not wait for generate, since there is no change to the
3598 // derivation path there is no new generation performed
3599 var pathIsReadonly
= page
.evaluate(function() {
3600 return $("#bip32-path").prop("readonly");
3602 if (pathIsReadonly
) {
3603 console
.log("Selecting Custom Derivation Path does not allow derivation path input");
3614 // https://github.com/iancoleman/bip39/issues/58
3615 // bip32 derivation is correct, does not drop leading zeros
3617 // https://medium.com/@alexberegszaszi/why-do-my-bip32-wallets-disagree-6f3254cc5846
3619 page
.open(url
, function(status
) {
3620 // set the phrase and passphrase
3621 var expected
= "17rxURoF96VhmkcEGCj5LNQkmN9HVhWb7F";
3622 // Note that bitcore generates an incorrect address
3623 // 13EuKhffWkBE2KUwcbkbELZb1MpzbimJ3Y
3624 // see the medium.com link above for more details
3625 page
.evaluate(function() {
3626 $(".phrase").val("fruit wave dwarf banana earth journey tattoo true farm silk olive fence");
3627 $(".passphrase").val("banana").trigger("input");
3629 // check the address is generated correctly
3630 waitForGenerate(function() {
3631 var actual
= page
.evaluate(function() {
3632 return $(".address:first").text();
3634 if (actual
!= expected
) {
3635 console
.log("BIP32 derivation is incorrect");
3636 console
.log("Expected: " + expected
);
3637 console
.log("Actual: " + actual
);
3647 // Japanese mnemonics generate incorrect bip32 seed
3648 // BIP39 seed is set from phrase
3650 page
.open(url
, function(status
) {
3652 var expected
= "a262d6fb6122ecf45be09c50492b31f92e9beb7d9a845987a02cefda57a15f9c467a17872029a9e92299b5cbdf306e3a0ee620245cbd508959b6cb7ca637bd55";
3653 page
.evaluate(function() {
3654 $(".phrase").val("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら");
3655 $("#passphrase").val("メートルガバヴァぱばぐゞちぢ十人十色");
3656 $("#passphrase").trigger("input");
3658 // check the seed is generated correctly
3659 waitForGenerate(function() {
3660 var actual
= page
.evaluate(function() {
3661 return $(".seed").val();
3663 if (actual
!= expected
) {
3664 console
.log("BIP39 seed is incorrectly generated from Japanese mnemonic");
3665 console
.log("Expected: " + expected
);
3666 console
.log("Actual: " + actual
);
3674 // If you wish to add more tests, do so here...
3676 // Here is a blank test template
3680 page.open(url, function(status) {
3681 // Do something on the page
3682 page.evaluate(function() {
3683 $(".phrase").val("abandon abandon ability").trigger("input");
3685 waitForGenerate(function() {
3686 // Check the result of doing the thing
3687 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3688 var actual = page.evaluate(function() {
3689 return $(".address:first").text();
3691 if (actual != expected) {
3692 console.log("A specific message about what failed");
3693 console.log("Expected: " + expected);
3694 console.log("Actual: " + actual);
3697 // Run the next test
3707 console
.log("Running tests...");
3708 tests
= shuffle(tests
);