]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blob - tests.js
Languages no longer experimental.
[perso/Immae/Projets/Cryptomonnaies/BIP39.git] / tests.js
1 // Usage:
2 // $ phantomjs tests.js
3
4
5 var page = require('webpage').create();
6 var url = 'src/index.html';
7 var testMaxTime = 5000;
8
9 page.onResourceError = function(e) {
10 console.log("Error loading " + e.url);
11 phantom.exit();
12 }
13
14 function fail() {
15 console.log("Failed");
16 phantom.exit();
17 }
18
19 function waitForGenerate(fn, maxTime) {
20 if (!maxTime) {
21 maxTime = testMaxTime;
22 }
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;
30 });
31 var hasFinished = addressCount > 0 && addressCount == prevAddressCount;
32 prevAddressCount = addressCount;
33 if (hasFinished) {
34 fn();
35 }
36 else if (hasTimedOut) {
37 console.log("Test timed out");
38 fn();
39 }
40 else {
41 setTimeout(keepWaiting, 100);
42 }
43 }
44 wait();
45 }
46
47 function waitForFeedback(fn, maxTime) {
48 if (!maxTime) {
49 maxTime = testMaxTime;
50 }
51 var start = new Date().getTime();
52 var wait = function keepWaiting() {
53 var now = new Date().getTime();
54 var hasTimedOut = now - start > maxTime;
55 if (hasTimedOut) {
56 console.log("Test timed out");
57 fn();
58 return;
59 }
60 var feedback = page.evaluate(function() {
61 var feedback = $(".feedback");
62 if (feedback.css("display") == "none") {
63 return "";
64 }
65 return feedback.text();
66 });
67 var hasFinished = feedback.length > 0 && feedback != "Calculating...";
68 if (hasFinished) {
69 fn();
70 }
71 else {
72 setTimeout(keepWaiting, 100);
73 }
74 }
75 wait();
76 }
77
78 function next() {
79 if (tests.length > 0) {
80 var testsStr = tests.length == 1 ? "test" : "tests";
81 console.log(tests.length + " " + testsStr + " remaining");
82 tests.shift()();
83 }
84 else {
85 console.log("Finished with 0 failures");
86 phantom.exit();
87 }
88 }
89
90 /**
91 * Randomize array element order in-place.
92 * Using Durstenfeld shuffle algorithm.
93 * See http://stackoverflow.com/a/12646864
94 */
95 function shuffle(array) {
96 for (var i = array.length - 1; i > 0; i--) {
97 var j = Math.floor(Math.random() * (i + 1));
98 var temp = array[i];
99 array[i] = array[j];
100 array[j] = temp;
101 }
102 return array;
103 }
104
105 tests = [
106
107 // Page loads with status of 'success'
108 function() {
109 page.open(url, function(status) {
110 if (status != "success") {
111 console.log("Page did not load with status 'success'");
112 fail();
113 }
114 next();
115 });
116 },
117
118 // Page has text
119 function() {
120 page.open(url, function(status) {
121 var content = page.evaluate(function() {
122 return document.body.textContent.trim();
123 });
124 if (!content) {
125 console.log("Page does not have text");
126 fail();
127 }
128 next();
129 });
130 },
131
132 // Entering mnemonic generates addresses
133 function() {
134 page.open(url, function(status) {
135 // set the phrase
136 page.evaluate(function() {
137 $(".phrase").val("abandon abandon ability").trigger("input");
138 });
139 // get the address
140 waitForGenerate(function() {
141 var addressCount = page.evaluate(function() {
142 return $(".address").length;
143 });
144 if (addressCount != 20) {
145 console.log("Mnemonic did not generate addresses");
146 console.log("Expected: " + expected);
147 console.log("Got: " + actual);
148 fail();
149 }
150 next();
151 });
152 });
153 },
154
155 // Random button generates random mnemonic
156 function() {
157 page.open(url, function(status) {
158 // check initial phrase is empty
159 var phrase = page.evaluate(function() {
160 return $(".phrase").text();
161 });
162 if (phrase != "") {
163 console.log("Initial phrase is not blank");
164 fail();
165 }
166 // press the 'generate' button
167 page.evaluate(function() {
168 $(".generate").click();
169 });
170 // get the new phrase
171 waitForGenerate(function() {
172 var phrase = page.evaluate(function() {
173 return $(".phrase").val();
174 });
175 if (phrase.length <= 0) {
176 console.log("Phrase not generated by pressing button");
177 fail();
178 }
179 next();
180 });
181 });
182 },
183
184 // Mnemonic length can be customized
185 function() {
186 page.open(url, function(status) {
187 // set the length to 6
188 var expectedLength = "6";
189 page.evaluate(function() {
190 $(".strength option[selected]").removeAttr("selected");
191 $(".strength option[value=6]").prop("selected", true);
192 });
193 // press the 'generate' button
194 page.evaluate(function() {
195 $(".generate").click();
196 });
197 // check the new phrase is six words long
198 waitForGenerate(function() {
199 var actualLength = page.evaluate(function() {
200 var words = $(".phrase").val().split(" ");
201 return words.length;
202 });
203 if (actualLength != expectedLength) {
204 console.log("Phrase not generated with correct length");
205 console.log("Expected: " + expectedLength);
206 console.log("Actual: " + actualLength);
207 fail();
208 }
209 next();
210 });
211 });
212 },
213
214 // Passphrase can be set
215 function() {
216 page.open(url, function(status) {
217 // set the phrase and passphrase
218 var expected = "15pJzUWPGzR7avffV9nY5by4PSgSKG9rba";
219 page.evaluate(function() {
220 $(".phrase").val("abandon abandon ability");
221 $(".passphrase").val("secure_passphrase").trigger("input");
222 });
223 // check the address is generated correctly
224 waitForGenerate(function() {
225 var actual = page.evaluate(function() {
226 return $(".address:first").text();
227 });
228 if (actual != expected) {
229 console.log("Passphrase results in wrong address");
230 console.log("Expected: " + expected);
231 console.log("Actual: " + actual);
232 fail();
233 }
234 next();
235 });
236 });
237 },
238
239 // Network can be set to bitcoin testnet
240 function() {
241 page.open(url, function(status) {
242 // set the phrase and coin
243 var expected = "mucaU5iiDaJDb69BHLeDv8JFfGiyg2nJKi";
244 page.evaluate(function() {
245 $(".phrase").val("abandon abandon ability");
246 $(".phrase").trigger("input");
247 $(".network option[selected]").removeAttr("selected");
248 $(".network option[value=1]").prop("selected", true);
249 $(".network").trigger("change");
250 });
251 // check the address is generated correctly
252 waitForGenerate(function() {
253 var actual = page.evaluate(function() {
254 return $(".address:first").text();
255 });
256 if (actual != expected) {
257 console.log("Bitcoin testnet address is incorrect");
258 console.log("Expected: " + expected);
259 console.log("Actual: " + actual);
260 fail();
261 }
262 next();
263 });
264 });
265 },
266
267 // Network can be set to litecoin
268 function() {
269 page.open(url, function(status) {
270 // set the phrase and coin
271 var expected = "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn";
272 page.evaluate(function() {
273 $(".phrase").val("abandon abandon ability");
274 $(".phrase").trigger("input");
275 $(".network option[selected]").removeAttr("selected");
276 $(".network option[value=2]").prop("selected", true);
277 $(".network").trigger("change");
278 });
279 // check the address is generated correctly
280 waitForGenerate(function() {
281 var actual = page.evaluate(function() {
282 return $(".address:first").text();
283 });
284 if (actual != expected) {
285 console.log("Litecoin address is incorrect");
286 console.log("Expected: " + expected);
287 console.log("Actual: " + actual);
288 fail();
289 }
290 next();
291 });
292 });
293 },
294
295 // Network can be set to dogecoin
296 function() {
297 page.open(url, function(status) {
298 // set the phrase and coin
299 var expected = "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA";
300 page.evaluate(function() {
301 $(".phrase").val("abandon abandon ability");
302 $(".phrase").trigger("input");
303 $(".network option[selected]").removeAttr("selected");
304 $(".network option[value=3]").prop("selected", true);
305 $(".network").trigger("change");
306 });
307 // check the address is generated correctly
308 waitForGenerate(function() {
309 var actual = page.evaluate(function() {
310 return $(".address:first").text();
311 });
312 if (actual != expected) {
313 console.log("Dogecoin address is incorrect");
314 console.log("Expected: " + expected);
315 console.log("Actual: " + actual);
316 fail();
317 }
318 next();
319 });
320 });
321 },
322
323 // Network can be set to shadowcash
324 function() {
325 page.open(url, function(status) {
326 // set the phrase and coin
327 var expected = "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG";
328 page.evaluate(function() {
329 $(".phrase").val("abandon abandon ability");
330 $(".phrase").trigger("input");
331 $(".network option[selected]").removeAttr("selected");
332 $(".network option[value=4]").prop("selected", true);
333 $(".network").trigger("change");
334 });
335 // check the address is generated correctly
336 waitForGenerate(function() {
337 var actual = page.evaluate(function() {
338 return $(".address:first").text();
339 });
340 if (actual != expected) {
341 console.log("Shadowcash address is incorrect");
342 console.log("Expected: " + expected);
343 console.log("Actual: " + actual);
344 fail();
345 }
346 next();
347 });
348 });
349 },
350
351 // Network can be set to shadowcash testnet
352 function() {
353 page.open(url, function(status) {
354 // set the phrase and coin
355 var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
356 page.evaluate(function() {
357 $(".phrase").val("abandon abandon ability");
358 $(".phrase").trigger("input");
359 $(".network option[selected]").removeAttr("selected");
360 $(".network option[value=5]").prop("selected", true);
361 $(".network").trigger("change");
362 });
363 // check the address is generated correctly
364 waitForGenerate(function() {
365 var actual = page.evaluate(function() {
366 return $(".address:first").text();
367 });
368 if (actual != expected) {
369 console.log("Shadowcash testnet address is incorrect");
370 console.log("Expected: " + expected);
371 console.log("Actual: " + actual);
372 fail();
373 }
374 next();
375 });
376 });
377 },
378
379 // Network can be set to viacoin
380 function() {
381 page.open(url, function(status) {
382 // set the phrase and coin
383 var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
384 page.evaluate(function() {
385 $(".phrase").val("abandon abandon ability");
386 $(".phrase").trigger("input");
387 $(".network option[selected]").removeAttr("selected");
388 $(".network option[value=6]").prop("selected", true);
389 $(".network").trigger("change");
390 });
391 // check the address is generated correctly
392 waitForGenerate(function() {
393 var actual = page.evaluate(function() {
394 return $(".address:first").text();
395 });
396 if (actual != expected) {
397 console.log("Viacoin address is incorrect");
398 console.log("Expected: " + expected);
399 console.log("Actual: " + actual);
400 fail();
401 }
402 next();
403 });
404 });
405 },
406
407 // Network can be set to viacoin testnet
408 function() {
409 page.open(url, function(status) {
410 // set the phrase and coin
411 var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
412 page.evaluate(function() {
413 $(".phrase").val("abandon abandon ability");
414 $(".phrase").trigger("input");
415 $(".network option[selected]").removeAttr("selected");
416 $(".network option[value=7]").prop("selected", true);
417 $(".network").trigger("change");
418 });
419 // check the address is generated correctly
420 waitForGenerate(function() {
421 var actual = page.evaluate(function() {
422 return $(".address:first").text();
423 });
424 if (actual != expected) {
425 console.log("Viacoin testnet address is incorrect");
426 console.log("Expected: " + expected);
427 console.log("Actual: " + actual);
428 fail();
429 }
430 next();
431 });
432 });
433 },
434
435 // Network can be set to jumbucks
436 function() {
437 page.open(url, function(status) {
438 // set the phrase and coin
439 var expected = "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew";
440 page.evaluate(function() {
441 $(".phrase").val("abandon abandon ability");
442 $(".phrase").trigger("input");
443 $(".network option[selected]").removeAttr("selected");
444 $(".network option[value=8]").prop("selected", true);
445 $(".network").trigger("change");
446 });
447 // check the address is generated correctly
448 waitForGenerate(function() {
449 var actual = page.evaluate(function() {
450 return $(".address:first").text();
451 });
452 if (actual != expected) {
453 console.log("Jumbucks address is incorrect");
454 console.log("Expected: " + expected);
455 console.log("Actual: " + actual);
456 fail();
457 }
458 next();
459 });
460 });
461 },
462
463 // Network can be set to clam
464 function() {
465 page.open(url, function(status) {
466 // set the phrase and coin
467 var expected = "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y";
468 page.evaluate(function() {
469 $(".phrase").val("abandon abandon ability");
470 $(".phrase").trigger("input");
471 $(".network option[selected]").removeAttr("selected");
472 $(".network option[value=9]").prop("selected", true);
473 $(".network").trigger("change");
474 });
475 // check the address is generated correctly
476 waitForGenerate(function() {
477 var actual = page.evaluate(function() {
478 return $(".address:first").text();
479 });
480 if (actual != expected) {
481 console.log("CLAM address is incorrect");
482 console.log("Expected: " + expected);
483 console.log("Actual: " + actual);
484 fail();
485 }
486 next();
487 });
488 });
489 },
490
491 // Network can be set to dash
492 function() {
493 page.open(url, function(status) {
494 // set the phrase and coin
495 var expected = "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT";
496 page.evaluate(function() {
497 $(".phrase").val("abandon abandon ability");
498 $(".phrase").trigger("input");
499 $(".network option[selected]").removeAttr("selected");
500 $(".network option[value=10]").prop("selected", true);
501 $(".network").trigger("change");
502 });
503 // check the address is generated correctly
504 waitForGenerate(function() {
505 var actual = page.evaluate(function() {
506 return $(".address:first").text();
507 });
508 if (actual != expected) {
509 console.log("DASH address is incorrect");
510 console.log("Expected: " + expected);
511 console.log("Actual: " + actual);
512 fail();
513 }
514 next();
515 });
516 });
517 },
518
519 // Network can be set to namecoin
520 function() {
521 page.open(url, function(status) {
522 // set the phrase and coin
523 var expected = "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2";
524 page.evaluate(function() {
525 $(".phrase").val("abandon abandon ability");
526 $(".phrase").trigger("input");
527 $(".network option[selected]").removeAttr("selected");
528 $(".network option[value=11]").prop("selected", true);
529 $(".network").trigger("change");
530 });
531 // check the address is generated correctly
532 waitForGenerate(function() {
533 var actual = page.evaluate(function() {
534 return $(".address:first").text();
535 });
536 if (actual != expected) {
537 console.log("Namecoin address is incorrect");
538 console.log("Expected: " + expected);
539 console.log("Actual: " + actual);
540 fail();
541 }
542 next();
543 });
544 });
545 },
546
547 // Network can be set to peercoin
548 function() {
549 page.open(url, function(status) {
550 // set the phrase and coin
551 var expected = "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm";
552 page.evaluate(function() {
553 $(".phrase").val("abandon abandon ability");
554 $(".phrase").trigger("input");
555 $(".network option[selected]").removeAttr("selected");
556 $(".network option[value=12]").prop("selected", true);
557 $(".network").trigger("change");
558 });
559 // check the address is generated correctly
560 waitForGenerate(function() {
561 var actual = page.evaluate(function() {
562 return $(".address:first").text();
563 });
564 if (actual != expected) {
565 console.log("Peercoin address is incorrect");
566 console.log("Expected: " + expected);
567 console.log("Actual: " + actual);
568 fail();
569 }
570 next();
571 });
572 });
573 },
574
575 // BIP39 seed is set from phrase
576 function() {
577 page.open(url, function(status) {
578 // set the phrase
579 var expected = "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
580 page.evaluate(function() {
581 $(".phrase").val("abandon abandon ability");
582 $(".phrase").trigger("input");
583 });
584 // check the address is generated correctly
585 waitForGenerate(function() {
586 var actual = page.evaluate(function() {
587 return $(".seed").val();
588 });
589 if (actual != expected) {
590 console.log("BIP39 seed is incorrectly generated from mnemonic");
591 console.log("Expected: " + expected);
592 console.log("Actual: " + actual);
593 fail();
594 }
595 next();
596 });
597 });
598 },
599
600 // BIP32 root key is set from phrase
601 function() {
602 page.open(url, function(status) {
603 // set the phrase
604 var expected = "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi";
605 page.evaluate(function() {
606 $(".phrase").val("abandon abandon ability");
607 $(".phrase").trigger("input");
608 });
609 // check the address is generated correctly
610 waitForGenerate(function() {
611 var actual = page.evaluate(function() {
612 return $(".root-key").val();
613 });
614 if (actual != expected) {
615 console.log("Root key is incorrectly generated from mnemonic");
616 console.log("Expected: " + expected);
617 console.log("Actual: " + actual);
618 fail();
619 }
620 next();
621 });
622 });
623 },
624
625 // Tabs show correct addresses when changed
626 function() {
627 page.open(url, function(status) {
628 // set the phrase
629 var expected = "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz";
630 page.evaluate(function() {
631 $(".phrase").val("abandon abandon ability");
632 $(".phrase").trigger("input");
633 });
634 // change tabs
635 waitForGenerate(function() {
636 page.evaluate(function() {
637 $("#bip32-tab a").click();
638 });
639 // check the address is generated correctly
640 waitForGenerate(function() {
641 var actual = page.evaluate(function() {
642 return $(".address:first").text();
643 });
644 if (actual != expected) {
645 console.log("Clicking tab generates incorrect address");
646 console.log("Expected: " + expected);
647 console.log("Actual: " + actual);
648 fail();
649 }
650 next();
651 });
652 });
653 });
654 },
655
656 // BIP44 derivation path is shown
657 function() {
658 page.open(url, function(status) {
659 // set the phrase
660 var expected = "m/44'/0'/0'/0";
661 page.evaluate(function() {
662 $(".phrase").val("abandon abandon ability");
663 $(".phrase").trigger("input");
664 });
665 // check the derivation path of the first address
666 waitForGenerate(function() {
667 var actual = page.evaluate(function() {
668 return $("#bip44 .path").val();
669 });
670 if (actual != expected) {
671 console.log("BIP44 derivation path is incorrect");
672 console.log("Expected: " + expected);
673 console.log("Actual: " + actual);
674 fail();
675 }
676 next();
677 });
678 });
679 },
680
681 // BIP44 extended private key is shown
682 function() {
683 page.open(url, function(status) {
684 // set the phrase
685 var expected = "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG";
686 page.evaluate(function() {
687 $(".phrase").val("abandon abandon ability");
688 $(".phrase").trigger("input");
689 });
690 // check the BIP44 extended private key
691 waitForGenerate(function() {
692 var actual = page.evaluate(function() {
693 return $(".extended-priv-key").val();
694 });
695 if (actual != expected) {
696 console.log("BIP44 extended private key is incorrect");
697 console.log("Expected: " + expected);
698 console.log("Actual: " + actual);
699 fail();
700 }
701 next();
702 });
703 });
704 },
705
706 // BIP44 extended public key is shown
707 function() {
708 page.open(url, function(status) {
709 // set the phrase
710 var expected = "xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM";
711 page.evaluate(function() {
712 $(".phrase").val("abandon abandon ability");
713 $(".phrase").trigger("input");
714 });
715 // check the BIP44 extended public key
716 waitForGenerate(function() {
717 var actual = page.evaluate(function() {
718 return $(".extended-pub-key").val();
719 });
720 if (actual != expected) {
721 console.log("BIP44 extended public key is incorrect");
722 console.log("Expected: " + expected);
723 console.log("Actual: " + actual);
724 fail();
725 }
726 next();
727 });
728 });
729 },
730
731 // BIP44 purpose field changes address list
732 function() {
733 page.open(url, function(status) {
734 // set the phrase
735 var expected = "1JbDzRJ2cDT8aat2xwKd6Pb2zzavow5MhF";
736 page.evaluate(function() {
737 $(".phrase").val("abandon abandon ability");
738 $(".phrase").trigger("input");
739 });
740 waitForGenerate(function() {
741 // change the bip44 purpose field to 45
742 page.evaluate(function() {
743 $("#bip44 .purpose").val("45");
744 $("#bip44 .purpose").trigger("input");
745 });
746 waitForGenerate(function() {
747 // check the address for the new derivation path
748 var actual = page.evaluate(function() {
749 return $(".address:first").text();
750 });
751 if (actual != expected) {
752 console.log("BIP44 purpose field generates incorrect address");
753 console.log("Expected: " + expected);
754 console.log("Actual: " + actual);
755 fail();
756 }
757 next();
758 });
759 });
760 });
761 },
762
763 // BIP44 coin field changes address list
764 function() {
765 page.open(url, function(status) {
766 // set the phrase
767 var expected = "1F6dB2djQYrxoyfZZmfr6D5voH8GkJTghk";
768 page.evaluate(function() {
769 $(".phrase").val("abandon abandon ability");
770 $(".phrase").trigger("input");
771 });
772 waitForGenerate(function() {
773 // change the bip44 purpose field to 45
774 page.evaluate(function() {
775 $("#bip44 .coin").val("1");
776 $("#bip44 .coin").trigger("input");
777 });
778 waitForGenerate(function() {
779 // check the address for the new derivation path
780 var actual = page.evaluate(function() {
781 return $(".address:first").text();
782 });
783 if (actual != expected) {
784 console.log("BIP44 coin field generates incorrect address");
785 console.log("Expected: " + expected);
786 console.log("Actual: " + actual);
787 fail();
788 }
789 next();
790 });
791 });
792 });
793 },
794
795 // BIP44 account field changes address list
796 function() {
797 page.open(url, function(status) {
798 // set the phrase
799 var expected = "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
800 page.evaluate(function() {
801 $(".phrase").val("abandon abandon ability");
802 $(".phrase").trigger("input");
803 });
804 waitForGenerate(function() {
805 // change the bip44 purpose field to 45
806 page.evaluate(function() {
807 $("#bip44 .account").val("1");
808 $("#bip44 .account").trigger("input");
809 });
810 waitForGenerate(function() {
811 // check the address for the new derivation path
812 var actual = page.evaluate(function() {
813 return $(".address:first").text();
814 });
815 if (actual != expected) {
816 console.log("BIP44 account field generates incorrect address");
817 console.log("Expected: " + expected);
818 console.log("Actual: " + actual);
819 fail();
820 }
821 next();
822 });
823 });
824 });
825 },
826
827 // BIP44 change field changes address list
828 function() {
829 page.open(url, function(status) {
830 // set the phrase
831 var expected = "1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo";
832 page.evaluate(function() {
833 $(".phrase").val("abandon abandon ability");
834 $(".phrase").trigger("input");
835 });
836 waitForGenerate(function() {
837 // change the bip44 purpose field to 45
838 page.evaluate(function() {
839 $("#bip44 .change").val("1");
840 $("#bip44 .change").trigger("input");
841 });
842 waitForGenerate(function() {
843 // check the address for the new derivation path
844 var actual = page.evaluate(function() {
845 return $(".address:first").text();
846 });
847 if (actual != expected) {
848 console.log("BIP44 change field generates incorrect address");
849 console.log("Expected: " + expected);
850 console.log("Actual: " + actual);
851 fail();
852 }
853 next();
854 });
855 });
856 });
857 },
858
859 // BIP32 derivation path can be set
860 function() {
861 page.open(url, function(status) {
862 // set the phrase
863 var expected = "16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L";
864 page.evaluate(function() {
865 $(".phrase").val("abandon abandon ability");
866 $(".phrase").trigger("input");
867 });
868 // change tabs
869 waitForGenerate(function() {
870 page.evaluate(function() {
871 $("#bip32-tab a").click();
872 });
873 // set the derivation path to m/1
874 waitForGenerate(function() {
875 page.evaluate(function() {
876 $("#bip32 .path").val("m/1");
877 $("#bip32 .path").trigger("input");
878 });
879 // check the address is generated correctly
880 waitForGenerate(function() {
881 var actual = page.evaluate(function() {
882 return $(".address:first").text();
883 });
884 if (actual != expected) {
885 console.log("Custom BIP32 path generates incorrect address");
886 console.log("Expected: " + expected);
887 console.log("Actual: " + actual);
888 fail();
889 }
890 next();
891 });
892 });
893 });
894 });
895 },
896
897 // BIP32 can use hardened derivation paths
898 function() {
899 page.open(url, function(status) {
900 // set the phrase
901 var expected = "14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4";
902 page.evaluate(function() {
903 $(".phrase").val("abandon abandon ability");
904 $(".phrase").trigger("input");
905 });
906 // change tabs
907 waitForGenerate(function() {
908 page.evaluate(function() {
909 $("#bip32-tab a").click();
910 });
911 // set the derivation path to m/0'
912 waitForGenerate(function() {
913 page.evaluate(function() {
914 $("#bip32 .path").val("m/0'");
915 $("#bip32 .path").trigger("input");
916 });
917 // check the address is generated correctly
918 waitForGenerate(function() {
919 var actual = page.evaluate(function() {
920 return $(".address:first").text();
921 });
922 if (actual != expected) {
923 console.log("Hardened BIP32 path generates incorrect address");
924 console.log("Expected: " + expected);
925 console.log("Actual: " + actual);
926 fail();
927 }
928 next();
929 });
930 });
931 });
932 });
933 },
934
935 // BIP32 extended private key is shown
936 function() {
937 page.open(url, function(status) {
938 // set the phrase
939 var expected = "xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe";
940 page.evaluate(function() {
941 $(".phrase").val("abandon abandon ability");
942 $(".phrase").trigger("input");
943 });
944 // change tabs
945 waitForGenerate(function() {
946 page.evaluate(function() {
947 $("#bip32-tab a").click();
948 });
949 // check the extended private key is generated correctly
950 waitForGenerate(function() {
951 var actual = page.evaluate(function() {
952 return $(".extended-priv-key").val();
953 });
954 if (actual != expected) {
955 console.log("BIP32 extended private key is incorrect");
956 console.log("Expected: " + expected);
957 console.log("Actual: " + actual);
958 fail();
959 }
960 next();
961 });
962 });
963 });
964 },
965
966 // BIP32 extended public key is shown
967 function() {
968 page.open(url, function(status) {
969 // set the phrase
970 var expected = "xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P";
971 page.evaluate(function() {
972 $(".phrase").val("abandon abandon ability");
973 $(".phrase").trigger("input");
974 });
975 // change tabs
976 waitForGenerate(function() {
977 page.evaluate(function() {
978 $("#bip32-tab a").click();
979 });
980 // check the extended public key is generated correctly
981 waitForGenerate(function() {
982 var actual = page.evaluate(function() {
983 return $(".extended-pub-key").val();
984 });
985 if (actual != expected) {
986 console.log("BIP32 extended public key is incorrect");
987 console.log("Expected: " + expected);
988 console.log("Actual: " + actual);
989 fail();
990 }
991 next();
992 });
993 });
994 });
995 },
996
997 // Derivation path is shown in table
998 function() {
999 page.open(url, function(status) {
1000 // set the phrase
1001 var expected = "m/44'/0'/0'/0/0";
1002 page.evaluate(function() {
1003 $(".phrase").val("abandon abandon ability");
1004 $(".phrase").trigger("input");
1005 });
1006 // check for derivation path in table
1007 waitForGenerate(function() {
1008 var actual = page.evaluate(function() {
1009 return $(".index:first").text();
1010 });
1011 if (actual != expected) {
1012 console.log("Derivation path shown incorrectly in table");
1013 console.log("Expected: " + expected);
1014 console.log("Actual: " + actual);
1015 fail();
1016 }
1017 next();
1018 });
1019 });
1020 },
1021
1022 // Derivation path for address can be hardened
1023 function() {
1024 page.open(url, function(status) {
1025 // set the phrase
1026 var expected = "18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd";
1027 page.evaluate(function() {
1028 $(".phrase").val("abandon abandon ability");
1029 $(".phrase").trigger("input");
1030 });
1031 // change tabs
1032 waitForGenerate(function() {
1033 page.evaluate(function() {
1034 $("#bip32-tab a").click();
1035 });
1036 waitForGenerate(function() {
1037 // select the hardened addresses option
1038 page.evaluate(function() {
1039 $(".hardened-addresses").prop("checked", true);
1040 $(".hardened-addresses").trigger("change");
1041 });
1042 waitForGenerate(function() {
1043 // check the generated address is hardened
1044 var actual = page.evaluate(function() {
1045 return $(".address:first").text();
1046 });
1047 if (actual != expected) {
1048 console.log("Hardened address is incorrect");
1049 console.log("Expected: " + expected);
1050 console.log("Actual: " + actual);
1051 fail();
1052 }
1053 next();
1054 });
1055 });
1056 });
1057 });
1058 },
1059
1060 // Derivation path visibility can be toggled
1061 function() {
1062 page.open(url, function(status) {
1063 // set the phrase
1064 page.evaluate(function() {
1065 $(".phrase").val("abandon abandon ability");
1066 $(".phrase").trigger("input");
1067 });
1068 waitForGenerate(function() {
1069 // toggle path visibility
1070 page.evaluate(function() {
1071 $(".index-toggle").click();
1072 });
1073 // check the path is not visible
1074 var isInvisible = page.evaluate(function() {
1075 return $(".index:first span").hasClass("invisible");
1076 });
1077 if (!isInvisible) {
1078 console.log("Toggled derivation path is visible");
1079 fail();
1080 }
1081 next();
1082 });
1083 });
1084 },
1085
1086 // Address is shown
1087 function() {
1088 page.open(url, function(status) {
1089 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1090 // set the phrase
1091 page.evaluate(function() {
1092 $(".phrase").val("abandon abandon ability").trigger("input");
1093 });
1094 // get the address
1095 waitForGenerate(function() {
1096 var actual = page.evaluate(function() {
1097 return $(".address:first").text();
1098 });
1099 if (actual != expected) {
1100 console.log("Address is not shown");
1101 console.log("Expected: " + expected);
1102 console.log("Got: " + actual);
1103 fail();
1104 }
1105 next();
1106 });
1107 });
1108 },
1109
1110 // Addresses are shown in order of derivation path
1111 function() {
1112 page.open(url, function(status) {
1113 // set the phrase
1114 page.evaluate(function() {
1115 $(".phrase").val("abandon abandon ability").trigger("input");
1116 });
1117 // get the derivation paths
1118 waitForGenerate(function() {
1119 var paths = page.evaluate(function() {
1120 return $(".index").map(function(i, e) {
1121 return $(e).text();
1122 });
1123 });
1124 if (paths.length != 20) {
1125 console.log("Total paths is less than expected: " + paths.length);
1126 fail();
1127 }
1128 for (var i=0; i<paths.length; i++) {
1129 var expected = "m/44'/0'/0'/0/" + i;
1130 var actual = paths[i];
1131 if (actual != expected) {
1132 console.log("Path " + i + " is incorrect");
1133 console.log("Expected: " + expected);
1134 console.log("Actual: " + actual);
1135 fail();
1136 }
1137 }
1138 next();
1139 });
1140 });
1141 },
1142
1143 // Address visibility can be toggled
1144 function() {
1145 page.open(url, function(status) {
1146 // set the phrase
1147 page.evaluate(function() {
1148 $(".phrase").val("abandon abandon ability");
1149 $(".phrase").trigger("input");
1150 });
1151 waitForGenerate(function() {
1152 // toggle address visibility
1153 page.evaluate(function() {
1154 $(".address-toggle").click();
1155 });
1156 // check the address is not visible
1157 var isInvisible = page.evaluate(function() {
1158 return $(".address:first span").hasClass("invisible");
1159 });
1160 if (!isInvisible) {
1161 console.log("Toggled address is visible");
1162 fail();
1163 }
1164 next();
1165 });
1166 });
1167 },
1168
1169 // Private key is shown
1170 function() {
1171 page.open(url, function(status) {
1172 var expected = "L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE";
1173 // set the phrase
1174 page.evaluate(function() {
1175 $(".phrase").val("abandon abandon ability").trigger("input");
1176 });
1177 // get the address
1178 waitForGenerate(function() {
1179 var actual = page.evaluate(function() {
1180 return $(".privkey:first").text();
1181 });
1182 if (actual != expected) {
1183 console.log("Private key is not shown");
1184 console.log("Expected: " + expected);
1185 console.log("Got: " + actual);
1186 fail();
1187 }
1188 next();
1189 });
1190 });
1191 },
1192
1193 // Private key visibility can be toggled
1194 function() {
1195 page.open(url, function(status) {
1196 // set the phrase
1197 page.evaluate(function() {
1198 $(".phrase").val("abandon abandon ability");
1199 $(".phrase").trigger("input");
1200 });
1201 waitForGenerate(function() {
1202 // toggle private key visibility
1203 page.evaluate(function() {
1204 $(".private-key-toggle").click();
1205 });
1206 // check the private key is not visible
1207 var isInvisible = page.evaluate(function() {
1208 return $(".privkey:first span").hasClass("invisible");
1209 });
1210 if (!isInvisible) {
1211 console.log("Toggled private key is visible");
1212 fail();
1213 }
1214 next();
1215 });
1216 });
1217 },
1218
1219 // More addresses can be generated
1220 function() {
1221 page.open(url, function(status) {
1222 // set the phrase
1223 page.evaluate(function() {
1224 $(".phrase").val("abandon abandon ability");
1225 $(".phrase").trigger("input");
1226 });
1227 waitForGenerate(function() {
1228 // generate more addresses
1229 page.evaluate(function() {
1230 $(".more").click();
1231 });
1232 waitForGenerate(function() {
1233 // check there are more addresses
1234 var addressCount = page.evaluate(function() {
1235 return $(".address").length;
1236 });
1237 if (addressCount != 40) {
1238 console.log("More addresses cannot be generated");
1239 fail();
1240 }
1241 next();
1242 });
1243 });
1244 });
1245 },
1246
1247 // A custom number of additional addresses can be generated
1248 function() {
1249 page.open(url, function(status) {
1250 // set the phrase
1251 page.evaluate(function() {
1252 $(".phrase").val("abandon abandon ability");
1253 $(".phrase").trigger("input");
1254 });
1255 waitForGenerate(function() {
1256 // get the current number of addresses
1257 var oldAddressCount = page.evaluate(function() {
1258 return $(".address").length;
1259 });
1260 // set a custom number of additional addresses
1261 page.evaluate(function() {
1262 $(".rows-to-add").val(1);
1263 });
1264 // generate more addresses
1265 page.evaluate(function() {
1266 $(".more").click();
1267 });
1268 waitForGenerate(function() {
1269 // check there are the correct number of addresses
1270 var newAddressCount = page.evaluate(function() {
1271 return $(".address").length;
1272 });
1273 if (newAddressCount - oldAddressCount != 1) {
1274 console.log("Number of additional addresses cannot be customized");
1275 console.log(newAddressCount)
1276 console.log(oldAddressCount)
1277 fail();
1278 }
1279 next();
1280 });
1281 });
1282 });
1283 },
1284
1285 // Additional addresses are shown in order of derivation path
1286 function() {
1287 page.open(url, function(status) {
1288 // set the phrase
1289 page.evaluate(function() {
1290 $(".phrase").val("abandon abandon ability").trigger("input");
1291 });
1292 waitForGenerate(function() {
1293 // generate more addresses
1294 page.evaluate(function() {
1295 $(".more").click();
1296 });
1297 // get the derivation paths
1298 waitForGenerate(function() {
1299 var paths = page.evaluate(function() {
1300 return $(".index").map(function(i, e) {
1301 return $(e).text();
1302 });
1303 });
1304 if (paths.length != 40) {
1305 console.log("Total additional paths is less than expected: " + paths.length);
1306 fail();
1307 }
1308 for (var i=0; i<paths.length; i++) {
1309 var expected = "m/44'/0'/0'/0/" + i;
1310 var actual = paths[i];
1311 if (actual != expected) {
1312 console.log("Path " + i + " is not in correct order");
1313 console.log("Expected: " + expected);
1314 console.log("Actual: " + actual);
1315 fail();
1316 }
1317 }
1318 next();
1319 });
1320 });
1321 });
1322 },
1323
1324 // BIP32 root key can be set by the user
1325 function() {
1326 page.open(url, function(status) {
1327 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1328 // set the root key
1329 page.evaluate(function() {
1330 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1331 });
1332 waitForGenerate(function() {
1333 var actual = page.evaluate(function() {
1334 return $(".address:first").text();
1335 });
1336 if (actual != expected) {
1337 console.log("Setting BIP32 root key results in wrong address");
1338 console.log("Expected: " + expected);
1339 console.log("Actual: " + actual);
1340 fail();
1341 }
1342 next();
1343 });
1344 });
1345 },
1346
1347 // Setting BIP32 root key clears the existing phrase, passphrase and seed
1348 function() {
1349 page.open(url, function(status) {
1350 var expected = "";
1351 // set a mnemonic
1352 page.evaluate(function() {
1353 $(".phrase").val("A non-blank but invalid value");
1354 });
1355 // Accept any confirm dialogs
1356 page.onConfirm = function() {
1357 return true;
1358 };
1359 // set the root key
1360 page.evaluate(function() {
1361 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1362 });
1363 waitForGenerate(function() {
1364 var actual = page.evaluate(function() {
1365 return $(".phrase").val();
1366 });
1367 if (actual != expected) {
1368 console.log("Phrase not cleared when setting BIP32 root key");
1369 console.log("Expected: " + expected);
1370 console.log("Actual: " + actual);
1371 fail();
1372 }
1373 next();
1374 });
1375 });
1376 },
1377
1378 // Clearing of phrase, passphrase and seed can be cancelled by user
1379 function() {
1380 page.open(url, function(status) {
1381 var expected = "abandon abandon ability";
1382 // set a mnemonic
1383 page.evaluate(function() {
1384 $(".phrase").val("abandon abandon ability");
1385 });
1386 // Cancel any confirm dialogs
1387 page.onConfirm = function() {
1388 return false;
1389 };
1390 // set the root key
1391 page.evaluate(function() {
1392 $(".root-key").val("xprv9s21ZrQH143K3d3vzEDD3KpSKmxsZ3y7CqhAL1tinwtP6wqK4TKEKjpBuo6P2hUhB6ZENo7TTSRytiP857hBZVpBdk8PooFuRspE1eywwNZ").trigger("input");
1393 });
1394 var actual = page.evaluate(function() {
1395 return $(".phrase").val();
1396 });
1397 if (actual != expected) {
1398 console.log("Phrase not retained when cancelling changes to BIP32 root key");
1399 console.log("Expected: " + expected);
1400 console.log("Actual: " + actual);
1401 fail();
1402 }
1403 next();
1404 });
1405 },
1406
1407 // Custom BIP32 root key is used when changing the derivation path
1408 function() {
1409 page.open(url, function(status) {
1410 var expected = "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1411 // set the root key
1412 page.evaluate(function() {
1413 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1414 });
1415 waitForGenerate(function() {
1416 // change the derivation path
1417 page.evaluate(function() {
1418 $("#account").val("1").trigger("input");
1419 });
1420 // check the bip32 root key is used for derivation, not the blank phrase
1421 waitForGenerate(function() {
1422 var actual = page.evaluate(function() {
1423 return $(".address:first").text();
1424 });
1425 if (actual != expected) {
1426 console.log("Changing the derivation path does not use BIP32 root key");
1427 console.log("Expected: " + expected);
1428 console.log("Actual: " + actual);
1429 fail();
1430 }
1431 next();
1432 });
1433 });
1434 });
1435 },
1436
1437 // Incorrect mnemonic shows error
1438 function() {
1439 page.open(url, function(status) {
1440 // set the root key
1441 page.evaluate(function() {
1442 $(".phrase").val("abandon abandon abandon").trigger("input");
1443 });
1444 waitForFeedback(function() {
1445 // check there is an error shown
1446 var feedback = page.evaluate(function() {
1447 return $(".feedback").text();
1448 });
1449 if (feedback.length <= 0) {
1450 console.log("Invalid mnemonic does not show error");
1451 fail();
1452 }
1453 next();
1454 });
1455 });
1456 },
1457
1458 // Incorrect word shows suggested replacement
1459 function() {
1460 page.open(url, function(status) {
1461 // set the root key
1462 page.evaluate(function() {
1463 $(".phrase").val("abandon abandon abiliti").trigger("input");
1464 });
1465 // check there is a suggestion shown
1466 waitForFeedback(function() {
1467 var feedback = page.evaluate(function() {
1468 return $(".feedback").text();
1469 });
1470 if (feedback.indexOf("did you mean ability?") < 0) {
1471 console.log("Incorrect word does not show suggested replacement");
1472 console.log("Error: " + error);
1473 fail();
1474 }
1475 next();
1476 });
1477 });
1478 },
1479
1480 // Incorrect BIP32 root key shows error
1481 function() {
1482 page.open(url, function(status) {
1483 // set the root key
1484 page.evaluate(function() {
1485 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj").trigger("input");
1486 });
1487 // check there is an error shown
1488 waitForFeedback(function() {
1489 var feedback = page.evaluate(function() {
1490 return $(".feedback").text();
1491 });
1492 if (feedback != "Invalid root key") {
1493 console.log("Invalid root key does not show error");
1494 console.log("Error: " + error);
1495 fail();
1496 }
1497 next();
1498 });
1499 });
1500 },
1501
1502 // Derivation path not starting with m shows error
1503 function() {
1504 page.open(url, function(status) {
1505 // set the mnemonic phrase
1506 page.evaluate(function() {
1507 $(".phrase").val("abandon abandon ability").trigger("input");
1508 });
1509 waitForGenerate(function() {
1510 // select the bip32 tab so custom derivation path can be set
1511 page.evaluate(function() {
1512 $("#bip32-tab a").click();
1513 });
1514 waitForGenerate(function() {
1515 // set the incorrect derivation path
1516 page.evaluate(function() {
1517 $("#bip32 .path").val("n/0").trigger("input");
1518 });
1519 waitForFeedback(function() {
1520 var feedback = page.evaluate(function() {
1521 return $(".feedback").text();
1522 });
1523 if (feedback != "First character must be 'm'") {
1524 console.log("Derivation path not starting with m should show error");
1525 console.log("Error: " + error);
1526 fail();
1527 }
1528 next();
1529 });
1530 });
1531 });
1532 });
1533 },
1534
1535 // Derivation path containing invalid characters shows useful error
1536 function() {
1537 page.open(url, function(status) {
1538 // set the mnemonic phrase
1539 page.evaluate(function() {
1540 $(".phrase").val("abandon abandon ability").trigger("input");
1541 });
1542 waitForGenerate(function() {
1543 // select the bip32 tab so custom derivation path can be set
1544 page.evaluate(function() {
1545 $("#bip32-tab a").click();
1546 });
1547 waitForGenerate(function() {
1548 // set the incorrect derivation path
1549 page.evaluate(function() {
1550 $("#bip32 .path").val("m/1/0wrong1/1").trigger("input");
1551 });
1552 waitForFeedback(function() {
1553 var feedback = page.evaluate(function() {
1554 return $(".feedback").text();
1555 });
1556 if (feedback != "Invalid characters 0wrong1 found at depth 2") {
1557 console.log("Derivation path with invalid characters should show error");
1558 console.log("Error: " + error);
1559 fail();
1560 }
1561 next();
1562 });
1563 });
1564 });
1565 });
1566 },
1567
1568 // Github Issue 11: Default word length is 15
1569 // https://github.com/iancoleman/bip39/issues/11
1570 function() {
1571 page.open(url, function(status) {
1572 // get the word length
1573 var defaultLength = page.evaluate(function() {
1574 return $(".strength").val();
1575 });
1576 if (defaultLength != 15) {
1577 console.log("Default word length is not 15");
1578 fail();
1579 }
1580 next();
1581 });
1582 },
1583
1584
1585 // Github Issue 12: Generate more rows with private keys hidden
1586 // https://github.com/iancoleman/bip39/issues/12
1587 function() {
1588 page.open(url, function(status) {
1589 // set the phrase
1590 page.evaluate(function() {
1591 $(".phrase").val("abandon abandon ability");
1592 $(".phrase").trigger("input");
1593 });
1594 waitForGenerate(function() {
1595 // toggle private keys hidden, then generate more addresses
1596 page.evaluate(function() {
1597 $(".private-key-toggle").click();
1598 $(".more").click();
1599 });
1600 waitForGenerate(function() {
1601 // check more have been generated
1602 var expected = 40;
1603 var numPrivKeys = page.evaluate(function() {
1604 return $(".privkey").length;
1605 });
1606 if (numPrivKeys != expected) {
1607 console.log("Wrong number of addresses when clicking 'more' with hidden privkeys");
1608 console.log("Expected: " + expected);
1609 console.log("Actual: " + numPrivKeys);
1610 fail();
1611 }
1612 // check no private keys are shown
1613 var numHiddenPrivKeys = page.evaluate(function() {
1614 return $(".privkey span[class=invisible]").length;
1615 });
1616 if (numHiddenPrivKeys != expected) {
1617 console.log("Generating more does not retain hidden state of privkeys");
1618 console.log("Expected: " + expected);
1619 console.log("Actual: " + numHiddenPrivKeys);
1620 fail();
1621 }
1622 next();
1623 });
1624 });
1625 });
1626 },
1627
1628 // Github Issue 19: Mnemonic is not sensitive to whitespace
1629 // https://github.com/iancoleman/bip39/issues/19
1630 function() {
1631 page.open(url, function(status) {
1632 // set the phrase
1633 var expected = "xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC";
1634 page.evaluate(function() {
1635 var doubleSpace = " ";
1636 $(".phrase").val("urge cat" + doubleSpace + "bid");
1637 $(".phrase").trigger("input");
1638 });
1639 waitForGenerate(function() {
1640 // Check the bip32 root key is correct
1641 var actual = page.evaluate(function() {
1642 return $(".root-key").val();
1643 });
1644 if (actual != expected) {
1645 console.log("Mnemonic is sensitive to whitespace");
1646 console.log("Expected: " + expected);
1647 console.log("Actual: " + actual);
1648 fail();
1649 }
1650 next();
1651 });
1652 });
1653 },
1654
1655 // Github Issue 23: Part 1: Use correct derivation path when changing tabs
1656 // https://github.com/iancoleman/bip39/issues/23
1657 function() {
1658 page.open(url, function(status) {
1659 // 1) and 2) set the phrase
1660 page.evaluate(function() {
1661 $(".phrase").val("abandon abandon ability").trigger("input");
1662 });
1663 waitForGenerate(function() {
1664 // 3) select bip32 tab
1665 page.evaluate(function() {
1666 $("#bip32-tab a").click();
1667 });
1668 waitForGenerate(function() {
1669 // 4) switch from bitcoin to litecoin
1670 page.evaluate(function() {
1671 $(".network").val("2").trigger("change");
1672 });
1673 waitForGenerate(function() {
1674 // 5) Check derivation path is displayed correctly
1675 var expected = "m/0/0";
1676 var actual = page.evaluate(function() {
1677 return $(".index:first").text();
1678 });
1679 if (actual != expected) {
1680 console.log("Github Issue 23 Part 1: derivation path display error");
1681 console.log("Expected: " + expected);
1682 console.log("Actual: " + actual);
1683 fail();
1684 }
1685 // 5) Check address is displayed correctly
1686 var expected = "LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5";
1687 var actual = page.evaluate(function() {
1688 return $(".address:first").text();
1689 });
1690 if (actual != expected) {
1691 console.log("Github Issue 23 Part 1: address display error");
1692 console.log("Expected: " + expected);
1693 console.log("Actual: " + actual);
1694 fail();
1695 }
1696 next();
1697 });
1698 });
1699 });
1700 });
1701 },
1702
1703 // Github Issue 23 Part 2: Coin selection in derivation path
1704 // https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
1705 function() {
1706 page.open(url, function(status) {
1707 // set the phrase
1708 page.evaluate(function() {
1709 $(".phrase").val("abandon abandon ability").trigger("input");
1710 });
1711 waitForGenerate(function() {
1712 // switch from bitcoin to clam
1713 page.evaluate(function() {
1714 $(".network").val("9").trigger("change");
1715 });
1716 waitForGenerate(function() {
1717 // check derivation path is displayed correctly
1718 var expected = "m/44'/23'/0'/0/0";
1719 var actual = page.evaluate(function() {
1720 return $(".index:first").text();
1721 });
1722 if (actual != expected) {
1723 console.log("Github Issue 23 Part 2: Coin in BIP44 derivation path is incorrect");
1724 console.log("Expected: " + expected);
1725 console.log("Actual: " + actual);
1726 fail();
1727 }
1728 next();
1729 });
1730 });
1731 });
1732 },
1733
1734 // Github Issue 26: When using a Root key derrived altcoins are incorrect
1735 // https://github.com/iancoleman/bip39/issues/26
1736 function() {
1737 page.open(url, function(status) {
1738 // 1) 2) and 3) set the root key
1739 page.evaluate(function() {
1740 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1741 });
1742 waitForGenerate(function() {
1743 // 4) switch from bitcoin to viacoin
1744 page.evaluate(function() {
1745 $(".network").val("6").trigger("change");
1746 });
1747 waitForGenerate(function() {
1748 // 5) ensure the derived address is correct
1749 var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
1750 var actual = page.evaluate(function() {
1751 return $(".address:first").text();
1752 });
1753 if (actual != expected) {
1754 console.log("Github Issue 26: address is incorrect when changing networks and using root-key to derive");
1755 console.log("Expected: " + expected);
1756 console.log("Actual: " + actual);
1757 fail();
1758 }
1759 next();
1760 });
1761 });
1762 });
1763 },
1764
1765 // Selecting a language with no existing phrase should generate a phrase in
1766 // that language.
1767 function() {
1768 page.open(url, function(status) {
1769 // Select a language
1770 // Need to manually simulate hash being set due to quirk between
1771 // 'click' event triggered by javascript vs triggered by mouse.
1772 // Perhaps look into page.sendEvent
1773 // http://phantomjs.org/api/webpage/method/send-event.html
1774 page.evaluate(function() {
1775 window.location.hash = "#japanese";
1776 $("a[href='#japanese']").trigger("click");
1777 });
1778 waitForGenerate(function() {
1779 // Check the mnemonic is in Japanese
1780 var phrase = page.evaluate(function() {
1781 return $(".phrase").val();
1782 });
1783 if (phrase.length <= 0) {
1784 console.log("No Japanese phrase generated");
1785 fail();
1786 }
1787 if (phrase.charCodeAt(0) < 128) {
1788 console.log("First character of Japanese phrase is ascii");
1789 console.log("Phrase: " + phrase);
1790 fail();
1791 }
1792 next();
1793 });
1794 });
1795 },
1796
1797 // Selecting a language with existing phrase should update the phrase to use
1798 // that language.
1799 function() {
1800 page.open(url, function(status) {
1801 // Set the phrase to an English phrase.
1802 page.evaluate(function() {
1803 $(".phrase").val("abandon abandon ability").trigger("input");
1804 });
1805 waitForGenerate(function() {
1806 // Change to Italian
1807 // Need to manually simulate hash being set due to quirk between
1808 // 'click' event triggered by javascript vs triggered by mouse.
1809 // Perhaps look into page.sendEvent
1810 // http://phantomjs.org/api/webpage/method/send-event.html
1811 page.evaluate(function() {
1812 window.location.hash = "#italian";
1813 $("a[href='#italian']").trigger("click");
1814 });
1815 waitForGenerate(function() {
1816 // Check only the language changes, not the phrase
1817 var expected = "abaco abaco abbaglio";
1818 var actual = page.evaluate(function() {
1819 return $(".phrase").val();
1820 });
1821 if (actual != expected) {
1822 console.log("Changing language with existing phrase");
1823 console.log("Expected: " + expected);
1824 console.log("Actual: " + actual);
1825 fail();
1826 }
1827 // Check the address is correct
1828 var expected = "1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV";
1829 var actual = page.evaluate(function() {
1830 return $(".address:first").text();
1831 });
1832 if (actual != expected) {
1833 console.log("Changing language generates incorrect address");
1834 console.log("Expected: " + expected);
1835 console.log("Actual: " + actual);
1836 fail();
1837 }
1838 next();
1839 });
1840 });
1841 });
1842 },
1843
1844 // Suggested replacement for erroneous word in non-English language
1845 function() {
1846 page.open(url, function(status) {
1847 // Set an incorrect phrase in Italian
1848 page.evaluate(function() {
1849 $(".phrase").val("abaco abaco zbbaglio").trigger("input");
1850 });
1851 waitForFeedback(function() {
1852 // Check the suggestion is correct
1853 var feedback = page.evaluate(function() {
1854 return $(".feedback").text();
1855 });
1856 if (feedback.indexOf("did you mean abbaglio?") < 0) {
1857 console.log("Incorrect Italian word does not show suggested replacement");
1858 console.log("Error: " + error);
1859 fail();
1860 }
1861 next();
1862 });
1863 });
1864 },
1865
1866
1867 // Japanese word does not break across lines.
1868 // Point 2 from
1869 // https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
1870 function() {
1871 page.open(url, function(status) {
1872 hasWordBreakCss = page.content.indexOf("word-break: keep-all;") > -1;
1873 if (!hasWordBreakCss) {
1874 console.log("Japanese words can break across lines mid-word");
1875 console.log("Check CSS for '.phrase { word-break: keep-all; }'");
1876 fail();
1877 }
1878 // Run the next test
1879 next();
1880 });
1881 },
1882
1883 // Language can be specified at page load using hash value in url
1884 function() {
1885 page.open(url, function(status) {
1886 // Set the page hash as if it were on a fresh page load
1887 page.evaluate(function() {
1888 window.location.hash = "#japanese";
1889 });
1890 // Generate a random phrase
1891 page.evaluate(function() {
1892 $(".generate").trigger("click");
1893 });
1894 waitForGenerate(function() {
1895 // Check the phrase is in Japanese
1896 var phrase = page.evaluate(function() {
1897 return $(".phrase").val();
1898 });
1899 if (phrase.length <= 0) {
1900 console.log("No phrase generated using url hash");
1901 fail();
1902 }
1903 if (phrase.charCodeAt(0) < 128) {
1904 console.log("Language not detected from url hash on page load.");
1905 console.log("Phrase: " + phrase);
1906 fail();
1907 }
1908 next();
1909 });
1910 });
1911 },
1912
1913 // If you wish to add more tests, do so here...
1914
1915 // Here is a blank test template
1916 /*
1917
1918 function() {
1919 page.open(url, function(status) {
1920 // Do something on the page
1921 page.evaluate(function() {
1922 $(".phrase").val("abandon abandon ability").trigger("input");
1923 });
1924 waitForGenerate(function() {
1925 // Check the result of doing the thing
1926 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1927 var actual = page.evaluate(function() {
1928 return $(".address:first").text();
1929 });
1930 if (actual != expected) {
1931 console.log("A specific message about what failed");
1932 console.log("Expected: " + expected);
1933 console.log("Actual: " + actual);
1934 fail();
1935 }
1936 // Run the next test
1937 next();
1938 });
1939 });
1940 },
1941
1942 */
1943
1944 ];
1945
1946 console.log("Running tests...");
1947 tests = shuffle(tests);
1948 next();