]>
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 ;
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 [ "acqs" , "00001100011" ],
2189 [ "acks" , "00001100100" ],
2190 [ "2cac" , "00001100101" ],
2203 [ "ks2c" , "101001011100" ],
2204 [ "KS2C" , "101001011100" ],
2206 for ( var i
= 0 ; i
< cards
. length
; i
++) {
2207 var card
= cards
[ i
][ 0 ];
2208 var result
= cards
[ i
][ 1 ];
2209 e
= Entropy
. fromString ( card
);
2210 console
. log ( e
. binary
+ " " + result
);
2211 if ( e
. binaryStr
!== result
) {
2212 return "card entropy " + card
+ " not parsed correctly: " + result
+ " != " + e
. binaryStr
;
2221 if ( response
!= "PASS" ) {
2222 console
. log ( "Entropy unit tests" );
2223 console
. log ( response
);
2230 // Entropy can be entered by the user
2232 page
. open ( url
, function ( status
) {
2234 mnemonic : "abandon abandon ability" ,
2235 address : "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug" ,
2238 page
. evaluate ( function () {
2239 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2240 $( ".mnemonic-length" ). val ( "raw" );
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
2506 [ "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
2509 page
. evaluate ( function ( e
) {
2510 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2513 var nextTest
= function runNextTest ( i
) {
2514 var entropy
= tests
[ i
][ 0 ];
2515 var expected
= tests
[ i
][ 1 ];
2517 page
. evaluate ( function ( e
) {
2518 $( ".entropy" ). val ( e
). trigger ( "input" );
2520 // check the number of bits of entropy is shown
2521 waitForEntropyFeedback ( function () {
2522 var entropyText
= page
. evaluate ( function () {
2523 return $( ".entropy-container" ). text ();
2525 if ( entropyText
. replace ( /\s/g , "" ). indexOf ( "Bits" + expected
) == - 1 ) {
2526 console
. log ( "Accumulated entropy is not shown correctly for " + entropy
);
2529 var isLastTest
= i
== tests
. length
- 1 ;
2542 // There is feedback provided about the supplied entropy
2544 page
. open ( url
, function ( status
) {
2549 type : "hexadecimal" ,
2553 strength : "extremely weak" ,
2556 entropy : "AAAAAAAA" ,
2557 filtered : "AAAAAAAA" ,
2558 type : "hexadecimal" ,
2562 strength : "extremely weak" ,
2565 entropy : "AAAAAAAA B" ,
2566 filtered : "AAAAAAAAB" ,
2567 type : "hexadecimal" ,
2571 strength : "extremely weak" ,
2574 entropy : "AAAAAAAA BBBBBBBB" ,
2575 filtered : "AAAAAAAABBBBBBBB" ,
2576 type : "hexadecimal" ,
2580 strength : "very weak" ,
2583 entropy : "AAAAAAAA BBBBBBBB CCCCCCCC" ,
2584 filtered : "AAAAAAAABBBBBBBBCCCCCCCC" ,
2585 type : "hexadecimal" ,
2592 entropy : "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD" ,
2593 filtered : "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD" ,
2594 type : "hexadecimal" ,
2598 strength : "easily cracked" ,
2601 entropy : "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA" ,
2602 filtered : "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA" ,
2603 type : "hexadecimal" ,
2610 entropy : "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE" ,
2611 filtered : "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE" ,
2612 type : "hexadecimal" ,
2616 strength : "very strong" ,
2619 entropy : "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF" ,
2620 filtered : "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF" ,
2621 type : "hexadecimal" ,
2625 strength : "extremely strong" ,
2633 strength : "extremely weak" ,
2636 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks" ,
2637 type : "card (full deck)" ,
2641 strength : "extremely strong" ,
2644 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d" ,
2645 type : "card (full deck, 1 duplicate: 3d)" ,
2649 strength : "extremely strong" ,
2652 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d" ,
2653 type : "card (2 duplicates: 3d 4d, 1 missing: KS)" ,
2657 strength : "extremely strong" ,
2660 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d" ,
2661 type : "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)" ,
2665 strength : "extremely strong" ,
2667 // Next test was throwing uncaught error in zxcvbn
2668 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
2670 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks" ,
2671 type : "card (full deck, 52 duplicates: ac 2c 3c...)" ,
2675 strength : "extremely strong" ,
2677 // Case insensitivity to duplicate cards
2680 type : "card (1 duplicate: AS)" ,
2684 strength : "extremely weak" ,
2688 type : "card (1 duplicate: as)" ,
2692 strength : "extremely weak" ,
2694 // Missing cards are detected
2696 entropy : "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks" ,
2697 type : "card (1 missing: 9C)" ,
2701 strength : "extremely strong" ,
2704 entropy : "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks" ,
2705 type : "card (2 missing: 9C 5D)" ,
2709 strength : "extremely strong" ,
2712 entropy : "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks" ,
2713 type : "card (4 missing: 9C 5D QD...)" ,
2717 strength : "extremely strong" ,
2719 // More than six missing cards does not show message
2721 entropy : "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks" ,
2726 strength : "extremely strong" ,
2728 // Additional entropy from each card decreases as deck is depleted.
2729 // Check the boundaries of this depletion
2730 // See table at https://github.com/iancoleman/bip39/issues/33#issuecomment-262855862
2731 // for following values of 'events, bits, words'
2732 // 2 cards remaining = 21 words
2734 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjs" ,
2735 type : "card (2 missing: QS KS)" ,
2739 strength : "extremely strong" ,
2741 // 3 cards remaining = 18 words
2743 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9sts" ,
2744 type : "card (3 missing: JS QS KS)" ,
2746 bits : 222 , // table uses different rounding - 222.99604
2748 strength : "extremely strong" ,
2750 // 13 cards remaining = 18 words
2752 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkh" ,
2757 strength : "extremely strong" ,
2759 // 14 cards remaining = 15 words
2761 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqh" ,
2766 strength : "very strong" ,
2768 // 21 cards remaining = 15 words
2770 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h" ,
2775 strength : "very strong" ,
2777 // 22 cards remaining = 12 words
2779 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h" ,
2786 // 27 cards remaining = 12 words
2788 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqd" ,
2795 // 28 cards remaining = 9 words
2797 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjd" ,
2804 // 34 cards remaining = 9 words
2806 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d" ,
2813 // 35 cards remaining = 6 words
2815 entropy : "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d" ,
2820 strength : "very weak" ,
2822 // 40 cards remaining = 6 words
2824 entropy : "ac2c3c4c5c6c7c8c9ctcjcqc" ,
2829 strength : "very weak" ,
2831 // 41 cards remaining = 3 words
2833 entropy : "ac2c3c4c5c6c7c8c9ctcjc" ,
2838 strength : "extremely weak" ,
2840 // 46 cards remaining = 3 words
2842 entropy : "ac2c3c4c5c6c" ,
2847 strength : "extremely weak" ,
2849 // 47 cards remaining = 0 words
2851 entropy : "ac2c3c4c5c" ,
2856 strength : "extremely weak" ,
2860 page
. evaluate ( function () {
2861 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2862 $( ".mnemonic-length" ). val ( "raw" );
2864 var nextTest
= function runNextTest ( i
) {
2865 function getFeedbackError ( expected
, actual
) {
2866 if ( "filtered" in expected
&& actual
. indexOf ( expected
. filtered
) == - 1 ) {
2867 return "Filtered value not in feedback" ;
2869 if ( actual
. indexOf ( expected
. type
) == - 1 ) {
2870 return "Entropy type not in feedback" ;
2872 if ( actual
. indexOf ( expected
. events
) == - 1 ) {
2873 return "Event count not in feedback" ;
2875 if ( actual
. indexOf ( expected
. bits
) == - 1 ) {
2876 return "Bit count not in feedback" ;
2878 if ( actual
. indexOf ( expected
. strength
) == - 1 ) {
2879 return "Strength not in feedback" ;
2884 page
. evaluate ( function ( e
) {
2885 $( ".addresses" ). empty ();
2886 $( ".phrase" ). val ( "" );
2887 $( ".entropy" ). val ( e
). trigger ( "input" );
2889 waitForEntropyFeedback ( function () {
2890 var mnemonic
= page
. evaluate ( function () {
2891 return $( ".phrase" ). val ();
2893 // Check mnemonic length
2894 if ( test
. words
== 0 ) {
2895 if ( mnemonic
. length
> 0 ) {
2896 console
. log ( "Mnemonic length for " + test
. strength
+ " strength is not " + test
. words
);
2897 console
. log ( "Mnemonic: " + mnemonic
);
2902 if ( mnemonic
. split ( " " ). length
!= test
. words
) {
2903 console
. log ( "Mnemonic length for " + test
. strength
+ " strength is not " + test
. words
);
2904 console
. log ( "Mnemonic: " + mnemonic
);
2909 var feedback
= page
. evaluate ( function () {
2910 return $( ".entropy-container" ). text ();
2912 var feedbackError
= getFeedbackError ( test
, feedback
);
2913 if ( feedbackError
) {
2914 console
. log ( "Entropy feedback for " + test
. entropy
+ " returned error" );
2915 console
. log ( feedbackError
);
2919 var isLastTest
= i
== tests
. length
- 1 ;
2932 // Entropy is truncated from the left
2934 page
. open ( url
, function ( status
) {
2935 var expected
= "avocado zoo zone" ;
2937 page
. evaluate ( function () {
2938 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2939 $( ".mnemonic-length" ). val ( "raw" );
2940 var entropy
= "00000000 00000000 00000000 00000000" ;
2941 entropy
+= "11111111 11111111 11111111 1111" ; // Missing last byte
2942 $( ".entropy" ). val ( entropy
). trigger ( "input" );
2944 // check the entropy is truncated from the right
2945 waitForGenerate ( function () {
2946 var actual
= page
. evaluate ( function () {
2947 return $( ".phrase" ). val ();
2949 if ( actual
!= expected
) {
2950 console
. log ( "Entropy is not truncated from the right" );
2951 console
. log ( "Expected: " + expected
);
2952 console
. log ( "Got: " + actual
);
2960 // Very large entropy results in very long mnemonics
2962 page
. open ( url
, function ( status
) {
2964 page
. evaluate ( function () {
2965 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
2966 $( ".mnemonic-length" ). val ( "raw" );
2968 // Generate a very long entropy string
2969 for ( var i
= 0 ; i
< 33 ; i
++) {
2970 entropy
+= "AAAAAAAA" ; // 3 words * 33 iterations = 99 words
2972 $( ".entropy" ). val ( entropy
). trigger ( "input" );
2974 // check the mnemonic is very long
2975 waitForGenerate ( function () {
2976 var wordCount
= page
. evaluate ( function () {
2977 return $( ".phrase" ). val (). split ( " " ). length
;
2979 if ( wordCount
!= 99 ) {
2980 console
. log ( "Large entropy does not generate long mnemonic" );
2981 console
. log ( "Expected 99 words, got " + wordCount
);
2989 // Is compatible with bip32jp entropy
2990 // https://bip32jp.github.io/english/index.html
2992 // Is incompatible with:
2995 page
. open ( url
, function ( status
) {
2996 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" ;
2998 page
. evaluate ( function () {
2999 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
3000 $( ".mnemonic-length" ). val ( "raw" );
3001 var entropy
= "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543" ;
3002 $( ".entropy" ). val ( entropy
). trigger ( "input" );
3004 // check the mnemonic matches the expected value from bip32jp
3005 waitForGenerate ( function () {
3006 var actual
= page
. evaluate ( function () {
3007 return $( ".phrase" ). val ();
3009 if ( actual
!= expected
) {
3010 console
. log ( "Mnemonic does not match bip32jp for base 6 entropy" );
3011 console
. log ( "Expected: " + expected
);
3012 console
. log ( "Got: " + actual
);
3020 // Blank entropy does not generate mnemonic or addresses
3022 page
. open ( url
, function ( status
) {
3024 page
. evaluate ( function () {
3025 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
3026 $( ".entropy" ). val ( "" ). trigger ( "input" );
3028 waitForFeedback ( function () {
3029 // check there is no mnemonic
3030 var phrase
= page
. evaluate ( function () {
3031 return $( ".phrase" ). val ();
3034 console
. log ( "Blank entropy does not result in blank mnemonic" );
3035 console
. log ( "Got: " + phrase
);
3038 // check there are no addresses displayed
3039 var addresses
= page
. evaluate ( function () {
3040 return $( ".address" ). length
;
3042 if ( addresses
!= 0 ) {
3043 console
. log ( "Blank entropy does not result in zero addresses" );
3046 // Check the feedback says 'blank entropy'
3047 var feedback
= page
. evaluate ( function () {
3048 return $( ".feedback" ). text ();
3050 if ( feedback
!= "Blank entropy" ) {
3051 console
. log ( "Blank entropy does not show feedback message" );
3059 // Mnemonic length can be selected even for weak entropy
3061 page
. open ( url
, function ( status
) {
3063 page
. evaluate ( function () {
3064 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
3065 $( ".entropy" ). val ( "012345" );
3066 $( ".mnemonic-length" ). val ( "18" ). trigger ( "change" );
3068 // check the mnemonic is the correct length
3069 waitForGenerate ( function () {
3070 var phrase
= page
. evaluate ( function () {
3071 return $( ".phrase" ). val ();
3073 var numberOfWords
= phrase
. split ( /\s/g ). length
;
3074 if ( numberOfWords
!= 18 ) {
3075 console
. log ( "Weak entropy cannot be overridden to give 18 word mnemonic" );
3076 console
. log ( phrase
);
3085 // https://github.com/iancoleman/bip39/issues/33
3086 // Final cards should contribute entropy
3088 page
. open ( url
, function ( status
) {
3090 page
. evaluate ( function () {
3091 $( ".use-entropy" ). prop ( "checked" , true ). trigger ( "change" );
3092 $( ".mnemonic-length" ). val ( "raw" );
3093 $( ".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" );
3096 waitForGenerate ( function () {
3097 var originalPhrase
= page
. evaluate ( function () {
3098 return $( ".phrase" ). val ();
3100 // Set the last 12 cards to be AS
3101 page
. evaluate ( function () {
3102 $( ".addresses" ). empty ();
3103 $( ".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" );
3105 // get the new mnemonic
3106 waitForGenerate ( function () {
3107 var newPhrase
= page
. evaluate ( function () {
3108 return $( ".phrase" ). val ();
3110 // check raw entropy is in use, ie the first bits should remain the same
3111 var startLength
= 20 ;
3112 var originalPhraseStart
= originalPhrase
. substring ( 0 , startLength
);
3113 var newPhraseStart
= newPhrase
. substring ( 0 , startLength
);
3114 if ( newPhraseStart
!= originalPhraseStart
) {
3115 console
. log ( "Changing last 12 cards changed first portion of mnemonic" );
3116 console
. log ( "Original:" );
3117 console
. log ( originalPhrase
);
3118 console
. log ( "New:" );
3119 console
. log ( newPhrase
);
3122 // check the phrase has changed
3123 if ( newPhrase
== originalPhrase
) {
3124 console
. log ( "Changing last 12 cards does not change mnemonic" );
3125 console
. log ( "Original:" );
3126 console
. log ( originalPhrase
);
3127 console
. log ( "New:" );
3128 console
. log ( newPhrase
);
3138 // If you wish to add more tests, do so here...
3140 // Here is a blank test template
3144 page.open(url, function(status) {
3145 // Do something on the page
3146 page.evaluate(function() {
3147 $(".phrase").val("abandon abandon ability").trigger("input");
3149 waitForGenerate(function() {
3150 // Check the result of doing the thing
3151 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3152 var actual = page.evaluate(function() {
3153 return $(".address:first").text();
3155 if (actual != expected) {
3156 console.log("A specific message about what failed");
3157 console.log("Expected: " + expected);
3158 console.log("Actual: " + actual);
3161 // Run the next test
3171 console
. log ( "Running tests..." );
3172 tests
= shuffle ( tests
);