]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blame - tests.js
Cards can be used for entropy
[perso/Immae/Projets/Cryptomonnaies/BIP39.git] / tests.js
CommitLineData
88e2cdaa
IC
1// Usage:
2// $ phantomjs tests.js
3
4
5var page = require('webpage').create();
6var url = 'src/index.html';
3eef9d0d 7var testMaxTime = 5000;
88e2cdaa
IC
8
9page.onResourceError = function(e) {
10 console.log("Error loading " + e.url);
11 phantom.exit();
12}
13
14function fail() {
15 console.log("Failed");
16 phantom.exit();
17}
18
3eef9d0d
IC
19function 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
c3719b00
IC
47function 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
057722b0
IC
78function waitForEntropyFeedback(fn, maxTime) {
79 if (!maxTime) {
80 maxTime = testMaxTime;
81 }
82 var origFeedback = page.evaluate(function() {
83 return $(".entropy-error").text();
84 });
85 var start = new Date().getTime();
86 var wait = function keepWaiting() {
87 var now = new Date().getTime();
88 var hasTimedOut = now - start > maxTime;
89 if (hasTimedOut) {
90 console.log("Test timed out");
91 fn();
92 return;
93 }
94 var feedback = page.evaluate(function() {
95 var feedback = $(".entropy-error");
96 if (feedback.css("display") == "none") {
97 return "";
98 }
99 return feedback.text();
100 });
101 var hasFinished = feedback != origFeedback;
102 if (hasFinished) {
103 fn();
104 }
105 else {
106 setTimeout(keepWaiting, 100);
107 }
108 }
109 wait();
110}
111
88e2cdaa
IC
112function next() {
113 if (tests.length > 0) {
114 var testsStr = tests.length == 1 ? "test" : "tests";
115 console.log(tests.length + " " + testsStr + " remaining");
116 tests.shift()();
117 }
118 else {
119 console.log("Finished with 0 failures");
120 phantom.exit();
121 }
122}
123
fb372687
IC
124/**
125 * Randomize array element order in-place.
126 * Using Durstenfeld shuffle algorithm.
127 * See http://stackoverflow.com/a/12646864
128 */
129function shuffle(array) {
130 for (var i = array.length - 1; i > 0; i--) {
131 var j = Math.floor(Math.random() * (i + 1));
132 var temp = array[i];
133 array[i] = array[j];
134 array[j] = temp;
135 }
136 return array;
137}
138
88e2cdaa
IC
139tests = [
140
141// Page loads with status of 'success'
142function() {
143page.open(url, function(status) {
144 if (status != "success") {
145 console.log("Page did not load with status 'success'");
146 fail();
147 }
148 next();
149});
150},
151
152// Page has text
153function() {
154page.open(url, function(status) {
155 var content = page.evaluate(function() {
156 return document.body.textContent.trim();
157 });
158 if (!content) {
159 console.log("Page does not have text");
160 fail();
161 }
162 next();
163});
164},
165
166// Entering mnemonic generates addresses
167function() {
168page.open(url, function(status) {
88e2cdaa
IC
169 // set the phrase
170 page.evaluate(function() {
171 $(".phrase").val("abandon abandon ability").trigger("input");
172 });
173 // get the address
3eef9d0d 174 waitForGenerate(function() {
06c4c6e3
IC
175 var addressCount = page.evaluate(function() {
176 return $(".address").length;
88e2cdaa 177 });
06c4c6e3
IC
178 if (addressCount != 20) {
179 console.log("Mnemonic did not generate addresses");
88e2cdaa
IC
180 console.log("Expected: " + expected);
181 console.log("Got: " + actual);
182 fail();
183 }
184 next();
3eef9d0d 185 });
88e2cdaa
IC
186});
187},
188
189// Random button generates random mnemonic
190function() {
191page.open(url, function(status) {
192 // check initial phrase is empty
193 var phrase = page.evaluate(function() {
194 return $(".phrase").text();
195 });
196 if (phrase != "") {
197 console.log("Initial phrase is not blank");
198 fail();
199 }
200 // press the 'generate' button
201 page.evaluate(function() {
202 $(".generate").click();
203 });
204 // get the new phrase
3eef9d0d 205 waitForGenerate(function() {
88e2cdaa
IC
206 var phrase = page.evaluate(function() {
207 return $(".phrase").val();
208 });
209 if (phrase.length <= 0) {
210 console.log("Phrase not generated by pressing button");
211 fail();
212 }
213 next();
3eef9d0d 214 });
88e2cdaa
IC
215});
216},
217
218// Mnemonic length can be customized
219function() {
220page.open(url, function(status) {
221 // set the length to 6
54563907 222 var expectedLength = "6";
88e2cdaa 223 page.evaluate(function() {
54563907
IC
224 $(".strength option[selected]").removeAttr("selected");
225 $(".strength option[value=6]").prop("selected", true);
88e2cdaa
IC
226 });
227 // press the 'generate' button
228 page.evaluate(function() {
229 $(".generate").click();
230 });
231 // check the new phrase is six words long
3eef9d0d 232 waitForGenerate(function() {
88e2cdaa
IC
233 var actualLength = page.evaluate(function() {
234 var words = $(".phrase").val().split(" ");
235 return words.length;
236 });
237 if (actualLength != expectedLength) {
238 console.log("Phrase not generated with correct length");
239 console.log("Expected: " + expectedLength);
240 console.log("Actual: " + actualLength);
241 fail();
242 }
54563907 243 next();
3eef9d0d 244 });
88e2cdaa
IC
245});
246},
247
88e2cdaa 248// Passphrase can be set
54563907
IC
249function() {
250page.open(url, function(status) {
251 // set the phrase and passphrase
252 var expected = "15pJzUWPGzR7avffV9nY5by4PSgSKG9rba";
253 page.evaluate(function() {
254 $(".phrase").val("abandon abandon ability");
255 $(".passphrase").val("secure_passphrase").trigger("input");
256 });
257 // check the address is generated correctly
3eef9d0d 258 waitForGenerate(function() {
54563907
IC
259 var actual = page.evaluate(function() {
260 return $(".address:first").text();
261 });
262 if (actual != expected) {
263 console.log("Passphrase results in wrong address");
264 console.log("Expected: " + expected);
265 console.log("Actual: " + actual);
266 fail();
267 }
268 next();
3eef9d0d 269 });
54563907
IC
270});
271},
272
88e2cdaa 273// Network can be set to bitcoin testnet
54563907
IC
274function() {
275page.open(url, function(status) {
59193779 276 // set the phrase and coin
54563907
IC
277 var expected = "mucaU5iiDaJDb69BHLeDv8JFfGiyg2nJKi";
278 page.evaluate(function() {
279 $(".phrase").val("abandon abandon ability");
280 $(".phrase").trigger("input");
281 $(".network option[selected]").removeAttr("selected");
282 $(".network option[value=1]").prop("selected", true);
283 $(".network").trigger("change");
284 });
285 // check the address is generated correctly
3eef9d0d 286 waitForGenerate(function() {
54563907
IC
287 var actual = page.evaluate(function() {
288 return $(".address:first").text();
289 });
290 if (actual != expected) {
291 console.log("Bitcoin testnet address is incorrect");
292 console.log("Expected: " + expected);
293 console.log("Actual: " + actual);
294 fail();
295 }
296 next();
3eef9d0d 297 });
54563907
IC
298});
299},
300
88e2cdaa 301// Network can be set to litecoin
59193779
IC
302function() {
303page.open(url, function(status) {
304 // set the phrase and coin
305 var expected = "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn";
306 page.evaluate(function() {
307 $(".phrase").val("abandon abandon ability");
308 $(".phrase").trigger("input");
309 $(".network option[selected]").removeAttr("selected");
310 $(".network option[value=2]").prop("selected", true);
311 $(".network").trigger("change");
312 });
313 // check the address is generated correctly
3eef9d0d 314 waitForGenerate(function() {
59193779
IC
315 var actual = page.evaluate(function() {
316 return $(".address:first").text();
317 });
318 if (actual != expected) {
319 console.log("Litecoin address is incorrect");
320 console.log("Expected: " + expected);
321 console.log("Actual: " + actual);
322 fail();
323 }
324 next();
3eef9d0d 325 });
59193779
IC
326});
327},
328
88e2cdaa 329// Network can be set to dogecoin
59193779
IC
330function() {
331page.open(url, function(status) {
332 // set the phrase and coin
333 var expected = "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA";
334 page.evaluate(function() {
335 $(".phrase").val("abandon abandon ability");
336 $(".phrase").trigger("input");
337 $(".network option[selected]").removeAttr("selected");
338 $(".network option[value=3]").prop("selected", true);
339 $(".network").trigger("change");
340 });
341 // check the address is generated correctly
3eef9d0d 342 waitForGenerate(function() {
59193779
IC
343 var actual = page.evaluate(function() {
344 return $(".address:first").text();
345 });
346 if (actual != expected) {
347 console.log("Dogecoin address is incorrect");
348 console.log("Expected: " + expected);
349 console.log("Actual: " + actual);
350 fail();
351 }
352 next();
3eef9d0d 353 });
59193779
IC
354});
355},
356
88e2cdaa 357// Network can be set to shadowcash
59193779
IC
358function() {
359page.open(url, function(status) {
360 // set the phrase and coin
361 var expected = "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG";
362 page.evaluate(function() {
363 $(".phrase").val("abandon abandon ability");
364 $(".phrase").trigger("input");
365 $(".network option[selected]").removeAttr("selected");
366 $(".network option[value=4]").prop("selected", true);
367 $(".network").trigger("change");
368 });
369 // check the address is generated correctly
3eef9d0d 370 waitForGenerate(function() {
59193779
IC
371 var actual = page.evaluate(function() {
372 return $(".address:first").text();
373 });
374 if (actual != expected) {
375 console.log("Shadowcash address is incorrect");
376 console.log("Expected: " + expected);
377 console.log("Actual: " + actual);
378 fail();
379 }
380 next();
3eef9d0d 381 });
59193779
IC
382});
383},
384
88e2cdaa 385// Network can be set to shadowcash testnet
59193779
IC
386function() {
387page.open(url, function(status) {
388 // set the phrase and coin
389 var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
390 page.evaluate(function() {
391 $(".phrase").val("abandon abandon ability");
392 $(".phrase").trigger("input");
393 $(".network option[selected]").removeAttr("selected");
394 $(".network option[value=5]").prop("selected", true);
395 $(".network").trigger("change");
396 });
397 // check the address is generated correctly
3eef9d0d 398 waitForGenerate(function() {
59193779
IC
399 var actual = page.evaluate(function() {
400 return $(".address:first").text();
401 });
402 if (actual != expected) {
403 console.log("Shadowcash testnet address is incorrect");
404 console.log("Expected: " + expected);
405 console.log("Actual: " + actual);
406 fail();
407 }
408 next();
3eef9d0d 409 });
59193779
IC
410});
411},
412
88e2cdaa 413// Network can be set to viacoin
59193779
IC
414function() {
415page.open(url, function(status) {
416 // set the phrase and coin
417 var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
418 page.evaluate(function() {
419 $(".phrase").val("abandon abandon ability");
420 $(".phrase").trigger("input");
421 $(".network option[selected]").removeAttr("selected");
422 $(".network option[value=6]").prop("selected", true);
423 $(".network").trigger("change");
424 });
425 // check the address is generated correctly
3eef9d0d 426 waitForGenerate(function() {
59193779
IC
427 var actual = page.evaluate(function() {
428 return $(".address:first").text();
429 });
430 if (actual != expected) {
431 console.log("Viacoin address is incorrect");
432 console.log("Expected: " + expected);
433 console.log("Actual: " + actual);
434 fail();
435 }
436 next();
3eef9d0d 437 });
59193779
IC
438});
439},
440
88e2cdaa 441// Network can be set to viacoin testnet
59193779
IC
442function() {
443page.open(url, function(status) {
444 // set the phrase and coin
445 var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
446 page.evaluate(function() {
447 $(".phrase").val("abandon abandon ability");
448 $(".phrase").trigger("input");
449 $(".network option[selected]").removeAttr("selected");
450 $(".network option[value=7]").prop("selected", true);
451 $(".network").trigger("change");
452 });
453 // check the address is generated correctly
3eef9d0d 454 waitForGenerate(function() {
59193779
IC
455 var actual = page.evaluate(function() {
456 return $(".address:first").text();
457 });
458 if (actual != expected) {
459 console.log("Viacoin testnet address is incorrect");
460 console.log("Expected: " + expected);
461 console.log("Actual: " + actual);
462 fail();
463 }
464 next();
3eef9d0d 465 });
59193779
IC
466});
467},
468
88e2cdaa 469// Network can be set to jumbucks
59193779
IC
470function() {
471page.open(url, function(status) {
472 // set the phrase and coin
473 var expected = "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew";
474 page.evaluate(function() {
475 $(".phrase").val("abandon abandon ability");
476 $(".phrase").trigger("input");
477 $(".network option[selected]").removeAttr("selected");
478 $(".network option[value=8]").prop("selected", true);
479 $(".network").trigger("change");
480 });
481 // check the address is generated correctly
3eef9d0d 482 waitForGenerate(function() {
59193779
IC
483 var actual = page.evaluate(function() {
484 return $(".address:first").text();
485 });
486 if (actual != expected) {
487 console.log("Jumbucks address is incorrect");
488 console.log("Expected: " + expected);
489 console.log("Actual: " + actual);
490 fail();
491 }
492 next();
3eef9d0d 493 });
59193779
IC
494});
495},
496
88e2cdaa 497// Network can be set to clam
59193779
IC
498function() {
499page.open(url, function(status) {
500 // set the phrase and coin
501 var expected = "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y";
502 page.evaluate(function() {
503 $(".phrase").val("abandon abandon ability");
504 $(".phrase").trigger("input");
505 $(".network option[selected]").removeAttr("selected");
506 $(".network option[value=9]").prop("selected", true);
507 $(".network").trigger("change");
508 });
509 // check the address is generated correctly
3eef9d0d 510 waitForGenerate(function() {
59193779
IC
511 var actual = page.evaluate(function() {
512 return $(".address:first").text();
513 });
514 if (actual != expected) {
515 console.log("CLAM address is incorrect");
516 console.log("Expected: " + expected);
517 console.log("Actual: " + actual);
518 fail();
f3fad1b5
IC
519 }
520 next();
521 });
522});
523},
524
525// Network can be set to dash
526function() {
527page.open(url, function(status) {
528 // set the phrase and coin
529 var expected = "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT";
530 page.evaluate(function() {
531 $(".phrase").val("abandon abandon ability");
532 $(".phrase").trigger("input");
533 $(".network option[selected]").removeAttr("selected");
534 $(".network option[value=10]").prop("selected", true);
535 $(".network").trigger("change");
536 });
537 // check the address is generated correctly
538 waitForGenerate(function() {
539 var actual = page.evaluate(function() {
540 return $(".address:first").text();
541 });
542 if (actual != expected) {
543 console.log("DASH address is incorrect");
544 console.log("Expected: " + expected);
545 console.log("Actual: " + actual);
546 fail();
59193779 547 }
bfb3dab6
IC
548 next();
549 });
550});
551},
552
553// Network can be set to namecoin
554function() {
555page.open(url, function(status) {
556 // set the phrase and coin
557 var expected = "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2";
558 page.evaluate(function() {
559 $(".phrase").val("abandon abandon ability");
560 $(".phrase").trigger("input");
561 $(".network option[selected]").removeAttr("selected");
562 $(".network option[value=11]").prop("selected", true);
563 $(".network").trigger("change");
564 });
565 // check the address is generated correctly
566 waitForGenerate(function() {
567 var actual = page.evaluate(function() {
568 return $(".address:first").text();
569 });
570 if (actual != expected) {
571 console.log("Namecoin address is incorrect");
572 console.log("Expected: " + expected);
573 console.log("Actual: " + actual);
574 fail();
575 }
576 next();
577 });
578});
579},
580
581// Network can be set to peercoin
582function() {
583page.open(url, function(status) {
584 // set the phrase and coin
585 var expected = "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm";
586 page.evaluate(function() {
587 $(".phrase").val("abandon abandon ability");
588 $(".phrase").trigger("input");
589 $(".network option[selected]").removeAttr("selected");
590 $(".network option[value=12]").prop("selected", true);
591 $(".network").trigger("change");
592 });
593 // check the address is generated correctly
594 waitForGenerate(function() {
595 var actual = page.evaluate(function() {
596 return $(".address:first").text();
597 });
598 if (actual != expected) {
599 console.log("Peercoin address is incorrect");
600 console.log("Expected: " + expected);
601 console.log("Actual: " + actual);
602 fail();
603 }
59193779 604 next();
3eef9d0d 605 });
59193779
IC
606});
607},
608
88e2cdaa 609// BIP39 seed is set from phrase
c196ad55
IC
610function() {
611page.open(url, function(status) {
612 // set the phrase
613 var expected = "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
614 page.evaluate(function() {
615 $(".phrase").val("abandon abandon ability");
616 $(".phrase").trigger("input");
617 });
618 // check the address is generated correctly
3eef9d0d 619 waitForGenerate(function() {
c196ad55
IC
620 var actual = page.evaluate(function() {
621 return $(".seed").val();
622 });
623 if (actual != expected) {
624 console.log("BIP39 seed is incorrectly generated from mnemonic");
625 console.log("Expected: " + expected);
626 console.log("Actual: " + actual);
627 fail();
628 }
629 next();
3eef9d0d 630 });
c196ad55
IC
631});
632},
633
88e2cdaa 634// BIP32 root key is set from phrase
ec60b662
IC
635function() {
636page.open(url, function(status) {
637 // set the phrase
638 var expected = "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi";
639 page.evaluate(function() {
640 $(".phrase").val("abandon abandon ability");
641 $(".phrase").trigger("input");
642 });
643 // check the address is generated correctly
3eef9d0d 644 waitForGenerate(function() {
ec60b662
IC
645 var actual = page.evaluate(function() {
646 return $(".root-key").val();
647 });
648 if (actual != expected) {
649 console.log("Root key is incorrectly generated from mnemonic");
650 console.log("Expected: " + expected);
651 console.log("Actual: " + actual);
652 fail();
653 }
654 next();
3eef9d0d 655 });
ec60b662
IC
656});
657},
658
88e2cdaa 659// Tabs show correct addresses when changed
cf7258fd
IC
660function() {
661page.open(url, function(status) {
662 // set the phrase
663 var expected = "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz";
664 page.evaluate(function() {
665 $(".phrase").val("abandon abandon ability");
666 $(".phrase").trigger("input");
667 });
668 // change tabs
669 waitForGenerate(function() {
670 page.evaluate(function() {
671 $("#bip32-tab a").click();
672 });
673 // check the address is generated correctly
674 waitForGenerate(function() {
675 var actual = page.evaluate(function() {
676 return $(".address:first").text();
677 });
678 if (actual != expected) {
679 console.log("Clicking tab generates incorrect address");
680 console.log("Expected: " + expected);
681 console.log("Actual: " + actual);
682 fail();
683 }
684 next();
685 });
686 });
687});
688},
88e2cdaa
IC
689
690// BIP44 derivation path is shown
d077e1e7
IC
691function() {
692page.open(url, function(status) {
693 // set the phrase
694 var expected = "m/44'/0'/0'/0";
695 page.evaluate(function() {
696 $(".phrase").val("abandon abandon ability");
697 $(".phrase").trigger("input");
698 });
699 // check the derivation path of the first address
700 waitForGenerate(function() {
701 var actual = page.evaluate(function() {
702 return $("#bip44 .path").val();
703 });
704 if (actual != expected) {
705 console.log("BIP44 derivation path is incorrect");
706 console.log("Expected: " + expected);
707 console.log("Actual: " + actual);
708 fail();
709 }
710 next();
711 });
712});
713},
714
88e2cdaa 715// BIP44 extended private key is shown
4fd2925d
IC
716function() {
717page.open(url, function(status) {
718 // set the phrase
719 var expected = "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG";
720 page.evaluate(function() {
721 $(".phrase").val("abandon abandon ability");
722 $(".phrase").trigger("input");
723 });
06286adb 724 // check the BIP44 extended private key
4fd2925d
IC
725 waitForGenerate(function() {
726 var actual = page.evaluate(function() {
727 return $(".extended-priv-key").val();
728 });
729 if (actual != expected) {
730 console.log("BIP44 extended private key is incorrect");
731 console.log("Expected: " + expected);
732 console.log("Actual: " + actual);
733 fail();
734 }
735 next();
736 });
737});
738},
739
88e2cdaa 740// BIP44 extended public key is shown
39fd45bb
IC
741function() {
742page.open(url, function(status) {
743 // set the phrase
744 var expected = "xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM";
745 page.evaluate(function() {
746 $(".phrase").val("abandon abandon ability");
747 $(".phrase").trigger("input");
748 });
06286adb 749 // check the BIP44 extended public key
39fd45bb
IC
750 waitForGenerate(function() {
751 var actual = page.evaluate(function() {
752 return $(".extended-pub-key").val();
753 });
754 if (actual != expected) {
755 console.log("BIP44 extended public key is incorrect");
756 console.log("Expected: " + expected);
757 console.log("Actual: " + actual);
758 fail();
759 }
760 next();
761 });
762});
763},
764
06286adb
IC
765// BIP44 purpose field changes address list
766function() {
767page.open(url, function(status) {
768 // set the phrase
769 var expected = "1JbDzRJ2cDT8aat2xwKd6Pb2zzavow5MhF";
770 page.evaluate(function() {
771 $(".phrase").val("abandon abandon ability");
772 $(".phrase").trigger("input");
773 });
774 waitForGenerate(function() {
775 // change the bip44 purpose field to 45
776 page.evaluate(function() {
777 $("#bip44 .purpose").val("45");
778 $("#bip44 .purpose").trigger("input");
779 });
780 waitForGenerate(function() {
781 // check the address for the new derivation path
782 var actual = page.evaluate(function() {
783 return $(".address:first").text();
784 });
785 if (actual != expected) {
786 console.log("BIP44 purpose field generates incorrect address");
787 console.log("Expected: " + expected);
788 console.log("Actual: " + actual);
789 fail();
790 }
791 next();
792 });
793 });
794});
795},
796
88e2cdaa 797// BIP44 coin field changes address list
9eb72cdf
IC
798function() {
799page.open(url, function(status) {
800 // set the phrase
801 var expected = "1F6dB2djQYrxoyfZZmfr6D5voH8GkJTghk";
802 page.evaluate(function() {
803 $(".phrase").val("abandon abandon ability");
804 $(".phrase").trigger("input");
805 });
806 waitForGenerate(function() {
807 // change the bip44 purpose field to 45
808 page.evaluate(function() {
809 $("#bip44 .coin").val("1");
810 $("#bip44 .coin").trigger("input");
811 });
812 waitForGenerate(function() {
813 // check the address for the new derivation path
814 var actual = page.evaluate(function() {
815 return $(".address:first").text();
816 });
817 if (actual != expected) {
818 console.log("BIP44 coin field generates incorrect address");
819 console.log("Expected: " + expected);
820 console.log("Actual: " + actual);
821 fail();
822 }
823 next();
824 });
825 });
826});
827},
828
88e2cdaa 829// BIP44 account field changes address list
048bc3e0
IC
830function() {
831page.open(url, function(status) {
832 // set the phrase
833 var expected = "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
834 page.evaluate(function() {
835 $(".phrase").val("abandon abandon ability");
836 $(".phrase").trigger("input");
837 });
838 waitForGenerate(function() {
839 // change the bip44 purpose field to 45
840 page.evaluate(function() {
841 $("#bip44 .account").val("1");
842 $("#bip44 .account").trigger("input");
843 });
844 waitForGenerate(function() {
845 // check the address for the new derivation path
846 var actual = page.evaluate(function() {
847 return $(".address:first").text();
848 });
849 if (actual != expected) {
850 console.log("BIP44 account field generates incorrect address");
851 console.log("Expected: " + expected);
852 console.log("Actual: " + actual);
853 fail();
854 }
855 next();
856 });
857 });
858});
859},
860
fa4da086
IC
861// BIP44 change field changes address list
862function() {
863page.open(url, function(status) {
864 // set the phrase
865 var expected = "1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo";
866 page.evaluate(function() {
867 $(".phrase").val("abandon abandon ability");
868 $(".phrase").trigger("input");
869 });
870 waitForGenerate(function() {
871 // change the bip44 purpose field to 45
872 page.evaluate(function() {
873 $("#bip44 .change").val("1");
874 $("#bip44 .change").trigger("input");
875 });
876 waitForGenerate(function() {
877 // check the address for the new derivation path
878 var actual = page.evaluate(function() {
879 return $(".address:first").text();
880 });
881 if (actual != expected) {
882 console.log("BIP44 change field generates incorrect address");
883 console.log("Expected: " + expected);
884 console.log("Actual: " + actual);
885 fail();
886 }
887 next();
888 });
889 });
890});
891},
048bc3e0 892
88e2cdaa 893// BIP32 derivation path can be set
651382a3
IC
894function() {
895page.open(url, function(status) {
896 // set the phrase
897 var expected = "16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L";
898 page.evaluate(function() {
899 $(".phrase").val("abandon abandon ability");
900 $(".phrase").trigger("input");
901 });
902 // change tabs
903 waitForGenerate(function() {
904 page.evaluate(function() {
905 $("#bip32-tab a").click();
906 });
907 // set the derivation path to m/1
908 waitForGenerate(function() {
909 page.evaluate(function() {
910 $("#bip32 .path").val("m/1");
911 $("#bip32 .path").trigger("input");
912 });
913 // check the address is generated correctly
914 waitForGenerate(function() {
915 var actual = page.evaluate(function() {
916 return $(".address:first").text();
917 });
918 if (actual != expected) {
919 console.log("Custom BIP32 path generates incorrect address");
920 console.log("Expected: " + expected);
921 console.log("Actual: " + actual);
922 fail();
923 }
924 next();
925 });
926 });
927 });
928});
929},
930
88e2cdaa 931// BIP32 can use hardened derivation paths
651382a3
IC
932function() {
933page.open(url, function(status) {
934 // set the phrase
935 var expected = "14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4";
936 page.evaluate(function() {
937 $(".phrase").val("abandon abandon ability");
938 $(".phrase").trigger("input");
939 });
940 // change tabs
941 waitForGenerate(function() {
942 page.evaluate(function() {
943 $("#bip32-tab a").click();
944 });
945 // set the derivation path to m/0'
946 waitForGenerate(function() {
947 page.evaluate(function() {
948 $("#bip32 .path").val("m/0'");
949 $("#bip32 .path").trigger("input");
950 });
951 // check the address is generated correctly
952 waitForGenerate(function() {
953 var actual = page.evaluate(function() {
954 return $(".address:first").text();
955 });
956 if (actual != expected) {
957 console.log("Hardened BIP32 path generates incorrect address");
958 console.log("Expected: " + expected);
959 console.log("Actual: " + actual);
960 fail();
961 }
962 next();
963 });
964 });
965 });
966});
967},
968
88e2cdaa 969// BIP32 extended private key is shown
9e9dcfda
IC
970function() {
971page.open(url, function(status) {
972 // set the phrase
973 var expected = "xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe";
974 page.evaluate(function() {
975 $(".phrase").val("abandon abandon ability");
976 $(".phrase").trigger("input");
977 });
978 // change tabs
979 waitForGenerate(function() {
980 page.evaluate(function() {
981 $("#bip32-tab a").click();
982 });
983 // check the extended private key is generated correctly
984 waitForGenerate(function() {
985 var actual = page.evaluate(function() {
986 return $(".extended-priv-key").val();
987 });
988 if (actual != expected) {
989 console.log("BIP32 extended private key is incorrect");
990 console.log("Expected: " + expected);
991 console.log("Actual: " + actual);
992 fail();
993 }
994 next();
995 });
996 });
997});
998},
999
88e2cdaa 1000// BIP32 extended public key is shown
9e9dcfda
IC
1001function() {
1002page.open(url, function(status) {
1003 // set the phrase
1004 var expected = "xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P";
1005 page.evaluate(function() {
1006 $(".phrase").val("abandon abandon ability");
1007 $(".phrase").trigger("input");
1008 });
1009 // change tabs
1010 waitForGenerate(function() {
1011 page.evaluate(function() {
1012 $("#bip32-tab a").click();
1013 });
1014 // check the extended public key is generated correctly
1015 waitForGenerate(function() {
1016 var actual = page.evaluate(function() {
1017 return $(".extended-pub-key").val();
1018 });
1019 if (actual != expected) {
1020 console.log("BIP32 extended public key is incorrect");
1021 console.log("Expected: " + expected);
1022 console.log("Actual: " + actual);
1023 fail();
1024 }
1025 next();
1026 });
1027 });
1028});
1029},
88e2cdaa
IC
1030
1031// Derivation path is shown in table
5f844c62
IC
1032function() {
1033page.open(url, function(status) {
1034 // set the phrase
1035 var expected = "m/44'/0'/0'/0/0";
1036 page.evaluate(function() {
1037 $(".phrase").val("abandon abandon ability");
1038 $(".phrase").trigger("input");
1039 });
1040 // check for derivation path in table
1041 waitForGenerate(function() {
1042 var actual = page.evaluate(function() {
1043 return $(".index:first").text();
1044 });
1045 if (actual != expected) {
1046 console.log("Derivation path shown incorrectly in table");
1047 console.log("Expected: " + expected);
1048 console.log("Actual: " + actual);
1049 fail();
1050 }
1051 next();
1052 });
1053});
1054},
1055
88e2cdaa 1056// Derivation path for address can be hardened
4974fd7f
IC
1057function() {
1058page.open(url, function(status) {
1059 // set the phrase
1060 var expected = "18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd";
1061 page.evaluate(function() {
1062 $(".phrase").val("abandon abandon ability");
1063 $(".phrase").trigger("input");
1064 });
1065 // change tabs
1066 waitForGenerate(function() {
1067 page.evaluate(function() {
1068 $("#bip32-tab a").click();
1069 });
1070 waitForGenerate(function() {
1071 // select the hardened addresses option
1072 page.evaluate(function() {
1073 $(".hardened-addresses").prop("checked", true);
1074 $(".hardened-addresses").trigger("change");
1075 });
1076 waitForGenerate(function() {
1077 // check the generated address is hardened
1078 var actual = page.evaluate(function() {
1079 return $(".address:first").text();
1080 });
1081 if (actual != expected) {
1082 console.log("Hardened address is incorrect");
1083 console.log("Expected: " + expected);
1084 console.log("Actual: " + actual);
1085 fail();
1086 }
1087 next();
1088 });
1089 });
1090 });
1091});
1092},
1093
88e2cdaa 1094// Derivation path visibility can be toggled
a775b5c6
IC
1095function() {
1096page.open(url, function(status) {
1097 // set the phrase
1098 page.evaluate(function() {
1099 $(".phrase").val("abandon abandon ability");
1100 $(".phrase").trigger("input");
1101 });
a775b5c6
IC
1102 waitForGenerate(function() {
1103 // toggle path visibility
1104 page.evaluate(function() {
1105 $(".index-toggle").click();
1106 });
1107 // check the path is not visible
1108 var isInvisible = page.evaluate(function() {
1109 return $(".index:first span").hasClass("invisible");
1110 });
1111 if (!isInvisible) {
1112 console.log("Toggled derivation path is visible");
1113 fail();
1114 }
1115 next();
1116 });
1117});
1118},
1119
88e2cdaa 1120// Address is shown
06c4c6e3
IC
1121function() {
1122page.open(url, function(status) {
1123 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1124 // set the phrase
1125 page.evaluate(function() {
1126 $(".phrase").val("abandon abandon ability").trigger("input");
1127 });
1128 // get the address
1129 waitForGenerate(function() {
1130 var actual = page.evaluate(function() {
1131 return $(".address:first").text();
1132 });
1133 if (actual != expected) {
1134 console.log("Address is not shown");
1135 console.log("Expected: " + expected);
1136 console.log("Got: " + actual);
1137 fail();
1138 }
1139 next();
1140 });
1141});
1142},
1143
88e2cdaa 1144// Addresses are shown in order of derivation path
3f1faa4d
IC
1145function() {
1146page.open(url, function(status) {
1147 // set the phrase
1148 page.evaluate(function() {
1149 $(".phrase").val("abandon abandon ability").trigger("input");
1150 });
1151 // get the derivation paths
1152 waitForGenerate(function() {
1153 var paths = page.evaluate(function() {
1154 return $(".index").map(function(i, e) {
1155 return $(e).text();
1156 });
1157 });
1158 if (paths.length != 20) {
1159 console.log("Total paths is less than expected: " + paths.length);
1160 fail();
1161 }
1162 for (var i=0; i<paths.length; i++) {
1163 var expected = "m/44'/0'/0'/0/" + i;
1164 var actual = paths[i];
1165 if (actual != expected) {
1166 console.log("Path " + i + " is incorrect");
1167 console.log("Expected: " + expected);
1168 console.log("Actual: " + actual);
1169 fail();
1170 }
1171 }
1172 next();
1173 });
1174});
1175},
21372fab 1176
88e2cdaa 1177// Address visibility can be toggled
21372fab
IC
1178function() {
1179page.open(url, function(status) {
1180 // set the phrase
1181 page.evaluate(function() {
1182 $(".phrase").val("abandon abandon ability");
1183 $(".phrase").trigger("input");
1184 });
1185 waitForGenerate(function() {
1186 // toggle address visibility
1187 page.evaluate(function() {
1188 $(".address-toggle").click();
1189 });
1190 // check the address is not visible
1191 var isInvisible = page.evaluate(function() {
1192 return $(".address:first span").hasClass("invisible");
1193 });
1194 if (!isInvisible) {
1195 console.log("Toggled address is visible");
1196 fail();
1197 }
1198 next();
1199 });
1200});
1201},
1202
1b12b2f5
IC
1203// Public key is shown
1204function() {
1205page.open(url, function(status) {
1206 var expected = "033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3";
1207 // set the phrase
1208 page.evaluate(function() {
1209 $(".phrase").val("abandon abandon ability").trigger("input");
1210 });
1211 // get the address
1212 waitForGenerate(function() {
1213 var actual = page.evaluate(function() {
1214 return $(".pubkey:first").text();
1215 });
1216 if (actual != expected) {
1217 console.log("Public key is not shown");
1218 console.log("Expected: " + expected);
1219 console.log("Got: " + actual);
1220 fail();
1221 }
1222 next();
1223 });
1224});
1225},
1226
1227// Public key visibility can be toggled
1228function() {
1229page.open(url, function(status) {
1230 // set the phrase
1231 page.evaluate(function() {
1232 $(".phrase").val("abandon abandon ability");
1233 $(".phrase").trigger("input");
1234 });
1235 waitForGenerate(function() {
1236 // toggle public key visibility
1237 page.evaluate(function() {
1238 $(".public-key-toggle").click();
1239 });
1240 // check the public key is not visible
1241 var isInvisible = page.evaluate(function() {
1242 return $(".pubkey:first span").hasClass("invisible");
1243 });
1244 if (!isInvisible) {
1245 console.log("Toggled public key is visible");
1246 fail();
1247 }
1248 next();
1249 });
1250});
1251},
1252
88e2cdaa 1253// Private key is shown
8cd5e231
IC
1254function() {
1255page.open(url, function(status) {
1256 var expected = "L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE";
1257 // set the phrase
1258 page.evaluate(function() {
1259 $(".phrase").val("abandon abandon ability").trigger("input");
1260 });
1261 // get the address
1262 waitForGenerate(function() {
1263 var actual = page.evaluate(function() {
1264 return $(".privkey:first").text();
1265 });
1266 if (actual != expected) {
1267 console.log("Private key is not shown");
1268 console.log("Expected: " + expected);
1269 console.log("Got: " + actual);
1270 fail();
1271 }
1272 next();
1273 });
1274});
1275},
1276
88e2cdaa 1277// Private key visibility can be toggled
6848d03b
IC
1278function() {
1279page.open(url, function(status) {
1280 // set the phrase
1281 page.evaluate(function() {
1282 $(".phrase").val("abandon abandon ability");
1283 $(".phrase").trigger("input");
1284 });
1285 waitForGenerate(function() {
1286 // toggle private key visibility
1287 page.evaluate(function() {
1288 $(".private-key-toggle").click();
1289 });
1290 // check the private key is not visible
1291 var isInvisible = page.evaluate(function() {
1292 return $(".privkey:first span").hasClass("invisible");
1293 });
1294 if (!isInvisible) {
1295 console.log("Toggled private key is visible");
1296 fail();
1297 }
1298 next();
1299 });
1300});
1301},
88e2cdaa
IC
1302
1303// More addresses can be generated
35631659
IC
1304function() {
1305page.open(url, function(status) {
1306 // set the phrase
1307 page.evaluate(function() {
1308 $(".phrase").val("abandon abandon ability");
1309 $(".phrase").trigger("input");
1310 });
1311 waitForGenerate(function() {
1312 // generate more addresses
1313 page.evaluate(function() {
1314 $(".more").click();
1315 });
1316 waitForGenerate(function() {
1317 // check there are more addresses
1318 var addressCount = page.evaluate(function() {
1319 return $(".address").length;
1320 });
1321 if (addressCount != 40) {
1322 console.log("More addresses cannot be generated");
1323 fail();
1324 }
1325 next();
1326 });
1327 });
1328});
1329},
1330
88e2cdaa 1331// A custom number of additional addresses can be generated
8a89b9da
IC
1332function() {
1333page.open(url, function(status) {
1334 // set the phrase
1335 page.evaluate(function() {
1336 $(".phrase").val("abandon abandon ability");
1337 $(".phrase").trigger("input");
1338 });
1339 waitForGenerate(function() {
1340 // get the current number of addresses
1341 var oldAddressCount = page.evaluate(function() {
1342 return $(".address").length;
1343 });
1344 // set a custom number of additional addresses
1345 page.evaluate(function() {
1346 $(".rows-to-add").val(1);
1347 });
1348 // generate more addresses
1349 page.evaluate(function() {
1350 $(".more").click();
1351 });
1352 waitForGenerate(function() {
1353 // check there are the correct number of addresses
1354 var newAddressCount = page.evaluate(function() {
1355 return $(".address").length;
1356 });
1357 if (newAddressCount - oldAddressCount != 1) {
1358 console.log("Number of additional addresses cannot be customized");
1359 console.log(newAddressCount)
1360 console.log(oldAddressCount)
1361 fail();
1362 }
1363 next();
1364 });
1365 });
1366});
1367},
1368
88e2cdaa 1369// Additional addresses are shown in order of derivation path
4d387bf5
IC
1370function() {
1371page.open(url, function(status) {
1372 // set the phrase
1373 page.evaluate(function() {
1374 $(".phrase").val("abandon abandon ability").trigger("input");
1375 });
1376 waitForGenerate(function() {
1377 // generate more addresses
1378 page.evaluate(function() {
1379 $(".more").click();
1380 });
1381 // get the derivation paths
1382 waitForGenerate(function() {
1383 var paths = page.evaluate(function() {
1384 return $(".index").map(function(i, e) {
1385 return $(e).text();
1386 });
1387 });
1388 if (paths.length != 40) {
1389 console.log("Total additional paths is less than expected: " + paths.length);
1390 fail();
1391 }
1392 for (var i=0; i<paths.length; i++) {
1393 var expected = "m/44'/0'/0'/0/" + i;
1394 var actual = paths[i];
1395 if (actual != expected) {
1396 console.log("Path " + i + " is not in correct order");
1397 console.log("Expected: " + expected);
1398 console.log("Actual: " + actual);
1399 fail();
1400 }
1401 }
1402 next();
1403 });
1404 });
1405});
1406},
88e2cdaa
IC
1407
1408// BIP32 root key can be set by the user
61ed16a9
IC
1409function() {
1410page.open(url, function(status) {
1411 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1412 // set the root key
1413 page.evaluate(function() {
1414 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1415 });
1416 waitForGenerate(function() {
1417 var actual = page.evaluate(function() {
1418 return $(".address:first").text();
1419 });
1420 if (actual != expected) {
bc324fd2 1421 console.log("Setting BIP32 root key results in wrong address");
61ed16a9
IC
1422 console.log("Expected: " + expected);
1423 console.log("Actual: " + actual);
1424 fail();
1425 }
1426 next();
1427 });
1428});
1429},
1430
54563907 1431// Setting BIP32 root key clears the existing phrase, passphrase and seed
bc324fd2
IC
1432function() {
1433page.open(url, function(status) {
1434 var expected = "";
1435 // set a mnemonic
1436 page.evaluate(function() {
1437 $(".phrase").val("A non-blank but invalid value");
1438 });
1439 // Accept any confirm dialogs
1440 page.onConfirm = function() {
1441 return true;
1442 };
1443 // set the root key
1444 page.evaluate(function() {
1445 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1446 });
1447 waitForGenerate(function() {
1448 var actual = page.evaluate(function() {
1449 return $(".phrase").val();
1450 });
1451 if (actual != expected) {
1452 console.log("Phrase not cleared when setting BIP32 root key");
1453 console.log("Expected: " + expected);
1454 console.log("Actual: " + actual);
1455 fail();
1456 }
1457 next();
1458 });
1459});
1460},
1461
54563907 1462// Clearing of phrase, passphrase and seed can be cancelled by user
abfbe450
IC
1463function() {
1464page.open(url, function(status) {
1465 var expected = "abandon abandon ability";
1466 // set a mnemonic
1467 page.evaluate(function() {
1468 $(".phrase").val("abandon abandon ability");
1469 });
1470 // Cancel any confirm dialogs
1471 page.onConfirm = function() {
1472 return false;
1473 };
1474 // set the root key
1475 page.evaluate(function() {
1476 $(".root-key").val("xprv9s21ZrQH143K3d3vzEDD3KpSKmxsZ3y7CqhAL1tinwtP6wqK4TKEKjpBuo6P2hUhB6ZENo7TTSRytiP857hBZVpBdk8PooFuRspE1eywwNZ").trigger("input");
1477 });
1478 var actual = page.evaluate(function() {
1479 return $(".phrase").val();
1480 });
1481 if (actual != expected) {
1482 console.log("Phrase not retained when cancelling changes to BIP32 root key");
1483 console.log("Expected: " + expected);
1484 console.log("Actual: " + actual);
1485 fail();
1486 }
1487 next();
1488});
1489},
7ff86d4c 1490
88e2cdaa 1491// Custom BIP32 root key is used when changing the derivation path
7ff86d4c
IC
1492function() {
1493page.open(url, function(status) {
1494 var expected = "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1495 // set the root key
1496 page.evaluate(function() {
1497 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1498 });
1499 waitForGenerate(function() {
1500 // change the derivation path
1501 page.evaluate(function() {
1502 $("#account").val("1").trigger("input");
1503 });
1504 // check the bip32 root key is used for derivation, not the blank phrase
1505 waitForGenerate(function() {
1506 var actual = page.evaluate(function() {
1507 return $(".address:first").text();
1508 });
1509 if (actual != expected) {
1510 console.log("Changing the derivation path does not use BIP32 root key");
1511 console.log("Expected: " + expected);
1512 console.log("Actual: " + actual);
1513 fail();
1514 }
1515 next();
1516 });
1517 });
1518});
1519},
88e2cdaa
IC
1520
1521// Incorrect mnemonic shows error
c3719b00
IC
1522function() {
1523page.open(url, function(status) {
1524 // set the root key
1525 page.evaluate(function() {
1526 $(".phrase").val("abandon abandon abandon").trigger("input");
1527 });
1528 waitForFeedback(function() {
1529 // check there is an error shown
1530 var feedback = page.evaluate(function() {
1531 return $(".feedback").text();
1532 });
1533 if (feedback.length <= 0) {
1534 console.log("Invalid mnemonic does not show error");
1535 fail();
1536 }
1537 next();
1538 });
1539});
1540},
1541
88e2cdaa 1542// Incorrect word shows suggested replacement
20f80cfa
IC
1543function() {
1544page.open(url, function(status) {
1545 // set the root key
1546 page.evaluate(function() {
1547 $(".phrase").val("abandon abandon abiliti").trigger("input");
1548 });
1549 // check there is a suggestion shown
1550 waitForFeedback(function() {
1551 var feedback = page.evaluate(function() {
1552 return $(".feedback").text();
1553 });
1554 if (feedback.indexOf("did you mean ability?") < 0) {
1555 console.log("Incorrect word does not show suggested replacement");
1556 console.log("Error: " + error);
1557 fail();
1558 }
1559 next();
1560 });
1561});
1562},
1563
88e2cdaa 1564// Incorrect BIP32 root key shows error
02f4a90e
IC
1565function() {
1566page.open(url, function(status) {
1567 // set the root key
1568 page.evaluate(function() {
1569 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj").trigger("input");
1570 });
1571 // check there is an error shown
1572 waitForFeedback(function() {
1573 var feedback = page.evaluate(function() {
1574 return $(".feedback").text();
1575 });
1576 if (feedback != "Invalid root key") {
1577 console.log("Invalid root key does not show error");
1578 console.log("Error: " + error);
1579 fail();
1580 }
1581 next();
1582 });
1583});
1584},
1585
88e2cdaa 1586// Derivation path not starting with m shows error
f976b541
IC
1587function() {
1588page.open(url, function(status) {
1589 // set the mnemonic phrase
1590 page.evaluate(function() {
1591 $(".phrase").val("abandon abandon ability").trigger("input");
1592 });
1593 waitForGenerate(function() {
1594 // select the bip32 tab so custom derivation path can be set
1595 page.evaluate(function() {
1596 $("#bip32-tab a").click();
1597 });
1598 waitForGenerate(function() {
1599 // set the incorrect derivation path
1600 page.evaluate(function() {
1601 $("#bip32 .path").val("n/0").trigger("input");
1602 });
1603 waitForFeedback(function() {
1604 var feedback = page.evaluate(function() {
1605 return $(".feedback").text();
1606 });
1607 if (feedback != "First character must be 'm'") {
1608 console.log("Derivation path not starting with m should show error");
1609 console.log("Error: " + error);
1610 fail();
1611 }
1612 next();
1613 });
1614 });
1615 });
1616});
1617},
1618
88e2cdaa 1619// Derivation path containing invalid characters shows useful error
55c0ffcb
IC
1620function() {
1621page.open(url, function(status) {
1622 // set the mnemonic phrase
1623 page.evaluate(function() {
1624 $(".phrase").val("abandon abandon ability").trigger("input");
1625 });
1626 waitForGenerate(function() {
1627 // select the bip32 tab so custom derivation path can be set
1628 page.evaluate(function() {
1629 $("#bip32-tab a").click();
1630 });
1631 waitForGenerate(function() {
1632 // set the incorrect derivation path
1633 page.evaluate(function() {
1634 $("#bip32 .path").val("m/1/0wrong1/1").trigger("input");
1635 });
1636 waitForFeedback(function() {
1637 var feedback = page.evaluate(function() {
1638 return $(".feedback").text();
1639 });
1640 if (feedback != "Invalid characters 0wrong1 found at depth 2") {
1641 console.log("Derivation path with invalid characters should show error");
1642 console.log("Error: " + error);
1643 fail();
1644 }
1645 next();
1646 });
1647 });
1648 });
1649});
1650},
88e2cdaa
IC
1651
1652// Github Issue 11: Default word length is 15
b630f83d 1653// https://github.com/iancoleman/bip39/issues/11
ca976aa9
IC
1654function() {
1655page.open(url, function(status) {
1656 // get the word length
1657 var defaultLength = page.evaluate(function() {
1658 return $(".strength").val();
1659 });
1660 if (defaultLength != 15) {
1661 console.log("Default word length is not 15");
1662 fail();
1663 }
1664 next();
1665});
1666},
1667
88e2cdaa
IC
1668
1669// Github Issue 12: Generate more rows with private keys hidden
b630f83d 1670// https://github.com/iancoleman/bip39/issues/12
c97627fa
IC
1671function() {
1672page.open(url, function(status) {
1673 // set the phrase
1674 page.evaluate(function() {
1675 $(".phrase").val("abandon abandon ability");
1676 $(".phrase").trigger("input");
1677 });
1678 waitForGenerate(function() {
1679 // toggle private keys hidden, then generate more addresses
1680 page.evaluate(function() {
1681 $(".private-key-toggle").click();
1682 $(".more").click();
1683 });
1684 waitForGenerate(function() {
1685 // check more have been generated
1686 var expected = 40;
1687 var numPrivKeys = page.evaluate(function() {
1688 return $(".privkey").length;
1689 });
1690 if (numPrivKeys != expected) {
1691 console.log("Wrong number of addresses when clicking 'more' with hidden privkeys");
1692 console.log("Expected: " + expected);
1693 console.log("Actual: " + numPrivKeys);
1694 fail();
1695 }
1696 // check no private keys are shown
1697 var numHiddenPrivKeys = page.evaluate(function() {
1698 return $(".privkey span[class=invisible]").length;
1699 });
1700 if (numHiddenPrivKeys != expected) {
1701 console.log("Generating more does not retain hidden state of privkeys");
1702 console.log("Expected: " + expected);
1703 console.log("Actual: " + numHiddenPrivKeys);
1704 fail();
1705 }
1706 next();
1707 });
1708 });
1709});
1710},
88e2cdaa
IC
1711
1712// Github Issue 19: Mnemonic is not sensitive to whitespace
b630f83d 1713// https://github.com/iancoleman/bip39/issues/19
a7becc43
IC
1714function() {
1715page.open(url, function(status) {
1716 // set the phrase
1717 var expected = "xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC";
1718 page.evaluate(function() {
1719 var doubleSpace = " ";
1720 $(".phrase").val("urge cat" + doubleSpace + "bid");
1721 $(".phrase").trigger("input");
1722 });
1723 waitForGenerate(function() {
1724 // Check the bip32 root key is correct
1725 var actual = page.evaluate(function() {
1726 return $(".root-key").val();
1727 });
1728 if (actual != expected) {
1729 console.log("Mnemonic is sensitive to whitespace");
1730 console.log("Expected: " + expected);
1731 console.log("Actual: " + actual);
1732 fail();
1733 }
1734 next();
1735 });
1736});
1737},
88e2cdaa 1738
e3001539 1739// Github Issue 23: Part 1: Use correct derivation path when changing tabs
b630f83d 1740// https://github.com/iancoleman/bip39/issues/23
e3001539
IC
1741function() {
1742page.open(url, function(status) {
1743 // 1) and 2) set the phrase
1744 page.evaluate(function() {
1745 $(".phrase").val("abandon abandon ability").trigger("input");
1746 });
1747 waitForGenerate(function() {
1748 // 3) select bip32 tab
1749 page.evaluate(function() {
1750 $("#bip32-tab a").click();
1751 });
1752 waitForGenerate(function() {
1753 // 4) switch from bitcoin to litecoin
1754 page.evaluate(function() {
1755 $(".network").val("2").trigger("change");
1756 });
1757 waitForGenerate(function() {
1758 // 5) Check derivation path is displayed correctly
1759 var expected = "m/0/0";
1760 var actual = page.evaluate(function() {
1761 return $(".index:first").text();
1762 });
1763 if (actual != expected) {
1764 console.log("Github Issue 23 Part 1: derivation path display error");
1765 console.log("Expected: " + expected);
1766 console.log("Actual: " + actual);
1767 fail();
1768 }
1769 // 5) Check address is displayed correctly
1770 var expected = "LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5";
1771 var actual = page.evaluate(function() {
1772 return $(".address:first").text();
1773 });
1774 if (actual != expected) {
1775 console.log("Github Issue 23 Part 1: address display error");
1776 console.log("Expected: " + expected);
1777 console.log("Actual: " + actual);
1778 fail();
1779 }
1780 next();
1781 });
1782 });
1783 });
1784});
1785},
1786
1787// Github Issue 23 Part 2: Coin selection in derivation path
b630f83d 1788// https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
af4fd3a2
IC
1789function() {
1790page.open(url, function(status) {
1791 // set the phrase
1792 page.evaluate(function() {
1793 $(".phrase").val("abandon abandon ability").trigger("input");
1794 });
1795 waitForGenerate(function() {
1796 // switch from bitcoin to clam
1797 page.evaluate(function() {
1798 $(".network").val("9").trigger("change");
1799 });
1800 waitForGenerate(function() {
1801 // check derivation path is displayed correctly
1802 var expected = "m/44'/23'/0'/0/0";
1803 var actual = page.evaluate(function() {
1804 return $(".index:first").text();
1805 });
1806 if (actual != expected) {
1807 console.log("Github Issue 23 Part 2: Coin in BIP44 derivation path is incorrect");
1808 console.log("Expected: " + expected);
1809 console.log("Actual: " + actual);
1810 fail();
1811 }
1812 next();
1813 });
1814 });
1815});
1816},
88e2cdaa 1817
f3d0aca1 1818// Github Issue 26: When using a Root key derrived altcoins are incorrect
b630f83d 1819// https://github.com/iancoleman/bip39/issues/26
558ef9ac
IC
1820function() {
1821page.open(url, function(status) {
1822 // 1) 2) and 3) set the root key
1823 page.evaluate(function() {
1824 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1825 });
1826 waitForGenerate(function() {
1827 // 4) switch from bitcoin to viacoin
1828 page.evaluate(function() {
1829 $(".network").val("6").trigger("change");
1830 });
1831 waitForGenerate(function() {
1832 // 5) ensure the derived address is correct
1833 var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
1834 var actual = page.evaluate(function() {
1835 return $(".address:first").text();
1836 });
1837 if (actual != expected) {
1838 console.log("Github Issue 26: address is incorrect when changing networks and using root-key to derive");
1839 console.log("Expected: " + expected);
1840 console.log("Actual: " + actual);
1841 fail();
1842 }
1843 next();
1844 });
1845 });
1846});
1847},
f3d0aca1 1848
e1bae843
IC
1849// Selecting a language with no existing phrase should generate a phrase in
1850// that language.
1851function() {
1852page.open(url, function(status) {
1853 // Select a language
1854 // Need to manually simulate hash being set due to quirk between
1855 // 'click' event triggered by javascript vs triggered by mouse.
1856 // Perhaps look into page.sendEvent
1857 // http://phantomjs.org/api/webpage/method/send-event.html
1858 page.evaluate(function() {
1859 window.location.hash = "#japanese";
1860 $("a[href='#japanese']").trigger("click");
1861 });
1862 waitForGenerate(function() {
1863 // Check the mnemonic is in Japanese
1864 var phrase = page.evaluate(function() {
1865 return $(".phrase").val();
1866 });
1867 if (phrase.length <= 0) {
1868 console.log("No Japanese phrase generated");
1869 fail();
1870 }
1871 if (phrase.charCodeAt(0) < 128) {
1872 console.log("First character of Japanese phrase is ascii");
1873 console.log("Phrase: " + phrase);
1874 fail();
1875 }
1876 next();
1877 });
1878});
1879},
1880
1881// Selecting a language with existing phrase should update the phrase to use
1882// that language.
1883function() {
1884page.open(url, function(status) {
1885 // Set the phrase to an English phrase.
1886 page.evaluate(function() {
1887 $(".phrase").val("abandon abandon ability").trigger("input");
1888 });
1889 waitForGenerate(function() {
1890 // Change to Italian
1891 // Need to manually simulate hash being set due to quirk between
1892 // 'click' event triggered by javascript vs triggered by mouse.
1893 // Perhaps look into page.sendEvent
1894 // http://phantomjs.org/api/webpage/method/send-event.html
1895 page.evaluate(function() {
1896 window.location.hash = "#italian";
1897 $("a[href='#italian']").trigger("click");
1898 });
1899 waitForGenerate(function() {
1900 // Check only the language changes, not the phrase
1901 var expected = "abaco abaco abbaglio";
1902 var actual = page.evaluate(function() {
1903 return $(".phrase").val();
1904 });
1905 if (actual != expected) {
1906 console.log("Changing language with existing phrase");
1907 console.log("Expected: " + expected);
1908 console.log("Actual: " + actual);
1909 fail();
1910 }
1911 // Check the address is correct
1912 var expected = "1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV";
1913 var actual = page.evaluate(function() {
1914 return $(".address:first").text();
1915 });
1916 if (actual != expected) {
1917 console.log("Changing language generates incorrect address");
1918 console.log("Expected: " + expected);
1919 console.log("Actual: " + actual);
1920 fail();
1921 }
1922 next();
1923 });
1924 });
1925});
1926},
1927
1928// Suggested replacement for erroneous word in non-English language
1929function() {
1930page.open(url, function(status) {
1931 // Set an incorrect phrase in Italian
1932 page.evaluate(function() {
1933 $(".phrase").val("abaco abaco zbbaglio").trigger("input");
1934 });
1935 waitForFeedback(function() {
1936 // Check the suggestion is correct
1937 var feedback = page.evaluate(function() {
1938 return $(".feedback").text();
1939 });
1940 if (feedback.indexOf("did you mean abbaglio?") < 0) {
1941 console.log("Incorrect Italian word does not show suggested replacement");
1942 console.log("Error: " + error);
1943 fail();
1944 }
1945 next();
1946 });
1947});
1948},
1949
1950
1951// Japanese word does not break across lines.
1952// Point 2 from
1953// https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
1954function() {
1955page.open(url, function(status) {
1956 hasWordBreakCss = page.content.indexOf("word-break: keep-all;") > -1;
1957 if (!hasWordBreakCss) {
1958 console.log("Japanese words can break across lines mid-word");
1959 console.log("Check CSS for '.phrase { word-break: keep-all; }'");
1960 fail();
1961 }
1962 // Run the next test
1963 next();
1964});
1965},
1966
1967// Language can be specified at page load using hash value in url
1968function() {
1969page.open(url, function(status) {
1970 // Set the page hash as if it were on a fresh page load
1971 page.evaluate(function() {
1972 window.location.hash = "#japanese";
1973 });
1974 // Generate a random phrase
1975 page.evaluate(function() {
1976 $(".generate").trigger("click");
1977 });
1978 waitForGenerate(function() {
1979 // Check the phrase is in Japanese
1980 var phrase = page.evaluate(function() {
1981 return $(".phrase").val();
1982 });
1983 if (phrase.length <= 0) {
1984 console.log("No phrase generated using url hash");
1985 fail();
1986 }
1987 if (phrase.charCodeAt(0) < 128) {
1988 console.log("Language not detected from url hash on page load.");
1989 console.log("Phrase: " + phrase);
1990 fail();
1991 }
1992 next();
1993 });
1994});
1995},
1996
c6624d51
IC
1997// Entropy unit tests
1998function() {
1999page.open(url, function(status) {
adc8ce12 2000 var response = page.evaluate(function() {
c6624d51
IC
2001 var e;
2002 // binary entropy is detected
adc8ce12
IC
2003 try {
2004 e = Entropy.fromString("01010101");
2005 if (e.base.str != "binary") {
2006 return "Binary entropy not detected correctly";
2007 }
2008 }
2009 catch (e) {
2010 return e.message;
c6624d51
IC
2011 }
2012 // base6 entropy is detected
adc8ce12
IC
2013 try {
2014 e = Entropy.fromString("012345012345");
2015 if (e.base.str != "base 6") {
2016 return "base6 entropy not detected correctly";
2017 }
2018 }
2019 catch (e) {
2020 return e.message;
c6624d51
IC
2021 }
2022 // dice entropy is detected
adc8ce12
IC
2023 try {
2024 e = Entropy.fromString("123456123456");
2025 if (e.base.str != "base 6 (dice)") {
2026 return "dice entropy not detected correctly";
2027 }
2028 }
2029 catch (e) {
2030 return e.message;
c6624d51
IC
2031 }
2032 // base10 entropy is detected
adc8ce12
IC
2033 try {
2034 e = Entropy.fromString("0123456789");
2035 if (e.base.str != "base 10") {
2036 return "base10 entropy not detected correctly";
2037 }
2038 }
2039 catch (e) {
2040 return e.message;
c6624d51
IC
2041 }
2042 // hex entropy is detected
adc8ce12
IC
2043 try {
2044 e = Entropy.fromString("0123456789ABCDEF");
2045 if (e.base.str != "hexadecimal") {
2046 return "hexadecimal entropy not detected correctly";
2047 }
2048 }
2049 catch (e) {
2050 return e.message;
2051 }
2052 // card entropy is detected
2053 try {
2054 e = Entropy.fromString("AC4DTHKS");
2055 if (e.base.str != "card") {
2056 return "card entropy not detected correctly";
2057 }
2058 }
2059 catch (e) {
2060 return e.message;
c6624d51
IC
2061 }
2062 // entropy is case insensitive
adc8ce12
IC
2063 try {
2064 e = Entropy.fromString("aBcDeF");
2065 if (e.cleanStr != "aBcDeF") {
2066 return "Entropy should not be case sensitive";
2067 }
2068 }
2069 catch (e) {
2070 return e.message;
c6624d51
IC
2071 }
2072 // dice entropy is converted to base6
adc8ce12
IC
2073 try {
2074 e = Entropy.fromString("123456");
2075 if (e.cleanStr != "012345") {
2076 return "Dice entropy is not automatically converted to base6";
2077 }
2078 }
2079 catch (e) {
2080 return e.message;
c6624d51
IC
2081 }
2082 // dice entropy is preferred to base6 if ambiguous
adc8ce12
IC
2083 try {
2084 e = Entropy.fromString("12345");
2085 if (e.base.str != "base 6 (dice)") {
2086 return "dice not used as default over base 6";
2087 }
2088 }
2089 catch (e) {
2090 return e.message;
c6624d51
IC
2091 }
2092 // unused characters are ignored
adc8ce12
IC
2093 try {
2094 e = Entropy.fromString("fghijkl");
2095 if (e.cleanStr != "f") {
2096 return "additional characters are not ignored";
2097 }
2098 }
2099 catch (e) {
2100 return e.message;
c6624d51
IC
2101 }
2102 // the lowest base is used by default
2103 // 7 could be decimal or hexadecimal, but should be detected as decimal
adc8ce12
IC
2104 try {
2105 e = Entropy.fromString("7");
2106 if (e.base.str != "base 10") {
2107 return "lowest base is not used";
2108 }
c6624d51 2109 }
adc8ce12
IC
2110 catch (e) {
2111 return e.message;
c6624d51
IC
2112 }
2113 // Leading zeros are retained
adc8ce12
IC
2114 try {
2115 e = Entropy.fromString("000A");
2116 if (e.cleanStr != "000A") {
2117 return "Leading zeros are not retained";
2118 }
2119 }
2120 catch (e) {
2121 return e.message;
c6624d51
IC
2122 }
2123 // Leading zeros are correctly preserved for hex in binary string
adc8ce12
IC
2124 try {
2125 e = Entropy.fromString("2A");
2126 if (e.binaryStr != "00101010") {
2127 return "Hex leading zeros are not correct in binary";
2128 }
2129 }
2130 catch (e) {
2131 return e.message;
2132 }
2133 // Leading zeros are correctly preserved for base 6 in binary string
2134 try {
2135 e = Entropy.fromString("2");
2136 if (e.binaryStr != "010") {
2137 return "Base 6 leading zeros are not correct in binary";
2138 }
2139 }
2140 catch (e) {
2141 return e.message;
c6624d51
IC
2142 }
2143 // Keyboard mashing results in weak entropy
2144 // Despite being a long string, it's less than 30 bits of entropy
adc8ce12
IC
2145 try {
2146 e = Entropy.fromString("aj;se ifj; ask,dfv js;ifj");
2147 if (e.binaryStr.length >= 30) {
2148 return "Keyboard mashing should produce weak entropy";
2149 }
c6624d51 2150 }
adc8ce12
IC
2151 catch (e) {
2152 return e.message;
2153 }
2154 // Card entropy is used if every pair could be a card
2155 try {
2156 e = Entropy.fromString("4c3c2c");
2157 if (e.base.str != "card") {
2158 return "Card entropy not used if all pairs are cards";
2159 }
2160 }
2161 catch (e) {
2162 return e.message;
2163 }
2164 // Card entropy uses base 52
2165 // [ cards, binary ]
2166 try {
2167 var cards = [
2168 [ "ac", "00000" ],
2169 [ "acac", "00000000000" ],
2170 [ "acac2c", "00000000000000001" ],
2171 [ "acks", "00000110011" ],
2172 [ "acacks", "00000000000110011" ],
2173 [ "2c", "000001" ],
2174 [ "3d", "001111" ],
2175 [ "4h", "011101" ],
2176 [ "5s", "101011" ],
2177 [ "6c", "000101" ],
2178 [ "7d", "010011" ],
2179 [ "8h", "100001" ],
2180 [ "9s", "101111" ],
2181 [ "tc", "001001" ],
2182 [ "jd", "010111" ],
2183 [ "qh", "100101" ],
2184 [ "ks", "110011" ],
2185 [ "ks2c", "101001011101" ],
2186 [ "KS2C", "101001011101" ],
2187 ];
2188 for (var i=0; i<cards.length; i++) {
2189 var card = cards[i][0];
2190 var result = cards[i][1];
2191 e = Entropy.fromString(card);
2192 console.log(e.binary + " " + result);
2193 if (e.binaryStr !== result) {
2194 return "card entropy not parsed correctly: " + result + " != " + e.binaryStr;
2195 }
2196 }
2197 }
2198 catch (e) {
2199 return e.message;
2200 }
2201 return "PASS";
c6624d51 2202 });
adc8ce12 2203 if (response != "PASS") {
c6624d51 2204 console.log("Entropy unit tests");
adc8ce12 2205 console.log(response);
c6624d51
IC
2206 fail();
2207 };
2208 next();
2209});
2210},
2211
2212// Entropy can be entered by the user
2213function() {
2214page.open(url, function(status) {
2215 expected = {
2216 mnemonic: "abandon abandon ability",
2217 address: "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug",
2218 }
2219 // use entropy
2220 page.evaluate(function() {
2221 $(".use-entropy").prop("checked", true).trigger("change");
2222 $(".entropy").val("00000000 00000000 00000000 00000000").trigger("input");
2223 });
2224 // check the mnemonic is set and address is correct
2225 waitForGenerate(function() {
2226 var actual = page.evaluate(function() {
2227 return {
2228 address: $(".address:first").text(),
2229 mnemonic: $(".phrase").val(),
2230 }
2231 });
2232 if (actual.mnemonic != expected.mnemonic) {
2233 console.log("Entropy does not generate correct mnemonic");
2234 console.log("Expected: " + expected.mnemonic);
2235 console.log("Got: " + actual.mnemonic);
2236 fail();
2237 }
2238 if (actual.address != expected.address) {
2239 console.log("Entropy does not generate correct address");
2240 console.log("Expected: " + expected.address);
2241 console.log("Got: " + actual.address);
2242 fail();
2243 }
2244 next();
2245 });
2246});
2247},
2248
2249// A warning about entropy is shown to the user, with additional information
2250function() {
2251page.open(url, function(status) {
2252 // get text content from entropy sections of page
2253 var hasWarning = page.evaluate(function() {
2254 var entropyText = $(".entropy-container").text();
2255 var warning = "mnemonic may be insecure";
2256 if (entropyText.indexOf(warning) == -1) {
2257 return false;
2258 }
2259 var readMoreText = $("#entropy-notes").parent().text();
2260 var goodSources = "flipping a fair coin, rolling a fair dice, noise measurements etc";
2261 if (readMoreText.indexOf(goodSources) == -1) {
2262 return false;
2263 }
2264 return true;
2265 });
2266 // check the warnings and information are shown
2267 if (!hasWarning) {
2268 console.log("Page does not contain warning about using own entropy");
2269 fail();
2270 }
2271 next();
2272});
2273},
2274
2275// The types of entropy available are described to the user
2276function() {
2277page.open(url, function(status) {
2278 // get placeholder text for entropy field
2279 var placeholder = page.evaluate(function() {
2280 return $(".entropy").attr("placeholder");
2281 });
2282 var options = [
2283 "binary",
2284 "base 6",
2285 "dice",
2286 "base 10",
2287 "hexadecimal",
2288 ];
2289 for (var i=0; i<options.length; i++) {
2290 var option = options[i];
2291 if (placeholder.indexOf(option) == -1) {
2292 console.log("Available entropy type is not shown to user: " + option);
2293 fail();
2294 }
2295 }
2296 next();
2297});
2298},
2299
2300// The actual entropy used is shown to the user
2301function() {
2302page.open(url, function(status) {
2303 // use entropy
2304 var badEntropySource = page.evaluate(function() {
2305 var entropy = "Not A Very Good Entropy Source At All";
2306 $(".use-entropy").prop("checked", true).trigger("change");
2307 $(".entropy").val(entropy).trigger("input");
2308 });
2309 // check the actual entropy being used is shown
057722b0 2310 waitForEntropyFeedback(function() {
c6624d51
IC
2311 var expectedText = "AedEceAA";
2312 var entropyText = page.evaluate(function() {
2313 return $(".entropy-container").text();
2314 });
2315 if (entropyText.indexOf(expectedText) == -1) {
2316 console.log("Actual entropy used is not shown");
2317 fail();
2318 }
2319 next();
2320 });
2321});
2322},
2323
2324// Binary entropy can be entered
2325function() {
2326page.open(url, function(status) {
2327 // use entropy
2328 page.evaluate(function() {
2329 $(".use-entropy").prop("checked", true).trigger("change");
2330 $(".entropy").val("01").trigger("input");
2331 });
2332 // check the entropy is shown to be the correct type
057722b0 2333 waitForEntropyFeedback(function() {
c6624d51
IC
2334 var entropyText = page.evaluate(function() {
2335 return $(".entropy-container").text();
2336 });
2337 if (entropyText.indexOf("binary") == -1) {
2338 console.log("Binary entropy is not detected and presented to user");
2339 fail();
2340 }
2341 next();
2342 });
2343});
2344},
2345
2346// Base 6 entropy can be entered
2347function() {
2348page.open(url, function(status) {
2349 // use entropy
2350 page.evaluate(function() {
2351 $(".use-entropy").prop("checked", true).trigger("change");
2352 $(".entropy").val("012345").trigger("input");
2353 });
2354 // check the entropy is shown to be the correct type
057722b0 2355 waitForEntropyFeedback(function() {
c6624d51
IC
2356 var entropyText = page.evaluate(function() {
2357 return $(".entropy-container").text();
2358 });
2359 if (entropyText.indexOf("base 6") == -1) {
2360 console.log("Base 6 entropy is not detected and presented to user");
2361 fail();
2362 }
2363 next();
2364 });
2365});
2366},
2367
2368// Base 6 dice entropy can be entered
2369function() {
2370page.open(url, function(status) {
2371 // use entropy
2372 page.evaluate(function() {
2373 $(".use-entropy").prop("checked", true).trigger("change");
2374 $(".entropy").val("123456").trigger("input");
2375 });
2376 // check the entropy is shown to be the correct type
057722b0 2377 waitForEntropyFeedback(function() {
c6624d51
IC
2378 var entropyText = page.evaluate(function() {
2379 return $(".entropy-container").text();
2380 });
2381 if (entropyText.indexOf("dice") == -1) {
2382 console.log("Dice entropy is not detected and presented to user");
2383 fail();
2384 }
2385 next();
2386 });
2387});
2388},
2389
2390// Base 10 entropy can be entered
2391function() {
2392page.open(url, function(status) {
2393 // use entropy
2394 page.evaluate(function() {
2395 $(".use-entropy").prop("checked", true).trigger("change");
2396 $(".entropy").val("789").trigger("input");
2397 });
2398 // check the entropy is shown to be the correct type
057722b0 2399 waitForEntropyFeedback(function() {
c6624d51
IC
2400 var entropyText = page.evaluate(function() {
2401 return $(".entropy-container").text();
2402 });
2403 if (entropyText.indexOf("base 10") == -1) {
2404 console.log("Base 10 entropy is not detected and presented to user");
2405 fail();
2406 }
2407 next();
2408 });
2409});
2410},
2411
2412// Hexadecimal entropy can be entered
2413function() {
2414page.open(url, function(status) {
2415 // use entropy
2416 page.evaluate(function() {
2417 $(".use-entropy").prop("checked", true).trigger("change");
2418 $(".entropy").val("abcdef").trigger("input");
2419 });
2420 // check the entropy is shown to be the correct type
057722b0 2421 waitForEntropyFeedback(function() {
c6624d51
IC
2422 var entropyText = page.evaluate(function() {
2423 return $(".entropy-container").text();
2424 });
2425 if (entropyText.indexOf("hexadecimal") == -1) {
2426 console.log("Hexadecimal entropy is not detected and presented to user");
2427 fail();
2428 }
2429 next();
2430 });
2431});
2432},
2433
2434// Dice entropy value is shown as the converted base 6 value
2435function() {
2436page.open(url, function(status) {
2437 // use entropy
2438 page.evaluate(function() {
2439 $(".use-entropy").prop("checked", true).trigger("change");
2440 $(".entropy").val("123456").trigger("input");
2441 });
2442 // check the entropy is shown as base 6, not as the original dice value
057722b0 2443 waitForEntropyFeedback(function() {
c6624d51
IC
2444 var entropyText = page.evaluate(function() {
2445 return $(".entropy-container").text();
2446 });
2447 if (entropyText.indexOf("012345") == -1) {
2448 console.log("Dice entropy is not shown to user as base 6 value");
2449 fail();
2450 }
2451 if (entropyText.indexOf("123456") > -1) {
2452 console.log("Dice entropy value is shown instead of true base 6 value");
2453 fail();
2454 }
2455 next();
2456 });
2457});
2458},
2459
2460// The number of bits of entropy accumulated is shown
2461function() {
2462page.open(url, function(status) {
057722b0
IC
2463 //[ entropy, bits ]
2464 var tests = [
2465 [ "0000 0000 0000 0000 0000", "20" ],
2466 [ "0", "1" ],
2467 [ "0000", "4" ],
2468 [ "6", "3" ],
adc8ce12 2469 [ "7", "4" ],
057722b0
IC
2470 [ "8", "4" ],
2471 [ "F", "4" ],
adc8ce12 2472 [ "29", "7" ],
057722b0
IC
2473 [ "0A", "8" ],
2474 [ "1A", "8" ], // hex is always multiple of 4 bits of entropy
2475 [ "2A", "8" ],
2476 [ "4A", "8" ],
2477 [ "8A", "8" ],
2478 [ "FA", "8" ],
2479 [ "000A", "16" ],
adc8ce12
IC
2480 [ "2220", "11" ],
2481 [ "2221", "11" ], // uses dice, so entropy is actually 1110
2482 [ "2227", "14" ],
057722b0
IC
2483 [ "222F", "16" ],
2484 [ "FFFF", "16" ],
2485 ]
c6624d51
IC
2486 // use entropy
2487 page.evaluate(function(e) {
2488 $(".use-entropy").prop("checked", true).trigger("change");
2489 });
2490 // Run each test
2491 var nextTest = function runNextTest(i) {
057722b0
IC
2492 var entropy = tests[i][0];
2493 var expected = tests[i][1];
c6624d51
IC
2494 // set entropy
2495 page.evaluate(function(e) {
c6624d51
IC
2496 $(".entropy").val(e).trigger("input");
2497 }, entropy);
2498 // check the number of bits of entropy is shown
057722b0 2499 waitForEntropyFeedback(function() {
c6624d51 2500 var entropyText = page.evaluate(function() {
057722b0 2501 return $(".entropy-error").text();
c6624d51
IC
2502 });
2503 if (entropyText.indexOf("Have " + expected + " bits of entropy") == -1) {
2504 console.log("Accumulated entropy is not shown correctly for " + entropy);
057722b0 2505 console.log(entropyText);
c6624d51
IC
2506 fail();
2507 }
057722b0 2508 var isLastTest = i == tests.length - 1;
c6624d51
IC
2509 if (isLastTest) {
2510 next();
2511 }
2512 else {
2513 runNextTest(i+1);
2514 }
2515 });
2516 }
2517 nextTest(0);
2518});
2519},
2520
2521// The number of bits of entropy to reach the next mnemonic strength is shown
2522function() {
2523page.open(url, function(status) {
2524 // use entropy
2525 page.evaluate(function() {
2526 $(".use-entropy").prop("checked", true).trigger("change");
2527 $(".entropy").val("7654321").trigger("input");
2528 });
2529 // check the amount of additional entropy required is shown
057722b0 2530 waitForEntropyFeedback(function() {
c6624d51
IC
2531 var entropyText = page.evaluate(function() {
2532 return $(".entropy-container").text();
2533 });
2534 if (entropyText.indexOf("3 more base 10 chars required") == -1) {
2535 console.log("Additional entropy requirement is not shown");
2536 fail();
2537 }
2538 next();
2539 });
2540});
2541},
2542
2543// The next strength above 0-word mnemonics is considered extremely weak
2544// The next strength above 3-word mnemonics is considered very weak
2545// The next strength above 6-word mnemonics is considered weak
2546// The next strength above 9-word mnemonics is considered strong
2547// The next strength above 12-word mnemonics is considered very strong
2548// The next strength above 15-word mnemonics is considered extremely strong
2549function() {
2550page.open(url, function(status) {
2551 var tests = [
2552 {
2553 entropy: "A",
2554 words: 0,
2555 nextStrength: "an extremely weak",
2556 },
2557 {
2558 entropy: "AAAAAAAA",
2559 words: 3,
2560 nextStrength: "a very weak",
2561 },
2562 {
2563 entropy: "AAAAAAAA B",
2564 words: 3,
2565 nextStrength: "a very weak",
2566 },
2567 {
2568 entropy: "AAAAAAAA BBBBBBBB",
2569 words: 6,
2570 nextStrength: "a weak",
2571 },
2572 {
2573 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
2574 words: 9,
2575 nextStrength: "a strong",
2576 },
2577 {
2578 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
2579 words: 12,
2580 nextStrength: "a very strong",
2581 },
2582 {
2583 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD EEEEEEEE",
2584 words: 15,
2585 nextStrength: "an extremely strong",
2586 }
2587 ];
2588 // use entropy
2589 page.evaluate(function() {
2590 $(".use-entropy").prop("checked", true).trigger("change");
2591 });
2592 var nextTest = function runNextTest(i) {
2593 test = tests[i];
2594 page.evaluate(function(e) {
2595 $(".addresses").empty();
057722b0 2596 $(".phrase").val("");
c6624d51
IC
2597 $(".entropy").val(e).trigger("input");
2598 }, test.entropy);
057722b0 2599 if (test.words == 0) {
c6624d51
IC
2600 var mnemonic = page.evaluate(function() {
2601 return $(".phrase").val();
2602 });
057722b0
IC
2603 if (mnemonic.length > 0) {
2604 console.log("Mnemonic length for " + test.nextStrength + " strength is not " + test.words);
2605 console.log("Mnemonic: " + mnemonic);
c6624d51
IC
2606 fail();
2607 }
2608 var isLastTest = i == tests.length - 1;
2609 if (isLastTest) {
2610 next();
2611 }
2612 else {
2613 runNextTest(i+1);
2614 }
057722b0
IC
2615 }
2616 else {
2617 waitForGenerate(function() {
2618 // check the strength of the current mnemonic
2619 var mnemonic = page.evaluate(function() {
2620 return $(".phrase").val();
2621 });
2622 if (mnemonic.split(" ").length != test.words) {
2623 console.log("Mnemonic length for " + test.nextStrength + " strength is not " + test.words);
2624 console.log("Mnemonic: " + mnemonic);
2625 fail();
2626 }
2627 // check the strength of the next mnemonic is shown
2628 var entropyText = page.evaluate(function() {
2629 return $(".entropy-container").text();
2630 });
2631 if (entropyText.indexOf("required to generate " + test.nextStrength + " mnemonic") == -1) {
2632 console.log("Strength indicator for " + test.nextStrength + " mnemonic is incorrect");
2633 fail();
2634 }
2635 var isLastTest = i == tests.length - 1;
2636 if (isLastTest) {
2637 next();
2638 }
2639 else {
2640 runNextTest(i+1);
2641 }
2642 });
2643 }
c6624d51
IC
2644 }
2645 nextTest(0);
2646});
2647},
2648
2649// Entropy is truncated from the right
2650function() {
2651page.open(url, function(status) {
2652 var expected = "abandon abandon ability";
2653 // use entropy
2654 page.evaluate(function() {
2655 $(".use-entropy").prop("checked", true).trigger("change");
2656 var entropy = "00000000 00000000 00000000 00000000";
2657 entropy += "11111111 11111111 11111111 1111"; // Missing last byte, only first 8 bytes are used
2658 $(".entropy").val(entropy).trigger("input");
2659 });
2660 // check the entropy is truncated from the right
2661 waitForGenerate(function() {
2662 var actual = page.evaluate(function() {
2663 return $(".phrase").val();
2664 });
2665 if (actual != expected) {
2666 console.log("Entropy is not truncated from the right");
2667 console.log("Expected: " + expected);
2668 console.log("Got: " + actual);
2669 fail();
2670 }
2671 next();
2672 });
2673});
2674},
2675
2676// Very large entropy results in very long mnemonics
2677function() {
2678page.open(url, function(status) {
2679 // use entropy
2680 page.evaluate(function() {
2681 $(".use-entropy").prop("checked", true).trigger("change");
2682 var entropy = "";
2683 // Generate a very long entropy string
2684 for (var i=0; i<33; i++) {
2685 entropy += "AAAAAAAA"; // 3 words * 33 iterations = 99 words
2686 }
2687 $(".entropy").val(entropy).trigger("input");
2688 });
2689 // check the mnemonic is very long
2690 waitForGenerate(function() {
2691 var wordCount = page.evaluate(function() {
2692 return $(".phrase").val().split(" ").length;
2693 });
2694 if (wordCount != 99) {
2695 console.log("Large entropy does not generate long mnemonic");
2696 console.log("Expected 99 words, got " + wordCount);
2697 fail();
2698 }
2699 next();
2700 });
2701});
2702},
2703
2704// Is compatible with bip32jp entropy
2705// https://bip32jp.github.io/english/index.html
2706// NOTES:
2707// Is incompatible with:
2708// base 6 with leading zeros
2709// base 6 wth 12 words / 53 chars
2710// base 20
2711function() {
2712page.open(url, function(status) {
2713 var expected = "defy trip fatal jaguar mean rack rifle survey satisfy drift twist champion steel wife state furnace night consider glove olympic oblige donor novel left";
2714 // use entropy
2715 page.evaluate(function() {
2716 $(".use-entropy").prop("checked", true).trigger("change");
2717 var entropy = "123450123450123450123450123450123450123450123450123450123450123450123450123450123450123450123450123";
2718 $(".entropy").val(entropy).trigger("input");
2719 });
2720 // check the mnemonic matches the expected value from bip32jp
2721 waitForGenerate(function() {
2722 var actual = page.evaluate(function() {
2723 return $(".phrase").val();
2724 });
2725 if (actual != expected) {
2726 console.log("Mnemonic does not match bip32jp for base 6 entropy");
2727 console.log("Expected: " + expected);
2728 console.log("Got: " + actual);
2729 fail();
2730 }
2731 next();
2732 });
2733});
2734},
2735
057722b0
IC
2736// Blank entropy does not generate mnemonic or addresses
2737function() {
2738page.open(url, function(status) {
2739 // use entropy
2740 page.evaluate(function() {
2741 $(".use-entropy").prop("checked", true).trigger("change");
2742 $(".entropy").val("").trigger("input");
2743 });
2744 waitForFeedback(function() {
2745 // check there is no mnemonic
2746 var phrase = page.evaluate(function() {
2747 return $(".phrase").val();
2748 });
2749 if (phrase != "") {
2750 console.log("Blank entropy does not result in blank mnemonic");
2751 console.log("Got: " + phrase);
2752 fail();
2753 }
2754 // check there are no addresses displayed
2755 var addresses = page.evaluate(function() {
2756 return $(".address").length;
2757 });
2758 if (addresses != 0) {
2759 console.log("Blank entropy does not result in zero addresses");
2760 fail();
2761 }
2762 // Check the feedback says 'blank entropy'
2763 var feedback = page.evaluate(function() {
2764 return $(".feedback").text();
2765 });
2766 if (feedback != "Blank entropy") {
2767 console.log("Blank entropy does not show feedback message");
2768 fail();
2769 }
2770 next();
2771 });
2772});
2773},
2774
b0fb45b9
IC
2775// If you wish to add more tests, do so here...
2776
2777// Here is a blank test template
2778/*
2779
2780function() {
2781page.open(url, function(status) {
2782 // Do something on the page
2783 page.evaluate(function() {
2784 $(".phrase").val("abandon abandon ability").trigger("input");
2785 });
2786 waitForGenerate(function() {
2787 // Check the result of doing the thing
2788 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
2789 var actual = page.evaluate(function() {
2790 return $(".address:first").text();
2791 });
2792 if (actual != expected) {
2793 console.log("A specific message about what failed");
2794 console.log("Expected: " + expected);
2795 console.log("Actual: " + actual);
2796 fail();
2797 }
2798 // Run the next test
2799 next();
2800 });
2801});
2802},
2803
2804*/
2805
88e2cdaa
IC
2806];
2807
2808console.log("Running tests...");
fb372687 2809tests = shuffle(tests);
88e2cdaa 2810next();