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 dogecoin
336 page
.open(url
, function(status
) {
337 // set the phrase and coin
338 var expected
= "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA";
339 page
.evaluate(function() {
340 $(".phrase").val("abandon abandon ability");
341 $(".phrase").trigger("input");
342 $(".network option[selected]").removeAttr("selected");
343 $(".network option").filter(function() {
344 return $(this).html() == "Dogecoin";
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("Dogecoin address is incorrect");
355 console
.log("Expected: " + expected
);
356 console
.log("Actual: " + actual
);
364 // Network can be set to shadowcash
366 page
.open(url
, function(status
) {
367 // set the phrase and coin
368 var expected
= "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG";
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() == "ShadowCash";
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("Shadowcash address is incorrect");
385 console
.log("Expected: " + expected
);
386 console
.log("Actual: " + actual
);
394 // Network can be set to shadowcash testnet
396 page
.open(url
, function(status
) {
397 // set the phrase and coin
398 var expected
= "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
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 Testnet";
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 testnet address is incorrect");
415 console
.log("Expected: " + expected
);
416 console
.log("Actual: " + actual
);
424 // Network can be set to viacoin
426 page
.open(url
, function(status
) {
427 // set the phrase and coin
428 var expected
= "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
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() == "Viacoin";
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("Viacoin address is incorrect");
445 console
.log("Expected: " + expected
);
446 console
.log("Actual: " + actual
);
454 // Network can be set to viacoin testnet
456 page
.open(url
, function(status
) {
457 // set the phrase and coin
458 var expected
= "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
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 Testnet";
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 testnet address is incorrect");
475 console
.log("Expected: " + expected
);
476 console
.log("Actual: " + actual
);
484 // Network can be set to jumbucks
486 page
.open(url
, function(status
) {
487 // set the phrase and coin
488 var expected
= "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew";
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() == "Jumbucks";
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("Jumbucks address is incorrect");
505 console
.log("Expected: " + expected
);
506 console
.log("Actual: " + actual
);
514 // Network can be set to clam
516 page
.open(url
, function(status
) {
517 // set the phrase and coin
518 var expected
= "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y";
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() == "CLAM";
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("CLAM address is incorrect");
535 console
.log("Expected: " + expected
);
536 console
.log("Actual: " + actual
);
544 // Network can be set to dash
546 page
.open(url
, function(status
) {
547 // set the phrase and coin
548 var expected
= "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT";
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() == "DASH";
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("DASH address is incorrect");
565 console
.log("Expected: " + expected
);
566 console
.log("Actual: " + actual
);
574 // Network can be set to game
576 page
.open(url
, function(status
) {
577 // set the phrase and coin
578 var expected
= "GSMY9bAp36cMR4zyT4uGVS7GFjpdXbao5Q";
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() == "GAME";
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("GAME address is incorrect");
595 console
.log("Expected: " + expected
);
596 console
.log("Actual: " + actual
);
604 // Network can be set to namecoin
606 page
.open(url
, function(status
) {
607 // set the phrase and coin
608 var expected
= "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2";
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() == "Namecoin";
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("Namecoin address is incorrect");
625 console
.log("Expected: " + expected
);
626 console
.log("Actual: " + actual
);
634 // Network can be set to peercoin
636 page
.open(url
, function(status
) {
637 // set the phrase and coin
638 var expected
= "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm";
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() == "Peercoin";
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("Peercoin address is incorrect");
655 console
.log("Expected: " + expected
);
656 console
.log("Actual: " + actual
);
664 // Network can be set to ethereum
667 page
.open(url
, function(status
) {
669 // set the phrase and coin
670 page
.evaluate(function() {
671 $(".phrase").val("abandon abandon ability");
672 $(".phrase").trigger("input");
673 $(".network option[selected]").removeAttr("selected");
674 $(".network option").filter(function() {
675 return $(this).html() == "Ethereum";
676 }).prop("selected", true);
677 $(".network").trigger("change");
679 waitForGenerate(function() {
680 // check the address is generated correctly
681 // this value comes from
682 // https://www.myetherwallet.com/#view-wallet-info
683 // Unusual capitalization is due to checksum
684 var expected
= "0xe5815d5902Ad612d49283DEdEc02100Bd44C2772";
685 var actual
= page
.evaluate(function() {
686 return $(".address:first").text();
688 if (actual
!= expected
) {
689 console
.log("Ethereum address is incorrect");
690 console
.log("Expected: " + expected
);
691 console
.log("Actual: " + actual
);
694 // check the private key is correct
695 // this private key can be imported into
696 // https://www.myetherwallet.com/#view-wallet-info
697 // and it should correlate to the address above
698 var expected
= "8f253078b73d7498302bb78c171b23ce7a8fb511987d2b2702b731638a4a15e7";
699 var actual
= page
.evaluate(function() {
700 return $(".privkey:first").text();
702 if (actual
!= expected
) {
703 console
.log("Ethereum privkey is incorrect");
704 console
.log("Expected: " + expected
);
705 console
.log("Actual: " + actual
);
708 // check the public key is correct
710 // don't have any third-party source to generate the expected value
711 //var expected = "?";
712 //var actual = page.evaluate(function() {
713 // return $(".pubkey:first").text();
715 //if (actual != expected) {
716 // console.log("Ethereum privkey is incorrect");
717 // console.log("Expected: " + expected);
718 // console.log("Actual: " + actual);
726 // BIP39 seed is set from phrase
728 page
.open(url
, function(status
) {
730 var expected
= "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
731 page
.evaluate(function() {
732 $(".phrase").val("abandon abandon ability");
733 $(".phrase").trigger("input");
735 // check the address is generated correctly
736 waitForGenerate(function() {
737 var actual
= page
.evaluate(function() {
738 return $(".seed").val();
740 if (actual
!= expected
) {
741 console
.log("BIP39 seed is incorrectly generated from mnemonic");
742 console
.log("Expected: " + expected
);
743 console
.log("Actual: " + actual
);
751 // BIP32 root key is set from phrase
753 page
.open(url
, function(status
) {
755 var expected
= "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi";
756 page
.evaluate(function() {
757 $(".phrase").val("abandon abandon ability");
758 $(".phrase").trigger("input");
760 // check the address is generated correctly
761 waitForGenerate(function() {
762 var actual
= page
.evaluate(function() {
763 return $(".root-key").val();
765 if (actual
!= expected
) {
766 console
.log("Root key is incorrectly generated from mnemonic");
767 console
.log("Expected: " + expected
);
768 console
.log("Actual: " + actual
);
776 // Tabs show correct addresses when changed
778 page
.open(url
, function(status
) {
780 var expected
= "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz";
781 page
.evaluate(function() {
782 $(".phrase").val("abandon abandon ability");
783 $(".phrase").trigger("input");
786 waitForGenerate(function() {
787 page
.evaluate(function() {
788 $("#bip32-tab a").click();
790 // check the address is generated correctly
791 waitForGenerate(function() {
792 var actual
= page
.evaluate(function() {
793 return $(".address:first").text();
795 if (actual
!= expected
) {
796 console
.log("Clicking tab generates incorrect address");
797 console
.log("Expected: " + expected
);
798 console
.log("Actual: " + actual
);
807 // BIP44 derivation path is shown
809 page
.open(url
, function(status
) {
811 var expected
= "m/44'/0'/0'/0";
812 page
.evaluate(function() {
813 $(".phrase").val("abandon abandon ability");
814 $(".phrase").trigger("input");
816 // check the derivation path of the first address
817 waitForGenerate(function() {
818 var actual
= page
.evaluate(function() {
819 return $("#bip44 .path").val();
821 if (actual
!= expected
) {
822 console
.log("BIP44 derivation path is incorrect");
823 console
.log("Expected: " + expected
);
824 console
.log("Actual: " + actual
);
832 // BIP44 extended private key is shown
834 page
.open(url
, function(status
) {
836 var expected
= "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG";
837 page
.evaluate(function() {
838 $(".phrase").val("abandon abandon ability");
839 $(".phrase").trigger("input");
841 // check the BIP44 extended private key
842 waitForGenerate(function() {
843 var actual
= page
.evaluate(function() {
844 return $(".extended-priv-key").val();
846 if (actual
!= expected
) {
847 console
.log("BIP44 extended private key is incorrect");
848 console
.log("Expected: " + expected
);
849 console
.log("Actual: " + actual
);
857 // BIP44 extended public key is shown
859 page
.open(url
, function(status
) {
861 var expected
= "xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM";
862 page
.evaluate(function() {
863 $(".phrase").val("abandon abandon ability");
864 $(".phrase").trigger("input");
866 // check the BIP44 extended public key
867 waitForGenerate(function() {
868 var actual
= page
.evaluate(function() {
869 return $(".extended-pub-key").val();
871 if (actual
!= expected
) {
872 console
.log("BIP44 extended public key is incorrect");
873 console
.log("Expected: " + expected
);
874 console
.log("Actual: " + actual
);
882 // BIP44 purpose field changes address list
884 page
.open(url
, function(status
) {
886 var expected
= "1JbDzRJ2cDT8aat2xwKd6Pb2zzavow5MhF";
887 page
.evaluate(function() {
888 $(".phrase").val("abandon abandon ability");
889 $(".phrase").trigger("input");
891 waitForGenerate(function() {
892 // change the bip44 purpose field to 45
893 page
.evaluate(function() {
894 $("#bip44 .purpose").val("45");
895 $("#bip44 .purpose").trigger("input");
897 waitForGenerate(function() {
898 // check the address for the new derivation path
899 var actual
= page
.evaluate(function() {
900 return $(".address:first").text();
902 if (actual
!= expected
) {
903 console
.log("BIP44 purpose field generates incorrect address");
904 console
.log("Expected: " + expected
);
905 console
.log("Actual: " + actual
);
914 // BIP44 coin field changes address list
916 page
.open(url
, function(status
) {
918 var expected
= "1F6dB2djQYrxoyfZZmfr6D5voH8GkJTghk";
919 page
.evaluate(function() {
920 $(".phrase").val("abandon abandon ability");
921 $(".phrase").trigger("input");
923 waitForGenerate(function() {
924 // change the bip44 purpose field to 45
925 page
.evaluate(function() {
926 $("#bip44 .coin").val("1");
927 $("#bip44 .coin").trigger("input");
929 waitForGenerate(function() {
930 // check the address for the new derivation path
931 var actual
= page
.evaluate(function() {
932 return $(".address:first").text();
934 if (actual
!= expected
) {
935 console
.log("BIP44 coin field generates incorrect address");
936 console
.log("Expected: " + expected
);
937 console
.log("Actual: " + actual
);
946 // BIP44 account field changes address list
948 page
.open(url
, function(status
) {
950 var expected
= "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
951 page
.evaluate(function() {
952 $(".phrase").val("abandon abandon ability");
953 $(".phrase").trigger("input");
955 waitForGenerate(function() {
956 // change the bip44 purpose field to 45
957 page
.evaluate(function() {
958 $("#bip44 .account").val("1");
959 $("#bip44 .account").trigger("input");
961 waitForGenerate(function() {
962 // check the address for the new derivation path
963 var actual
= page
.evaluate(function() {
964 return $(".address:first").text();
966 if (actual
!= expected
) {
967 console
.log("BIP44 account field generates incorrect address");
968 console
.log("Expected: " + expected
);
969 console
.log("Actual: " + actual
);
978 // BIP44 change field changes address list
980 page
.open(url
, function(status
) {
982 var expected
= "1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo";
983 page
.evaluate(function() {
984 $(".phrase").val("abandon abandon ability");
985 $(".phrase").trigger("input");
987 waitForGenerate(function() {
988 // change the bip44 purpose field to 45
989 page
.evaluate(function() {
990 $("#bip44 .change").val("1");
991 $("#bip44 .change").trigger("input");
993 waitForGenerate(function() {
994 // check the address for the new derivation path
995 var actual
= page
.evaluate(function() {
996 return $(".address:first").text();
998 if (actual
!= expected
) {
999 console
.log("BIP44 change field generates incorrect address");
1000 console
.log("Expected: " + expected
);
1001 console
.log("Actual: " + actual
);
1010 // BIP32 derivation path can be set
1012 page
.open(url
, function(status
) {
1014 var expected
= "16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L";
1015 page
.evaluate(function() {
1016 $(".phrase").val("abandon abandon ability");
1017 $(".phrase").trigger("input");
1020 waitForGenerate(function() {
1021 page
.evaluate(function() {
1022 $("#bip32-tab a").click();
1024 // set the derivation path to m/1
1025 waitForGenerate(function() {
1026 page
.evaluate(function() {
1027 $("#bip32 .path").val("m/1");
1028 $("#bip32 .path").trigger("input");
1030 // check the address is generated correctly
1031 waitForGenerate(function() {
1032 var actual
= page
.evaluate(function() {
1033 return $(".address:first").text();
1035 if (actual
!= expected
) {
1036 console
.log("Custom BIP32 path generates incorrect address");
1037 console
.log("Expected: " + expected
);
1038 console
.log("Actual: " + actual
);
1048 // BIP32 can use hardened derivation paths
1050 page
.open(url
, function(status
) {
1052 var expected
= "14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4";
1053 page
.evaluate(function() {
1054 $(".phrase").val("abandon abandon ability");
1055 $(".phrase").trigger("input");
1058 waitForGenerate(function() {
1059 page
.evaluate(function() {
1060 $("#bip32-tab a").click();
1062 // set the derivation path to m/0'
1063 waitForGenerate(function() {
1064 page
.evaluate(function() {
1065 $("#bip32 .path").val("m/0'");
1066 $("#bip32 .path").trigger("input");
1068 // check the address is generated correctly
1069 waitForGenerate(function() {
1070 var actual
= page
.evaluate(function() {
1071 return $(".address:first").text();
1073 if (actual
!= expected
) {
1074 console
.log("Hardened BIP32 path generates incorrect address");
1075 console
.log("Expected: " + expected
);
1076 console
.log("Actual: " + actual
);
1086 // BIP32 extended private key is shown
1088 page
.open(url
, function(status
) {
1090 var expected
= "xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe";
1091 page
.evaluate(function() {
1092 $(".phrase").val("abandon abandon ability");
1093 $(".phrase").trigger("input");
1096 waitForGenerate(function() {
1097 page
.evaluate(function() {
1098 $("#bip32-tab a").click();
1100 // check the extended private key is generated correctly
1101 waitForGenerate(function() {
1102 var actual
= page
.evaluate(function() {
1103 return $(".extended-priv-key").val();
1105 if (actual
!= expected
) {
1106 console
.log("BIP32 extended private key is incorrect");
1107 console
.log("Expected: " + expected
);
1108 console
.log("Actual: " + actual
);
1117 // BIP32 extended public key is shown
1119 page
.open(url
, function(status
) {
1121 var expected
= "xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P";
1122 page
.evaluate(function() {
1123 $(".phrase").val("abandon abandon ability");
1124 $(".phrase").trigger("input");
1127 waitForGenerate(function() {
1128 page
.evaluate(function() {
1129 $("#bip32-tab a").click();
1131 // check the extended public key is generated correctly
1132 waitForGenerate(function() {
1133 var actual
= page
.evaluate(function() {
1134 return $(".extended-pub-key").val();
1136 if (actual
!= expected
) {
1137 console
.log("BIP32 extended public key is incorrect");
1138 console
.log("Expected: " + expected
);
1139 console
.log("Actual: " + actual
);
1148 // Derivation path is shown in table
1150 page
.open(url
, function(status
) {
1152 var expected
= "m/44'/0'/0'/0/0";
1153 page
.evaluate(function() {
1154 $(".phrase").val("abandon abandon ability");
1155 $(".phrase").trigger("input");
1157 // check for derivation path in table
1158 waitForGenerate(function() {
1159 var actual
= page
.evaluate(function() {
1160 return $(".index:first").text();
1162 if (actual
!= expected
) {
1163 console
.log("Derivation path shown incorrectly in table");
1164 console
.log("Expected: " + expected
);
1165 console
.log("Actual: " + actual
);
1173 // Derivation path for address can be hardened
1175 page
.open(url
, function(status
) {
1177 var expected
= "18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd";
1178 page
.evaluate(function() {
1179 $(".phrase").val("abandon abandon ability");
1180 $(".phrase").trigger("input");
1183 waitForGenerate(function() {
1184 page
.evaluate(function() {
1185 $("#bip32-tab a").click();
1187 waitForGenerate(function() {
1188 // select the hardened addresses option
1189 page
.evaluate(function() {
1190 $(".hardened-addresses").prop("checked", true);
1191 $(".hardened-addresses").trigger("change");
1193 waitForGenerate(function() {
1194 // check the generated address is hardened
1195 var actual
= page
.evaluate(function() {
1196 return $(".address:first").text();
1198 if (actual
!= expected
) {
1199 console
.log("Hardened address is incorrect");
1200 console
.log("Expected: " + expected
);
1201 console
.log("Actual: " + actual
);
1211 // Derivation path visibility can be toggled
1213 page
.open(url
, function(status
) {
1215 page
.evaluate(function() {
1216 $(".phrase").val("abandon abandon ability");
1217 $(".phrase").trigger("input");
1219 waitForGenerate(function() {
1220 // toggle path visibility
1221 page
.evaluate(function() {
1222 $(".index-toggle").click();
1224 // check the path is not visible
1225 var isInvisible
= page
.evaluate(function() {
1226 return $(".index:first span").hasClass("invisible");
1229 console
.log("Toggled derivation path is visible");
1239 page
.open(url
, function(status
) {
1240 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1242 page
.evaluate(function() {
1243 $(".phrase").val("abandon abandon ability").trigger("input");
1246 waitForGenerate(function() {
1247 var actual
= page
.evaluate(function() {
1248 return $(".address:first").text();
1250 if (actual
!= expected
) {
1251 console
.log("Address is not shown");
1252 console
.log("Expected: " + expected
);
1253 console
.log("Got: " + actual
);
1261 // Addresses are shown in order of derivation path
1263 page
.open(url
, function(status
) {
1265 page
.evaluate(function() {
1266 $(".phrase").val("abandon abandon ability").trigger("input");
1268 // get the derivation paths
1269 waitForGenerate(function() {
1270 var paths
= page
.evaluate(function() {
1271 return $(".index").map(function(i
, e
) {
1275 if (paths
.length
!= 20) {
1276 console
.log("Total paths is less than expected: " + paths
.length
);
1279 for (var i
=0; i
<paths
.length
; i
++) {
1280 var expected
= "m/44'/0'/0'/0/" + i
;
1281 var actual
= paths
[i
];
1282 if (actual
!= expected
) {
1283 console
.log("Path " + i
+ " is incorrect");
1284 console
.log("Expected: " + expected
);
1285 console
.log("Actual: " + actual
);
1294 // Address visibility can be toggled
1296 page
.open(url
, function(status
) {
1298 page
.evaluate(function() {
1299 $(".phrase").val("abandon abandon ability");
1300 $(".phrase").trigger("input");
1302 waitForGenerate(function() {
1303 // toggle address visibility
1304 page
.evaluate(function() {
1305 $(".address-toggle").click();
1307 // check the address is not visible
1308 var isInvisible
= page
.evaluate(function() {
1309 return $(".address:first span").hasClass("invisible");
1312 console
.log("Toggled address is visible");
1320 // Public key is shown
1322 page
.open(url
, function(status
) {
1323 var expected
= "033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3";
1325 page
.evaluate(function() {
1326 $(".phrase").val("abandon abandon ability").trigger("input");
1329 waitForGenerate(function() {
1330 var actual
= page
.evaluate(function() {
1331 return $(".pubkey:first").text();
1333 if (actual
!= expected
) {
1334 console
.log("Public key is not shown");
1335 console
.log("Expected: " + expected
);
1336 console
.log("Got: " + actual
);
1344 // Public key visibility can be toggled
1346 page
.open(url
, function(status
) {
1348 page
.evaluate(function() {
1349 $(".phrase").val("abandon abandon ability");
1350 $(".phrase").trigger("input");
1352 waitForGenerate(function() {
1353 // toggle public key visibility
1354 page
.evaluate(function() {
1355 $(".public-key-toggle").click();
1357 // check the public key is not visible
1358 var isInvisible
= page
.evaluate(function() {
1359 return $(".pubkey:first span").hasClass("invisible");
1362 console
.log("Toggled public key is visible");
1370 // Private key is shown
1372 page
.open(url
, function(status
) {
1373 var expected
= "L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE";
1375 page
.evaluate(function() {
1376 $(".phrase").val("abandon abandon ability").trigger("input");
1379 waitForGenerate(function() {
1380 var actual
= page
.evaluate(function() {
1381 return $(".privkey:first").text();
1383 if (actual
!= expected
) {
1384 console
.log("Private key is not shown");
1385 console
.log("Expected: " + expected
);
1386 console
.log("Got: " + actual
);
1394 // Private key visibility can be toggled
1396 page
.open(url
, function(status
) {
1398 page
.evaluate(function() {
1399 $(".phrase").val("abandon abandon ability");
1400 $(".phrase").trigger("input");
1402 waitForGenerate(function() {
1403 // toggle private key visibility
1404 page
.evaluate(function() {
1405 $(".private-key-toggle").click();
1407 // check the private key is not visible
1408 var isInvisible
= page
.evaluate(function() {
1409 return $(".privkey:first span").hasClass("invisible");
1412 console
.log("Toggled private key is visible");
1420 // More addresses can be generated
1422 page
.open(url
, function(status
) {
1424 page
.evaluate(function() {
1425 $(".phrase").val("abandon abandon ability");
1426 $(".phrase").trigger("input");
1428 waitForGenerate(function() {
1429 // generate more addresses
1430 page
.evaluate(function() {
1433 waitForGenerate(function() {
1434 // check there are more addresses
1435 var addressCount
= page
.evaluate(function() {
1436 return $(".address").length
;
1438 if (addressCount
!= 40) {
1439 console
.log("More addresses cannot be generated");
1448 // A custom number of additional addresses can be generated
1450 page
.open(url
, function(status
) {
1452 page
.evaluate(function() {
1453 $(".phrase").val("abandon abandon ability");
1454 $(".phrase").trigger("input");
1456 waitForGenerate(function() {
1457 // get the current number of addresses
1458 var oldAddressCount
= page
.evaluate(function() {
1459 return $(".address").length
;
1461 // set a custom number of additional addresses
1462 page
.evaluate(function() {
1463 $(".rows-to-add").val(1);
1465 // generate more addresses
1466 page
.evaluate(function() {
1469 waitForGenerate(function() {
1470 // check there are the correct number of addresses
1471 var newAddressCount
= page
.evaluate(function() {
1472 return $(".address").length
;
1474 if (newAddressCount
- oldAddressCount
!= 1) {
1475 console
.log("Number of additional addresses cannot be customized");
1476 console
.log(newAddressCount
)
1477 console
.log(oldAddressCount
)
1486 // Additional addresses are shown in order of derivation path
1488 page
.open(url
, function(status
) {
1490 page
.evaluate(function() {
1491 $(".phrase").val("abandon abandon ability").trigger("input");
1493 waitForGenerate(function() {
1494 // generate more addresses
1495 page
.evaluate(function() {
1498 // get the derivation paths
1499 waitForGenerate(function() {
1500 var paths
= page
.evaluate(function() {
1501 return $(".index").map(function(i
, e
) {
1505 if (paths
.length
!= 40) {
1506 console
.log("Total additional paths is less than expected: " + paths
.length
);
1509 for (var i
=0; i
<paths
.length
; i
++) {
1510 var expected
= "m/44'/0'/0'/0/" + i
;
1511 var actual
= paths
[i
];
1512 if (actual
!= expected
) {
1513 console
.log("Path " + i
+ " is not in correct order");
1514 console
.log("Expected: " + expected
);
1515 console
.log("Actual: " + actual
);
1525 // BIP32 root key can be set by the user
1527 page
.open(url
, function(status
) {
1528 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1530 page
.evaluate(function() {
1531 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1533 waitForGenerate(function() {
1534 var actual
= page
.evaluate(function() {
1535 return $(".address:first").text();
1537 if (actual
!= expected
) {
1538 console
.log("Setting BIP32 root key results in wrong address");
1539 console
.log("Expected: " + expected
);
1540 console
.log("Actual: " + actual
);
1548 // Setting BIP32 root key clears the existing phrase, passphrase and seed
1550 page
.open(url
, function(status
) {
1553 page
.evaluate(function() {
1554 $(".phrase").val("A non-blank but invalid value");
1556 // Accept any confirm dialogs
1557 page
.onConfirm = function() {
1561 page
.evaluate(function() {
1562 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1564 waitForGenerate(function() {
1565 var actual
= page
.evaluate(function() {
1566 return $(".phrase").val();
1568 if (actual
!= expected
) {
1569 console
.log("Phrase not cleared when setting BIP32 root key");
1570 console
.log("Expected: " + expected
);
1571 console
.log("Actual: " + actual
);
1579 // Clearing of phrase, passphrase and seed can be cancelled by user
1581 page
.open(url
, function(status
) {
1582 var expected
= "abandon abandon ability";
1584 page
.evaluate(function() {
1585 $(".phrase").val("abandon abandon ability");
1587 // Cancel any confirm dialogs
1588 page
.onConfirm = function() {
1592 page
.evaluate(function() {
1593 $(".root-key").val("xprv9s21ZrQH143K3d3vzEDD3KpSKmxsZ3y7CqhAL1tinwtP6wqK4TKEKjpBuo6P2hUhB6ZENo7TTSRytiP857hBZVpBdk8PooFuRspE1eywwNZ").trigger("input");
1595 var actual
= page
.evaluate(function() {
1596 return $(".phrase").val();
1598 if (actual
!= expected
) {
1599 console
.log("Phrase not retained when cancelling changes to BIP32 root key");
1600 console
.log("Expected: " + expected
);
1601 console
.log("Actual: " + actual
);
1608 // Custom BIP32 root key is used when changing the derivation path
1610 page
.open(url
, function(status
) {
1611 var expected
= "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1613 page
.evaluate(function() {
1614 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1616 waitForGenerate(function() {
1617 // change the derivation path
1618 page
.evaluate(function() {
1619 $("#account").val("1").trigger("input");
1621 // check the bip32 root key is used for derivation, not the blank phrase
1622 waitForGenerate(function() {
1623 var actual
= page
.evaluate(function() {
1624 return $(".address:first").text();
1626 if (actual
!= expected
) {
1627 console
.log("Changing the derivation path does not use BIP32 root key");
1628 console
.log("Expected: " + expected
);
1629 console
.log("Actual: " + actual
);
1638 // Incorrect mnemonic shows error
1640 page
.open(url
, function(status
) {
1642 page
.evaluate(function() {
1643 $(".phrase").val("abandon abandon abandon").trigger("input");
1645 waitForFeedback(function() {
1646 // check there is an error shown
1647 var feedback
= page
.evaluate(function() {
1648 return $(".feedback").text();
1650 if (feedback
.length
<= 0) {
1651 console
.log("Invalid mnemonic does not show error");
1659 // Incorrect word shows suggested replacement
1661 page
.open(url
, function(status
) {
1663 page
.evaluate(function() {
1664 $(".phrase").val("abandon abandon abiliti").trigger("input");
1666 // check there is a suggestion shown
1667 waitForFeedback(function() {
1668 var feedback
= page
.evaluate(function() {
1669 return $(".feedback").text();
1671 if (feedback
.indexOf("did you mean ability?") < 0) {
1672 console
.log("Incorrect word does not show suggested replacement");
1673 console
.log("Error: " + error
);
1681 // Github pull request 48
1682 // First four letters of word shows that word, not closest
1683 // since first four letters gives unique word in BIP39 wordlist
1684 // eg ille should show illegal, not idle
1686 page
.open(url
, function(status
) {
1687 // set the incomplete word
1688 page
.evaluate(function() {
1689 $(".phrase").val("ille").trigger("input");
1691 // check there is a suggestion shown
1692 waitForFeedback(function() {
1693 var feedback
= page
.evaluate(function() {
1694 return $(".feedback").text();
1696 if (feedback
.indexOf("did you mean illegal?") < 0) {
1697 console
.log("Start of word does not show correct suggestion");
1698 console
.log("Error: " + error
);
1706 // Incorrect BIP32 root key shows error
1708 page
.open(url
, function(status
) {
1710 page
.evaluate(function() {
1711 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj").trigger("input");
1713 // check there is an error shown
1714 waitForFeedback(function() {
1715 var feedback
= page
.evaluate(function() {
1716 return $(".feedback").text();
1718 if (feedback
!= "Invalid root key") {
1719 console
.log("Invalid root key does not show error");
1720 console
.log("Error: " + error
);
1728 // Derivation path not starting with m shows error
1730 page
.open(url
, function(status
) {
1731 // set the mnemonic phrase
1732 page
.evaluate(function() {
1733 $(".phrase").val("abandon abandon ability").trigger("input");
1735 waitForGenerate(function() {
1736 // select the bip32 tab so custom derivation path can be set
1737 page
.evaluate(function() {
1738 $("#bip32-tab a").click();
1740 waitForGenerate(function() {
1741 // set the incorrect derivation path
1742 page
.evaluate(function() {
1743 $("#bip32 .path").val("n/0").trigger("input");
1745 waitForFeedback(function() {
1746 var feedback
= page
.evaluate(function() {
1747 return $(".feedback").text();
1749 if (feedback
!= "First character must be 'm'") {
1750 console
.log("Derivation path not starting with m should show error");
1751 console
.log("Error: " + error
);
1761 // Derivation path containing invalid characters shows useful error
1763 page
.open(url
, function(status
) {
1764 // set the mnemonic phrase
1765 page
.evaluate(function() {
1766 $(".phrase").val("abandon abandon ability").trigger("input");
1768 waitForGenerate(function() {
1769 // select the bip32 tab so custom derivation path can be set
1770 page
.evaluate(function() {
1771 $("#bip32-tab a").click();
1773 waitForGenerate(function() {
1774 // set the incorrect derivation path
1775 page
.evaluate(function() {
1776 $("#bip32 .path").val("m/1/0wrong1/1").trigger("input");
1778 waitForFeedback(function() {
1779 var feedback
= page
.evaluate(function() {
1780 return $(".feedback").text();
1782 if (feedback
!= "Invalid characters 0wrong1 found at depth 2") {
1783 console
.log("Derivation path with invalid characters should show error");
1784 console
.log("Error: " + error
);
1794 // Github Issue 11: Default word length is 15
1795 // https://github.com/iancoleman/bip39/issues/11
1797 page
.open(url
, function(status
) {
1798 // get the word length
1799 var defaultLength
= page
.evaluate(function() {
1800 return $(".strength").val();
1802 if (defaultLength
!= 15) {
1803 console
.log("Default word length is not 15");
1811 // Github Issue 12: Generate more rows with private keys hidden
1812 // https://github.com/iancoleman/bip39/issues/12
1814 page
.open(url
, function(status
) {
1816 page
.evaluate(function() {
1817 $(".phrase").val("abandon abandon ability");
1818 $(".phrase").trigger("input");
1820 waitForGenerate(function() {
1821 // toggle private keys hidden, then generate more addresses
1822 page
.evaluate(function() {
1823 $(".private-key-toggle").click();
1826 waitForGenerate(function() {
1827 // check more have been generated
1829 var numPrivKeys
= page
.evaluate(function() {
1830 return $(".privkey").length
;
1832 if (numPrivKeys
!= expected
) {
1833 console
.log("Wrong number of addresses when clicking 'more' with hidden privkeys");
1834 console
.log("Expected: " + expected
);
1835 console
.log("Actual: " + numPrivKeys
);
1838 // check no private keys are shown
1839 var numHiddenPrivKeys
= page
.evaluate(function() {
1840 return $(".privkey span[class=invisible]").length
;
1842 if (numHiddenPrivKeys
!= expected
) {
1843 console
.log("Generating more does not retain hidden state of privkeys");
1844 console
.log("Expected: " + expected
);
1845 console
.log("Actual: " + numHiddenPrivKeys
);
1854 // Github Issue 19: Mnemonic is not sensitive to whitespace
1855 // https://github.com/iancoleman/bip39/issues/19
1857 page
.open(url
, function(status
) {
1859 var expected
= "xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC";
1860 page
.evaluate(function() {
1861 var doubleSpace
= " ";
1862 $(".phrase").val("urge cat" + doubleSpace
+ "bid");
1863 $(".phrase").trigger("input");
1865 waitForGenerate(function() {
1866 // Check the bip32 root key is correct
1867 var actual
= page
.evaluate(function() {
1868 return $(".root-key").val();
1870 if (actual
!= expected
) {
1871 console
.log("Mnemonic is sensitive to whitespace");
1872 console
.log("Expected: " + expected
);
1873 console
.log("Actual: " + actual
);
1881 // Github Issue 23: Part 1: Use correct derivation path when changing tabs
1882 // https://github.com/iancoleman/bip39/issues/23
1884 page
.open(url
, function(status
) {
1885 // 1) and 2) set the phrase
1886 page
.evaluate(function() {
1887 $(".phrase").val("abandon abandon ability").trigger("input");
1889 waitForGenerate(function() {
1890 // 3) select bip32 tab
1891 page
.evaluate(function() {
1892 $("#bip32-tab a").click();
1894 waitForGenerate(function() {
1895 // 4) switch from bitcoin to litecoin
1896 page
.evaluate(function() {
1897 $(".network option").filter(function() {
1898 return $(this).html() == "Litecoin";
1899 }).prop("selected", true);
1900 $(".network").trigger("change");
1902 waitForGenerate(function() {
1903 // 5) Check derivation path is displayed correctly
1904 var expected
= "m/0/0";
1905 var actual
= page
.evaluate(function() {
1906 return $(".index:first").text();
1908 if (actual
!= expected
) {
1909 console
.log("Github Issue 23 Part 1: derivation path display error");
1910 console
.log("Expected: " + expected
);
1911 console
.log("Actual: " + actual
);
1914 // 5) Check address is displayed correctly
1915 var expected
= "LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5";
1916 var actual
= page
.evaluate(function() {
1917 return $(".address:first").text();
1919 if (actual
!= expected
) {
1920 console
.log("Github Issue 23 Part 1: address display error");
1921 console
.log("Expected: " + expected
);
1922 console
.log("Actual: " + actual
);
1932 // Github Issue 23 Part 2: Coin selection in derivation path
1933 // https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
1935 page
.open(url
, function(status
) {
1937 page
.evaluate(function() {
1938 $(".phrase").val("abandon abandon ability").trigger("input");
1940 waitForGenerate(function() {
1941 // switch from bitcoin to clam
1942 page
.evaluate(function() {
1943 $(".network option").filter(function() {
1944 return $(this).html() == "CLAM";
1945 }).prop("selected", true);
1946 $(".network").trigger("change");
1948 waitForGenerate(function() {
1949 // check derivation path is displayed correctly
1950 var expected
= "m/44'/23'/0'/0/0";
1951 var actual
= page
.evaluate(function() {
1952 return $(".index:first").text();
1954 if (actual
!= expected
) {
1955 console
.log("Github Issue 23 Part 2: Coin in BIP44 derivation path is incorrect");
1956 console
.log("Expected: " + expected
);
1957 console
.log("Actual: " + actual
);
1966 // Github Issue 26: When using a Root key derrived altcoins are incorrect
1967 // https://github.com/iancoleman/bip39/issues/26
1969 page
.open(url
, function(status
) {
1970 // 1) 2) and 3) set the root key
1971 page
.evaluate(function() {
1972 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1974 waitForGenerate(function() {
1975 // 4) switch from bitcoin to viacoin
1976 page
.evaluate(function() {
1977 $(".network option").filter(function() {
1978 return $(this).html() == "Viacoin";
1979 }).prop("selected", true);
1980 $(".network").trigger("change");
1982 waitForGenerate(function() {
1983 // 5) ensure the derived address is correct
1984 var expected
= "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
1985 var actual
= page
.evaluate(function() {
1986 return $(".address:first").text();
1988 if (actual
!= expected
) {
1989 console
.log("Github Issue 26: address is incorrect when changing networks and using root-key to derive");
1990 console
.log("Expected: " + expected
);
1991 console
.log("Actual: " + actual
);
2000 // Selecting a language with no existing phrase should generate a phrase in
2003 page
.open(url
, function(status
) {
2004 // Select a language
2005 // Need to manually simulate hash being set due to quirk between
2006 // 'click' event triggered by javascript vs triggered by mouse.
2007 // Perhaps look into page.sendEvent
2008 // http://phantomjs.org/api/webpage/method/send-event.html
2009 page
.evaluate(function() {
2010 window
.location
.hash
= "#japanese";
2011 $("a[href='#japanese']").trigger("click");
2013 waitForGenerate(function() {
2014 // Check the mnemonic is in Japanese
2015 var phrase
= page
.evaluate(function() {
2016 return $(".phrase").val();
2018 if (phrase
.length
<= 0) {
2019 console
.log("No Japanese phrase generated");
2022 if (phrase
.charCodeAt(0) < 128) {
2023 console
.log("First character of Japanese phrase is ascii");
2024 console
.log("Phrase: " + phrase
);
2032 // Selecting a language with existing phrase should update the phrase to use
2035 page
.open(url
, function(status
) {
2036 // Set the phrase to an English phrase.
2037 page
.evaluate(function() {
2038 $(".phrase").val("abandon abandon ability").trigger("input");
2040 waitForGenerate(function() {
2041 // Change to Italian
2042 // Need to manually simulate hash being set due to quirk between
2043 // 'click' event triggered by javascript vs triggered by mouse.
2044 // Perhaps look into page.sendEvent
2045 // http://phantomjs.org/api/webpage/method/send-event.html
2046 page
.evaluate(function() {
2047 window
.location
.hash
= "#italian";
2048 $("a[href='#italian']").trigger("click");
2050 waitForGenerate(function() {
2051 // Check only the language changes, not the phrase
2052 var expected
= "abaco abaco abbaglio";
2053 var actual
= page
.evaluate(function() {
2054 return $(".phrase").val();
2056 if (actual
!= expected
) {
2057 console
.log("Changing language with existing phrase");
2058 console
.log("Expected: " + expected
);
2059 console
.log("Actual: " + actual
);
2062 // Check the address is correct
2063 var expected
= "1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV";
2064 var actual
= page
.evaluate(function() {
2065 return $(".address:first").text();
2067 if (actual
!= expected
) {
2068 console
.log("Changing language generates incorrect address");
2069 console
.log("Expected: " + expected
);
2070 console
.log("Actual: " + actual
);
2079 // Suggested replacement for erroneous word in non-English language
2081 page
.open(url
, function(status
) {
2082 // Set an incorrect phrase in Italian
2083 page
.evaluate(function() {
2084 $(".phrase").val("abaco abaco zbbaglio").trigger("input");
2086 waitForFeedback(function() {
2087 // Check the suggestion is correct
2088 var feedback
= page
.evaluate(function() {
2089 return $(".feedback").text();
2091 if (feedback
.indexOf("did you mean abbaglio?") < 0) {
2092 console
.log("Incorrect Italian word does not show suggested replacement");
2093 console
.log("Error: " + error
);
2102 // Japanese word does not break across lines.
2104 // https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
2106 page
.open(url
, function(status
) {
2107 hasWordBreakCss
= page
.content
.indexOf("word-break: keep-all;") > -1;
2108 if (!hasWordBreakCss
) {
2109 console
.log("Japanese words can break across lines mid-word");
2110 console
.log("Check CSS for '.phrase { word-break: keep-all; }'");
2113 // Run the next test
2118 // Language can be specified at page load using hash value in url
2120 page
.open(url
, function(status
) {
2121 // Set the page hash as if it were on a fresh page load
2122 page
.evaluate(function() {
2123 window
.location
.hash
= "#japanese";
2125 // Generate a random phrase
2126 page
.evaluate(function() {
2127 $(".generate").trigger("click");
2129 waitForGenerate(function() {
2130 // Check the phrase is in Japanese
2131 var phrase
= page
.evaluate(function() {
2132 return $(".phrase").val();
2134 if (phrase
.length
<= 0) {
2135 console
.log("No phrase generated using url hash");
2138 if (phrase
.charCodeAt(0) < 128) {
2139 console
.log("Language not detected from url hash on page load.");
2140 console
.log("Phrase: " + phrase
);
2148 // Entropy unit tests
2150 page
.open(url
, function(status
) {
2151 var response
= page
.evaluate(function() {
2153 // binary entropy is detected
2155 e
= Entropy
.fromString("01010101");
2156 if (e
.base
.str
!= "binary") {
2157 return "Binary entropy not detected correctly";
2163 // base6 entropy is detected
2165 e
= Entropy
.fromString("012345012345");
2166 if (e
.base
.str
!= "base 6") {
2167 return "base6 entropy not detected correctly";
2173 // dice entropy is detected
2175 e
= Entropy
.fromString("123456123456");
2176 if (e
.base
.str
!= "base 6 (dice)") {
2177 return "dice entropy not detected correctly";
2183 // base10 entropy is detected
2185 e
= Entropy
.fromString("0123456789");
2186 if (e
.base
.str
!= "base 10") {
2187 return "base10 entropy not detected correctly";
2193 // hex entropy is detected
2195 e
= Entropy
.fromString("0123456789ABCDEF");
2196 if (e
.base
.str
!= "hexadecimal") {
2197 return "hexadecimal entropy not detected correctly";
2203 // card entropy is detected
2205 e
= Entropy
.fromString("AC4DTHKS");
2206 if (e
.base
.str
!= "card") {
2207 return "card entropy not detected correctly";
2213 // entropy is case insensitive
2215 e
= Entropy
.fromString("aBcDeF");
2216 if (e
.cleanStr
!= "aBcDeF") {
2217 return "Entropy should not be case sensitive";
2223 // dice entropy is converted to base6
2225 e
= Entropy
.fromString("123456");
2226 if (e
.cleanStr
!= "123450") {
2227 return "Dice entropy is not automatically converted to base6";
2233 // dice entropy is preferred to base6 if ambiguous
2235 e
= Entropy
.fromString("12345");
2236 if (e
.base
.str
!= "base 6 (dice)") {
2237 return "dice not used as default over base 6";
2243 // unused characters are ignored
2245 e
= Entropy
.fromString("fghijkl");
2246 if (e
.cleanStr
!= "f") {
2247 return "additional characters are not ignored";
2253 // the lowest base is used by default
2254 // 7 could be decimal or hexadecimal, but should be detected as decimal
2256 e
= Entropy
.fromString("7");
2257 if (e
.base
.str
!= "base 10") {
2258 return "lowest base is not used";
2264 // Leading zeros are retained
2266 e
= Entropy
.fromString("000A");
2267 if (e
.cleanStr
!= "000A") {
2268 return "Leading zeros are not retained";
2274 // Leading zeros are correctly preserved for hex in binary string
2276 e
= Entropy
.fromString("2A");
2277 if (e
.binaryStr
!= "00101010") {
2278 return "Hex leading zeros are not correct in binary";
2284 // Leading zeros for base 6 as binary string
2285 // 20 = 2 events at 2.58 bits per event = 5 bits
2286 // 20 in base 6 = 12 in base 10 = 1100 in base 2
2287 // so it needs 1 bit of padding to be the right bit length
2289 e
= Entropy
.fromString("20");
2290 if (e
.binaryStr
!= "01100") {
2291 return "Base 6 as binary has leading zeros";
2297 // Leading zeros for base 10 as binary string
2299 e
= Entropy
.fromString("17");
2300 if (e
.binaryStr
!= "010001") {
2301 return "Base 10 as binary has leading zeros";
2307 // Leading zeros for card entropy as binary string.
2308 // Card entropy is hashed so 2c does not necessarily produce leading zeros.
2310 e
= Entropy
.fromString("2c");
2311 if (e
.binaryStr
!= "0010") {
2312 return "Card entropy as binary has leading zeros";
2318 // Keyboard mashing results in weak entropy
2319 // Despite being a long string, it's less than 30 bits of entropy
2321 e
= Entropy
.fromString("aj;se ifj; ask,dfv js;ifj");
2322 if (e
.binaryStr
.length
>= 30) {
2323 return "Keyboard mashing should produce weak entropy";
2329 // Card entropy is used if every pair could be a card
2331 e
= Entropy
.fromString("4c3c2c");
2332 if (e
.base
.str
!= "card") {
2333 return "Card entropy not used if all pairs are cards";
2339 // Card entropy uses base 52
2340 // [ cards, binary ]
2344 [ "acqs", "11011100" ],
2345 [ "acks", "01011100" ],
2346 [ "2cac", "11111000" ],
2359 [ "ks2c", "01010100" ],
2360 [ "KS2C", "01010100" ],
2362 for (var i
=0; i
<cards
.length
; i
++) {
2363 var card
= cards
[i
][0];
2364 var result
= cards
[i
][1];
2365 e
= Entropy
.fromString(card
);
2366 console
.log(e
.binary
+ " " + result
);
2367 if (e
.binaryStr
!== result
) {
2368 return "card entropy " + card
+ " not parsed correctly: " + result
+ " != " + e
.binaryStr
;
2377 if (response
!= "PASS") {
2378 console
.log("Entropy unit tests");
2379 console
.log(response
);
2386 // Entropy can be entered by the user
2388 page
.open(url
, function(status
) {
2390 mnemonic: "abandon abandon ability",
2391 address: "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug",
2394 page
.evaluate(function() {
2395 $(".use-entropy").prop("checked", true).trigger("change");
2396 $(".entropy").val("00000000 00000000 00000000 00000000").trigger("input");
2398 // check the mnemonic is set and address is correct
2399 waitForGenerate(function() {
2400 var actual
= page
.evaluate(function() {
2402 address: $(".address:first").text(),
2403 mnemonic: $(".phrase").val(),
2406 if (actual
.mnemonic
!= expected
.mnemonic
) {
2407 console
.log("Entropy does not generate correct mnemonic");
2408 console
.log("Expected: " + expected
.mnemonic
);
2409 console
.log("Got: " + actual
.mnemonic
);
2412 if (actual
.address
!= expected
.address
) {
2413 console
.log("Entropy does not generate correct address");
2414 console
.log("Expected: " + expected
.address
);
2415 console
.log("Got: " + actual
.address
);
2423 // A warning about entropy is shown to the user, with additional information
2425 page
.open(url
, function(status
) {
2426 // get text content from entropy sections of page
2427 var hasWarning
= page
.evaluate(function() {
2428 var entropyText
= $(".entropy-container").text();
2429 var warning
= "mnemonic may be insecure";
2430 if (entropyText
.indexOf(warning
) == -1) {
2433 var readMoreText
= $("#entropy-notes").parent().text();
2434 var goodSources
= "flipping a fair coin, rolling a fair dice, noise measurements etc";
2435 if (readMoreText
.indexOf(goodSources
) == -1) {
2440 // check the warnings and information are shown
2442 console
.log("Page does not contain warning about using own entropy");
2449 // The types of entropy available are described to the user
2451 page
.open(url
, function(status
) {
2452 // get placeholder text for entropy field
2453 var placeholder
= page
.evaluate(function() {
2454 return $(".entropy").attr("placeholder");
2464 for (var i
=0; i
<options
.length
; i
++) {
2465 var option
= options
[i
];
2466 if (placeholder
.indexOf(option
) == -1) {
2467 console
.log("Available entropy type is not shown to user: " + option
);
2475 // The actual entropy used is shown to the user
2477 page
.open(url
, function(status
) {
2479 var badEntropySource
= page
.evaluate(function() {
2480 var entropy
= "Not A Very Good Entropy Source At All";
2481 $(".use-entropy").prop("checked", true).trigger("change");
2482 $(".entropy").val(entropy
).trigger("input");
2484 // check the actual entropy being used is shown
2485 waitForEntropyFeedback(function() {
2486 var expectedText
= "AedEceAA";
2487 var entropyText
= page
.evaluate(function() {
2488 return $(".entropy-container").text();
2490 if (entropyText
.indexOf(expectedText
) == -1) {
2491 console
.log("Actual entropy used is not shown");
2499 // Binary entropy can be entered
2501 page
.open(url
, function(status
) {
2503 page
.evaluate(function() {
2504 $(".use-entropy").prop("checked", true).trigger("change");
2505 $(".entropy").val("01").trigger("input");
2507 // check the entropy is shown to be the correct type
2508 waitForEntropyFeedback(function() {
2509 var entropyText
= page
.evaluate(function() {
2510 return $(".entropy-container").text();
2512 if (entropyText
.indexOf("binary") == -1) {
2513 console
.log("Binary entropy is not detected and presented to user");
2521 // Base 6 entropy can be entered
2523 page
.open(url
, function(status
) {
2525 page
.evaluate(function() {
2526 $(".use-entropy").prop("checked", true).trigger("change");
2527 $(".entropy").val("012345").trigger("input");
2529 // check the entropy is shown to be the correct type
2530 waitForEntropyFeedback(function() {
2531 var entropyText
= page
.evaluate(function() {
2532 return $(".entropy-container").text();
2534 if (entropyText
.indexOf("base 6") == -1) {
2535 console
.log("Base 6 entropy is not detected and presented to user");
2543 // Base 6 dice entropy can be entered
2545 page
.open(url
, function(status
) {
2547 page
.evaluate(function() {
2548 $(".use-entropy").prop("checked", true).trigger("change");
2549 $(".entropy").val("123456").trigger("input");
2551 // check the entropy is shown to be the correct type
2552 waitForEntropyFeedback(function() {
2553 var entropyText
= page
.evaluate(function() {
2554 return $(".entropy-container").text();
2556 if (entropyText
.indexOf("dice") == -1) {
2557 console
.log("Dice entropy is not detected and presented to user");
2565 // Base 10 entropy can be entered
2567 page
.open(url
, function(status
) {
2569 page
.evaluate(function() {
2570 $(".use-entropy").prop("checked", true).trigger("change");
2571 $(".entropy").val("789").trigger("input");
2573 // check the entropy is shown to be the correct type
2574 waitForEntropyFeedback(function() {
2575 var entropyText
= page
.evaluate(function() {
2576 return $(".entropy-container").text();
2578 if (entropyText
.indexOf("base 10") == -1) {
2579 console
.log("Base 10 entropy is not detected and presented to user");
2587 // Hexadecimal entropy can be entered
2589 page
.open(url
, function(status
) {
2591 page
.evaluate(function() {
2592 $(".use-entropy").prop("checked", true).trigger("change");
2593 $(".entropy").val("abcdef").trigger("input");
2595 // check the entropy is shown to be the correct type
2596 waitForEntropyFeedback(function() {
2597 var entropyText
= page
.evaluate(function() {
2598 return $(".entropy-container").text();
2600 if (entropyText
.indexOf("hexadecimal") == -1) {
2601 console
.log("Hexadecimal entropy is not detected and presented to user");
2609 // Dice entropy value is shown as the converted base 6 value
2611 page
.open(url
, function(status
) {
2613 page
.evaluate(function() {
2614 $(".use-entropy").prop("checked", true).trigger("change");
2615 $(".entropy").val("123456").trigger("input");
2617 // check the entropy is shown as base 6, not as the original dice value
2618 waitForEntropyFeedback(function() {
2619 var entropyText
= page
.evaluate(function() {
2620 return $(".entropy-container").text();
2622 if (entropyText
.indexOf("123450") == -1) {
2623 console
.log("Dice entropy is not shown to user as base 6 value");
2626 if (entropyText
.indexOf("123456") > -1) {
2627 console
.log("Dice entropy value is shown instead of true base 6 value");
2635 // The number of bits of entropy accumulated is shown
2637 page
.open(url
, function(status
) {
2640 [ "0000 0000 0000 0000 0000", "20" ],
2643 [ "6", "2" ], // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
2644 [ "7", "3" ], // 7 in base 10 is 111 in base 2, no leading zeros
2649 [ "1A", "8" ], // hex is always multiple of 4 bits of entropy
2656 [ "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)
2657 [ "2227", "13" ], // Uses base 10, which is 4 lots of 3.32 bits, which is 13.3 bits (rounded down to 13)
2660 [ "0000101017", "33" ], // 10 events at 3.32 bits per event
2661 [ "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
2664 page
.evaluate(function(e
) {
2665 $(".use-entropy").prop("checked", true).trigger("change");
2668 var nextTest
= function runNextTest(i
) {
2669 var entropy
= tests
[i
][0];
2670 var expected
= tests
[i
][1];
2672 page
.evaluate(function(e
) {
2673 $(".entropy").val(e
).trigger("input");
2675 // check the number of bits of entropy is shown
2676 waitForEntropyFeedback(function() {
2677 var entropyText
= page
.evaluate(function() {
2678 return $(".entropy-container").text();
2680 if (entropyText
.replace(/\s/g,"").indexOf("Bits" + expected
) == -1) {
2681 console
.log("Accumulated entropy is not shown correctly for " + entropy
);
2684 var isLastTest
= i
== tests
.length
- 1;
2697 // There is feedback provided about the supplied entropy
2699 page
.open(url
, function(status
) {
2704 type: "hexadecimal",
2708 strength: "extremely weak",
2711 entropy: "AAAAAAAA",
2712 filtered: "AAAAAAAA",
2713 type: "hexadecimal",
2717 strength: "extremely weak",
2720 entropy: "AAAAAAAA B",
2721 filtered: "AAAAAAAAB",
2722 type: "hexadecimal",
2726 strength: "extremely weak",
2729 entropy: "AAAAAAAA BBBBBBBB",
2730 filtered: "AAAAAAAABBBBBBBB",
2731 type: "hexadecimal",
2735 strength: "very weak",
2738 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
2739 filtered: "AAAAAAAABBBBBBBBCCCCCCCC",
2740 type: "hexadecimal",
2747 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
2748 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD",
2749 type: "hexadecimal",
2753 strength: "easily cracked",
2756 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA",
2757 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA",
2758 type: "hexadecimal",
2765 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE",
2766 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE",
2767 type: "hexadecimal",
2771 strength: "very strong",
2774 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF",
2775 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF",
2776 type: "hexadecimal",
2780 strength: "extremely strong",
2788 strength: "extremely weak",
2791 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2792 type: "card (full deck)",
2796 strength: "extremely strong",
2799 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d",
2800 type: "card (full deck, 1 duplicate: 3d)",
2804 strength: "extremely strong",
2807 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d",
2808 type: "card (2 duplicates: 3d 4d, 1 missing: KS)",
2812 strength: "extremely strong",
2815 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d",
2816 type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)",
2820 strength: "extremely strong",
2822 // Next test was throwing uncaught error in zxcvbn
2823 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
2825 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2826 type: "card (full deck, 52 duplicates: ac 2c 3c...)",
2830 strength: "extremely strong",
2832 // Case insensitivity to duplicate cards
2835 type: "card (1 duplicate: AS)",
2839 strength: "extremely weak",
2843 type: "card (1 duplicate: as)",
2847 strength: "extremely weak",
2849 // Missing cards are detected
2851 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2852 type: "card (1 missing: 9C)",
2856 strength: "extremely strong",
2859 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2860 type: "card (2 missing: 9C 5D)",
2864 strength: "extremely strong",
2867 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2868 type: "card (4 missing: 9C 5D QD...)",
2872 strength: "extremely strong",
2874 // More than six missing cards does not show message
2876 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks",
2881 strength: "extremely strong",
2883 // Multiple decks of cards increases bits per event
2903 entropy: "3d3d3d3d",
2909 entropy: "3d3d3d3d3d",
2915 entropy: "3d3d3d3d3d3d",
2921 entropy: "3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d",
2925 strength: 'easily cracked - Repeats like "abcabcabc" are only slightly harder to guess than "abc"',
2929 page
.evaluate(function() {
2930 $(".use-entropy").prop("checked", true).trigger("change");
2932 var nextTest
= function runNextTest(i
) {
2933 function getFeedbackError(expected
, actual
) {
2934 if ("filtered" in expected
&& actual
.indexOf(expected
.filtered
) == -1) {
2935 return "Filtered value not in feedback";
2937 if ("type" in expected
&& actual
.indexOf(expected
.type
) == -1) {
2938 return "Entropy type not in feedback";
2940 if ("events" in expected
&& actual
.indexOf(expected
.events
) == -1) {
2941 return "Event count not in feedback";
2943 if ("bits" in expected
&& actual
.indexOf(expected
.bits
) == -1) {
2944 return "Bit count not in feedback";
2946 if ("strength" in expected
&& actual
.indexOf(expected
.strength
) == -1) {
2947 return "Strength not in feedback";
2949 if ("bitsPerEvent" in expected
&& actual
.indexOf(expected
.bitsPerEvent
) == -1) {
2950 return "bitsPerEvent not in feedback";
2955 page
.evaluate(function(e
) {
2956 $(".addresses").empty();
2957 $(".phrase").val("");
2958 $(".entropy").val(e
).trigger("input");
2960 waitForEntropyFeedback(function() {
2961 var mnemonic
= page
.evaluate(function() {
2962 return $(".phrase").val();
2964 // Check mnemonic length
2965 if ("words" in test
&& test
.words
== 0) {
2966 if (mnemonic
.length
> 0) {
2967 console
.log("Mnemonic length for " + test
.strength
+ " strength is not " + test
.words
);
2968 console
.log("Entropy: " + test
.entropy
);
2969 console
.log("Mnemonic: " + mnemonic
);
2973 else if ("words" in test
) {
2974 if (mnemonic
.split(" ").length
!= test
.words
) {
2975 console
.log("Mnemonic length for " + test
.strength
+ " strength is not " + test
.words
);
2976 console
.log("Entropy: " + test
.entropy
);
2977 console
.log("Mnemonic: " + mnemonic
);
2982 var feedback
= page
.evaluate(function() {
2983 return $(".entropy-container").text();
2985 var feedbackError
= getFeedbackError(test
, feedback
);
2986 if (feedbackError
) {
2987 console
.log("Entropy feedback for " + test
.entropy
+ " returned error");
2988 console
.log(feedbackError
);
2992 var isLastTest
= i
== tests
.length
- 1;
3005 // Entropy is truncated from the left
3007 page
.open(url
, function(status
) {
3008 var expected
= "avocado zoo zone";
3010 page
.evaluate(function() {
3011 $(".use-entropy").prop("checked", true).trigger("change");
3012 var entropy
= "00000000 00000000 00000000 00000000";
3013 entropy
+= "11111111 11111111 11111111 1111"; // Missing last byte
3014 $(".entropy").val(entropy
).trigger("input");
3016 // check the entropy is truncated from the right
3017 waitForGenerate(function() {
3018 var actual
= page
.evaluate(function() {
3019 return $(".phrase").val();
3021 if (actual
!= expected
) {
3022 console
.log("Entropy is not truncated from the right");
3023 console
.log("Expected: " + expected
);
3024 console
.log("Got: " + actual
);
3032 // Very large entropy results in very long mnemonics
3034 page
.open(url
, function(status
) {
3036 page
.evaluate(function() {
3037 $(".use-entropy").prop("checked", true).trigger("change");
3039 // Generate a very long entropy string
3040 for (var i
=0; i
<33; i
++) {
3041 entropy
+= "AAAAAAAA"; // 3 words * 33 iterations = 99 words
3043 $(".entropy").val(entropy
).trigger("input");
3045 // check the mnemonic is very long
3046 waitForGenerate(function() {
3047 var wordCount
= page
.evaluate(function() {
3048 return $(".phrase").val().split(" ").length
;
3050 if (wordCount
!= 99) {
3051 console
.log("Large entropy does not generate long mnemonic");
3052 console
.log("Expected 99 words, got " + wordCount
);
3060 // Is compatible with bip32jp entropy
3061 // https://bip32jp.github.io/english/index.html
3063 // Is incompatible with:
3066 page
.open(url
, function(status
) {
3067 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";
3069 page
.evaluate(function() {
3070 $(".use-entropy").prop("checked", true).trigger("change");
3071 var entropy
= "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543";
3072 $(".entropy").val(entropy
).trigger("input");
3074 // check the mnemonic matches the expected value from bip32jp
3075 waitForGenerate(function() {
3076 var actual
= page
.evaluate(function() {
3077 return $(".phrase").val();
3079 if (actual
!= expected
) {
3080 console
.log("Mnemonic does not match bip32jp for base 6 entropy");
3081 console
.log("Expected: " + expected
);
3082 console
.log("Got: " + actual
);
3090 // Blank entropy does not generate mnemonic or addresses
3092 page
.open(url
, function(status
) {
3094 page
.evaluate(function() {
3095 $(".use-entropy").prop("checked", true).trigger("change");
3096 $(".entropy").val("").trigger("input");
3098 waitForFeedback(function() {
3099 // check there is no mnemonic
3100 var phrase
= page
.evaluate(function() {
3101 return $(".phrase").val();
3104 console
.log("Blank entropy does not result in blank mnemonic");
3105 console
.log("Got: " + phrase
);
3108 // check there are no addresses displayed
3109 var addresses
= page
.evaluate(function() {
3110 return $(".address").length
;
3112 if (addresses
!= 0) {
3113 console
.log("Blank entropy does not result in zero addresses");
3116 // Check the feedback says 'blank entropy'
3117 var feedback
= page
.evaluate(function() {
3118 return $(".feedback").text();
3120 if (feedback
!= "Blank entropy") {
3121 console
.log("Blank entropy does not show feedback message");
3129 // Mnemonic length can be selected even for weak entropy
3131 page
.open(url
, function(status
) {
3133 page
.evaluate(function() {
3134 $(".use-entropy").prop("checked", true).trigger("change");
3135 $(".entropy").val("012345");
3136 $(".mnemonic-length").val("18").trigger("change");
3138 // check the mnemonic is the correct length
3139 waitForGenerate(function() {
3140 var phrase
= page
.evaluate(function() {
3141 return $(".phrase").val();
3143 var numberOfWords
= phrase
.split(/\s/g).length
;
3144 if (numberOfWords
!= 18) {
3145 console
.log("Weak entropy cannot be overridden to give 18 word mnemonic");
3146 console
.log(phrase
);
3155 // https://github.com/iancoleman/bip39/issues/33
3156 // Final cards should contribute entropy
3158 page
.open(url
, function(status
) {
3160 page
.evaluate(function() {
3161 $(".use-entropy").prop("checked", true).trigger("change");
3162 $(".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");
3165 waitForGenerate(function() {
3166 var originalPhrase
= page
.evaluate(function() {
3167 return $(".phrase").val();
3169 // Set the last 12 cards to be AS
3170 page
.evaluate(function() {
3171 $(".addresses").empty();
3172 $(".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");
3174 // get the new mnemonic
3175 waitForGenerate(function() {
3176 var newPhrase
= page
.evaluate(function() {
3177 return $(".phrase").val();
3179 // check the phrase has changed
3180 if (newPhrase
== originalPhrase
) {
3181 console
.log("Changing last 12 cards does not change mnemonic");
3182 console
.log("Original:");
3183 console
.log(originalPhrase
);
3184 console
.log("New:");
3185 console
.log(newPhrase
);
3195 // https://github.com/iancoleman/bip39/issues/35
3198 page
.open(url
, function(status
) {
3200 page
.evaluate(function() {
3201 $(".generate").click();
3203 waitForGenerate(function() {
3204 var p
= page
.evaluate(function() {
3205 // get position of mnemonic element
3206 return $(".phrase").offset();
3208 p
.top
= Math
.ceil(p
.top
);
3209 p
.left
= Math
.ceil(p
.left
);
3210 // check the qr code shows
3211 page
.sendEvent("mousemove", p
.left
+4, p
.top
+4);
3212 var qrShowing
= page
.evaluate(function() {
3213 return $(".qr-container").find("canvas").length
> 0;
3216 console
.log("QR Code does not show");
3219 // check the qr code hides
3220 page
.sendEvent("mousemove", p
.left
-4, p
.top
-4);
3221 var qrHidden
= page
.evaluate(function() {
3222 return $(".qr-container").find("canvas").length
== 0;
3225 console
.log("QR Code does not hide");
3233 // BIP44 account extendend private key is shown
3234 // github issue 37 - compatibility with electrum
3236 page
.open(url
, function(status
) {
3238 var expected
= "xprv9yzrnt4zWVJUr1k2VxSPy9ettKz5PpeDMgaVG7UKedhqnw1tDkxP2UyYNhuNSumk2sLE5ctwKZs9vwjsq3e1vo9egCK6CzP87H2cVYXpfwQ";
3239 page
.evaluate(function() {
3240 $(".phrase").val("abandon abandon ability");
3241 $(".phrase").trigger("input");
3243 // check the BIP44 account extended private key
3244 waitForGenerate(function() {
3245 var actual
= page
.evaluate(function() {
3246 return $(".account-xprv").val();
3248 if (actual
!= expected
) {
3249 console
.log("BIP44 account extended private key is incorrect");
3250 console
.log("Expected: " + expected
);
3251 console
.log("Actual: " + actual
);
3259 // BIP44 account extendend public key is shown
3260 // github issue 37 - compatibility with electrum
3262 page
.open(url
, function(status
) {
3264 var expected
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3265 page
.evaluate(function() {
3266 $(".phrase").val("abandon abandon ability");
3267 $(".phrase").trigger("input");
3269 // check the BIP44 account extended public key
3270 waitForGenerate(function() {
3271 var actual
= page
.evaluate(function() {
3272 return $(".account-xpub").val();
3274 if (actual
!= expected
) {
3275 console
.log("BIP44 account extended public key is incorrect");
3276 console
.log("Expected: " + expected
);
3277 console
.log("Actual: " + actual
);
3286 // BIP32 root key can be set as an xpub
3288 page
.open(url
, function(status
) {
3290 page
.evaluate(function() {
3291 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3292 var bip44AccountXpub
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3293 $("#root-key").val(bip44AccountXpub
);
3294 $("#root-key").trigger("input");
3296 waitForFeedback(function() {
3297 page
.evaluate(function() {
3299 $("#bip32-tab a").click();
3301 waitForGenerate(function() {
3302 page
.evaluate(function() {
3303 // derive external addresses for this xpub
3304 var firstAccountDerivationPath
= "m/0";
3305 $("#bip32-path").val(firstAccountDerivationPath
);
3306 $("#bip32-path").trigger("input");
3308 waitForGenerate(function() {
3309 // check the addresses are generated
3310 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3311 var actual
= page
.evaluate(function() {
3312 return $(".address:first").text();
3314 if (actual
!= expected
) {
3315 console
.log("xpub key does not generate addresses in table");
3316 console
.log("Expected: " + expected
);
3317 console
.log("Actual: " + actual
);
3320 // check the xprv key is not set
3321 var expected
= "NA";
3322 var actual
= page
.evaluate(function() {
3323 return $(".extended-priv-key").val();
3325 if (actual
!= expected
) {
3326 console
.log("xpub key as root shows derived bip32 xprv key");
3327 console
.log("Expected: " + expected
);
3328 console
.log("Actual: " + actual
);
3331 // check the private key is not set
3332 var expected
= "NA";
3333 var actual
= page
.evaluate(function() {
3334 return $(".privkey:first").text();
3336 if (actual
!= expected
) {
3337 console
.log("xpub key generates private key in addresses table");
3338 console
.log("Expected: " + expected
);
3339 console
.log("Actual: " + actual
);
3350 // xpub for bip32 root key will not work with hardened derivation paths
3352 page
.open(url
, function(status
) {
3354 page
.evaluate(function() {
3355 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3356 var bip44AccountXpub
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3357 $("#root-key").val(bip44AccountXpub
);
3358 $("#root-key").trigger("input");
3360 waitForFeedback(function() {
3361 // Check feedback is correct
3362 var expected
= "Hardened derivation path is invalid with xpub key";
3363 var actual
= page
.evaluate(function() {
3364 return $(".feedback").text();
3366 if (actual
!= expected
) {
3367 console
.log("xpub key with hardened derivation path does not show feedback");
3368 console
.log("Expected: " + expected
);
3369 console
.log("Actual: " + actual
);
3372 // Check no addresses are shown
3374 var actual
= page
.evaluate(function() {
3375 return $(".addresses tr").length
;
3377 if (actual
!= expected
) {
3378 console
.log("addresses still show after setting xpub key with hardened derivation path");
3379 console
.log("Expected: " + expected
);
3380 console
.log("Actual: " + actual
);
3389 // no root key shows feedback
3391 page
.open(url
, function(status
) {
3392 // click the bip32 tab on fresh page
3393 page
.evaluate(function() {
3394 $("#bip32-tab a").click();
3396 waitForFeedback(function() {
3397 // Check feedback is correct
3398 var expected
= "No root key";
3399 var actual
= page
.evaluate(function() {
3400 return $(".feedback").text();
3402 if (actual
!= expected
) {
3403 console
.log("Blank root key not detected");
3404 console
.log("Expected: " + expected
);
3405 console
.log("Actual: " + actual
);
3414 // display error switching tabs while addresses are generating
3416 page
.open(url
, function(status
) {
3418 page
.evaluate(function() {
3419 $(".phrase").val("abandon abandon ability").trigger("input");
3421 waitForGenerate(function() {
3422 // set to generate 500 more addresses
3423 // generate more addresses
3424 // change tabs which should cancel the previous generating
3425 page
.evaluate(function() {
3426 $(".rows-to-add").val("100");
3428 $("#bip32-tab a").click();
3430 // check the derivation paths are in order and of the right quantity
3431 waitForGenerate(function() {
3432 var paths
= page
.evaluate(function() {
3433 return $(".index").map(function(i
, e
) {
3437 for (var i
=0; i
<paths
.length
; i
++) {
3438 var expected
= "m/0/" + i
;
3439 var actual
= paths
[i
];
3440 if (actual
!= expected
) {
3441 console
.log("Path " + i
+ " is not in correct order");
3442 console
.log("Expected: " + expected
);
3443 console
.log("Actual: " + actual
);
3447 if (paths
.length
!= 20) {
3448 console
.log("Generation was not cancelled by new action");
3458 // padding for binary should give length with multiple of 256
3459 // hashed entropy 1111 is length 252, so requires 4 leading zeros
3460 // prior to issue 49 it would only generate 2 leading zeros, ie missing 2
3462 page
.open(url
, function(status
) {
3463 expected
= "avocado valid quantum cross link predict excuse edit street able flame large galaxy ginger nuclear"
3465 page
.evaluate(function() {
3466 $(".use-entropy").prop("checked", true).trigger("change");
3467 $(".mnemonic-length").val("15");
3468 $(".entropy").val("1111").trigger("input");
3470 waitForGenerate(function() {
3472 var actual
= page
.evaluate(function() {
3473 return $(".phrase").val();
3475 // check the mnemonic is correct
3476 if (actual
!= expected
) {
3477 console
.log("Left padding error for entropy");
3478 console
.log("Expected: " + expected
);
3479 console
.log("Actual: " + actual
);
3487 // Github pull request 55
3488 // https://github.com/iancoleman/bip39/pull/55
3491 page
.open(url
, function(status
) {
3492 // set mnemonic and select bip32 tab
3493 page
.evaluate(function() {
3494 $("#bip32-tab a").click();
3495 $(".phrase").val("abandon abandon ability").trigger("input");
3497 waitForGenerate(function() {
3499 // set bip32 client to bitcoin core
3500 page
.evaluate(function() {
3501 var bitcoinCoreIndex
= "0";
3502 $("#bip32-client").val(bitcoinCoreIndex
).trigger("change");
3504 waitForGenerate(function() {
3505 // get the derivation path
3506 var actual
= page
.evaluate(function() {
3507 return $("#bip32-path").val();
3509 // check the derivation path is correct
3510 expected
= "m/0'/0'"
3511 if (actual
!= expected
) {
3512 console
.log("Selecting Bitcoin Core client does not set correct derivation path");
3513 console
.log("Expected: " + expected
);
3514 console
.log("Actual: " + actual
);
3517 // get hardened addresses
3518 var usesHardenedAddresses
= page
.evaluate(function() {
3519 return $(".hardened-addresses").prop("checked");
3521 // check hardened addresses is selected
3522 if(!usesHardenedAddresses
) {
3523 console
.log("Selecting Bitcoin Core client does not use hardened addresses");
3526 // check input is readonly
3527 var pathIsReadonly
= page
.evaluate(function() {
3528 return $("#bip32-path").prop("readonly");
3530 if (!pathIsReadonly
) {
3531 console
.log("Selecting Bitcoin Core client does not set derivation path to readonly");
3535 // set bip32 client to multibit
3536 page
.evaluate(function() {
3537 var multibitIndex
= "2";
3538 $("#bip32-client").val(multibitIndex
).trigger("change");
3540 waitForGenerate(function() {
3541 // get the derivation path
3542 var actual
= page
.evaluate(function() {
3543 return $("#bip32-path").val();
3545 // check the derivation path is correct
3547 if (actual
!= expected
) {
3548 console
.log("Selecting Multibit client does not set correct derivation path");
3549 console
.log("Expected: " + expected
);
3550 console
.log("Actual: " + actual
);
3553 // get hardened addresses
3554 var usesHardenedAddresses
= page
.evaluate(function() {
3555 return $(".hardened-addresses").prop("checked");
3557 // check hardened addresses is selected
3558 if(usesHardenedAddresses
) {
3559 console
.log("Selecting Multibit client does not uncheck hardened addresses");
3562 // CUSTOM DERIVATION PATH
3563 // check input is not readonly
3564 page
.evaluate(function() {
3565 $("#bip32-client").val("custom").trigger("change");
3567 // do not wait for generate, since there is no change to the
3568 // derivation path there is no new generation performed
3569 var pathIsReadonly
= page
.evaluate(function() {
3570 return $("#bip32-path").prop("readonly");
3572 if (pathIsReadonly
) {
3573 console
.log("Selecting Custom Derivation Path does not allow derivation path input");
3584 // https://github.com/iancoleman/bip39/issues/58
3585 // bip32 derivation is correct, does not drop leading zeros
3587 // https://medium.com/@alexberegszaszi/why-do-my-bip32-wallets-disagree-6f3254cc5846
3589 page
.open(url
, function(status
) {
3590 // set the phrase and passphrase
3591 var expected
= "17rxURoF96VhmkcEGCj5LNQkmN9HVhWb7F";
3592 // Note that bitcore generates an incorrect address
3593 // 13EuKhffWkBE2KUwcbkbELZb1MpzbimJ3Y
3594 // see the medium.com link above for more details
3595 page
.evaluate(function() {
3596 $(".phrase").val("fruit wave dwarf banana earth journey tattoo true farm silk olive fence");
3597 $(".passphrase").val("banana").trigger("input");
3599 // check the address is generated correctly
3600 waitForGenerate(function() {
3601 var actual
= page
.evaluate(function() {
3602 return $(".address:first").text();
3604 if (actual
!= expected
) {
3605 console
.log("BIP32 derivation is incorrect");
3606 console
.log("Expected: " + expected
);
3607 console
.log("Actual: " + actual
);
3617 // Japanese mnemonics generate incorrect bip32 seed
3618 // BIP39 seed is set from phrase
3620 page
.open(url
, function(status
) {
3622 var expected
= "a262d6fb6122ecf45be09c50492b31f92e9beb7d9a845987a02cefda57a15f9c467a17872029a9e92299b5cbdf306e3a0ee620245cbd508959b6cb7ca637bd55";
3623 page
.evaluate(function() {
3624 $(".phrase").val("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら");
3625 $("#passphrase").val("メートルガバヴァぱばぐゞちぢ十人十色");
3626 $("#passphrase").trigger("input");
3628 // check the seed is generated correctly
3629 waitForGenerate(function() {
3630 var actual
= page
.evaluate(function() {
3631 return $(".seed").val();
3633 if (actual
!= expected
) {
3634 console
.log("BIP39 seed is incorrectly generated from Japanese mnemonic");
3635 console
.log("Expected: " + expected
);
3636 console
.log("Actual: " + actual
);
3644 // If you wish to add more tests, do so here...
3646 // Here is a blank test template
3650 page.open(url, function(status) {
3651 // Do something on the page
3652 page.evaluate(function() {
3653 $(".phrase").val("abandon abandon ability").trigger("input");
3655 waitForGenerate(function() {
3656 // Check the result of doing the thing
3657 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3658 var actual = page.evaluate(function() {
3659 return $(".address:first").text();
3661 if (actual != expected) {
3662 console.log("A specific message about what failed");
3663 console.log("Expected: " + expected);
3664 console.log("Actual: " + actual);
3667 // Run the next test
3677 console
.log("Running tests...");
3678 tests
= shuffle(tests
);