]>
git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blob - tests.js
9abe573a0c1222478d16f35b13ac8caad0c3ebca
2 // $ phantomjs tests.js
5 var page
= require('webpage').create();
6 var url
= 'src/index.html';
7 var testMaxTime
= 5000;
9 page
.onResourceError = function(e
) {
10 console
.log("Error loading " + e
.url
);
15 console
.log("Failed");
19 function waitForGenerate(fn
, maxTime
) {
21 maxTime
= testMaxTime
;
23 var start
= new Date().getTime();
24 var prevAddressCount
= -1;
25 var wait
= function keepWaiting() {
26 var now
= new Date().getTime();
27 var hasTimedOut
= now
- start
> maxTime
;
28 var addressCount
= page
.evaluate(function() {
29 return $(".address").length
;
31 var hasFinished
= addressCount
> 0 && addressCount
== prevAddressCount
;
32 prevAddressCount
= addressCount
;
36 else if (hasTimedOut
) {
37 console
.log("Test timed out");
41 setTimeout(keepWaiting
, 100);
47 function waitForFeedback(fn
, maxTime
) {
49 maxTime
= testMaxTime
;
51 var start
= new Date().getTime();
52 var wait
= function keepWaiting() {
53 var now
= new Date().getTime();
54 var hasTimedOut
= now
- start
> maxTime
;
56 console
.log("Test timed out");
60 var feedback
= page
.evaluate(function() {
61 var feedback
= $(".feedback");
62 if (feedback
.css("display") == "none") {
65 return feedback
.text();
67 var hasFinished
= feedback
.length
> 0 && feedback
!= "Calculating...";
72 setTimeout(keepWaiting
, 100);
78 function waitForEntropyFeedback(fn
, maxTime
) {
80 maxTime
= testMaxTime
;
82 var origFeedback
= page
.evaluate(function() {
83 return $(".entropy-container").text();
85 var start
= new Date().getTime();
86 var wait
= function keepWaiting() {
87 var now
= new Date().getTime();
88 var hasTimedOut
= now
- start
> maxTime
;
90 console
.log("Test timed out");
94 var feedback
= page
.evaluate(function() {
95 return $(".entropy-container").text();
97 var hasFinished
= feedback
!= origFeedback
;
102 setTimeout(keepWaiting
, 100);
109 if (tests
.length
> 0) {
110 var testsStr
= tests
.length
== 1 ? "test" : "tests";
111 console
.log(tests
.length
+ " " + testsStr
+ " remaining");
115 console
.log("Finished with 0 failures");
121 * Randomize array element order in-place.
122 * Using Durstenfeld shuffle algorithm.
123 * See http://stackoverflow.com/a/12646864
125 function shuffle(array
) {
126 for (var i
= array
.length
- 1; i
> 0; i
--) {
127 var j
= Math
.floor(Math
.random() * (i
+ 1));
137 // Page loads with status of 'success'
139 page
.open(url
, function(status
) {
140 if (status
!= "success") {
141 console
.log("Page did not load with status 'success'");
150 page
.open(url
, function(status
) {
151 var content
= page
.evaluate(function() {
152 return document
.body
.textContent
.trim();
155 console
.log("Page does not have text");
162 // Entering mnemonic generates addresses
164 page
.open(url
, function(status
) {
166 page
.evaluate(function() {
167 $(".phrase").val("abandon abandon ability").trigger("input");
170 waitForGenerate(function() {
171 var addressCount
= page
.evaluate(function() {
172 return $(".address").length
;
174 if (addressCount
!= 20) {
175 console
.log("Mnemonic did not generate addresses");
176 console
.log("Expected: " + expected
);
177 console
.log("Got: " + actual
);
185 // Random button generates random mnemonic
187 page
.open(url
, function(status
) {
188 // check initial phrase is empty
189 var phrase
= page
.evaluate(function() {
190 return $(".phrase").text();
193 console
.log("Initial phrase is not blank");
196 // press the 'generate' button
197 page
.evaluate(function() {
198 $(".generate").click();
200 // get the new phrase
201 waitForGenerate(function() {
202 var phrase
= page
.evaluate(function() {
203 return $(".phrase").val();
205 if (phrase
.length
<= 0) {
206 console
.log("Phrase not generated by pressing button");
214 // Mnemonic length can be customized
216 page
.open(url
, function(status
) {
217 // set the length to 6
218 var expectedLength
= "6";
219 page
.evaluate(function() {
220 $(".strength option[selected]").removeAttr("selected");
221 $(".strength option[value=6]").prop("selected", true);
223 // press the 'generate' button
224 page
.evaluate(function() {
225 $(".generate").click();
227 // check the new phrase is six words long
228 waitForGenerate(function() {
229 var actualLength
= page
.evaluate(function() {
230 var words
= $(".phrase").val().split(" ");
233 if (actualLength
!= expectedLength
) {
234 console
.log("Phrase not generated with correct length");
235 console
.log("Expected: " + expectedLength
);
236 console
.log("Actual: " + actualLength
);
244 // Passphrase can be set
246 page
.open(url
, function(status
) {
247 // set the phrase and passphrase
248 var expected
= "15pJzUWPGzR7avffV9nY5by4PSgSKG9rba";
249 page
.evaluate(function() {
250 $(".phrase").val("abandon abandon ability");
251 $(".passphrase").val("secure_passphrase").trigger("input");
253 // check the address is generated correctly
254 waitForGenerate(function() {
255 var actual
= page
.evaluate(function() {
256 return $(".address:first").text();
258 if (actual
!= expected
) {
259 console
.log("Passphrase results in wrong address");
260 console
.log("Expected: " + expected
);
261 console
.log("Actual: " + actual
);
269 // Network can be set to bitcoin testnet
271 page
.open(url
, function(status
) {
272 // set the phrase and coin
273 var expected
= "mucaU5iiDaJDb69BHLeDv8JFfGiyg2nJKi";
274 page
.evaluate(function() {
275 $(".phrase").val("abandon abandon ability");
276 $(".phrase").trigger("input");
277 $(".network option[selected]").removeAttr("selected");
278 $(".network option[value=1]").prop("selected", true);
279 $(".network").trigger("change");
281 // check the address is generated correctly
282 waitForGenerate(function() {
283 var actual
= page
.evaluate(function() {
284 return $(".address:first").text();
286 if (actual
!= expected
) {
287 console
.log("Bitcoin testnet address is incorrect");
288 console
.log("Expected: " + expected
);
289 console
.log("Actual: " + actual
);
297 // Network can be set to litecoin
299 page
.open(url
, function(status
) {
300 // set the phrase and coin
301 var expected
= "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn";
302 page
.evaluate(function() {
303 $(".phrase").val("abandon abandon ability");
304 $(".phrase").trigger("input");
305 $(".network option[selected]").removeAttr("selected");
306 $(".network option[value=2]").prop("selected", true);
307 $(".network").trigger("change");
309 // check the address is generated correctly
310 waitForGenerate(function() {
311 var actual
= page
.evaluate(function() {
312 return $(".address:first").text();
314 if (actual
!= expected
) {
315 console
.log("Litecoin address is incorrect");
316 console
.log("Expected: " + expected
);
317 console
.log("Actual: " + actual
);
325 // Network can be set to dogecoin
327 page
.open(url
, function(status
) {
328 // set the phrase and coin
329 var expected
= "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA";
330 page
.evaluate(function() {
331 $(".phrase").val("abandon abandon ability");
332 $(".phrase").trigger("input");
333 $(".network option[selected]").removeAttr("selected");
334 $(".network option[value=3]").prop("selected", true);
335 $(".network").trigger("change");
337 // check the address is generated correctly
338 waitForGenerate(function() {
339 var actual
= page
.evaluate(function() {
340 return $(".address:first").text();
342 if (actual
!= expected
) {
343 console
.log("Dogecoin address is incorrect");
344 console
.log("Expected: " + expected
);
345 console
.log("Actual: " + actual
);
353 // Network can be set to shadowcash
355 page
.open(url
, function(status
) {
356 // set the phrase and coin
357 var expected
= "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG";
358 page
.evaluate(function() {
359 $(".phrase").val("abandon abandon ability");
360 $(".phrase").trigger("input");
361 $(".network option[selected]").removeAttr("selected");
362 $(".network option[value=4]").prop("selected", true);
363 $(".network").trigger("change");
365 // check the address is generated correctly
366 waitForGenerate(function() {
367 var actual
= page
.evaluate(function() {
368 return $(".address:first").text();
370 if (actual
!= expected
) {
371 console
.log("Shadowcash address is incorrect");
372 console
.log("Expected: " + expected
);
373 console
.log("Actual: " + actual
);
381 // Network can be set to shadowcash testnet
383 page
.open(url
, function(status
) {
384 // set the phrase and coin
385 var expected
= "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
386 page
.evaluate(function() {
387 $(".phrase").val("abandon abandon ability");
388 $(".phrase").trigger("input");
389 $(".network option[selected]").removeAttr("selected");
390 $(".network option[value=5]").prop("selected", true);
391 $(".network").trigger("change");
393 // check the address is generated correctly
394 waitForGenerate(function() {
395 var actual
= page
.evaluate(function() {
396 return $(".address:first").text();
398 if (actual
!= expected
) {
399 console
.log("Shadowcash testnet address is incorrect");
400 console
.log("Expected: " + expected
);
401 console
.log("Actual: " + actual
);
409 // Network can be set to viacoin
411 page
.open(url
, function(status
) {
412 // set the phrase and coin
413 var expected
= "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
414 page
.evaluate(function() {
415 $(".phrase").val("abandon abandon ability");
416 $(".phrase").trigger("input");
417 $(".network option[selected]").removeAttr("selected");
418 $(".network option[value=6]").prop("selected", true);
419 $(".network").trigger("change");
421 // check the address is generated correctly
422 waitForGenerate(function() {
423 var actual
= page
.evaluate(function() {
424 return $(".address:first").text();
426 if (actual
!= expected
) {
427 console
.log("Viacoin address is incorrect");
428 console
.log("Expected: " + expected
);
429 console
.log("Actual: " + actual
);
437 // Network can be set to viacoin testnet
439 page
.open(url
, function(status
) {
440 // set the phrase and coin
441 var expected
= "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
442 page
.evaluate(function() {
443 $(".phrase").val("abandon abandon ability");
444 $(".phrase").trigger("input");
445 $(".network option[selected]").removeAttr("selected");
446 $(".network option[value=7]").prop("selected", true);
447 $(".network").trigger("change");
449 // check the address is generated correctly
450 waitForGenerate(function() {
451 var actual
= page
.evaluate(function() {
452 return $(".address:first").text();
454 if (actual
!= expected
) {
455 console
.log("Viacoin testnet address is incorrect");
456 console
.log("Expected: " + expected
);
457 console
.log("Actual: " + actual
);
465 // Network can be set to jumbucks
467 page
.open(url
, function(status
) {
468 // set the phrase and coin
469 var expected
= "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew";
470 page
.evaluate(function() {
471 $(".phrase").val("abandon abandon ability");
472 $(".phrase").trigger("input");
473 $(".network option[selected]").removeAttr("selected");
474 $(".network option[value=8]").prop("selected", true);
475 $(".network").trigger("change");
477 // check the address is generated correctly
478 waitForGenerate(function() {
479 var actual
= page
.evaluate(function() {
480 return $(".address:first").text();
482 if (actual
!= expected
) {
483 console
.log("Jumbucks address is incorrect");
484 console
.log("Expected: " + expected
);
485 console
.log("Actual: " + actual
);
493 // Network can be set to clam
495 page
.open(url
, function(status
) {
496 // set the phrase and coin
497 var expected
= "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y";
498 page
.evaluate(function() {
499 $(".phrase").val("abandon abandon ability");
500 $(".phrase").trigger("input");
501 $(".network option[selected]").removeAttr("selected");
502 $(".network option[value=9]").prop("selected", true);
503 $(".network").trigger("change");
505 // check the address is generated correctly
506 waitForGenerate(function() {
507 var actual
= page
.evaluate(function() {
508 return $(".address:first").text();
510 if (actual
!= expected
) {
511 console
.log("CLAM address is incorrect");
512 console
.log("Expected: " + expected
);
513 console
.log("Actual: " + actual
);
521 // Network can be set to dash
523 page
.open(url
, function(status
) {
524 // set the phrase and coin
525 var expected
= "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT";
526 page
.evaluate(function() {
527 $(".phrase").val("abandon abandon ability");
528 $(".phrase").trigger("input");
529 $(".network option[selected]").removeAttr("selected");
530 $(".network option[value=10]").prop("selected", true);
531 $(".network").trigger("change");
533 // check the address is generated correctly
534 waitForGenerate(function() {
535 var actual
= page
.evaluate(function() {
536 return $(".address:first").text();
538 if (actual
!= expected
) {
539 console
.log("DASH address is incorrect");
540 console
.log("Expected: " + expected
);
541 console
.log("Actual: " + actual
);
549 // Network can be set to namecoin
551 page
.open(url
, function(status
) {
552 // set the phrase and coin
553 var expected
= "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2";
554 page
.evaluate(function() {
555 $(".phrase").val("abandon abandon ability");
556 $(".phrase").trigger("input");
557 $(".network option[selected]").removeAttr("selected");
558 $(".network option[value=11]").prop("selected", true);
559 $(".network").trigger("change");
561 // check the address is generated correctly
562 waitForGenerate(function() {
563 var actual
= page
.evaluate(function() {
564 return $(".address:first").text();
566 if (actual
!= expected
) {
567 console
.log("Namecoin address is incorrect");
568 console
.log("Expected: " + expected
);
569 console
.log("Actual: " + actual
);
577 // Network can be set to peercoin
579 page
.open(url
, function(status
) {
580 // set the phrase and coin
581 var expected
= "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm";
582 page
.evaluate(function() {
583 $(".phrase").val("abandon abandon ability");
584 $(".phrase").trigger("input");
585 $(".network option[selected]").removeAttr("selected");
586 $(".network option[value=12]").prop("selected", true);
587 $(".network").trigger("change");
589 // check the address is generated correctly
590 waitForGenerate(function() {
591 var actual
= page
.evaluate(function() {
592 return $(".address:first").text();
594 if (actual
!= expected
) {
595 console
.log("Peercoin address is incorrect");
596 console
.log("Expected: " + expected
);
597 console
.log("Actual: " + actual
);
605 // BIP39 seed is set from phrase
607 page
.open(url
, function(status
) {
609 var expected
= "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
610 page
.evaluate(function() {
611 $(".phrase").val("abandon abandon ability");
612 $(".phrase").trigger("input");
614 // check the address is generated correctly
615 waitForGenerate(function() {
616 var actual
= page
.evaluate(function() {
617 return $(".seed").val();
619 if (actual
!= expected
) {
620 console
.log("BIP39 seed is incorrectly generated from mnemonic");
621 console
.log("Expected: " + expected
);
622 console
.log("Actual: " + actual
);
630 // BIP32 root key is set from phrase
632 page
.open(url
, function(status
) {
634 var expected
= "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi";
635 page
.evaluate(function() {
636 $(".phrase").val("abandon abandon ability");
637 $(".phrase").trigger("input");
639 // check the address is generated correctly
640 waitForGenerate(function() {
641 var actual
= page
.evaluate(function() {
642 return $(".root-key").val();
644 if (actual
!= expected
) {
645 console
.log("Root key is incorrectly generated from mnemonic");
646 console
.log("Expected: " + expected
);
647 console
.log("Actual: " + actual
);
655 // Tabs show correct addresses when changed
657 page
.open(url
, function(status
) {
659 var expected
= "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz";
660 page
.evaluate(function() {
661 $(".phrase").val("abandon abandon ability");
662 $(".phrase").trigger("input");
665 waitForGenerate(function() {
666 page
.evaluate(function() {
667 $("#bip32-tab a").click();
669 // check the address is generated correctly
670 waitForGenerate(function() {
671 var actual
= page
.evaluate(function() {
672 return $(".address:first").text();
674 if (actual
!= expected
) {
675 console
.log("Clicking tab generates incorrect address");
676 console
.log("Expected: " + expected
);
677 console
.log("Actual: " + actual
);
686 // BIP44 derivation path is shown
688 page
.open(url
, function(status
) {
690 var expected
= "m/44'/0'/0'/0";
691 page
.evaluate(function() {
692 $(".phrase").val("abandon abandon ability");
693 $(".phrase").trigger("input");
695 // check the derivation path of the first address
696 waitForGenerate(function() {
697 var actual
= page
.evaluate(function() {
698 return $("#bip44 .path").val();
700 if (actual
!= expected
) {
701 console
.log("BIP44 derivation path is incorrect");
702 console
.log("Expected: " + expected
);
703 console
.log("Actual: " + actual
);
711 // BIP44 extended private key is shown
713 page
.open(url
, function(status
) {
715 var expected
= "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG";
716 page
.evaluate(function() {
717 $(".phrase").val("abandon abandon ability");
718 $(".phrase").trigger("input");
720 // check the BIP44 extended private key
721 waitForGenerate(function() {
722 var actual
= page
.evaluate(function() {
723 return $(".extended-priv-key").val();
725 if (actual
!= expected
) {
726 console
.log("BIP44 extended private key is incorrect");
727 console
.log("Expected: " + expected
);
728 console
.log("Actual: " + actual
);
736 // BIP44 extended public key is shown
738 page
.open(url
, function(status
) {
740 var expected
= "xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM";
741 page
.evaluate(function() {
742 $(".phrase").val("abandon abandon ability");
743 $(".phrase").trigger("input");
745 // check the BIP44 extended public key
746 waitForGenerate(function() {
747 var actual
= page
.evaluate(function() {
748 return $(".extended-pub-key").val();
750 if (actual
!= expected
) {
751 console
.log("BIP44 extended public key is incorrect");
752 console
.log("Expected: " + expected
);
753 console
.log("Actual: " + actual
);
761 // BIP44 purpose field changes address list
763 page
.open(url
, function(status
) {
765 var expected
= "1JbDzRJ2cDT8aat2xwKd6Pb2zzavow5MhF";
766 page
.evaluate(function() {
767 $(".phrase").val("abandon abandon ability");
768 $(".phrase").trigger("input");
770 waitForGenerate(function() {
771 // change the bip44 purpose field to 45
772 page
.evaluate(function() {
773 $("#bip44 .purpose").val("45");
774 $("#bip44 .purpose").trigger("input");
776 waitForGenerate(function() {
777 // check the address for the new derivation path
778 var actual
= page
.evaluate(function() {
779 return $(".address:first").text();
781 if (actual
!= expected
) {
782 console
.log("BIP44 purpose field generates incorrect address");
783 console
.log("Expected: " + expected
);
784 console
.log("Actual: " + actual
);
793 // BIP44 coin field changes address list
795 page
.open(url
, function(status
) {
797 var expected
= "1F6dB2djQYrxoyfZZmfr6D5voH8GkJTghk";
798 page
.evaluate(function() {
799 $(".phrase").val("abandon abandon ability");
800 $(".phrase").trigger("input");
802 waitForGenerate(function() {
803 // change the bip44 purpose field to 45
804 page
.evaluate(function() {
805 $("#bip44 .coin").val("1");
806 $("#bip44 .coin").trigger("input");
808 waitForGenerate(function() {
809 // check the address for the new derivation path
810 var actual
= page
.evaluate(function() {
811 return $(".address:first").text();
813 if (actual
!= expected
) {
814 console
.log("BIP44 coin field generates incorrect address");
815 console
.log("Expected: " + expected
);
816 console
.log("Actual: " + actual
);
825 // BIP44 account field changes address list
827 page
.open(url
, function(status
) {
829 var expected
= "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
830 page
.evaluate(function() {
831 $(".phrase").val("abandon abandon ability");
832 $(".phrase").trigger("input");
834 waitForGenerate(function() {
835 // change the bip44 purpose field to 45
836 page
.evaluate(function() {
837 $("#bip44 .account").val("1");
838 $("#bip44 .account").trigger("input");
840 waitForGenerate(function() {
841 // check the address for the new derivation path
842 var actual
= page
.evaluate(function() {
843 return $(".address:first").text();
845 if (actual
!= expected
) {
846 console
.log("BIP44 account field generates incorrect address");
847 console
.log("Expected: " + expected
);
848 console
.log("Actual: " + actual
);
857 // BIP44 change field changes address list
859 page
.open(url
, function(status
) {
861 var expected
= "1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo";
862 page
.evaluate(function() {
863 $(".phrase").val("abandon abandon ability");
864 $(".phrase").trigger("input");
866 waitForGenerate(function() {
867 // change the bip44 purpose field to 45
868 page
.evaluate(function() {
869 $("#bip44 .change").val("1");
870 $("#bip44 .change").trigger("input");
872 waitForGenerate(function() {
873 // check the address for the new derivation path
874 var actual
= page
.evaluate(function() {
875 return $(".address:first").text();
877 if (actual
!= expected
) {
878 console
.log("BIP44 change field generates incorrect address");
879 console
.log("Expected: " + expected
);
880 console
.log("Actual: " + actual
);
889 // BIP32 derivation path can be set
891 page
.open(url
, function(status
) {
893 var expected
= "16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L";
894 page
.evaluate(function() {
895 $(".phrase").val("abandon abandon ability");
896 $(".phrase").trigger("input");
899 waitForGenerate(function() {
900 page
.evaluate(function() {
901 $("#bip32-tab a").click();
903 // set the derivation path to m/1
904 waitForGenerate(function() {
905 page
.evaluate(function() {
906 $("#bip32 .path").val("m/1");
907 $("#bip32 .path").trigger("input");
909 // check the address is generated correctly
910 waitForGenerate(function() {
911 var actual
= page
.evaluate(function() {
912 return $(".address:first").text();
914 if (actual
!= expected
) {
915 console
.log("Custom BIP32 path generates incorrect address");
916 console
.log("Expected: " + expected
);
917 console
.log("Actual: " + actual
);
927 // BIP32 can use hardened derivation paths
929 page
.open(url
, function(status
) {
931 var expected
= "14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4";
932 page
.evaluate(function() {
933 $(".phrase").val("abandon abandon ability");
934 $(".phrase").trigger("input");
937 waitForGenerate(function() {
938 page
.evaluate(function() {
939 $("#bip32-tab a").click();
941 // set the derivation path to m/0'
942 waitForGenerate(function() {
943 page
.evaluate(function() {
944 $("#bip32 .path").val("m/0'");
945 $("#bip32 .path").trigger("input");
947 // check the address is generated correctly
948 waitForGenerate(function() {
949 var actual
= page
.evaluate(function() {
950 return $(".address:first").text();
952 if (actual
!= expected
) {
953 console
.log("Hardened BIP32 path generates incorrect address");
954 console
.log("Expected: " + expected
);
955 console
.log("Actual: " + actual
);
965 // BIP32 extended private key is shown
967 page
.open(url
, function(status
) {
969 var expected
= "xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe";
970 page
.evaluate(function() {
971 $(".phrase").val("abandon abandon ability");
972 $(".phrase").trigger("input");
975 waitForGenerate(function() {
976 page
.evaluate(function() {
977 $("#bip32-tab a").click();
979 // check the extended private key is generated correctly
980 waitForGenerate(function() {
981 var actual
= page
.evaluate(function() {
982 return $(".extended-priv-key").val();
984 if (actual
!= expected
) {
985 console
.log("BIP32 extended private key is incorrect");
986 console
.log("Expected: " + expected
);
987 console
.log("Actual: " + actual
);
996 // BIP32 extended public key is shown
998 page
.open(url
, function(status
) {
1000 var expected
= "xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P";
1001 page
.evaluate(function() {
1002 $(".phrase").val("abandon abandon ability");
1003 $(".phrase").trigger("input");
1006 waitForGenerate(function() {
1007 page
.evaluate(function() {
1008 $("#bip32-tab a").click();
1010 // check the extended public key is generated correctly
1011 waitForGenerate(function() {
1012 var actual
= page
.evaluate(function() {
1013 return $(".extended-pub-key").val();
1015 if (actual
!= expected
) {
1016 console
.log("BIP32 extended public key is incorrect");
1017 console
.log("Expected: " + expected
);
1018 console
.log("Actual: " + actual
);
1027 // Derivation path is shown in table
1029 page
.open(url
, function(status
) {
1031 var expected
= "m/44'/0'/0'/0/0";
1032 page
.evaluate(function() {
1033 $(".phrase").val("abandon abandon ability");
1034 $(".phrase").trigger("input");
1036 // check for derivation path in table
1037 waitForGenerate(function() {
1038 var actual
= page
.evaluate(function() {
1039 return $(".index:first").text();
1041 if (actual
!= expected
) {
1042 console
.log("Derivation path shown incorrectly in table");
1043 console
.log("Expected: " + expected
);
1044 console
.log("Actual: " + actual
);
1052 // Derivation path for address can be hardened
1054 page
.open(url
, function(status
) {
1056 var expected
= "18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd";
1057 page
.evaluate(function() {
1058 $(".phrase").val("abandon abandon ability");
1059 $(".phrase").trigger("input");
1062 waitForGenerate(function() {
1063 page
.evaluate(function() {
1064 $("#bip32-tab a").click();
1066 waitForGenerate(function() {
1067 // select the hardened addresses option
1068 page
.evaluate(function() {
1069 $(".hardened-addresses").prop("checked", true);
1070 $(".hardened-addresses").trigger("change");
1072 waitForGenerate(function() {
1073 // check the generated address is hardened
1074 var actual
= page
.evaluate(function() {
1075 return $(".address:first").text();
1077 if (actual
!= expected
) {
1078 console
.log("Hardened address is incorrect");
1079 console
.log("Expected: " + expected
);
1080 console
.log("Actual: " + actual
);
1090 // Derivation path visibility can be toggled
1092 page
.open(url
, function(status
) {
1094 page
.evaluate(function() {
1095 $(".phrase").val("abandon abandon ability");
1096 $(".phrase").trigger("input");
1098 waitForGenerate(function() {
1099 // toggle path visibility
1100 page
.evaluate(function() {
1101 $(".index-toggle").click();
1103 // check the path is not visible
1104 var isInvisible
= page
.evaluate(function() {
1105 return $(".index:first span").hasClass("invisible");
1108 console
.log("Toggled derivation path is visible");
1118 page
.open(url
, function(status
) {
1119 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1121 page
.evaluate(function() {
1122 $(".phrase").val("abandon abandon ability").trigger("input");
1125 waitForGenerate(function() {
1126 var actual
= page
.evaluate(function() {
1127 return $(".address:first").text();
1129 if (actual
!= expected
) {
1130 console
.log("Address is not shown");
1131 console
.log("Expected: " + expected
);
1132 console
.log("Got: " + actual
);
1140 // Addresses are shown in order of derivation path
1142 page
.open(url
, function(status
) {
1144 page
.evaluate(function() {
1145 $(".phrase").val("abandon abandon ability").trigger("input");
1147 // get the derivation paths
1148 waitForGenerate(function() {
1149 var paths
= page
.evaluate(function() {
1150 return $(".index").map(function(i
, e
) {
1154 if (paths
.length
!= 20) {
1155 console
.log("Total paths is less than expected: " + paths
.length
);
1158 for (var i
=0; i
<paths
.length
; i
++) {
1159 var expected
= "m/44'/0'/0'/0/" + i
;
1160 var actual
= paths
[i
];
1161 if (actual
!= expected
) {
1162 console
.log("Path " + i
+ " is incorrect");
1163 console
.log("Expected: " + expected
);
1164 console
.log("Actual: " + actual
);
1173 // Address visibility can be toggled
1175 page
.open(url
, function(status
) {
1177 page
.evaluate(function() {
1178 $(".phrase").val("abandon abandon ability");
1179 $(".phrase").trigger("input");
1181 waitForGenerate(function() {
1182 // toggle address visibility
1183 page
.evaluate(function() {
1184 $(".address-toggle").click();
1186 // check the address is not visible
1187 var isInvisible
= page
.evaluate(function() {
1188 return $(".address:first span").hasClass("invisible");
1191 console
.log("Toggled address is visible");
1199 // Public key is shown
1201 page
.open(url
, function(status
) {
1202 var expected
= "033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3";
1204 page
.evaluate(function() {
1205 $(".phrase").val("abandon abandon ability").trigger("input");
1208 waitForGenerate(function() {
1209 var actual
= page
.evaluate(function() {
1210 return $(".pubkey:first").text();
1212 if (actual
!= expected
) {
1213 console
.log("Public key is not shown");
1214 console
.log("Expected: " + expected
);
1215 console
.log("Got: " + actual
);
1223 // Public key visibility can be toggled
1225 page
.open(url
, function(status
) {
1227 page
.evaluate(function() {
1228 $(".phrase").val("abandon abandon ability");
1229 $(".phrase").trigger("input");
1231 waitForGenerate(function() {
1232 // toggle public key visibility
1233 page
.evaluate(function() {
1234 $(".public-key-toggle").click();
1236 // check the public key is not visible
1237 var isInvisible
= page
.evaluate(function() {
1238 return $(".pubkey:first span").hasClass("invisible");
1241 console
.log("Toggled public key is visible");
1249 // Private key is shown
1251 page
.open(url
, function(status
) {
1252 var expected
= "L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE";
1254 page
.evaluate(function() {
1255 $(".phrase").val("abandon abandon ability").trigger("input");
1258 waitForGenerate(function() {
1259 var actual
= page
.evaluate(function() {
1260 return $(".privkey:first").text();
1262 if (actual
!= expected
) {
1263 console
.log("Private key is not shown");
1264 console
.log("Expected: " + expected
);
1265 console
.log("Got: " + actual
);
1273 // Private key visibility can be toggled
1275 page
.open(url
, function(status
) {
1277 page
.evaluate(function() {
1278 $(".phrase").val("abandon abandon ability");
1279 $(".phrase").trigger("input");
1281 waitForGenerate(function() {
1282 // toggle private key visibility
1283 page
.evaluate(function() {
1284 $(".private-key-toggle").click();
1286 // check the private key is not visible
1287 var isInvisible
= page
.evaluate(function() {
1288 return $(".privkey:first span").hasClass("invisible");
1291 console
.log("Toggled private key is visible");
1299 // More addresses can be generated
1301 page
.open(url
, function(status
) {
1303 page
.evaluate(function() {
1304 $(".phrase").val("abandon abandon ability");
1305 $(".phrase").trigger("input");
1307 waitForGenerate(function() {
1308 // generate more addresses
1309 page
.evaluate(function() {
1312 waitForGenerate(function() {
1313 // check there are more addresses
1314 var addressCount
= page
.evaluate(function() {
1315 return $(".address").length
;
1317 if (addressCount
!= 40) {
1318 console
.log("More addresses cannot be generated");
1327 // A custom number of additional addresses can be generated
1329 page
.open(url
, function(status
) {
1331 page
.evaluate(function() {
1332 $(".phrase").val("abandon abandon ability");
1333 $(".phrase").trigger("input");
1335 waitForGenerate(function() {
1336 // get the current number of addresses
1337 var oldAddressCount
= page
.evaluate(function() {
1338 return $(".address").length
;
1340 // set a custom number of additional addresses
1341 page
.evaluate(function() {
1342 $(".rows-to-add").val(1);
1344 // generate more addresses
1345 page
.evaluate(function() {
1348 waitForGenerate(function() {
1349 // check there are the correct number of addresses
1350 var newAddressCount
= page
.evaluate(function() {
1351 return $(".address").length
;
1353 if (newAddressCount
- oldAddressCount
!= 1) {
1354 console
.log("Number of additional addresses cannot be customized");
1355 console
.log(newAddressCount
)
1356 console
.log(oldAddressCount
)
1365 // Additional addresses are shown in order of derivation path
1367 page
.open(url
, function(status
) {
1369 page
.evaluate(function() {
1370 $(".phrase").val("abandon abandon ability").trigger("input");
1372 waitForGenerate(function() {
1373 // generate more addresses
1374 page
.evaluate(function() {
1377 // get the derivation paths
1378 waitForGenerate(function() {
1379 var paths
= page
.evaluate(function() {
1380 return $(".index").map(function(i
, e
) {
1384 if (paths
.length
!= 40) {
1385 console
.log("Total additional paths is less than expected: " + paths
.length
);
1388 for (var i
=0; i
<paths
.length
; i
++) {
1389 var expected
= "m/44'/0'/0'/0/" + i
;
1390 var actual
= paths
[i
];
1391 if (actual
!= expected
) {
1392 console
.log("Path " + i
+ " is not in correct order");
1393 console
.log("Expected: " + expected
);
1394 console
.log("Actual: " + actual
);
1404 // BIP32 root key can be set by the user
1406 page
.open(url
, function(status
) {
1407 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1409 page
.evaluate(function() {
1410 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1412 waitForGenerate(function() {
1413 var actual
= page
.evaluate(function() {
1414 return $(".address:first").text();
1416 if (actual
!= expected
) {
1417 console
.log("Setting BIP32 root key results in wrong address");
1418 console
.log("Expected: " + expected
);
1419 console
.log("Actual: " + actual
);
1427 // Setting BIP32 root key clears the existing phrase, passphrase and seed
1429 page
.open(url
, function(status
) {
1432 page
.evaluate(function() {
1433 $(".phrase").val("A non-blank but invalid value");
1435 // Accept any confirm dialogs
1436 page
.onConfirm = function() {
1440 page
.evaluate(function() {
1441 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1443 waitForGenerate(function() {
1444 var actual
= page
.evaluate(function() {
1445 return $(".phrase").val();
1447 if (actual
!= expected
) {
1448 console
.log("Phrase not cleared when setting BIP32 root key");
1449 console
.log("Expected: " + expected
);
1450 console
.log("Actual: " + actual
);
1458 // Clearing of phrase, passphrase and seed can be cancelled by user
1460 page
.open(url
, function(status
) {
1461 var expected
= "abandon abandon ability";
1463 page
.evaluate(function() {
1464 $(".phrase").val("abandon abandon ability");
1466 // Cancel any confirm dialogs
1467 page
.onConfirm = function() {
1471 page
.evaluate(function() {
1472 $(".root-key").val("xprv9s21ZrQH143K3d3vzEDD3KpSKmxsZ3y7CqhAL1tinwtP6wqK4TKEKjpBuo6P2hUhB6ZENo7TTSRytiP857hBZVpBdk8PooFuRspE1eywwNZ").trigger("input");
1474 var actual
= page
.evaluate(function() {
1475 return $(".phrase").val();
1477 if (actual
!= expected
) {
1478 console
.log("Phrase not retained when cancelling changes to BIP32 root key");
1479 console
.log("Expected: " + expected
);
1480 console
.log("Actual: " + actual
);
1487 // Custom BIP32 root key is used when changing the derivation path
1489 page
.open(url
, function(status
) {
1490 var expected
= "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1492 page
.evaluate(function() {
1493 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1495 waitForGenerate(function() {
1496 // change the derivation path
1497 page
.evaluate(function() {
1498 $("#account").val("1").trigger("input");
1500 // check the bip32 root key is used for derivation, not the blank phrase
1501 waitForGenerate(function() {
1502 var actual
= page
.evaluate(function() {
1503 return $(".address:first").text();
1505 if (actual
!= expected
) {
1506 console
.log("Changing the derivation path does not use BIP32 root key");
1507 console
.log("Expected: " + expected
);
1508 console
.log("Actual: " + actual
);
1517 // Incorrect mnemonic shows error
1519 page
.open(url
, function(status
) {
1521 page
.evaluate(function() {
1522 $(".phrase").val("abandon abandon abandon").trigger("input");
1524 waitForFeedback(function() {
1525 // check there is an error shown
1526 var feedback
= page
.evaluate(function() {
1527 return $(".feedback").text();
1529 if (feedback
.length
<= 0) {
1530 console
.log("Invalid mnemonic does not show error");
1538 // Incorrect word shows suggested replacement
1540 page
.open(url
, function(status
) {
1542 page
.evaluate(function() {
1543 $(".phrase").val("abandon abandon abiliti").trigger("input");
1545 // check there is a suggestion shown
1546 waitForFeedback(function() {
1547 var feedback
= page
.evaluate(function() {
1548 return $(".feedback").text();
1550 if (feedback
.indexOf("did you mean ability?") < 0) {
1551 console
.log("Incorrect word does not show suggested replacement");
1552 console
.log("Error: " + error
);
1560 // Incorrect BIP32 root key shows error
1562 page
.open(url
, function(status
) {
1564 page
.evaluate(function() {
1565 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj").trigger("input");
1567 // check there is an error shown
1568 waitForFeedback(function() {
1569 var feedback
= page
.evaluate(function() {
1570 return $(".feedback").text();
1572 if (feedback
!= "Invalid root key") {
1573 console
.log("Invalid root key does not show error");
1574 console
.log("Error: " + error
);
1582 // Derivation path not starting with m shows error
1584 page
.open(url
, function(status
) {
1585 // set the mnemonic phrase
1586 page
.evaluate(function() {
1587 $(".phrase").val("abandon abandon ability").trigger("input");
1589 waitForGenerate(function() {
1590 // select the bip32 tab so custom derivation path can be set
1591 page
.evaluate(function() {
1592 $("#bip32-tab a").click();
1594 waitForGenerate(function() {
1595 // set the incorrect derivation path
1596 page
.evaluate(function() {
1597 $("#bip32 .path").val("n/0").trigger("input");
1599 waitForFeedback(function() {
1600 var feedback
= page
.evaluate(function() {
1601 return $(".feedback").text();
1603 if (feedback
!= "First character must be 'm'") {
1604 console
.log("Derivation path not starting with m should show error");
1605 console
.log("Error: " + error
);
1615 // Derivation path containing invalid characters shows useful error
1617 page
.open(url
, function(status
) {
1618 // set the mnemonic phrase
1619 page
.evaluate(function() {
1620 $(".phrase").val("abandon abandon ability").trigger("input");
1622 waitForGenerate(function() {
1623 // select the bip32 tab so custom derivation path can be set
1624 page
.evaluate(function() {
1625 $("#bip32-tab a").click();
1627 waitForGenerate(function() {
1628 // set the incorrect derivation path
1629 page
.evaluate(function() {
1630 $("#bip32 .path").val("m/1/0wrong1/1").trigger("input");
1632 waitForFeedback(function() {
1633 var feedback
= page
.evaluate(function() {
1634 return $(".feedback").text();
1636 if (feedback
!= "Invalid characters 0wrong1 found at depth 2") {
1637 console
.log("Derivation path with invalid characters should show error");
1638 console
.log("Error: " + error
);
1648 // Github Issue 11: Default word length is 15
1649 // https://github.com/iancoleman/bip39/issues/11
1651 page
.open(url
, function(status
) {
1652 // get the word length
1653 var defaultLength
= page
.evaluate(function() {
1654 return $(".strength").val();
1656 if (defaultLength
!= 15) {
1657 console
.log("Default word length is not 15");
1665 // Github Issue 12: Generate more rows with private keys hidden
1666 // https://github.com/iancoleman/bip39/issues/12
1668 page
.open(url
, function(status
) {
1670 page
.evaluate(function() {
1671 $(".phrase").val("abandon abandon ability");
1672 $(".phrase").trigger("input");
1674 waitForGenerate(function() {
1675 // toggle private keys hidden, then generate more addresses
1676 page
.evaluate(function() {
1677 $(".private-key-toggle").click();
1680 waitForGenerate(function() {
1681 // check more have been generated
1683 var numPrivKeys
= page
.evaluate(function() {
1684 return $(".privkey").length
;
1686 if (numPrivKeys
!= expected
) {
1687 console
.log("Wrong number of addresses when clicking 'more' with hidden privkeys");
1688 console
.log("Expected: " + expected
);
1689 console
.log("Actual: " + numPrivKeys
);
1692 // check no private keys are shown
1693 var numHiddenPrivKeys
= page
.evaluate(function() {
1694 return $(".privkey span[class=invisible]").length
;
1696 if (numHiddenPrivKeys
!= expected
) {
1697 console
.log("Generating more does not retain hidden state of privkeys");
1698 console
.log("Expected: " + expected
);
1699 console
.log("Actual: " + numHiddenPrivKeys
);
1708 // Github Issue 19: Mnemonic is not sensitive to whitespace
1709 // https://github.com/iancoleman/bip39/issues/19
1711 page
.open(url
, function(status
) {
1713 var expected
= "xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC";
1714 page
.evaluate(function() {
1715 var doubleSpace
= " ";
1716 $(".phrase").val("urge cat" + doubleSpace
+ "bid");
1717 $(".phrase").trigger("input");
1719 waitForGenerate(function() {
1720 // Check the bip32 root key is correct
1721 var actual
= page
.evaluate(function() {
1722 return $(".root-key").val();
1724 if (actual
!= expected
) {
1725 console
.log("Mnemonic is sensitive to whitespace");
1726 console
.log("Expected: " + expected
);
1727 console
.log("Actual: " + actual
);
1735 // Github Issue 23: Part 1: Use correct derivation path when changing tabs
1736 // https://github.com/iancoleman/bip39/issues/23
1738 page
.open(url
, function(status
) {
1739 // 1) and 2) set the phrase
1740 page
.evaluate(function() {
1741 $(".phrase").val("abandon abandon ability").trigger("input");
1743 waitForGenerate(function() {
1744 // 3) select bip32 tab
1745 page
.evaluate(function() {
1746 $("#bip32-tab a").click();
1748 waitForGenerate(function() {
1749 // 4) switch from bitcoin to litecoin
1750 page
.evaluate(function() {
1751 $(".network").val("2").trigger("change");
1753 waitForGenerate(function() {
1754 // 5) Check derivation path is displayed correctly
1755 var expected
= "m/0/0";
1756 var actual
= page
.evaluate(function() {
1757 return $(".index:first").text();
1759 if (actual
!= expected
) {
1760 console
.log("Github Issue 23 Part 1: derivation path display error");
1761 console
.log("Expected: " + expected
);
1762 console
.log("Actual: " + actual
);
1765 // 5) Check address is displayed correctly
1766 var expected
= "LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5";
1767 var actual
= page
.evaluate(function() {
1768 return $(".address:first").text();
1770 if (actual
!= expected
) {
1771 console
.log("Github Issue 23 Part 1: address display error");
1772 console
.log("Expected: " + expected
);
1773 console
.log("Actual: " + actual
);
1783 // Github Issue 23 Part 2: Coin selection in derivation path
1784 // https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
1786 page
.open(url
, function(status
) {
1788 page
.evaluate(function() {
1789 $(".phrase").val("abandon abandon ability").trigger("input");
1791 waitForGenerate(function() {
1792 // switch from bitcoin to clam
1793 page
.evaluate(function() {
1794 $(".network").val("9").trigger("change");
1796 waitForGenerate(function() {
1797 // check derivation path is displayed correctly
1798 var expected
= "m/44'/23'/0'/0/0";
1799 var actual
= page
.evaluate(function() {
1800 return $(".index:first").text();
1802 if (actual
!= expected
) {
1803 console
.log("Github Issue 23 Part 2: Coin in BIP44 derivation path is incorrect");
1804 console
.log("Expected: " + expected
);
1805 console
.log("Actual: " + actual
);
1814 // Github Issue 26: When using a Root key derrived altcoins are incorrect
1815 // https://github.com/iancoleman/bip39/issues/26
1817 page
.open(url
, function(status
) {
1818 // 1) 2) and 3) set the root key
1819 page
.evaluate(function() {
1820 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1822 waitForGenerate(function() {
1823 // 4) switch from bitcoin to viacoin
1824 page
.evaluate(function() {
1825 $(".network").val("6").trigger("change");
1827 waitForGenerate(function() {
1828 // 5) ensure the derived address is correct
1829 var expected
= "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
1830 var actual
= page
.evaluate(function() {
1831 return $(".address:first").text();
1833 if (actual
!= expected
) {
1834 console
.log("Github Issue 26: address is incorrect when changing networks and using root-key to derive");
1835 console
.log("Expected: " + expected
);
1836 console
.log("Actual: " + actual
);
1845 // Selecting a language with no existing phrase should generate a phrase in
1848 page
.open(url
, function(status
) {
1849 // Select a language
1850 // Need to manually simulate hash being set due to quirk between
1851 // 'click' event triggered by javascript vs triggered by mouse.
1852 // Perhaps look into page.sendEvent
1853 // http://phantomjs.org/api/webpage/method/send-event.html
1854 page
.evaluate(function() {
1855 window
.location
.hash
= "#japanese";
1856 $("a[href='#japanese']").trigger("click");
1858 waitForGenerate(function() {
1859 // Check the mnemonic is in Japanese
1860 var phrase
= page
.evaluate(function() {
1861 return $(".phrase").val();
1863 if (phrase
.length
<= 0) {
1864 console
.log("No Japanese phrase generated");
1867 if (phrase
.charCodeAt(0) < 128) {
1868 console
.log("First character of Japanese phrase is ascii");
1869 console
.log("Phrase: " + phrase
);
1877 // Selecting a language with existing phrase should update the phrase to use
1880 page
.open(url
, function(status
) {
1881 // Set the phrase to an English phrase.
1882 page
.evaluate(function() {
1883 $(".phrase").val("abandon abandon ability").trigger("input");
1885 waitForGenerate(function() {
1886 // Change to Italian
1887 // Need to manually simulate hash being set due to quirk between
1888 // 'click' event triggered by javascript vs triggered by mouse.
1889 // Perhaps look into page.sendEvent
1890 // http://phantomjs.org/api/webpage/method/send-event.html
1891 page
.evaluate(function() {
1892 window
.location
.hash
= "#italian";
1893 $("a[href='#italian']").trigger("click");
1895 waitForGenerate(function() {
1896 // Check only the language changes, not the phrase
1897 var expected
= "abaco abaco abbaglio";
1898 var actual
= page
.evaluate(function() {
1899 return $(".phrase").val();
1901 if (actual
!= expected
) {
1902 console
.log("Changing language with existing phrase");
1903 console
.log("Expected: " + expected
);
1904 console
.log("Actual: " + actual
);
1907 // Check the address is correct
1908 var expected
= "1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV";
1909 var actual
= page
.evaluate(function() {
1910 return $(".address:first").text();
1912 if (actual
!= expected
) {
1913 console
.log("Changing language generates incorrect address");
1914 console
.log("Expected: " + expected
);
1915 console
.log("Actual: " + actual
);
1924 // Suggested replacement for erroneous word in non-English language
1926 page
.open(url
, function(status
) {
1927 // Set an incorrect phrase in Italian
1928 page
.evaluate(function() {
1929 $(".phrase").val("abaco abaco zbbaglio").trigger("input");
1931 waitForFeedback(function() {
1932 // Check the suggestion is correct
1933 var feedback
= page
.evaluate(function() {
1934 return $(".feedback").text();
1936 if (feedback
.indexOf("did you mean abbaglio?") < 0) {
1937 console
.log("Incorrect Italian word does not show suggested replacement");
1938 console
.log("Error: " + error
);
1947 // Japanese word does not break across lines.
1949 // https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
1951 page
.open(url
, function(status
) {
1952 hasWordBreakCss
= page
.content
.indexOf("word-break: keep-all;") > -1;
1953 if (!hasWordBreakCss
) {
1954 console
.log("Japanese words can break across lines mid-word");
1955 console
.log("Check CSS for '.phrase { word-break: keep-all; }'");
1958 // Run the next test
1963 // Language can be specified at page load using hash value in url
1965 page
.open(url
, function(status
) {
1966 // Set the page hash as if it were on a fresh page load
1967 page
.evaluate(function() {
1968 window
.location
.hash
= "#japanese";
1970 // Generate a random phrase
1971 page
.evaluate(function() {
1972 $(".generate").trigger("click");
1974 waitForGenerate(function() {
1975 // Check the phrase is in Japanese
1976 var phrase
= page
.evaluate(function() {
1977 return $(".phrase").val();
1979 if (phrase
.length
<= 0) {
1980 console
.log("No phrase generated using url hash");
1983 if (phrase
.charCodeAt(0) < 128) {
1984 console
.log("Language not detected from url hash on page load.");
1985 console
.log("Phrase: " + phrase
);
1993 // Entropy unit tests
1995 page
.open(url
, function(status
) {
1996 var response
= page
.evaluate(function() {
1998 // binary entropy is detected
2000 e
= Entropy
.fromString("01010101");
2001 if (e
.base
.str
!= "binary") {
2002 return "Binary entropy not detected correctly";
2008 // base6 entropy is detected
2010 e
= Entropy
.fromString("012345012345");
2011 if (e
.base
.str
!= "base 6") {
2012 return "base6 entropy not detected correctly";
2018 // dice entropy is detected
2020 e
= Entropy
.fromString("123456123456");
2021 if (e
.base
.str
!= "base 6 (dice)") {
2022 return "dice entropy not detected correctly";
2028 // base10 entropy is detected
2030 e
= Entropy
.fromString("0123456789");
2031 if (e
.base
.str
!= "base 10") {
2032 return "base10 entropy not detected correctly";
2038 // hex entropy is detected
2040 e
= Entropy
.fromString("0123456789ABCDEF");
2041 if (e
.base
.str
!= "hexadecimal") {
2042 return "hexadecimal entropy not detected correctly";
2048 // card entropy is detected
2050 e
= Entropy
.fromString("AC4DTHKS");
2051 if (e
.base
.str
!= "card") {
2052 return "card entropy not detected correctly";
2058 // entropy is case insensitive
2060 e
= Entropy
.fromString("aBcDeF");
2061 if (e
.cleanStr
!= "aBcDeF") {
2062 return "Entropy should not be case sensitive";
2068 // dice entropy is converted to base6
2070 e
= Entropy
.fromString("123456");
2071 if (e
.cleanStr
!= "123450") {
2072 return "Dice entropy is not automatically converted to base6";
2078 // dice entropy is preferred to base6 if ambiguous
2080 e
= Entropy
.fromString("12345");
2081 if (e
.base
.str
!= "base 6 (dice)") {
2082 return "dice not used as default over base 6";
2088 // unused characters are ignored
2090 e
= Entropy
.fromString("fghijkl");
2091 if (e
.cleanStr
!= "f") {
2092 return "additional characters are not ignored";
2098 // the lowest base is used by default
2099 // 7 could be decimal or hexadecimal, but should be detected as decimal
2101 e
= Entropy
.fromString("7");
2102 if (e
.base
.str
!= "base 10") {
2103 return "lowest base is not used";
2109 // Leading zeros are retained
2111 e
= Entropy
.fromString("000A");
2112 if (e
.cleanStr
!= "000A") {
2113 return "Leading zeros are not retained";
2119 // Leading zeros are correctly preserved for hex in binary string
2121 e
= Entropy
.fromString("2A");
2122 if (e
.binaryStr
!= "00101010") {
2123 return "Hex leading zeros are not correct in binary";
2129 // Leading zeros for base 6 as binary string
2130 // 20 = 2 events at 2.58 bits per event = 5 bits
2131 // 20 in base 6 = 12 in base 10 = 1100 in base 2
2132 // so it needs 1 bit of padding to be the right bit length
2134 e
= Entropy
.fromString("20");
2135 if (e
.binaryStr
!= "01100") {
2136 return "Base 6 as binary has leading zeros";
2142 // Leading zeros for base 10 as binary string
2144 e
= Entropy
.fromString("17");
2145 if (e
.binaryStr
!= "010001") {
2146 return "Base 10 as binary has leading zeros";
2152 // Leading zeros for card entropy as binary string
2154 e
= Entropy
.fromString("2c");
2155 if (e
.binaryStr
!= "00001") {
2156 return "Card entropy as binary has leading zeros";
2162 // Keyboard mashing results in weak entropy
2163 // Despite being a long string, it's less than 30 bits of entropy
2165 e
= Entropy
.fromString("aj;se ifj; ask,dfv js;ifj");
2166 if (e
.binaryStr
.length
>= 30) {
2167 return "Keyboard mashing should produce weak entropy";
2173 // Card entropy is used if every pair could be a card
2175 e
= Entropy
.fromString("4c3c2c");
2176 if (e
.base
.str
!= "card") {
2177 return "Card entropy not used if all pairs are cards";
2183 // Card entropy uses base 52
2184 // [ cards, binary ]
2188 [ "acac", "00000000000" ],
2189 [ "acac2c", "00000000000000001" ],
2190 [ "acks", "00000110011" ],
2191 [ "acacks", "00000000000110011" ],
2204 [ "ks2c", "101001011101" ],
2205 [ "KS2C", "101001011101" ],
2207 for (var i
=0; i
<cards
.length
; i
++) {
2208 var card
= cards
[i
][0];
2209 var result
= cards
[i
][1];
2210 e
= Entropy
.fromString(card
);
2211 console
.log(e
.binary
+ " " + result
);
2212 if (e
.binaryStr
!== result
) {
2213 return "card entropy not parsed correctly: " + result
+ " != " + e
.binaryStr
;
2222 if (response
!= "PASS") {
2223 console
.log("Entropy unit tests");
2224 console
.log(response
);
2231 // Entropy can be entered by the user
2233 page
.open(url
, function(status
) {
2235 mnemonic: "abandon abandon ability",
2236 address: "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug",
2239 page
.evaluate(function() {
2240 $(".use-entropy").prop("checked", true).trigger("change");
2241 $(".entropy").val("00000000 00000000 00000000 00000000").trigger("input");
2243 // check the mnemonic is set and address is correct
2244 waitForGenerate(function() {
2245 var actual
= page
.evaluate(function() {
2247 address: $(".address:first").text(),
2248 mnemonic: $(".phrase").val(),
2251 if (actual
.mnemonic
!= expected
.mnemonic
) {
2252 console
.log("Entropy does not generate correct mnemonic");
2253 console
.log("Expected: " + expected
.mnemonic
);
2254 console
.log("Got: " + actual
.mnemonic
);
2257 if (actual
.address
!= expected
.address
) {
2258 console
.log("Entropy does not generate correct address");
2259 console
.log("Expected: " + expected
.address
);
2260 console
.log("Got: " + actual
.address
);
2268 // A warning about entropy is shown to the user, with additional information
2270 page
.open(url
, function(status
) {
2271 // get text content from entropy sections of page
2272 var hasWarning
= page
.evaluate(function() {
2273 var entropyText
= $(".entropy-container").text();
2274 var warning
= "mnemonic may be insecure";
2275 if (entropyText
.indexOf(warning
) == -1) {
2278 var readMoreText
= $("#entropy-notes").parent().text();
2279 var goodSources
= "flipping a fair coin, rolling a fair dice, noise measurements etc";
2280 if (readMoreText
.indexOf(goodSources
) == -1) {
2285 // check the warnings and information are shown
2287 console
.log("Page does not contain warning about using own entropy");
2294 // The types of entropy available are described to the user
2296 page
.open(url
, function(status
) {
2297 // get placeholder text for entropy field
2298 var placeholder
= page
.evaluate(function() {
2299 return $(".entropy").attr("placeholder");
2309 for (var i
=0; i
<options
.length
; i
++) {
2310 var option
= options
[i
];
2311 if (placeholder
.indexOf(option
) == -1) {
2312 console
.log("Available entropy type is not shown to user: " + option
);
2320 // The actual entropy used is shown to the user
2322 page
.open(url
, function(status
) {
2324 var badEntropySource
= page
.evaluate(function() {
2325 var entropy
= "Not A Very Good Entropy Source At All";
2326 $(".use-entropy").prop("checked", true).trigger("change");
2327 $(".entropy").val(entropy
).trigger("input");
2329 // check the actual entropy being used is shown
2330 waitForEntropyFeedback(function() {
2331 var expectedText
= "AedEceAA";
2332 var entropyText
= page
.evaluate(function() {
2333 return $(".entropy-container").text();
2335 if (entropyText
.indexOf(expectedText
) == -1) {
2336 console
.log("Actual entropy used is not shown");
2344 // Binary entropy can be entered
2346 page
.open(url
, function(status
) {
2348 page
.evaluate(function() {
2349 $(".use-entropy").prop("checked", true).trigger("change");
2350 $(".entropy").val("01").trigger("input");
2352 // check the entropy is shown to be the correct type
2353 waitForEntropyFeedback(function() {
2354 var entropyText
= page
.evaluate(function() {
2355 return $(".entropy-container").text();
2357 if (entropyText
.indexOf("binary") == -1) {
2358 console
.log("Binary entropy is not detected and presented to user");
2366 // Base 6 entropy can be entered
2368 page
.open(url
, function(status
) {
2370 page
.evaluate(function() {
2371 $(".use-entropy").prop("checked", true).trigger("change");
2372 $(".entropy").val("012345").trigger("input");
2374 // check the entropy is shown to be the correct type
2375 waitForEntropyFeedback(function() {
2376 var entropyText
= page
.evaluate(function() {
2377 return $(".entropy-container").text();
2379 if (entropyText
.indexOf("base 6") == -1) {
2380 console
.log("Base 6 entropy is not detected and presented to user");
2388 // Base 6 dice entropy can be entered
2390 page
.open(url
, function(status
) {
2392 page
.evaluate(function() {
2393 $(".use-entropy").prop("checked", true).trigger("change");
2394 $(".entropy").val("123456").trigger("input");
2396 // check the entropy is shown to be the correct type
2397 waitForEntropyFeedback(function() {
2398 var entropyText
= page
.evaluate(function() {
2399 return $(".entropy-container").text();
2401 if (entropyText
.indexOf("dice") == -1) {
2402 console
.log("Dice entropy is not detected and presented to user");
2410 // Base 10 entropy can be entered
2412 page
.open(url
, function(status
) {
2414 page
.evaluate(function() {
2415 $(".use-entropy").prop("checked", true).trigger("change");
2416 $(".entropy").val("789").trigger("input");
2418 // check the entropy is shown to be the correct type
2419 waitForEntropyFeedback(function() {
2420 var entropyText
= page
.evaluate(function() {
2421 return $(".entropy-container").text();
2423 if (entropyText
.indexOf("base 10") == -1) {
2424 console
.log("Base 10 entropy is not detected and presented to user");
2432 // Hexadecimal entropy can be entered
2434 page
.open(url
, function(status
) {
2436 page
.evaluate(function() {
2437 $(".use-entropy").prop("checked", true).trigger("change");
2438 $(".entropy").val("abcdef").trigger("input");
2440 // check the entropy is shown to be the correct type
2441 waitForEntropyFeedback(function() {
2442 var entropyText
= page
.evaluate(function() {
2443 return $(".entropy-container").text();
2445 if (entropyText
.indexOf("hexadecimal") == -1) {
2446 console
.log("Hexadecimal entropy is not detected and presented to user");
2454 // Dice entropy value is shown as the converted base 6 value
2456 page
.open(url
, function(status
) {
2458 page
.evaluate(function() {
2459 $(".use-entropy").prop("checked", true).trigger("change");
2460 $(".entropy").val("123456").trigger("input");
2462 // check the entropy is shown as base 6, not as the original dice value
2463 waitForEntropyFeedback(function() {
2464 var entropyText
= page
.evaluate(function() {
2465 return $(".entropy-container").text();
2467 if (entropyText
.indexOf("123450") == -1) {
2468 console
.log("Dice entropy is not shown to user as base 6 value");
2471 if (entropyText
.indexOf("123456") > -1) {
2472 console
.log("Dice entropy value is shown instead of true base 6 value");
2480 // The number of bits of entropy accumulated is shown
2482 page
.open(url
, function(status
) {
2485 [ "0000 0000 0000 0000 0000", "20" ],
2488 [ "6", "2" ], // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
2489 [ "7", "3" ], // 7 in base 10 is 111 in base 2, no leading zeros
2494 [ "1A", "8" ], // hex is always multiple of 4 bits of entropy
2501 [ "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)
2502 [ "2227", "13" ], // Uses base 10, which is 4 lots of 3.32 bits, which is 13.3 bits (rounded down to 13)
2505 [ "0000101017", "33" ], // 10 events at 3.32 bits per event
2508 page
.evaluate(function(e
) {
2509 $(".use-entropy").prop("checked", true).trigger("change");
2512 var nextTest
= function runNextTest(i
) {
2513 var entropy
= tests
[i
][0];
2514 var expected
= tests
[i
][1];
2516 page
.evaluate(function(e
) {
2517 $(".entropy").val(e
).trigger("input");
2519 // check the number of bits of entropy is shown
2520 waitForEntropyFeedback(function() {
2521 var entropyText
= page
.evaluate(function() {
2522 return $(".entropy-container").text();
2524 if (entropyText
.replace(/\s/g,"").indexOf("Bits" + expected
) == -1) {
2525 console
.log("Accumulated entropy is not shown correctly for " + entropy
);
2528 var isLastTest
= i
== tests
.length
- 1;
2541 // There is feedback provided about the supplied entropy
2543 page
.open(url
, function(status
) {
2548 type: "hexadecimal",
2552 strength: "extremely weak",
2555 entropy: "AAAAAAAA",
2556 filtered: "AAAAAAAA",
2557 type: "hexadecimal",
2561 strength: "extremely weak",
2564 entropy: "AAAAAAAA B",
2565 filtered: "AAAAAAAAB",
2566 type: "hexadecimal",
2570 strength: "extremely weak",
2573 entropy: "AAAAAAAA BBBBBBBB",
2574 filtered: "AAAAAAAABBBBBBBB",
2575 type: "hexadecimal",
2579 strength: "very weak",
2582 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
2583 filtered: "AAAAAAAABBBBBBBBCCCCCCCC",
2584 type: "hexadecimal",
2591 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
2592 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD",
2593 type: "hexadecimal",
2597 strength: "easily cracked",
2600 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA",
2601 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA",
2602 type: "hexadecimal",
2609 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE",
2610 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE",
2611 type: "hexadecimal",
2615 strength: "very strong",
2618 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF",
2619 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF",
2620 type: "hexadecimal",
2624 strength: "extremely strong",
2628 page
.evaluate(function() {
2629 $(".use-entropy").prop("checked", true).trigger("change");
2631 var nextTest
= function runNextTest(i
) {
2632 function getFeedbackError(expected
, actual
) {
2633 if (actual
.indexOf(expected
.filtered
) == -1) {
2634 return "Filtered value not in feedback";
2636 if (actual
.indexOf(expected
.type
) == -1) {
2637 return "Entropy type not in feedback";
2639 if (actual
.indexOf(expected
.events
) == -1) {
2640 return "Event count not in feedback";
2642 if (actual
.indexOf(expected
.bits
) == -1) {
2643 return "Bit count not in feedback";
2645 if (actual
.indexOf(expected
.strength
) == -1) {
2646 return "Strength not in feedback";
2651 page
.evaluate(function(e
) {
2652 $(".addresses").empty();
2653 $(".phrase").val("");
2654 $(".entropy").val(e
).trigger("input");
2656 waitForEntropyFeedback(function() {
2657 var mnemonic
= page
.evaluate(function() {
2658 return $(".phrase").val();
2660 // Check mnemonic length
2661 if (test
.words
== 0) {
2662 if (mnemonic
.length
> 0) {
2663 console
.log("Mnemonic length for " + test
.strength
+ " strength is not " + test
.words
);
2664 console
.log("Mnemonic: " + mnemonic
);
2669 if (mnemonic
.split(" ").length
!= test
.words
) {
2670 console
.log("Mnemonic length for " + test
.strength
+ " strength is not " + test
.words
);
2671 console
.log("Mnemonic: " + mnemonic
);
2676 var feedback
= page
.evaluate(function() {
2677 return $(".entropy-container").text();
2679 var feedbackError
= getFeedbackError(test
, feedback
);
2680 if (feedbackError
) {
2681 console
.log("Entropy feedback for " + test
.entropy
+ " returned error");
2682 console
.log(feedbackError
);
2686 var isLastTest
= i
== tests
.length
- 1;
2699 // Entropy is truncated from the left
2701 page
.open(url
, function(status
) {
2702 var expected
= "avocado zoo zone";
2704 page
.evaluate(function() {
2705 $(".use-entropy").prop("checked", true).trigger("change");
2706 var entropy
= "00000000 00000000 00000000 00000000";
2707 entropy
+= "11111111 11111111 11111111 1111"; // Missing last byte
2708 $(".entropy").val(entropy
).trigger("input");
2710 // check the entropy is truncated from the right
2711 waitForGenerate(function() {
2712 var actual
= page
.evaluate(function() {
2713 return $(".phrase").val();
2715 if (actual
!= expected
) {
2716 console
.log("Entropy is not truncated from the right");
2717 console
.log("Expected: " + expected
);
2718 console
.log("Got: " + actual
);
2726 // Very large entropy results in very long mnemonics
2728 page
.open(url
, function(status
) {
2730 page
.evaluate(function() {
2731 $(".use-entropy").prop("checked", true).trigger("change");
2733 // Generate a very long entropy string
2734 for (var i
=0; i
<33; i
++) {
2735 entropy
+= "AAAAAAAA"; // 3 words * 33 iterations = 99 words
2737 $(".entropy").val(entropy
).trigger("input");
2739 // check the mnemonic is very long
2740 waitForGenerate(function() {
2741 var wordCount
= page
.evaluate(function() {
2742 return $(".phrase").val().split(" ").length
;
2744 if (wordCount
!= 99) {
2745 console
.log("Large entropy does not generate long mnemonic");
2746 console
.log("Expected 99 words, got " + wordCount
);
2754 // Is compatible with bip32jp entropy
2755 // https://bip32jp.github.io/english/index.html
2757 // Is incompatible with:
2760 page
.open(url
, function(status
) {
2761 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";
2763 page
.evaluate(function() {
2764 $(".use-entropy").prop("checked", true).trigger("change");
2765 var entropy
= "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543";
2766 $(".entropy").val(entropy
).trigger("input");
2768 // check the mnemonic matches the expected value from bip32jp
2769 waitForGenerate(function() {
2770 var actual
= page
.evaluate(function() {
2771 return $(".phrase").val();
2773 if (actual
!= expected
) {
2774 console
.log("Mnemonic does not match bip32jp for base 6 entropy");
2775 console
.log("Expected: " + expected
);
2776 console
.log("Got: " + actual
);
2784 // Blank entropy does not generate mnemonic or addresses
2786 page
.open(url
, function(status
) {
2788 page
.evaluate(function() {
2789 $(".use-entropy").prop("checked", true).trigger("change");
2790 $(".entropy").val("").trigger("input");
2792 waitForFeedback(function() {
2793 // check there is no mnemonic
2794 var phrase
= page
.evaluate(function() {
2795 return $(".phrase").val();
2798 console
.log("Blank entropy does not result in blank mnemonic");
2799 console
.log("Got: " + phrase
);
2802 // check there are no addresses displayed
2803 var addresses
= page
.evaluate(function() {
2804 return $(".address").length
;
2806 if (addresses
!= 0) {
2807 console
.log("Blank entropy does not result in zero addresses");
2810 // Check the feedback says 'blank entropy'
2811 var feedback
= page
.evaluate(function() {
2812 return $(".feedback").text();
2814 if (feedback
!= "Blank entropy") {
2815 console
.log("Blank entropy does not show feedback message");
2823 // Mnemonic length can be selected even for weak entropy
2825 page
.open(url
, function(status
) {
2827 page
.evaluate(function() {
2828 $(".use-entropy").prop("checked", true).trigger("change");
2829 $(".entropy").val("012345");
2830 $(".mnemonic-length").val("18").trigger("change");
2832 // check the mnemonic is the correct length
2833 waitForGenerate(function() {
2834 var phrase
= page
.evaluate(function() {
2835 return $(".phrase").val();
2837 var numberOfWords
= phrase
.split(/\s/g).length
;
2838 if (numberOfWords
!= 18) {
2839 console
.log("Weak entropy cannot be overridden to give 18 word mnemonic");
2840 console
.log(phrase
);
2848 // If you wish to add more tests, do so here...
2850 // Here is a blank test template
2854 page.open(url, function(status) {
2855 // Do something on the page
2856 page.evaluate(function() {
2857 $(".phrase").val("abandon abandon ability").trigger("input");
2859 waitForGenerate(function() {
2860 // Check the result of doing the thing
2861 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
2862 var actual = page.evaluate(function() {
2863 return $(".address:first").text();
2865 if (actual != expected) {
2866 console.log("A specific message about what failed");
2867 console.log("Expected: " + expected);
2868 console.log("Actual: " + actual);
2871 // Run the next test
2881 console
.log("Running tests...");
2882 tests
= shuffle(tests
);