]>
git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blob - tests.js
2 // $ phantomjs tests.js
5 var page
= require ( 'webpage' ). create ();
6 var url
= 'src/index.html' ;
7 var testMaxTime
= 10000 ;
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[value=1]" ). prop ( "selected" , true );
284 $( ".network" ). trigger ( "change" );
286 // check the address is generated correctly
287 waitForGenerate ( function () {
288 var actual
= page
. evaluate ( function () {
289 return $( ".address:first" ). text ();
291 if ( actual
!= expected
) {
292 console
. log ( "Bitcoin testnet address is incorrect" );
293 console
. log ( "Expected: " + expected
);
294 console
. log ( "Actual: " + actual
);
302 // Network can be set to litecoin
304 page
. open ( url
, function ( status
) {
305 // set the phrase and coin
306 var expected
= "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn" ;
307 page
. evaluate ( function () {
308 $( ".phrase" ). val ( "abandon abandon ability" );
309 $( ".phrase" ). trigger ( "input" );
310 $( ".network option[selected]" ). removeAttr ( "selected" );
311 $( ".network option[value=2]" ). prop ( "selected" , true );
312 $( ".network" ). trigger ( "change" );
314 // check the address is generated correctly
315 waitForGenerate ( function () {
316 var actual
= page
. evaluate ( function () {
317 return $( ".address:first" ). text ();
319 if ( actual
!= expected
) {
320 console
. log ( "Litecoin address is incorrect" );
321 console
. log ( "Expected: " + expected
);
322 console
. log ( "Actual: " + actual
);
330 // Network can be set to dogecoin
332 page
. open ( url
, function ( status
) {
333 // set the phrase and coin
334 var expected
= "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA" ;
335 page
. evaluate ( function () {
336 $( ".phrase" ). val ( "abandon abandon ability" );
337 $( ".phrase" ). trigger ( "input" );
338 $( ".network option[selected]" ). removeAttr ( "selected" );
339 $( ".network option[value=3]" ). prop ( "selected" , true );
340 $( ".network" ). trigger ( "change" );
342 // check the address is generated correctly
343 waitForGenerate ( function () {
344 var actual
= page
. evaluate ( function () {
345 return $( ".address:first" ). text ();
347 if ( actual
!= expected
) {
348 console
. log ( "Dogecoin address is incorrect" );
349 console
. log ( "Expected: " + expected
);
350 console
. log ( "Actual: " + actual
);
358 // Network can be set to shadowcash
360 page
. open ( url
, function ( status
) {
361 // set the phrase and coin
362 var expected
= "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG" ;
363 page
. evaluate ( function () {
364 $( ".phrase" ). val ( "abandon abandon ability" );
365 $( ".phrase" ). trigger ( "input" );
366 $( ".network option[selected]" ). removeAttr ( "selected" );
367 $( ".network option[value=4]" ). prop ( "selected" , true );
368 $( ".network" ). trigger ( "change" );
370 // check the address is generated correctly
371 waitForGenerate ( function () {
372 var actual
= page
. evaluate ( function () {
373 return $( ".address:first" ). text ();
375 if ( actual
!= expected
) {
376 console
. log ( "Shadowcash address is incorrect" );
377 console
. log ( "Expected: " + expected
);
378 console
. log ( "Actual: " + actual
);
386 // Network can be set to shadowcash testnet
388 page
. open ( url
, function ( status
) {
389 // set the phrase and coin
390 var expected
= "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe" ;
391 page
. evaluate ( function () {
392 $( ".phrase" ). val ( "abandon abandon ability" );
393 $( ".phrase" ). trigger ( "input" );
394 $( ".network option[selected]" ). removeAttr ( "selected" );
395 $( ".network option[value=5]" ). prop ( "selected" , true );
396 $( ".network" ). trigger ( "change" );
398 // check the address is generated correctly
399 waitForGenerate ( function () {
400 var actual
= page
. evaluate ( function () {
401 return $( ".address:first" ). text ();
403 if ( actual
!= expected
) {
404 console
. log ( "Shadowcash testnet address is incorrect" );
405 console
. log ( "Expected: " + expected
);
406 console
. log ( "Actual: " + actual
);
414 // Network can be set to viacoin
416 page
. open ( url
, function ( status
) {
417 // set the phrase and coin
418 var expected
= "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT" ;
419 page
. evaluate ( function () {
420 $( ".phrase" ). val ( "abandon abandon ability" );
421 $( ".phrase" ). trigger ( "input" );
422 $( ".network option[selected]" ). removeAttr ( "selected" );
423 $( ".network option[value=6]" ). prop ( "selected" , true );
424 $( ".network" ). trigger ( "change" );
426 // check the address is generated correctly
427 waitForGenerate ( function () {
428 var actual
= page
. evaluate ( function () {
429 return $( ".address:first" ). text ();
431 if ( actual
!= expected
) {
432 console
. log ( "Viacoin address is incorrect" );
433 console
. log ( "Expected: " + expected
);
434 console
. log ( "Actual: " + actual
);
442 // Network can be set to viacoin testnet
444 page
. open ( url
, function ( status
) {
445 // set the phrase and coin
446 var expected
= "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe" ;
447 page
. evaluate ( function () {
448 $( ".phrase" ). val ( "abandon abandon ability" );
449 $( ".phrase" ). trigger ( "input" );
450 $( ".network option[selected]" ). removeAttr ( "selected" );
451 $( ".network option[value=7]" ). prop ( "selected" , true );
452 $( ".network" ). trigger ( "change" );
454 // check the address is generated correctly
455 waitForGenerate ( function () {
456 var actual
= page
. evaluate ( function () {
457 return $( ".address:first" ). text ();
459 if ( actual
!= expected
) {
460 console
. log ( "Viacoin testnet address is incorrect" );
461 console
. log ( "Expected: " + expected
);
462 console
. log ( "Actual: " + actual
);
470 // Network can be set to jumbucks
472 page
. open ( url
, function ( status
) {
473 // set the phrase and coin
474 var expected
= "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew" ;
475 page
. evaluate ( function () {
476 $( ".phrase" ). val ( "abandon abandon ability" );
477 $( ".phrase" ). trigger ( "input" );
478 $( ".network option[selected]" ). removeAttr ( "selected" );
479 $( ".network option[value=8]" ). prop ( "selected" , true );
480 $( ".network" ). trigger ( "change" );
482 // check the address is generated correctly
483 waitForGenerate ( function () {
484 var actual
= page
. evaluate ( function () {
485 return $( ".address:first" ). text ();
487 if ( actual
!= expected
) {
488 console
. log ( "Jumbucks address is incorrect" );
489 console
. log ( "Expected: " + expected
);
490 console
. log ( "Actual: " + actual
);
498 // Network can be set to clam
500 page
. open ( url
, function ( status
) {
501 // set the phrase and coin
502 var expected
= "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y" ;
503 page
. evaluate ( function () {
504 $( ".phrase" ). val ( "abandon abandon ability" );
505 $( ".phrase" ). trigger ( "input" );
506 $( ".network option[selected]" ). removeAttr ( "selected" );
507 $( ".network option[value=9]" ). prop ( "selected" , true );
508 $( ".network" ). trigger ( "change" );
510 // check the address is generated correctly
511 waitForGenerate ( function () {
512 var actual
= page
. evaluate ( function () {
513 return $( ".address:first" ). text ();
515 if ( actual
!= expected
) {
516 console
. log ( "CLAM address is incorrect" );
517 console
. log ( "Expected: " + expected
);
518 console
. log ( "Actual: " + actual
);
526 // Network can be set to dash
528 page
. open ( url
, function ( status
) {
529 // set the phrase and coin
530 var expected
= "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT" ;
531 page
. evaluate ( function () {
532 $( ".phrase" ). val ( "abandon abandon ability" );
533 $( ".phrase" ). trigger ( "input" );
534 $( ".network option[selected]" ). removeAttr ( "selected" );
535 $( ".network option[value=10]" ). prop ( "selected" , true );
536 $( ".network" ). trigger ( "change" );
538 // check the address is generated correctly
539 waitForGenerate ( function () {
540 var actual
= page
. evaluate ( function () {
541 return $( ".address:first" ). text ();
543 if ( actual
!= expected
) {
544 console
. log ( "DASH address is incorrect" );
545 console
. log ( "Expected: " + expected
);
546 console
. log ( "Actual: " + actual
);
554 // Network can be set to namecoin
556 page
. open ( url
, function ( status
) {
557 // set the phrase and coin
558 var expected
= "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2" ;
559 page
. evaluate ( function () {
560 $( ".phrase" ). val ( "abandon abandon ability" );
561 $( ".phrase" ). trigger ( "input" );
562 $( ".network option[selected]" ). removeAttr ( "selected" );
563 $( ".network option[value=11]" ). prop ( "selected" , true );
564 $( ".network" ). trigger ( "change" );
566 // check the address is generated correctly
567 waitForGenerate ( function () {
568 var actual
= page
. evaluate ( function () {
569 return $( ".address:first" ). text ();
571 if ( actual
!= expected
) {
572 console
. log ( "Namecoin address is incorrect" );
573 console
. log ( "Expected: " + expected
);
574 console
. log ( "Actual: " + actual
);
582 // Network can be set to peercoin
584 page
. open ( url
, function ( status
) {
585 // set the phrase and coin
586 var expected
= "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm" ;
587 page
. evaluate ( function () {
588 $( ".phrase" ). val ( "abandon abandon ability" );
589 $( ".phrase" ). trigger ( "input" );
590 $( ".network option[selected]" ). removeAttr ( "selected" );
591 $( ".network option[value=12]" ). prop ( "selected" , true );
592 $( ".network" ). trigger ( "change" );
594 // check the address is generated correctly
595 waitForGenerate ( function () {
596 var actual
= page
. evaluate ( function () {
597 return $( ".address:first" ). text ();
599 if ( actual
!= expected
) {
600 console
. log ( "Peercoin address is incorrect" );
601 console
. log ( "Expected: " + expected
);
602 console
. log ( "Actual: " + actual
);
610 // BIP39 seed is set from phrase
612 page
. open ( url
, function ( status
) {
614 var expected
= "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3" ;
615 page
. evaluate ( function () {
616 $( ".phrase" ). val ( "abandon abandon ability" );
617 $( ".phrase" ). trigger ( "input" );
619 // check the address is generated correctly
620 waitForGenerate ( function () {
621 var actual
= page
. evaluate ( function () {
622 return $( ".seed" ). val ();
624 if ( actual
!= expected
) {
625 console
. log ( "BIP39 seed is incorrectly generated from mnemonic" );
626 console
. log ( "Expected: " + expected
);
627 console
. log ( "Actual: " + actual
);
635 // BIP32 root key is set from phrase
637 page
. open ( url
, function ( status
) {
639 var expected
= "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi" ;
640 page
. evaluate ( function () {
641 $( ".phrase" ). val ( "abandon abandon ability" );
642 $( ".phrase" ). trigger ( "input" );
644 // check the address is generated correctly
645 waitForGenerate ( function () {
646 var actual
= page
. evaluate ( function () {
647 return $( ".root-key" ). val ();
649 if ( actual
!= expected
) {
650 console
. log ( "Root key is incorrectly generated from mnemonic" );
651 console
. log ( "Expected: " + expected
);
652 console
. log ( "Actual: " + actual
);
660 // Tabs show correct addresses when changed
662 page
. open ( url
, function ( status
) {
664 var expected
= "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz" ;
665 page
. evaluate ( function () {
666 $( ".phrase" ). val ( "abandon abandon ability" );
667 $( ".phrase" ). trigger ( "input" );
670 waitForGenerate ( function () {
671 page
. evaluate ( function () {
672 $( "#bip32-tab a" ). click ();
674 // check the address is generated correctly
675 waitForGenerate ( function () {
676 var actual
= page
. evaluate ( function () {
677 return $( ".address:first" ). text ();
679 if ( actual
!= expected
) {
680 console
. log ( "Clicking tab generates incorrect address" );
681 console
. log ( "Expected: " + expected
);
682 console
. log ( "Actual: " + actual
);
691 // BIP44 derivation path is shown
693 page
. open ( url
, function ( status
) {
695 var expected
= "m/44'/0'/0'/0" ;
696 page
. evaluate ( function () {
697 $( ".phrase" ). val ( "abandon abandon ability" );
698 $( ".phrase" ). trigger ( "input" );
700 // check the derivation path of the first address
701 waitForGenerate ( function () {
702 var actual
= page
. evaluate ( function () {
703 return $( "#bip44 .path" ). val ();
705 if ( actual
!= expected
) {
706 console
. log ( "BIP44 derivation path is incorrect" );
707 console
. log ( "Expected: " + expected
);
708 console
. log ( "Actual: " + actual
);
716 // BIP44 extended private key is shown
718 page
. open ( url
, function ( status
) {
720 var expected
= "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG" ;
721 page
. evaluate ( function () {
722 $( ".phrase" ). val ( "abandon abandon ability" );
723 $( ".phrase" ). trigger ( "input" );
725 // check the BIP44 extended private key
726 waitForGenerate ( function () {
727 var actual
= page
. evaluate ( function () {
728 return $( ".extended-priv-key" ). val ();
730 if ( actual
!= expected
) {
731 console
. log ( "BIP44 extended private key is incorrect" );
732 console
. log ( "Expected: " + expected
);
733 console
. log ( "Actual: " + actual
);
741 // BIP44 extended public key is shown
743 page
. open ( url
, function ( status
) {
745 var expected
= "xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM" ;
746 page
. evaluate ( function () {
747 $( ".phrase" ). val ( "abandon abandon ability" );
748 $( ".phrase" ). trigger ( "input" );
750 // check the BIP44 extended public key
751 waitForGenerate ( function () {
752 var actual
= page
. evaluate ( function () {
753 return $( ".extended-pub-key" ). val ();
755 if ( actual
!= expected
) {
756 console
. log ( "BIP44 extended public key is incorrect" );
757 console
. log ( "Expected: " + expected
);
758 console
. log ( "Actual: " + actual
);
766 // BIP44 purpose field changes address list
768 page
. open ( url
, function ( status
) {
770 var expected
= "1JbDzRJ2cDT8aat2xwKd6Pb2zzavow5MhF" ;
771 page
. evaluate ( function () {
772 $( ".phrase" ). val ( "abandon abandon ability" );
773 $( ".phrase" ). trigger ( "input" );
775 waitForGenerate ( function () {
776 // change the bip44 purpose field to 45
777 page
. evaluate ( function () {
778 $( "#bip44 .purpose" ). val ( "45" );
779 $( "#bip44 .purpose" ). trigger ( "input" );
781 waitForGenerate ( function () {
782 // check the address for the new derivation path
783 var actual
= page
. evaluate ( function () {
784 return $( ".address:first" ). text ();
786 if ( actual
!= expected
) {
787 console
. log ( "BIP44 purpose field generates incorrect address" );
788 console
. log ( "Expected: " + expected
);
789 console
. log ( "Actual: " + actual
);
798 // BIP44 coin field changes address list
800 page
. open ( url
, function ( status
) {
802 var expected
= "1F6dB2djQYrxoyfZZmfr6D5voH8GkJTghk" ;
803 page
. evaluate ( function () {
804 $( ".phrase" ). val ( "abandon abandon ability" );
805 $( ".phrase" ). trigger ( "input" );
807 waitForGenerate ( function () {
808 // change the bip44 purpose field to 45
809 page
. evaluate ( function () {
810 $( "#bip44 .coin" ). val ( "1" );
811 $( "#bip44 .coin" ). trigger ( "input" );
813 waitForGenerate ( function () {
814 // check the address for the new derivation path
815 var actual
= page
. evaluate ( function () {
816 return $( ".address:first" ). text ();
818 if ( actual
!= expected
) {
819 console
. log ( "BIP44 coin field generates incorrect address" );
820 console
. log ( "Expected: " + expected
);
821 console
. log ( "Actual: " + actual
);
830 // BIP44 account field changes address list
832 page
. open ( url
, function ( status
) {
834 var expected
= "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H" ;
835 page
. evaluate ( function () {
836 $( ".phrase" ). val ( "abandon abandon ability" );
837 $( ".phrase" ). trigger ( "input" );
839 waitForGenerate ( function () {
840 // change the bip44 purpose field to 45
841 page
. evaluate ( function () {
842 $( "#bip44 .account" ). val ( "1" );
843 $( "#bip44 .account" ). trigger ( "input" );
845 waitForGenerate ( function () {
846 // check the address for the new derivation path
847 var actual
= page
. evaluate ( function () {
848 return $( ".address:first" ). text ();
850 if ( actual
!= expected
) {
851 console
. log ( "BIP44 account field generates incorrect address" );
852 console
. log ( "Expected: " + expected
);
853 console
. log ( "Actual: " + actual
);
862 // BIP44 change field changes address list
864 page
. open ( url
, function ( status
) {
866 var expected
= "1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo" ;
867 page
. evaluate ( function () {
868 $( ".phrase" ). val ( "abandon abandon ability" );
869 $( ".phrase" ). trigger ( "input" );
871 waitForGenerate ( function () {
872 // change the bip44 purpose field to 45
873 page
. evaluate ( function () {
874 $( "#bip44 .change" ). val ( "1" );
875 $( "#bip44 .change" ). trigger ( "input" );
877 waitForGenerate ( function () {
878 // check the address for the new derivation path
879 var actual
= page
. evaluate ( function () {
880 return $( ".address:first" ). text ();
882 if ( actual
!= expected
) {
883 console
. log ( "BIP44 change field generates incorrect address" );
884 console
. log ( "Expected: " + expected
);
885 console
. log ( "Actual: " + actual
);
894 // BIP32 derivation path can be set
896 page
. open ( url
, function ( status
) {
898 var expected
= "16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L" ;
899 page
. evaluate ( function () {
900 $( ".phrase" ). val ( "abandon abandon ability" );
901 $( ".phrase" ). trigger ( "input" );
904 waitForGenerate ( function () {
905 page
. evaluate ( function () {
906 $( "#bip32-tab a" ). click ();
908 // set the derivation path to m/1
909 waitForGenerate ( function () {
910 page
. evaluate ( function () {
911 $( "#bip32 .path" ). val ( "m/1" );
912 $( "#bip32 .path" ). trigger ( "input" );
914 // check the address is generated correctly
915 waitForGenerate ( function () {
916 var actual
= page
. evaluate ( function () {
917 return $( ".address:first" ). text ();
919 if ( actual
!= expected
) {
920 console
. log ( "Custom BIP32 path generates incorrect address" );
921 console
. log ( "Expected: " + expected
);
922 console
. log ( "Actual: " + actual
);
932 // BIP32 can use hardened derivation paths
934 page
. open ( url
, function ( status
) {
936 var expected
= "14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4" ;
937 page
. evaluate ( function () {
938 $( ".phrase" ). val ( "abandon abandon ability" );
939 $( ".phrase" ). trigger ( "input" );
942 waitForGenerate ( function () {
943 page
. evaluate ( function () {
944 $( "#bip32-tab a" ). click ();
946 // set the derivation path to m/0'
947 waitForGenerate ( function () {
948 page
. evaluate ( function () {
949 $( "#bip32 .path" ). val ( "m/0'" );
950 $( "#bip32 .path" ). trigger ( "input" );
952 // check the address is generated correctly
953 waitForGenerate ( function () {
954 var actual
= page
. evaluate ( function () {
955 return $( ".address:first" ). text ();
957 if ( actual
!= expected
) {
958 console
. log ( "Hardened BIP32 path generates incorrect address" );
959 console
. log ( "Expected: " + expected
);
960 console
. log ( "Actual: " + actual
);
970 // BIP32 extended private key is shown
972 page
. open ( url
, function ( status
) {
974 var expected
= "xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe" ;
975 page
. evaluate ( function () {
976 $( ".phrase" ). val ( "abandon abandon ability" );
977 $( ".phrase" ). trigger ( "input" );
980 waitForGenerate ( function () {
981 page
. evaluate ( function () {
982 $( "#bip32-tab a" ). click ();
984 // check the extended private key is generated correctly
985 waitForGenerate ( function () {
986 var actual
= page
. evaluate ( function () {
987 return $( ".extended-priv-key" ). val ();
989 if ( actual
!= expected
) {
990 console
. log ( "BIP32 extended private key is incorrect" );
991 console
. log ( "Expected: " + expected
);
992 console
. log ( "Actual: " + actual
);
1001 // BIP32 extended public key is shown
1003 page
. open ( url
, function ( status
) {
1005 var expected
= "xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P" ;
1006 page
. evaluate ( function () {
1007 $( ".phrase" ). val ( "abandon abandon ability" );
1008 $( ".phrase" ). trigger ( "input" );
1011 waitForGenerate ( function () {
1012 page
. evaluate ( function () {
1013 $( "#bip32-tab a" ). click ();
1015 // check the extended public key is generated correctly
1016 waitForGenerate ( function () {
1017 var actual
= page
. evaluate ( function () {
1018 return $( ".extended-pub-key" ). val ();
1020 if ( actual
!= expected
) {
1021 console
. log ( "BIP32 extended public key is incorrect" );
1022 console
. log ( "Expected: " + expected
);
1023 console
. log ( "Actual: " + actual
);
1032 // Derivation path is shown in table
1034 page
. open ( url
, function ( status
) {
1036 var expected
= "m/44'/0'/0'/0/0" ;
1037 page
. evaluate ( function () {
1038 $( ".phrase" ). val ( "abandon abandon ability" );
1039 $( ".phrase" ). trigger ( "input" );
1041 // check for derivation path in table
1042 waitForGenerate ( function () {
1043 var actual
= page
. evaluate ( function () {
1044 return $( ".index:first" ). text ();
1046 if ( actual
!= expected
) {
1047 console
. log ( "Derivation path shown incorrectly in table" );
1048 console
. log ( "Expected: " + expected
);
1049 console
. log ( "Actual: " + actual
);
1057 // Derivation path for address can be hardened
1059 page
. open ( url
, function ( status
) {
1061 var expected
= "18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd" ;
1062 page
. evaluate ( function () {
1063 $( ".phrase" ). val ( "abandon abandon ability" );
1064 $( ".phrase" ). trigger ( "input" );
1067 waitForGenerate ( function () {
1068 page
. evaluate ( function () {
1069 $( "#bip32-tab a" ). click ();
1071 waitForGenerate ( function () {
1072 // select the hardened addresses option
1073 page
. evaluate ( function () {
1074 $( ".hardened-addresses" ). prop ( "checked" , true );
1075 $( ".hardened-addresses" ). trigger ( "change" );
1077 waitForGenerate ( function () {
1078 // check the generated address is hardened
1079 var actual
= page
. evaluate ( function () {
1080 return $( ".address:first" ). text ();
1082 if ( actual
!= expected
) {
1083 console
. log ( "Hardened address is incorrect" );
1084 console
. log ( "Expected: " + expected
);
1085 console
. log ( "Actual: " + actual
);
1095 // Derivation path visibility can be toggled
1097 page
. open ( url
, function ( status
) {
1099 page
. evaluate ( function () {
1100 $( ".phrase" ). val ( "abandon abandon ability" );
1101 $( ".phrase" ). trigger ( "input" );
1103 waitForGenerate ( function () {
1104 // toggle path visibility
1105 page
. evaluate ( function () {
1106 $( ".index-toggle" ). click ();
1108 // check the path is not visible
1109 var isInvisible
= page
. evaluate ( function () {
1110 return $( ".index:first span" ). hasClass ( "invisible" );
1113 console
. log ( "Toggled derivation path is visible" );
1123 page
. open ( url
, function ( status
) {
1124 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug" ;
1126 page
. evaluate ( function () {
1127 $( ".phrase" ). val ( "abandon abandon ability" ). trigger ( "input" );
1130 waitForGenerate ( function () {
1131 var actual
= page
. evaluate ( function () {
1132 return $( ".address:first" ). text ();
1134 if ( actual
!= expected
) {
1135 console
. log ( "Address is not shown" );
1136 console
. log ( "Expected: " + expected
);
1137 console
. log ( "Got: " + actual
);
1145 // Addresses are shown in order of derivation path
1147 page
. open ( url
, function ( status
) {
1149 page
. evaluate ( function () {
1150 $( ".phrase" ). val ( "abandon abandon ability" ). trigger ( "input" );
1152 // get the derivation paths
1153 waitForGenerate ( function () {
1154 var paths
= page
. evaluate ( function () {
1155 return $( ".index" ). map ( function ( i
, e
) {
1159 if ( paths
. length
!= 20 ) {
1160 console
. log ( "Total paths is less than expected: " + paths
. length
);
1163 for ( var i
= 0 ; i
< paths
. length
; i
++) {
1164 var expected
= "m/44'/0'/0'/0/" + i
;
1165 var actual
= paths
[ i
];
1166 if ( actual
!= expected
) {
1167 console
. log ( "Path " + i
+ " is incorrect" );
1168 console
. log ( "Expected: " + expected
);
1169 console
. log ( "Actual: " + actual
);
1178 // Address visibility can be toggled
1180 page
. open ( url
, function ( status
) {
1182 page
. evaluate ( function () {
1183 $( ".phrase" ). val ( "abandon abandon ability" );
1184 $( ".phrase" ). trigger ( "input" );
1186 waitForGenerate ( function () {
1187 // toggle address visibility
1188 page
. evaluate ( function () {
1189 $( ".address-toggle" ). click ();
1191 // check the address is not visible
1192 var isInvisible
= page
. evaluate ( function () {
1193 return $( ".address:first span" ). hasClass ( "invisible" );
1196 console
. log ( "Toggled address is visible" );
1204 // Public key is shown
1206 page
. open ( url
, function ( status
) {
1207 var expected
= "033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3" ;
1209 page
. evaluate ( function () {
1210 $( ".phrase" ). val ( "abandon abandon ability" ). trigger ( "input" );
1213 waitForGenerate ( function () {
1214 var actual
= page
. evaluate ( function () {
1215 return $( ".pubkey:first" ). text ();
1217 if ( actual
!= expected
) {
1218 console
. log ( "Public key is not shown" );
1219 console
. log ( "Expected: " + expected
);
1220 console
. log ( "Got: " + actual
);
1228 // Public key visibility can be toggled
1230 page
. open ( url
, function ( status
) {
1232 page
. evaluate ( function () {
1233 $( ".phrase" ). val ( "abandon abandon ability" );
1234 $( ".phrase" ). trigger ( "input" );
1236 waitForGenerate ( function () {
1237 // toggle public key visibility
1238 page
. evaluate ( function () {
1239 $( ".public-key-toggle" ). click ();
1241 // check the public key is not visible
1242 var isInvisible
= page
. evaluate ( function () {
1243 return $( ".pubkey:first span" ). hasClass ( "invisible" );
1246 console
. log ( "Toggled public key is visible" );
1254 // Private key is shown
1256 page
. open ( url
, function ( status
) {
1257 var expected
= "L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE" ;
1259 page
. evaluate ( function () {
1260 $( ".phrase" ). val ( "abandon abandon ability" ). trigger ( "input" );
1263 waitForGenerate ( function () {
1264 var actual
= page
. evaluate ( function () {
1265 return $( ".privkey:first" ). text ();
1267 if ( actual
!= expected
) {
1268 console
. log ( "Private key is not shown" );
1269 console
. log ( "Expected: " + expected
);
1270 console
. log ( "Got: " + actual
);
1278 // Private key visibility can be toggled
1280 page
. open ( url
, function ( status
) {
1282 page
. evaluate ( function () {
1283 $( ".phrase" ). val ( "abandon abandon ability" );
1284 $( ".phrase" ). trigger ( "input" );
1286 waitForGenerate ( function () {
1287 // toggle private key visibility
1288 page
. evaluate ( function () {
1289 $( ".private-key-toggle" ). click ();
1291 // check the private key is not visible
1292 var isInvisible
= page
. evaluate ( function () {
1293 return $( ".privkey:first span" ). hasClass ( "invisible" );
1296 console
. log ( "Toggled private key is visible" );
1304 // More addresses can be generated
1306 page
. open ( url
, function ( status
) {
1308 page
. evaluate ( function () {
1309 $( ".phrase" ). val ( "abandon abandon ability" );
1310 $( ".phrase" ). trigger ( "input" );
1312 waitForGenerate ( function () {
1313 // generate more addresses
1314 page
. evaluate ( function () {
1317 waitForGenerate ( function () {
1318 // check there are more addresses
1319 var addressCount
= page
. evaluate ( function () {
1320 return $( ".address" ). length
;
1322 if ( addressCount
!= 40 ) {
1323 console
. log ( "More addresses cannot be generated" );
1332 // A custom number of additional addresses can be generated
1334 page
. open ( url
, function ( status
) {
1336 page
. evaluate ( function () {
1337 $( ".phrase" ). val ( "abandon abandon ability" );
1338 $( ".phrase" ). trigger ( "input" );
1340 waitForGenerate ( function () {
1341 // get the current number of addresses
1342 var oldAddressCount
= page
. evaluate ( function () {
1343 return $( ".address" ). length
;
1345 // set a custom number of additional addresses
1346 page
. evaluate ( function () {
1347 $( ".rows-to-add" ). val ( 1 );
1349 // generate more addresses
1350 page
. evaluate ( function () {
1353 waitForGenerate ( function () {
1354 // check there are the correct number of addresses
1355 var newAddressCount
= page
. evaluate ( function () {
1356 return $( ".address" ). length
;
1358 if ( newAddressCount
- oldAddressCount
!= 1 ) {
1359 console
. log ( "Number of additional addresses cannot be customized" );
1360 console
. log ( newAddressCount
)
1361 console
. log ( oldAddressCount
)
1370 // Additional addresses are shown in order of derivation path
1372 page
. open ( url
, function ( status
) {
1374 page
. evaluate ( function () {
1375 $( ".phrase" ). val ( "abandon abandon ability" ). trigger ( "input" );
1377 waitForGenerate ( function () {
1378 // generate more addresses
1379 page
. evaluate ( function () {
1382 // get the derivation paths
1383 waitForGenerate ( function () {
1384 var paths
= page
. evaluate ( function () {
1385 return $( ".index" ). map ( function ( i
, e
) {
1389 if ( paths
. length
!= 40 ) {
1390 console
. log ( "Total additional paths is less than expected: " + paths
. length
);
1393 for ( var i
= 0 ; i
< paths
. length
; i
++) {
1394 var expected
= "m/44'/0'/0'/0/" + i
;
1395 var actual
= paths
[ i
];
1396 if ( actual
!= expected
) {
1397 console
. log ( "Path " + i
+ " is not in correct order" );
1398 console
. log ( "Expected: " + expected
);
1399 console
. log ( "Actual: " + actual
);
1409 // BIP32 root key can be set by the user
1411 page
. open ( url
, function ( status
) {
1412 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug" ;
1414 page
. evaluate ( function () {
1415 $( ".root-key" ). val ( "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi" ). trigger ( "input" );
1417 waitForGenerate ( function () {
1418 var actual
= page
. evaluate ( function () {
1419 return $( ".address:first" ). text ();
1421 if ( actual
!= expected
) {
1422 console
. log ( "Setting BIP32 root key results in wrong address" );
1423 console
. log ( "Expected: " + expected
);
1424 console
. log ( "Actual: " + actual
);
1432 // Setting BIP32 root key clears the existing phrase, passphrase and seed
1434 page
. open ( url
, function ( status
) {
1437 page
. evaluate ( function () {
1438 $( ".phrase" ). val ( "A non-blank but invalid value" );
1440 // Accept any confirm dialogs
1441 page
. onConfirm = function () {
1445 page
. evaluate ( function () {
1446 $( ".root-key" ). val ( "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi" ). trigger ( "input" );
1448 waitForGenerate ( function () {
1449 var actual
= page
. evaluate ( function () {
1450 return $( ".phrase" ). val ();
1452 if ( actual
!= expected
) {
1453 console
. log ( "Phrase not cleared when setting BIP32 root key" );
1454 console
. log ( "Expected: " + expected
);
1455 console
. log ( "Actual: " + actual
);
1463 // Clearing of phrase, passphrase and seed can be cancelled by user
1465 page
. open ( url
, function ( status
) {
1466 var expected
= "abandon abandon ability" ;
1468 page
. evaluate ( function () {
1469 $( ".phrase" ). val ( "abandon abandon ability" );
1471 // Cancel any confirm dialogs
1472 page
. onConfirm = function () {
1476 page
. evaluate ( function () {
1477 $( ".root-key" ). val ( "xprv9s21ZrQH143K3d3vzEDD3KpSKmxsZ3y7CqhAL1tinwtP6wqK4TKEKjpBuo6P2hUhB6ZENo7TTSRytiP857hBZVpBdk8PooFuRspE1eywwNZ" ). trigger ( "input" );
1479 var actual
= page
. evaluate ( function () {
1480 return $( ".phrase" ). val ();
1482 if ( actual
!= expected
) {
1483 console
. log ( "Phrase not retained when cancelling changes to BIP32 root key" );
1484 console
. log ( "Expected: " + expected
);
1485 console
. log ( "Actual: " + actual
);
1492 // Custom BIP32 root key is used when changing the derivation path
1494 page
. open ( url
, function ( status
) {
1495 var expected
= "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H" ;
1497 page
. evaluate ( function () {
1498 $( ".root-key" ). val ( "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi" ). trigger ( "input" );
1500 waitForGenerate ( function () {
1501 // change the derivation path
1502 page
. evaluate ( function () {
1503 $( "#account" ). val ( "1" ). trigger ( "input" );
1505 // check the bip32 root key is used for derivation, not the blank phrase
1506 waitForGenerate ( function () {
1507 var actual
= page
. evaluate ( function () {
1508 return $( ".address:first" ). text ();
1510 if ( actual
!= expected
) {
1511 console
. log ( "Changing the derivation path does not use BIP32 root key" );
1512 console
. log ( "Expected: " + expected
);
1513 console
. log ( "Actual: " + actual
);
1522 // Incorrect mnemonic shows error
1524 page
. open ( url
, function ( status
) {
1526 page
. evaluate ( function () {
1527 $( ".phrase" ). val ( "abandon abandon abandon" ). trigger ( "input" );
1529 waitForFeedback ( function () {
1530 // check there is an error shown
1531 var feedback
= page
. evaluate ( function () {
1532 return $( ".feedback" ). text ();
1534 if ( feedback
. length
<= 0 ) {
1535 console
. log ( "Invalid mnemonic does not show error" );
1543 // Incorrect word shows suggested replacement
1545 page
. open ( url
, function ( status
) {
1547 page
. evaluate ( function () {
1548 $( ".phrase" ). val ( "abandon abandon abiliti" ). trigger ( "input" );
1550 // check there is a suggestion shown
1551 waitForFeedback ( function () {
1552 var feedback
= page
. evaluate ( function () {
1553 return $( ".feedback" ). text ();
1555 if ( feedback
. indexOf ( "did you mean ability?" ) < 0 ) {
1556 console
. log ( "Incorrect word does not show suggested replacement" );
1557 console
. log ( "Error: " + error
);
1565 // Incorrect BIP32 root key shows error
1567 page
. open ( url
, function ( status
) {
1569 page
. evaluate ( function () {
1570 $( ".root-key" ). val ( "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj" ). trigger ( "input" );
1572 // check there is an error shown
1573 waitForFeedback ( function () {
1574 var feedback
= page
. evaluate ( function () {
1575 return $( ".feedback" ). text ();
1577 if ( feedback
!= "Invalid root key" ) {
1578 console
. log ( "Invalid root key does not show error" );
1579 console
. log ( "Error: " + error
);
1587 // Derivation path not starting with m shows error
1589 page
. open ( url
, function ( status
) {
1590 // set the mnemonic phrase
1591 page
. evaluate ( function () {
1592 $( ".phrase" ). val ( "abandon abandon ability" ). trigger ( "input" );
1594 waitForGenerate ( function () {
1595 // select the bip32 tab so custom derivation path can be set
1596 page
. evaluate ( function () {
1597 $( "#bip32-tab a" ). click ();
1599 waitForGenerate ( function () {
1600 // set the incorrect derivation path
1601 page
. evaluate ( function () {
1602 $( "#bip32 .path" ). val ( "n/0" ). trigger ( "input" );
1604 waitForFeedback ( function () {
1605 var feedback
= page
. evaluate ( function () {
1606 return $( ".feedback" ). text ();
1608 if ( feedback
!= "First character must be 'm'" ) {
1609 console
. log ( "Derivation path not starting with m should show error" );
1610 console
. log ( "Error: " + error
);
1620 // Derivation path containing invalid characters shows useful error
1622 page
. open ( url
, function ( status
) {
1623 // set the mnemonic phrase
1624 page
. evaluate ( function () {
1625 $( ".phrase" ). val ( "abandon abandon ability" ). trigger ( "input" );
1627 waitForGenerate ( function () {
1628 // select the bip32 tab so custom derivation path can be set
1629 page
. evaluate ( function () {
1630 $( "#bip32-tab a" ). click ();
1632 waitForGenerate ( function () {
1633 // set the incorrect derivation path
1634 page
. evaluate ( function () {
1635 $( "#bip32 .path" ). val ( "m/1/0wrong1/1" ). trigger ( "input" );
1637 waitForFeedback ( function () {
1638 var feedback
= page
. evaluate ( function () {
1639 return $( ".feedback" ). text ();
1641 if ( feedback
!= "Invalid characters 0wrong1 found at depth 2" ) {
1642 console
. log ( "Derivation path with invalid characters should show error" );
1643 console
. log ( "Error: " + error
);
1653 // Github Issue 11: Default word length is 15
1654 // https://github.com/iancoleman/bip39/issues/11
1656 page
. open ( url
, function ( status
) {
1657 // get the word length
1658 var defaultLength
= page
. evaluate ( function () {
1659 return $( ".strength" ). val ();
1661 if ( defaultLength
!= 15 ) {
1662 console
. log ( "Default word length is not 15" );
1670 // Github Issue 12: Generate more rows with private keys hidden
1671 // https://github.com/iancoleman/bip39/issues/12
1673 page
. open ( url
, function ( status
) {
1675 page
. evaluate ( function () {
1676 $( ".phrase" ). val ( "abandon abandon ability" );
1677 $( ".phrase" ). trigger ( "input" );
1679 waitForGenerate ( function () {
1680 // toggle private keys hidden, then generate more addresses
1681 page
. evaluate ( function () {
1682 $( ".private-key-toggle" ). click ();
1685 waitForGenerate ( function () {
1686 // check more have been generated
1688 var numPrivKeys
= page
. evaluate ( function () {
1689 return $( ".privkey" ). length
;
1691 if ( numPrivKeys
!= expected
) {
1692 console
. log ( "Wrong number of addresses when clicking 'more' with hidden privkeys" );
1693 console
. log ( "Expected: " + expected
);
1694 console
. log ( "Actual: " + numPrivKeys
);
1697 // check no private keys are shown
1698 var numHiddenPrivKeys
= page
. evaluate ( function () {
1699 return $( ".privkey span[class=invisible]" ). length
;
1701 if ( numHiddenPrivKeys
!= expected
) {
1702 console
. log ( "Generating more does not retain hidden state of privkeys" );
1703 console
. log ( "Expected: " + expected
);
1704 console
. log ( "Actual: " + numHiddenPrivKeys
);
1713 // Github Issue 19: Mnemonic is not sensitive to whitespace
1714 // https://github.com/iancoleman/bip39/issues/19
1716 page
. open ( url
, function ( status
) {
1718 var expected
= "xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC" ;
1719 page
. evaluate ( function () {
1720 var doubleSpace
= " " ;
1721 $( ".phrase" ). val ( "urge cat" + doubleSpace
+ "bid" );
1722 $( ".phrase" ). trigger ( "input" );
1724 waitForGenerate ( function () {
1725 // Check the bip32 root key is correct
1726 var actual
= page
. evaluate ( function () {
1727 return $( ".root-key" ). val ();
1729 if ( actual
!= expected
) {
1730 console
. log ( "Mnemonic is sensitive to whitespace" );
1731 console
. log ( "Expected: " + expected
);
1732 console
. log ( "Actual: " + actual
);
1740 // Github Issue 23: Part 1: Use correct derivation path when changing tabs
1741 // https://github.com/iancoleman/bip39/issues/23
1743 page
. open ( url
, function ( status
) {
1744 // 1) and 2) set the phrase
1745 page
. evaluate ( function () {
1746 $( ".phrase" ). val ( "abandon abandon ability" ). trigger ( "input" );
1748 waitForGenerate ( function () {
1749 // 3) select bip32 tab
1750 page
. evaluate ( function () {
1751 $( "#bip32-tab a" ). click ();
1753 waitForGenerate ( function () {
1754 // 4) switch from bitcoin to litecoin
1755 page
. evaluate ( function () {
1756 $( ".network" ). val ( "2" ). trigger ( "change" );
1758 waitForGenerate ( function () {
1759 // 5) Check derivation path is displayed correctly
1760 var expected
= "m/0/0" ;
1761 var actual
= page
. evaluate ( function () {
1762 return $( ".index:first" ). text ();
1764 if ( actual
!= expected
) {
1765 console
. log ( "Github Issue 23 Part 1: derivation path display error" );
1766 console
. log ( "Expected: " + expected
);
1767 console
. log ( "Actual: " + actual
);
1770 // 5) Check address is displayed correctly
1771 var expected
= "LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5" ;
1772 var actual
= page
. evaluate ( function () {
1773 return $( ".address:first" ). text ();
1775 if ( actual
!= expected
) {
1776 console
. log ( "Github Issue 23 Part 1: address display error" );
1777 console
. log ( "Expected: " + expected
);
1778 console
. log ( "Actual: " + actual
);
1788 // Github Issue 23 Part 2: Coin selection in derivation path
1789 // https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
1791 page
. open ( url
, function ( status
) {
1793 page
. evaluate ( function () {
1794 $( ".phrase" ). val ( "abandon abandon ability" ). trigger ( "input" );
1796 waitForGenerate ( function () {
1797 // switch from bitcoin to clam
1798 page
. evaluate ( function () {
1799 $( ".network" ). val ( "9" ). trigger ( "change" );
1801 waitForGenerate ( function () {
1802 // check derivation path is displayed correctly
1803 var expected
= "m/44'/23'/0'/0/0" ;
1804 var actual
= page
. evaluate ( function () {
1805 return $( ".index:first" ). text ();
1807 if ( actual
!= expected
) {
1808 console
. log ( "Github Issue 23 Part 2: Coin in BIP44 derivation path is incorrect" );
1809 console
. log ( "Expected: " + expected
);
1810 console
. log ( "Actual: " + actual
);
1819 // Github Issue 26: When using a Root key derrived altcoins are incorrect
1820 // https://github.com/iancoleman/bip39/issues/26
1822 page
. open ( url
, function ( status
) {
1823 // 1) 2) and 3) set the root key
1824 page
. evaluate ( function () {
1825 $( ".root-key" ). val ( "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi" ). trigger ( "input" );
1827 waitForGenerate ( function () {
1828 // 4) switch from bitcoin to viacoin
1829 page
. evaluate ( function () {
1830 $( ".network" ). val ( "6" ). trigger ( "change" );
1832 waitForGenerate ( function () {
1833 // 5) ensure the derived address is correct
1834 var expected
= "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT" ;
1835 var actual
= page
. evaluate ( function () {
1836 return $( ".address:first" ). text ();
1838 if ( actual
!= expected
) {
1839 console
. log ( "Github Issue 26: address is incorrect when changing networks and using root-key to derive" );
1840 console
. log ( "Expected: " + expected
);
1841 console
. log ( "Actual: " + actual
);
1850 // Selecting a language with no existing phrase should generate a phrase in
1853 page
. open ( url
, function ( status
) {
1854 // Select a language
1855 // Need to manually simulate hash being set due to quirk between
1856 // 'click' event triggered by javascript vs triggered by mouse.
1857 // Perhaps look into page.sendEvent
1858 // http://phantomjs.org/api/webpage/method/send-event.html
1859 page
. evaluate ( function () {
1860 window
. location
. hash
= "#japanese" ;
1861 $( "a[href='#japanese']" ). trigger ( "click" );
1863 waitForGenerate ( function () {
1864 // Check the mnemonic is in Japanese
1865 var phrase
= page
. evaluate ( function () {
1866 return $( ".phrase" ). val ();
1868 if ( phrase
. length
<= 0 ) {
1869 console
. log ( "No Japanese phrase generated" );
1872 if ( phrase
. charCodeAt ( 0 ) < 128 ) {
1873 console
. log ( "First character of Japanese phrase is ascii" );
1874 console
. log ( "Phrase: " + phrase
);
1882 // Selecting a language with existing phrase should update the phrase to use
1885 page
. open ( url
, function ( status
) {
1886 // Set the phrase to an English phrase.
1887 page
. evaluate ( function () {
1888 $( ".phrase" ). val ( "abandon abandon ability" ). trigger ( "input" );
1890 waitForGenerate ( function () {
1891 // Change to Italian
1892 // Need to manually simulate hash being set due to quirk between
1893 // 'click' event triggered by javascript vs triggered by mouse.
1894 // Perhaps look into page.sendEvent
1895 // http://phantomjs.org/api/webpage/method/send-event.html
1896 page
. evaluate ( function () {
1897 window
. location
. hash
= "#italian" ;
1898 $( "a[href='#italian']" ). trigger ( "click" );
1900 waitForGenerate ( function () {
1901 // Check only the language changes, not the phrase
1902 var expected
= "abaco abaco abbaglio" ;
1903 var actual
= page
. evaluate ( function () {
1904 return $( ".phrase" ). val ();
1906 if ( actual
!= expected
) {
1907 console
. log ( "Changing language with existing phrase" );
1908 console
. log ( "Expected: " + expected
);
1909 console
. log ( "Actual: " + actual
);
1912 // Check the address is correct
1913 var expected
= "1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV" ;
1914 var actual
= page
. evaluate ( function () {
1915 return $( ".address:first" ). text ();
1917 if ( actual
!= expected
) {
1918 console
. log ( "Changing language generates incorrect address" );
1919 console
. log ( "Expected: " + expected
);
1920 console
. log ( "Actual: " + actual
);
1929 // Suggested replacement for erroneous word in non-English language
1931 page
. open ( url
, function ( status
) {
1932 // Set an incorrect phrase in Italian
1933 page
. evaluate ( function () {
1934 $( ".phrase" ). val ( "abaco abaco zbbaglio" ). trigger ( "input" );
1936 waitForFeedback ( function () {
1937 // Check the suggestion is correct
1938 var feedback
= page
. evaluate ( function () {
1939 return $( ".feedback" ). text ();
1941 if ( feedback
. indexOf ( "did you mean abbaglio?" ) < 0 ) {
1942 console
. log ( "Incorrect Italian word does not show suggested replacement" );
1943 console
. log ( "Error: " + error
);
1952 // Japanese word does not break across lines.
1954 // https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
1956 page
. open ( url
, function ( status
) {
1957 hasWordBreakCss
= page
. content
. indexOf ( "word-break: keep-all;" ) > - 1 ;
1958 if (! hasWordBreakCss
) {
1959 console
. log ( "Japanese words can break across lines mid-word" );
1960 console
. log ( "Check CSS for '.phrase { word-break: keep-all; }'" );
1963 // Run the next test
1968 // Language can be specified at page load using hash value in url
1970 page
. open ( url
, function ( status
) {
1971 // Set the page hash as if it were on a fresh page load
1972 page
. evaluate ( function () {
1973 window
. location
. hash
= "#japanese" ;
1975 // Generate a random phrase
1976 page
. evaluate ( function () {
1977 $( ".generate" ). trigger ( "click" );
1979 waitForGenerate ( function () {
1980 // Check the phrase is in Japanese
1981 var phrase
= page
. evaluate ( function () {
1982 return $( ".phrase" ). val ();
1984 if ( phrase
. length
<= 0 ) {
1985 console
. log ( "No phrase generated using url hash" );
1988 if ( phrase
. charCodeAt ( 0 ) < 128 ) {
1989 console
. log ( "Language not detected from url hash on page load." );
1990 console
. log ( "Phrase: " + phrase
);
1998 // Entropy unit tests
2000 page
. open ( url
, function ( status
) {
2001 var response
= page
. evaluate ( function () {
2003 // binary entropy is detected
2005 e
= Entropy
. fromString ( "01010101" );
2006 if ( e
. base
. str
!= "binary" ) {
2007 return "Binary entropy not detected correctly" ;
2013 // base6 entropy is detected
2015 e
= Entropy
. fromString ( "012345012345" );
2016 if ( e
. base
. str
!= "base 6" ) {
2017 return "base6 entropy not detected correctly" ;
2023 // dice entropy is detected
2025 e
= Entropy
. fromString ( "123456123456" );
2026 if ( e
. base
. str
!= "base 6 (dice)" ) {
2027 return "dice entropy not detected correctly" ;
2033 // base10 entropy is detected
2035 e
= Entropy
. fromString ( "0123456789" );
2036 if ( e
. base
. str
!= "base 10" ) {
2037 return "base10 entropy not detected correctly" ;
2043 // hex entropy is detected
2045 e
= Entropy
. fromString ( "0123456789ABCDEF" );
2046 if ( e
. base
. str
!= "hexadecimal" ) {
2047 return "hexadecimal entropy not detected correctly" ;
2053 // card entropy is detected
2055 e
= Entropy
. fromString ( "AC4DTHKS" );
2056 if ( e
. base
. str
!= "card" ) {
2057 return "card entropy not detected correctly" ;
2063 // entropy is case insensitive
2065 e
= Entropy
. fromString ( "aBcDeF" );
2066 if ( e
. cleanStr
!= "aBcDeF" ) {
2067 return "Entropy should not be case sensitive" ;
2073 // dice entropy is converted to base6
2075 e
= Entropy
. fromString ( "123456" );
2076 if ( e
. cleanStr
!= "123450" ) {
2077 return "Dice entropy is not automatically converted to base6" ;
2083 // dice entropy is preferred to base6 if ambiguous
2085 e
= Entropy
. fromString ( "12345" );
2086 if ( e
. base
. str
!= "base 6 (dice)" ) {
2087 return "dice not used as default over base 6" ;
2093 // unused characters are ignored
2095 e
= Entropy
. fromString ( "fghijkl" );
2096 if ( e
. cleanStr
!= "f" ) {
2097 return "additional characters are not ignored" ;
2103 // the lowest base is used by default
2104 // 7 could be decimal or hexadecimal, but should be detected as decimal
2106 e
= Entropy
. fromString ( "7" );
2107 if ( e
. base
. str
!= "base 10" ) {
2108 return "lowest base is not used" ;
2114 // Leading zeros are retained
2116 e
= Entropy
. fromString ( "000A" );
2117 if ( e
. cleanStr
!= "000A" ) {
2118 return "Leading zeros are not retained" ;
2124 // Leading zeros are correctly preserved for hex in binary string
2126 e
= Entropy
. fromString ( "2A" );
2127 if ( e
. binaryStr
!= "00101010" ) {
2128 return "Hex leading zeros are not correct in binary" ;
2134 // Leading zeros for base 6 as binary string
2135 // 20 = 2 events at 2.58 bits per event = 5 bits
2136 // 20 in base 6 = 12 in base 10 = 1100 in base 2
2137 // so it needs 1 bit of padding to be the right bit length
2139 e
= Entropy
. fromString ( "20" );
2140 if ( e
. binaryStr
!= "01100" ) {
2141 return "Base 6 as binary has leading zeros" ;
2147 // Leading zeros for base 10 as binary string
2149 e
= Entropy
. fromString ( "17" );
2150 if ( e
. binaryStr
!= "010001" ) {
2151 return "Base 10 as binary has leading zeros" ;
2157 // Leading zeros for card entropy as binary string.
2158 // Card entropy is hashed so 2c does not produce leading zeros.
2160 e
= Entropy
. fromString ( "4c" );
2161 if ( e
. binaryStr
!= "0001" ) {
2162 return "Card entropy as binary has leading zeros" ;
2168 // Keyboard mashing results in weak entropy
2169 // Despite being a long string, it's less than 30 bits of entropy
2171 e
= Entropy
. fromString ( "aj;se ifj; ask,dfv js;ifj" );
2172 if ( e
. binaryStr
. length
>= 30 ) {
2173 return "Keyboard mashing should produce weak entropy" ;
2179 // Card entropy is used if every pair could be a card
2181 e
= Entropy
. fromString ( "4c3c2c" );
2182 if ( e
. base
. str
!= "card" ) {
2183 return "Card entropy not used if all pairs are cards" ;
2189 // Card entropy uses base 52
2190 // [ cards, binary ]
2194 [ "acqs" , "10111101" ],
2195 [ "acks" , "11110000" ],
2196 [ "2cac" , "11000010" ],
2209 [ "ks2c" , "10000001" ],
2210 [ "KS2C" , "10000001" ],
2212 for ( var i
= 0 ; i
< cards
. length
; i
++) {
2213 var card
= cards
[ i
][ 0 ];
2214 var result
= cards
[ i
][ 1 ];
2215 e
= Entropy
. fromString ( card
);
2216 console
. log ( e
. binary
+ " " + result
);
2217 if ( e
. binaryStr
!== result
) {
2218 return "card entropy " + card
+ " not parsed correctly: " + result
+ " != " + e
. binaryStr
;
2227 if ( response
!= "PASS" ) {
2228 console
. log ( "Entropy unit tests" );
2229 console
. log ( response
);
2236 // Entropy can be entered by the user
2238 page
. open ( url
, function ( status
) {
2240 mnemonic : "abandon abandon ability" ,
2241 address : "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug" ,
2244 page
. evaluate ( function () {
2245 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2246 $( ".mnemonic-length" ). val ( "raw" );
2247 $( ".entropy" ). val ( "00000000 00000000 00000000 00000000" ). trigger ( "input" );
2249 // check the mnemonic is set and address is correct
2250 waitForGenerate ( function () {
2251 var actual
= page
. evaluate ( function () {
2253 address : $( ".address:first" ). text (),
2254 mnemonic : $( ".phrase" ). val (),
2257 if ( actual
. mnemonic
!= expected
. mnemonic
) {
2258 console
. log ( "Entropy does not generate correct mnemonic" );
2259 console
. log ( "Expected: " + expected
. mnemonic
);
2260 console
. log ( "Got: " + actual
. mnemonic
);
2263 if ( actual
. address
!= expected
. address
) {
2264 console
. log ( "Entropy does not generate correct address" );
2265 console
. log ( "Expected: " + expected
. address
);
2266 console
. log ( "Got: " + actual
. address
);
2274 // A warning about entropy is shown to the user, with additional information
2276 page
. open ( url
, function ( status
) {
2277 // get text content from entropy sections of page
2278 var hasWarning
= page
. evaluate ( function () {
2279 var entropyText
= $( ".entropy-container" ). text ();
2280 var warning
= "mnemonic may be insecure" ;
2281 if ( entropyText
. indexOf ( warning
) == - 1 ) {
2284 var readMoreText
= $( "#entropy-notes" ). parent (). text ();
2285 var goodSources
= "flipping a fair coin, rolling a fair dice, noise measurements etc" ;
2286 if ( readMoreText
. indexOf ( goodSources
) == - 1 ) {
2291 // check the warnings and information are shown
2293 console
. log ( "Page does not contain warning about using own entropy" );
2300 // The types of entropy available are described to the user
2302 page
. open ( url
, function ( status
) {
2303 // get placeholder text for entropy field
2304 var placeholder
= page
. evaluate ( function () {
2305 return $( ".entropy" ). attr ( "placeholder" );
2315 for ( var i
= 0 ; i
< options
. length
; i
++) {
2316 var option
= options
[ i
];
2317 if ( placeholder
. indexOf ( option
) == - 1 ) {
2318 console
. log ( "Available entropy type is not shown to user: " + option
);
2326 // The actual entropy used is shown to the user
2328 page
. open ( url
, function ( status
) {
2330 var badEntropySource
= page
. evaluate ( function () {
2331 var entropy
= "Not A Very Good Entropy Source At All" ;
2332 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2333 $( ".entropy" ). val ( entropy
). trigger ( "input" );
2335 // check the actual entropy being used is shown
2336 waitForEntropyFeedback ( function () {
2337 var expectedText
= "AedEceAA" ;
2338 var entropyText
= page
. evaluate ( function () {
2339 return $( ".entropy-container" ). text ();
2341 if ( entropyText
. indexOf ( expectedText
) == - 1 ) {
2342 console
. log ( "Actual entropy used is not shown" );
2350 // Binary entropy can be entered
2352 page
. open ( url
, function ( status
) {
2354 page
. evaluate ( function () {
2355 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2356 $( ".entropy" ). val ( "01" ). trigger ( "input" );
2358 // check the entropy is shown to be the correct type
2359 waitForEntropyFeedback ( function () {
2360 var entropyText
= page
. evaluate ( function () {
2361 return $( ".entropy-container" ). text ();
2363 if ( entropyText
. indexOf ( "binary" ) == - 1 ) {
2364 console
. log ( "Binary entropy is not detected and presented to user" );
2372 // Base 6 entropy can be entered
2374 page
. open ( url
, function ( status
) {
2376 page
. evaluate ( function () {
2377 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2378 $( ".entropy" ). val ( "012345" ). trigger ( "input" );
2380 // check the entropy is shown to be the correct type
2381 waitForEntropyFeedback ( function () {
2382 var entropyText
= page
. evaluate ( function () {
2383 return $( ".entropy-container" ). text ();
2385 if ( entropyText
. indexOf ( "base 6" ) == - 1 ) {
2386 console
. log ( "Base 6 entropy is not detected and presented to user" );
2394 // Base 6 dice entropy can be entered
2396 page
. open ( url
, function ( status
) {
2398 page
. evaluate ( function () {
2399 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2400 $( ".entropy" ). val ( "123456" ). trigger ( "input" );
2402 // check the entropy is shown to be the correct type
2403 waitForEntropyFeedback ( function () {
2404 var entropyText
= page
. evaluate ( function () {
2405 return $( ".entropy-container" ). text ();
2407 if ( entropyText
. indexOf ( "dice" ) == - 1 ) {
2408 console
. log ( "Dice entropy is not detected and presented to user" );
2416 // Base 10 entropy can be entered
2418 page
. open ( url
, function ( status
) {
2420 page
. evaluate ( function () {
2421 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2422 $( ".entropy" ). val ( "789" ). trigger ( "input" );
2424 // check the entropy is shown to be the correct type
2425 waitForEntropyFeedback ( function () {
2426 var entropyText
= page
. evaluate ( function () {
2427 return $( ".entropy-container" ). text ();
2429 if ( entropyText
. indexOf ( "base 10" ) == - 1 ) {
2430 console
. log ( "Base 10 entropy is not detected and presented to user" );
2438 // Hexadecimal entropy can be entered
2440 page
. open ( url
, function ( status
) {
2442 page
. evaluate ( function () {
2443 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2444 $( ".entropy" ). val ( "abcdef" ). trigger ( "input" );
2446 // check the entropy is shown to be the correct type
2447 waitForEntropyFeedback ( function () {
2448 var entropyText
= page
. evaluate ( function () {
2449 return $( ".entropy-container" ). text ();
2451 if ( entropyText
. indexOf ( "hexadecimal" ) == - 1 ) {
2452 console
. log ( "Hexadecimal entropy is not detected and presented to user" );
2460 // Dice entropy value is shown as the converted base 6 value
2462 page
. open ( url
, function ( status
) {
2464 page
. evaluate ( function () {
2465 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2466 $( ".entropy" ). val ( "123456" ). trigger ( "input" );
2468 // check the entropy is shown as base 6, not as the original dice value
2469 waitForEntropyFeedback ( function () {
2470 var entropyText
= page
. evaluate ( function () {
2471 return $( ".entropy-container" ). text ();
2473 if ( entropyText
. indexOf ( "123450" ) == - 1 ) {
2474 console
. log ( "Dice entropy is not shown to user as base 6 value" );
2477 if ( entropyText
. indexOf ( "123456" ) > - 1 ) {
2478 console
. log ( "Dice entropy value is shown instead of true base 6 value" );
2486 // The number of bits of entropy accumulated is shown
2488 page
. open ( url
, function ( status
) {
2491 [ "0000 0000 0000 0000 0000" , "20" ],
2494 [ "6" , "2" ], // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
2495 [ "7" , "3" ], // 7 in base 10 is 111 in base 2, no leading zeros
2500 [ "1A" , "8" ], // hex is always multiple of 4 bits of entropy
2507 [ "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)
2508 [ "2227" , "13" ], // Uses base 10, which is 4 lots of 3.32 bits, which is 13.3 bits (rounded down to 13)
2511 [ "0000101017" , "33" ], // 10 events at 3.32 bits per event
2512 [ "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
2515 page
. evaluate ( function ( e
) {
2516 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2519 var nextTest
= function runNextTest ( i
) {
2520 var entropy
= tests
[ i
][ 0 ];
2521 var expected
= tests
[ i
][ 1 ];
2523 page
. evaluate ( function ( e
) {
2524 $( ".entropy" ). val ( e
). trigger ( "input" );
2526 // check the number of bits of entropy is shown
2527 waitForEntropyFeedback ( function () {
2528 var entropyText
= page
. evaluate ( function () {
2529 return $( ".entropy-container" ). text ();
2531 if ( entropyText
. replace ( /\s/g , "" ). indexOf ( "Bits" + expected
) == - 1 ) {
2532 console
. log ( "Accumulated entropy is not shown correctly for " + entropy
);
2535 var isLastTest
= i
== tests
. length
- 1 ;
2548 // There is feedback provided about the supplied entropy
2550 page
. open ( url
, function ( status
) {
2555 type : "hexadecimal" ,
2559 strength : "extremely weak" ,
2562 entropy : "AAAAAAAA" ,
2563 filtered : "AAAAAAAA" ,
2564 type : "hexadecimal" ,
2568 strength : "extremely weak" ,
2571 entropy : "AAAAAAAA B" ,
2572 filtered : "AAAAAAAAB" ,
2573 type : "hexadecimal" ,
2577 strength : "extremely weak" ,
2580 entropy : "AAAAAAAA BBBBBBBB" ,
2581 filtered : "AAAAAAAABBBBBBBB" ,
2582 type : "hexadecimal" ,
2586 strength : "very weak" ,
2589 entropy : "AAAAAAAA BBBBBBBB CCCCCCCC" ,
2590 filtered : "AAAAAAAABBBBBBBBCCCCCCCC" ,
2591 type : "hexadecimal" ,
2598 entropy : "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD" ,
2599 filtered : "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD" ,
2600 type : "hexadecimal" ,
2604 strength : "easily cracked" ,
2607 entropy : "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA" ,
2608 filtered : "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA" ,
2609 type : "hexadecimal" ,
2616 entropy : "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE" ,
2617 filtered : "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE" ,
2618 type : "hexadecimal" ,
2622 strength : "very strong" ,
2625 entropy : "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF" ,
2626 filtered : "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF" ,
2627 type : "hexadecimal" ,
2631 strength : "extremely strong" ,
2639 strength : "extremely weak" ,
2642 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks" ,
2643 type : "card (full deck)" ,
2647 strength : "extremely strong" ,
2650 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d" ,
2651 type : "card (full deck, 1 duplicate: 3d)" ,
2655 strength : "extremely strong" ,
2658 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d" ,
2659 type : "card (2 duplicates: 3d 4d, 1 missing: KS)" ,
2663 strength : "extremely strong" ,
2666 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d" ,
2667 type : "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)" ,
2671 strength : "extremely strong" ,
2673 // Next test was throwing uncaught error in zxcvbn
2674 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
2676 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks" ,
2677 type : "card (full deck, 52 duplicates: ac 2c 3c...)" ,
2681 strength : "extremely strong" ,
2683 // Case insensitivity to duplicate cards
2686 type : "card (1 duplicate: AS)" ,
2690 strength : "extremely weak" ,
2694 type : "card (1 duplicate: as)" ,
2698 strength : "extremely weak" ,
2700 // Missing cards are detected
2702 entropy : "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks" ,
2703 type : "card (1 missing: 9C)" ,
2707 strength : "extremely strong" ,
2710 entropy : "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks" ,
2711 type : "card (2 missing: 9C 5D)" ,
2715 strength : "extremely strong" ,
2718 entropy : "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks" ,
2719 type : "card (4 missing: 9C 5D QD...)" ,
2723 strength : "extremely strong" ,
2725 // More than six missing cards does not show message
2727 entropy : "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks" ,
2732 strength : "extremely strong" ,
2734 // Multiple decks of cards increases bits per event
2754 entropy : "3d3d3d3d" ,
2760 entropy : "3d3d3d3d3d" ,
2766 entropy : "3d3d3d3d3d3d" ,
2772 entropy : "3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d" ,
2776 strength : 'easily cracked - Repeats like "abcabcabc" are only slightly harder to guess than "abc"' ,
2780 page
. evaluate ( function () {
2781 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2782 $( ".mnemonic-length" ). val ( "raw" );
2784 var nextTest
= function runNextTest ( i
) {
2785 function getFeedbackError ( expected
, actual
) {
2786 if ( "filtered" in expected
&& actual
. indexOf ( expected
. filtered
) == - 1 ) {
2787 return "Filtered value not in feedback" ;
2789 if ( "type" in expected
&& actual
. indexOf ( expected
. type
) == - 1 ) {
2790 return "Entropy type not in feedback" ;
2792 if ( "events" in expected
&& actual
. indexOf ( expected
. events
) == - 1 ) {
2793 return "Event count not in feedback" ;
2795 if ( "bits" in expected
&& actual
. indexOf ( expected
. bits
) == - 1 ) {
2796 return "Bit count not in feedback" ;
2798 if ( "strength" in expected
&& actual
. indexOf ( expected
. strength
) == - 1 ) {
2799 return "Strength not in feedback" ;
2801 if ( "bitsPerEvent" in expected
&& actual
. indexOf ( expected
. bitsPerEvent
) == - 1 ) {
2802 return "bitsPerEvent not in feedback" ;
2807 page
. evaluate ( function ( e
) {
2808 $( ".addresses" ). empty ();
2809 $( ".phrase" ). val ( "" );
2810 $( ".entropy" ). val ( e
). trigger ( "input" );
2812 waitForEntropyFeedback ( function () {
2813 var mnemonic
= page
. evaluate ( function () {
2814 return $( ".phrase" ). val ();
2816 // Check mnemonic length
2817 if ( "words" in test
&& test
. words
== 0 ) {
2818 if ( mnemonic
. length
> 0 ) {
2819 console
. log ( "Mnemonic length for " + test
. strength
+ " strength is not " + test
. words
);
2820 console
. log ( "Entropy: " + test
. entropy
);
2821 console
. log ( "Mnemonic: " + mnemonic
);
2825 else if ( "words" in test
) {
2826 if ( mnemonic
. split ( " " ). length
!= test
. words
) {
2827 console
. log ( "Mnemonic length for " + test
. strength
+ " strength is not " + test
. words
);
2828 console
. log ( "Entropy: " + test
. entropy
);
2829 console
. log ( "Mnemonic: " + mnemonic
);
2834 var feedback
= page
. evaluate ( function () {
2835 return $( ".entropy-container" ). text ();
2837 var feedbackError
= getFeedbackError ( test
, feedback
);
2838 if ( feedbackError
) {
2839 console
. log ( "Entropy feedback for " + test
. entropy
+ " returned error" );
2840 console
. log ( feedbackError
);
2844 var isLastTest
= i
== tests
. length
- 1 ;
2857 // Entropy is truncated from the left
2859 page
. open ( url
, function ( status
) {
2860 var expected
= "avocado zoo zone" ;
2862 page
. evaluate ( function () {
2863 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2864 $( ".mnemonic-length" ). val ( "raw" );
2865 var entropy
= "00000000 00000000 00000000 00000000" ;
2866 entropy
+= "11111111 11111111 11111111 1111" ; // Missing last byte
2867 $( ".entropy" ). val ( entropy
). trigger ( "input" );
2869 // check the entropy is truncated from the right
2870 waitForGenerate ( function () {
2871 var actual
= page
. evaluate ( function () {
2872 return $( ".phrase" ). val ();
2874 if ( actual
!= expected
) {
2875 console
. log ( "Entropy is not truncated from the right" );
2876 console
. log ( "Expected: " + expected
);
2877 console
. log ( "Got: " + actual
);
2885 // Very large entropy results in very long mnemonics
2887 page
. open ( url
, function ( status
) {
2889 page
. evaluate ( function () {
2890 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2891 $( ".mnemonic-length" ). val ( "raw" );
2893 // Generate a very long entropy string
2894 for ( var i
= 0 ; i
< 33 ; i
++) {
2895 entropy
+= "AAAAAAAA" ; // 3 words * 33 iterations = 99 words
2897 $( ".entropy" ). val ( entropy
). trigger ( "input" );
2899 // check the mnemonic is very long
2900 waitForGenerate ( function () {
2901 var wordCount
= page
. evaluate ( function () {
2902 return $( ".phrase" ). val (). split ( " " ). length
;
2904 if ( wordCount
!= 99 ) {
2905 console
. log ( "Large entropy does not generate long mnemonic" );
2906 console
. log ( "Expected 99 words, got " + wordCount
);
2914 // Is compatible with bip32jp entropy
2915 // https://bip32jp.github.io/english/index.html
2917 // Is incompatible with:
2920 page
. open ( url
, function ( status
) {
2921 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" ;
2923 page
. evaluate ( function () {
2924 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2925 $( ".mnemonic-length" ). val ( "raw" );
2926 var entropy
= "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543" ;
2927 $( ".entropy" ). val ( entropy
). trigger ( "input" );
2929 // check the mnemonic matches the expected value from bip32jp
2930 waitForGenerate ( function () {
2931 var actual
= page
. evaluate ( function () {
2932 return $( ".phrase" ). val ();
2934 if ( actual
!= expected
) {
2935 console
. log ( "Mnemonic does not match bip32jp for base 6 entropy" );
2936 console
. log ( "Expected: " + expected
);
2937 console
. log ( "Got: " + actual
);
2945 // Blank entropy does not generate mnemonic or addresses
2947 page
. open ( url
, function ( status
) {
2949 page
. evaluate ( function () {
2950 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2951 $( ".entropy" ). val ( "" ). trigger ( "input" );
2953 waitForFeedback ( function () {
2954 // check there is no mnemonic
2955 var phrase
= page
. evaluate ( function () {
2956 return $( ".phrase" ). val ();
2959 console
. log ( "Blank entropy does not result in blank mnemonic" );
2960 console
. log ( "Got: " + phrase
);
2963 // check there are no addresses displayed
2964 var addresses
= page
. evaluate ( function () {
2965 return $( ".address" ). length
;
2967 if ( addresses
!= 0 ) {
2968 console
. log ( "Blank entropy does not result in zero addresses" );
2971 // Check the feedback says 'blank entropy'
2972 var feedback
= page
. evaluate ( function () {
2973 return $( ".feedback" ). text ();
2975 if ( feedback
!= "Blank entropy" ) {
2976 console
. log ( "Blank entropy does not show feedback message" );
2984 // Mnemonic length can be selected even for weak entropy
2986 page
. open ( url
, function ( status
) {
2988 page
. evaluate ( function () {
2989 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2990 $( ".entropy" ). val ( "012345" );
2991 $( ".mnemonic-length" ). val ( "18" ). trigger ( "change" );
2993 // check the mnemonic is the correct length
2994 waitForGenerate ( function () {
2995 var phrase
= page
. evaluate ( function () {
2996 return $( ".phrase" ). val ();
2998 var numberOfWords
= phrase
. split ( /\s/g ). length
;
2999 if ( numberOfWords
!= 18 ) {
3000 console
. log ( "Weak entropy cannot be overridden to give 18 word mnemonic" );
3001 console
. log ( phrase
);
3010 // https://github.com/iancoleman/bip39/issues/33
3011 // Final cards should contribute entropy
3013 page
. open ( url
, function ( status
) {
3015 page
. evaluate ( function () {
3016 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
3017 $( ".mnemonic-length" ). val ( "raw" );
3018 $( ".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" );
3021 waitForGenerate ( function () {
3022 var originalPhrase
= page
. evaluate ( function () {
3023 return $( ".phrase" ). val ();
3025 // Set the last 12 cards to be AS
3026 page
. evaluate ( function () {
3027 $( ".addresses" ). empty ();
3028 $( ".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" );
3030 // get the new mnemonic
3031 waitForGenerate ( function () {
3032 var newPhrase
= page
. evaluate ( function () {
3033 return $( ".phrase" ). val ();
3035 // check the phrase has changed
3036 if ( newPhrase
== originalPhrase
) {
3037 console
. log ( "Changing last 12 cards does not change mnemonic" );
3038 console
. log ( "Original:" );
3039 console
. log ( originalPhrase
);
3040 console
. log ( "New:" );
3041 console
. log ( newPhrase
);
3051 // https://github.com/iancoleman/bip39/issues/35
3054 page
. open ( url
, function ( status
) {
3056 page
. evaluate ( function () {
3057 $( ".generate" ). click ();
3059 waitForGenerate ( function () {
3060 var p
= page
. evaluate ( function () {
3061 // get position of mnemonic element
3062 return $( ".phrase" ). offset ();
3064 p
. top
= Math
. ceil ( p
. top
);
3065 p
. left
= Math
. ceil ( p
. left
);
3066 // check the qr code shows
3067 page
. sendEvent ( "mousemove" , p
. left
+ 4 , p
. top
+ 4 );
3068 var qrShowing
= page
. evaluate ( function () {
3069 return $( ".qr-container" ). find ( "canvas" ). length
> 0 ;
3072 console
. log ( "QR Code does not show" );
3075 // check the qr code hides
3076 page
. sendEvent ( "mousemove" , p
. left
- 4 , p
. top
- 4 );
3077 var qrHidden
= page
. evaluate ( function () {
3078 return $( ".qr-container" ). find ( "canvas" ). length
== 0 ;
3081 console
. log ( "QR Code does not hide" );
3089 // BIP44 account extendend private key is shown
3090 // github issue 37 - compatibility with electrum
3092 page
. open ( url
, function ( status
) {
3094 var expected
= "xprv9yzrnt4zWVJUr1k2VxSPy9ettKz5PpeDMgaVG7UKedhqnw1tDkxP2UyYNhuNSumk2sLE5ctwKZs9vwjsq3e1vo9egCK6CzP87H2cVYXpfwQ" ;
3095 page
. evaluate ( function () {
3096 $( ".phrase" ). val ( "abandon abandon ability" );
3097 $( ".phrase" ). trigger ( "input" );
3099 // check the BIP44 account extended private key
3100 waitForGenerate ( function () {
3101 var actual
= page
. evaluate ( function () {
3102 return $( ".account-xprv" ). val ();
3104 if ( actual
!= expected
) {
3105 console
. log ( "BIP44 account extended private key is incorrect" );
3106 console
. log ( "Expected: " + expected
);
3107 console
. log ( "Actual: " + actual
);
3115 // BIP44 account extendend public key is shown
3116 // github issue 37 - compatibility with electrum
3118 page
. open ( url
, function ( status
) {
3120 var expected
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf" ;
3121 page
. evaluate ( function () {
3122 $( ".phrase" ). val ( "abandon abandon ability" );
3123 $( ".phrase" ). trigger ( "input" );
3125 // check the BIP44 account extended public key
3126 waitForGenerate ( function () {
3127 var actual
= page
. evaluate ( function () {
3128 return $( ".account-xpub" ). val ();
3130 if ( actual
!= expected
) {
3131 console
. log ( "BIP44 account extended public key is incorrect" );
3132 console
. log ( "Expected: " + expected
);
3133 console
. log ( "Actual: " + actual
);
3142 // If you wish to add more tests, do so here...
3144 // Here is a blank test template
3148 page.open(url, function(status) {
3149 // Do something on the page
3150 page.evaluate(function() {
3151 $(".phrase").val("abandon abandon ability").trigger("input");
3153 waitForGenerate(function() {
3154 // Check the result of doing the thing
3155 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3156 var actual = page.evaluate(function() {
3157 return $(".address:first").text();
3159 if (actual != expected) {
3160 console.log("A specific message about what failed");
3161 console.log("Expected: " + expected);
3162 console.log("Actual: " + actual);
3165 // Run the next test
3175 console
. log ( "Running tests..." );
3176 tests
= shuffle ( tests
);