]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blame - tests.js
DOM.strength renamed DOM.generatedStrength
[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");
425b75a9 2075 if (e.cleanStr != "123450") {
adc8ce12
IC
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 }
0d0f07f9 2133 // Leading zeros are not used for base 6 as binary string
adc8ce12
IC
2134 try {
2135 e = Entropy.fromString("2");
0d0f07f9
IC
2136 if (e.binaryStr != "10") {
2137 return "Base 6 as binary has leading zeros";
2138 }
2139 }
2140 catch (e) {
2141 return e.message;
2142 }
2143 // Leading zeros are not used for base 10 as binary string
2144 try {
2145 e = Entropy.fromString("7");
2146 if (e.binaryStr != "111") {
2147 return "Base 10 as binary has leading zeros";
2148 }
2149 }
2150 catch (e) {
2151 return e.message;
2152 }
2153 // Leading zeros are not used for card entropy as binary string
2154 try {
2155 e = Entropy.fromString("2c");
2156 if (e.binaryStr != "1") {
2157 return "Card entropy as binary has leading zeros";
adc8ce12
IC
2158 }
2159 }
2160 catch (e) {
2161 return e.message;
c6624d51
IC
2162 }
2163 // Keyboard mashing results in weak entropy
2164 // Despite being a long string, it's less than 30 bits of entropy
adc8ce12
IC
2165 try {
2166 e = Entropy.fromString("aj;se ifj; ask,dfv js;ifj");
2167 if (e.binaryStr.length >= 30) {
2168 return "Keyboard mashing should produce weak entropy";
2169 }
c6624d51 2170 }
adc8ce12
IC
2171 catch (e) {
2172 return e.message;
2173 }
2174 // Card entropy is used if every pair could be a card
2175 try {
2176 e = Entropy.fromString("4c3c2c");
2177 if (e.base.str != "card") {
2178 return "Card entropy not used if all pairs are cards";
2179 }
2180 }
2181 catch (e) {
2182 return e.message;
2183 }
2184 // Card entropy uses base 52
2185 // [ cards, binary ]
2186 try {
2187 var cards = [
2188 [ "ac", "00000" ],
2189 [ "acac", "00000000000" ],
0d0f07f9 2190 [ "acac2c", "000000000001" ],
adc8ce12
IC
2191 [ "acks", "00000110011" ],
2192 [ "acacks", "00000000000110011" ],
0d0f07f9
IC
2193 [ "2c", "1" ],
2194 [ "3d", "1111" ],
2195 [ "4h", "11101" ],
adc8ce12 2196 [ "5s", "101011" ],
0d0f07f9
IC
2197 [ "6c", "101" ],
2198 [ "7d", "10011" ],
adc8ce12
IC
2199 [ "8h", "100001" ],
2200 [ "9s", "101111" ],
0d0f07f9
IC
2201 [ "tc", "1001" ],
2202 [ "jd", "10111" ],
adc8ce12
IC
2203 [ "qh", "100101" ],
2204 [ "ks", "110011" ],
2205 [ "ks2c", "101001011101" ],
2206 [ "KS2C", "101001011101" ],
2207 ];
2208 for (var i=0; i<cards.length; i++) {
2209 var card = cards[i][0];
2210 var result = cards[i][1];
2211 e = Entropy.fromString(card);
2212 console.log(e.binary + " " + result);
2213 if (e.binaryStr !== result) {
2214 return "card entropy not parsed correctly: " + result + " != " + e.binaryStr;
2215 }
2216 }
2217 }
2218 catch (e) {
2219 return e.message;
2220 }
2221 return "PASS";
c6624d51 2222 });
adc8ce12 2223 if (response != "PASS") {
c6624d51 2224 console.log("Entropy unit tests");
adc8ce12 2225 console.log(response);
c6624d51
IC
2226 fail();
2227 };
2228 next();
2229});
2230},
2231
2232// Entropy can be entered by the user
2233function() {
2234page.open(url, function(status) {
2235 expected = {
2236 mnemonic: "abandon abandon ability",
2237 address: "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug",
2238 }
2239 // use entropy
2240 page.evaluate(function() {
2241 $(".use-entropy").prop("checked", true).trigger("change");
2242 $(".entropy").val("00000000 00000000 00000000 00000000").trigger("input");
2243 });
2244 // check the mnemonic is set and address is correct
2245 waitForGenerate(function() {
2246 var actual = page.evaluate(function() {
2247 return {
2248 address: $(".address:first").text(),
2249 mnemonic: $(".phrase").val(),
2250 }
2251 });
2252 if (actual.mnemonic != expected.mnemonic) {
2253 console.log("Entropy does not generate correct mnemonic");
2254 console.log("Expected: " + expected.mnemonic);
2255 console.log("Got: " + actual.mnemonic);
2256 fail();
2257 }
2258 if (actual.address != expected.address) {
2259 console.log("Entropy does not generate correct address");
2260 console.log("Expected: " + expected.address);
2261 console.log("Got: " + actual.address);
2262 fail();
2263 }
2264 next();
2265 });
2266});
2267},
2268
2269// A warning about entropy is shown to the user, with additional information
2270function() {
2271page.open(url, function(status) {
2272 // get text content from entropy sections of page
2273 var hasWarning = page.evaluate(function() {
2274 var entropyText = $(".entropy-container").text();
2275 var warning = "mnemonic may be insecure";
2276 if (entropyText.indexOf(warning) == -1) {
2277 return false;
2278 }
2279 var readMoreText = $("#entropy-notes").parent().text();
2280 var goodSources = "flipping a fair coin, rolling a fair dice, noise measurements etc";
2281 if (readMoreText.indexOf(goodSources) == -1) {
2282 return false;
2283 }
2284 return true;
2285 });
2286 // check the warnings and information are shown
2287 if (!hasWarning) {
2288 console.log("Page does not contain warning about using own entropy");
2289 fail();
2290 }
2291 next();
2292});
2293},
2294
2295// The types of entropy available are described to the user
2296function() {
2297page.open(url, function(status) {
2298 // get placeholder text for entropy field
2299 var placeholder = page.evaluate(function() {
2300 return $(".entropy").attr("placeholder");
2301 });
2302 var options = [
2303 "binary",
2304 "base 6",
2305 "dice",
2306 "base 10",
2307 "hexadecimal",
2308 ];
2309 for (var i=0; i<options.length; i++) {
2310 var option = options[i];
2311 if (placeholder.indexOf(option) == -1) {
2312 console.log("Available entropy type is not shown to user: " + option);
2313 fail();
2314 }
2315 }
2316 next();
2317});
2318},
2319
2320// The actual entropy used is shown to the user
2321function() {
2322page.open(url, function(status) {
2323 // use entropy
2324 var badEntropySource = page.evaluate(function() {
2325 var entropy = "Not A Very Good Entropy Source At All";
2326 $(".use-entropy").prop("checked", true).trigger("change");
2327 $(".entropy").val(entropy).trigger("input");
2328 });
2329 // check the actual entropy being used is shown
057722b0 2330 waitForEntropyFeedback(function() {
c6624d51
IC
2331 var expectedText = "AedEceAA";
2332 var entropyText = page.evaluate(function() {
2333 return $(".entropy-container").text();
2334 });
2335 if (entropyText.indexOf(expectedText) == -1) {
2336 console.log("Actual entropy used is not shown");
2337 fail();
2338 }
2339 next();
2340 });
2341});
2342},
2343
2344// Binary entropy can be entered
2345function() {
2346page.open(url, function(status) {
2347 // use entropy
2348 page.evaluate(function() {
2349 $(".use-entropy").prop("checked", true).trigger("change");
2350 $(".entropy").val("01").trigger("input");
2351 });
2352 // check the entropy is shown to be the correct type
057722b0 2353 waitForEntropyFeedback(function() {
c6624d51
IC
2354 var entropyText = page.evaluate(function() {
2355 return $(".entropy-container").text();
2356 });
2357 if (entropyText.indexOf("binary") == -1) {
2358 console.log("Binary entropy is not detected and presented to user");
2359 fail();
2360 }
2361 next();
2362 });
2363});
2364},
2365
2366// Base 6 entropy can be entered
2367function() {
2368page.open(url, function(status) {
2369 // use entropy
2370 page.evaluate(function() {
2371 $(".use-entropy").prop("checked", true).trigger("change");
2372 $(".entropy").val("012345").trigger("input");
2373 });
2374 // check the entropy is shown to be the correct type
057722b0 2375 waitForEntropyFeedback(function() {
c6624d51
IC
2376 var entropyText = page.evaluate(function() {
2377 return $(".entropy-container").text();
2378 });
2379 if (entropyText.indexOf("base 6") == -1) {
2380 console.log("Base 6 entropy is not detected and presented to user");
2381 fail();
2382 }
2383 next();
2384 });
2385});
2386},
2387
2388// Base 6 dice entropy can be entered
2389function() {
2390page.open(url, function(status) {
2391 // use entropy
2392 page.evaluate(function() {
2393 $(".use-entropy").prop("checked", true).trigger("change");
2394 $(".entropy").val("123456").trigger("input");
2395 });
2396 // check the entropy is shown to be the correct type
057722b0 2397 waitForEntropyFeedback(function() {
c6624d51
IC
2398 var entropyText = page.evaluate(function() {
2399 return $(".entropy-container").text();
2400 });
2401 if (entropyText.indexOf("dice") == -1) {
2402 console.log("Dice entropy is not detected and presented to user");
2403 fail();
2404 }
2405 next();
2406 });
2407});
2408},
2409
2410// Base 10 entropy can be entered
2411function() {
2412page.open(url, function(status) {
2413 // use entropy
2414 page.evaluate(function() {
2415 $(".use-entropy").prop("checked", true).trigger("change");
2416 $(".entropy").val("789").trigger("input");
2417 });
2418 // check the entropy is shown to be the correct type
057722b0 2419 waitForEntropyFeedback(function() {
c6624d51
IC
2420 var entropyText = page.evaluate(function() {
2421 return $(".entropy-container").text();
2422 });
2423 if (entropyText.indexOf("base 10") == -1) {
2424 console.log("Base 10 entropy is not detected and presented to user");
2425 fail();
2426 }
2427 next();
2428 });
2429});
2430},
2431
2432// Hexadecimal entropy can be entered
2433function() {
2434page.open(url, function(status) {
2435 // use entropy
2436 page.evaluate(function() {
2437 $(".use-entropy").prop("checked", true).trigger("change");
2438 $(".entropy").val("abcdef").trigger("input");
2439 });
2440 // check the entropy is shown to be the correct type
057722b0 2441 waitForEntropyFeedback(function() {
c6624d51
IC
2442 var entropyText = page.evaluate(function() {
2443 return $(".entropy-container").text();
2444 });
2445 if (entropyText.indexOf("hexadecimal") == -1) {
2446 console.log("Hexadecimal entropy is not detected and presented to user");
2447 fail();
2448 }
2449 next();
2450 });
2451});
2452},
2453
2454// Dice entropy value is shown as the converted base 6 value
2455function() {
2456page.open(url, function(status) {
2457 // use entropy
2458 page.evaluate(function() {
2459 $(".use-entropy").prop("checked", true).trigger("change");
2460 $(".entropy").val("123456").trigger("input");
2461 });
2462 // check the entropy is shown as base 6, not as the original dice value
057722b0 2463 waitForEntropyFeedback(function() {
c6624d51
IC
2464 var entropyText = page.evaluate(function() {
2465 return $(".entropy-container").text();
2466 });
425b75a9 2467 if (entropyText.indexOf("123450") == -1) {
c6624d51
IC
2468 console.log("Dice entropy is not shown to user as base 6 value");
2469 fail();
2470 }
2471 if (entropyText.indexOf("123456") > -1) {
2472 console.log("Dice entropy value is shown instead of true base 6 value");
2473 fail();
2474 }
2475 next();
2476 });
2477});
2478},
2479
2480// The number of bits of entropy accumulated is shown
2481function() {
2482page.open(url, function(status) {
057722b0
IC
2483 //[ entropy, bits ]
2484 var tests = [
2485 [ "0000 0000 0000 0000 0000", "20" ],
2486 [ "0", "1" ],
2487 [ "0000", "4" ],
0d0f07f9
IC
2488 [ "6", "2" ], // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
2489 [ "7", "3" ], // 7 in base 10 is 111 in base 2, no leading zeros
057722b0
IC
2490 [ "8", "4" ],
2491 [ "F", "4" ],
0d0f07f9 2492 [ "29", "5" ],
057722b0
IC
2493 [ "0A", "8" ],
2494 [ "1A", "8" ], // hex is always multiple of 4 bits of entropy
2495 [ "2A", "8" ],
2496 [ "4A", "8" ],
2497 [ "8A", "8" ],
2498 [ "FA", "8" ],
2499 [ "000A", "16" ],
0d0f07f9
IC
2500 [ "5555", "11" ],
2501 [ "6666", "10" ], // uses dice, so entropy is actually 0000 in base 6, which is 4 lots of 2.58 bits, which is 10.32 bits (rounded down to 10 bits)
2502 [ "2227", "12" ],
057722b0
IC
2503 [ "222F", "16" ],
2504 [ "FFFF", "16" ],
2505 ]
c6624d51
IC
2506 // use entropy
2507 page.evaluate(function(e) {
2508 $(".use-entropy").prop("checked", true).trigger("change");
2509 });
2510 // Run each test
2511 var nextTest = function runNextTest(i) {
057722b0
IC
2512 var entropy = tests[i][0];
2513 var expected = tests[i][1];
c6624d51
IC
2514 // set entropy
2515 page.evaluate(function(e) {
c6624d51
IC
2516 $(".entropy").val(e).trigger("input");
2517 }, entropy);
2518 // check the number of bits of entropy is shown
057722b0 2519 waitForEntropyFeedback(function() {
c6624d51 2520 var entropyText = page.evaluate(function() {
057722b0 2521 return $(".entropy-error").text();
c6624d51
IC
2522 });
2523 if (entropyText.indexOf("Have " + expected + " bits of entropy") == -1) {
2524 console.log("Accumulated entropy is not shown correctly for " + entropy);
057722b0 2525 console.log(entropyText);
c6624d51
IC
2526 fail();
2527 }
057722b0 2528 var isLastTest = i == tests.length - 1;
c6624d51
IC
2529 if (isLastTest) {
2530 next();
2531 }
2532 else {
2533 runNextTest(i+1);
2534 }
2535 });
2536 }
2537 nextTest(0);
2538});
2539},
2540
2541// The number of bits of entropy to reach the next mnemonic strength is shown
2542function() {
2543page.open(url, function(status) {
2544 // use entropy
2545 page.evaluate(function() {
2546 $(".use-entropy").prop("checked", true).trigger("change");
2547 $(".entropy").val("7654321").trigger("input");
2548 });
2549 // check the amount of additional entropy required is shown
057722b0 2550 waitForEntropyFeedback(function() {
c6624d51
IC
2551 var entropyText = page.evaluate(function() {
2552 return $(".entropy-container").text();
2553 });
2554 if (entropyText.indexOf("3 more base 10 chars required") == -1) {
2555 console.log("Additional entropy requirement is not shown");
2556 fail();
2557 }
2558 next();
2559 });
2560});
2561},
2562
2563// The next strength above 0-word mnemonics is considered extremely weak
2564// The next strength above 3-word mnemonics is considered very weak
2565// The next strength above 6-word mnemonics is considered weak
2566// The next strength above 9-word mnemonics is considered strong
2567// The next strength above 12-word mnemonics is considered very strong
2568// The next strength above 15-word mnemonics is considered extremely strong
2569function() {
2570page.open(url, function(status) {
2571 var tests = [
2572 {
2573 entropy: "A",
2574 words: 0,
2575 nextStrength: "an extremely weak",
2576 },
2577 {
2578 entropy: "AAAAAAAA",
2579 words: 3,
2580 nextStrength: "a very weak",
2581 },
2582 {
2583 entropy: "AAAAAAAA B",
2584 words: 3,
2585 nextStrength: "a very weak",
2586 },
2587 {
2588 entropy: "AAAAAAAA BBBBBBBB",
2589 words: 6,
2590 nextStrength: "a weak",
2591 },
2592 {
2593 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
2594 words: 9,
2595 nextStrength: "a strong",
2596 },
2597 {
2598 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
2599 words: 12,
2600 nextStrength: "a very strong",
2601 },
2602 {
2603 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD EEEEEEEE",
2604 words: 15,
2605 nextStrength: "an extremely strong",
2606 }
2607 ];
2608 // use entropy
2609 page.evaluate(function() {
2610 $(".use-entropy").prop("checked", true).trigger("change");
2611 });
2612 var nextTest = function runNextTest(i) {
2613 test = tests[i];
2614 page.evaluate(function(e) {
2615 $(".addresses").empty();
057722b0 2616 $(".phrase").val("");
c6624d51
IC
2617 $(".entropy").val(e).trigger("input");
2618 }, test.entropy);
057722b0 2619 if (test.words == 0) {
c6624d51
IC
2620 var mnemonic = page.evaluate(function() {
2621 return $(".phrase").val();
2622 });
057722b0
IC
2623 if (mnemonic.length > 0) {
2624 console.log("Mnemonic length for " + test.nextStrength + " strength is not " + test.words);
2625 console.log("Mnemonic: " + mnemonic);
c6624d51
IC
2626 fail();
2627 }
2628 var isLastTest = i == tests.length - 1;
2629 if (isLastTest) {
2630 next();
2631 }
2632 else {
2633 runNextTest(i+1);
2634 }
057722b0
IC
2635 }
2636 else {
2637 waitForGenerate(function() {
2638 // check the strength of the current mnemonic
2639 var mnemonic = page.evaluate(function() {
2640 return $(".phrase").val();
2641 });
2642 if (mnemonic.split(" ").length != test.words) {
2643 console.log("Mnemonic length for " + test.nextStrength + " strength is not " + test.words);
2644 console.log("Mnemonic: " + mnemonic);
2645 fail();
2646 }
2647 // check the strength of the next mnemonic is shown
2648 var entropyText = page.evaluate(function() {
2649 return $(".entropy-container").text();
2650 });
2651 if (entropyText.indexOf("required to generate " + test.nextStrength + " mnemonic") == -1) {
2652 console.log("Strength indicator for " + test.nextStrength + " mnemonic is incorrect");
2653 fail();
2654 }
2655 var isLastTest = i == tests.length - 1;
2656 if (isLastTest) {
2657 next();
2658 }
2659 else {
2660 runNextTest(i+1);
2661 }
2662 });
2663 }
c6624d51
IC
2664 }
2665 nextTest(0);
2666});
2667},
2668
2669// Entropy is truncated from the right
2670function() {
2671page.open(url, function(status) {
2672 var expected = "abandon abandon ability";
2673 // use entropy
2674 page.evaluate(function() {
2675 $(".use-entropy").prop("checked", true).trigger("change");
2676 var entropy = "00000000 00000000 00000000 00000000";
2677 entropy += "11111111 11111111 11111111 1111"; // Missing last byte, only first 8 bytes are used
2678 $(".entropy").val(entropy).trigger("input");
2679 });
2680 // check the entropy is truncated from the right
2681 waitForGenerate(function() {
2682 var actual = page.evaluate(function() {
2683 return $(".phrase").val();
2684 });
2685 if (actual != expected) {
2686 console.log("Entropy is not truncated from the right");
2687 console.log("Expected: " + expected);
2688 console.log("Got: " + actual);
2689 fail();
2690 }
2691 next();
2692 });
2693});
2694},
2695
2696// Very large entropy results in very long mnemonics
2697function() {
2698page.open(url, function(status) {
2699 // use entropy
2700 page.evaluate(function() {
2701 $(".use-entropy").prop("checked", true).trigger("change");
2702 var entropy = "";
2703 // Generate a very long entropy string
2704 for (var i=0; i<33; i++) {
2705 entropy += "AAAAAAAA"; // 3 words * 33 iterations = 99 words
2706 }
2707 $(".entropy").val(entropy).trigger("input");
2708 });
2709 // check the mnemonic is very long
2710 waitForGenerate(function() {
2711 var wordCount = page.evaluate(function() {
2712 return $(".phrase").val().split(" ").length;
2713 });
2714 if (wordCount != 99) {
2715 console.log("Large entropy does not generate long mnemonic");
2716 console.log("Expected 99 words, got " + wordCount);
2717 fail();
2718 }
2719 next();
2720 });
2721});
2722},
2723
2724// Is compatible with bip32jp entropy
2725// https://bip32jp.github.io/english/index.html
2726// NOTES:
2727// Is incompatible with:
2728// base 6 with leading zeros
2729// base 6 wth 12 words / 53 chars
2730// base 20
2731function() {
2732page.open(url, function(status) {
0d0f07f9 2733 var expected = "train then jungle barely whip fiber purpose puppy eagle cloud clump hospital robot brave balcony utility detect estate old green desk skill multiply virus";
c6624d51
IC
2734 // use entropy
2735 page.evaluate(function() {
2736 $(".use-entropy").prop("checked", true).trigger("change");
0d0f07f9 2737 var entropy = "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543";
c6624d51
IC
2738 $(".entropy").val(entropy).trigger("input");
2739 });
2740 // check the mnemonic matches the expected value from bip32jp
2741 waitForGenerate(function() {
2742 var actual = page.evaluate(function() {
2743 return $(".phrase").val();
2744 });
2745 if (actual != expected) {
2746 console.log("Mnemonic does not match bip32jp for base 6 entropy");
2747 console.log("Expected: " + expected);
2748 console.log("Got: " + actual);
2749 fail();
2750 }
2751 next();
2752 });
2753});
2754},
2755
057722b0
IC
2756// Blank entropy does not generate mnemonic or addresses
2757function() {
2758page.open(url, function(status) {
2759 // use entropy
2760 page.evaluate(function() {
2761 $(".use-entropy").prop("checked", true).trigger("change");
2762 $(".entropy").val("").trigger("input");
2763 });
2764 waitForFeedback(function() {
2765 // check there is no mnemonic
2766 var phrase = page.evaluate(function() {
2767 return $(".phrase").val();
2768 });
2769 if (phrase != "") {
2770 console.log("Blank entropy does not result in blank mnemonic");
2771 console.log("Got: " + phrase);
2772 fail();
2773 }
2774 // check there are no addresses displayed
2775 var addresses = page.evaluate(function() {
2776 return $(".address").length;
2777 });
2778 if (addresses != 0) {
2779 console.log("Blank entropy does not result in zero addresses");
2780 fail();
2781 }
2782 // Check the feedback says 'blank entropy'
2783 var feedback = page.evaluate(function() {
2784 return $(".feedback").text();
2785 });
2786 if (feedback != "Blank entropy") {
2787 console.log("Blank entropy does not show feedback message");
2788 fail();
2789 }
2790 next();
2791 });
2792});
2793},
2794
b0fb45b9
IC
2795// If you wish to add more tests, do so here...
2796
2797// Here is a blank test template
2798/*
2799
2800function() {
2801page.open(url, function(status) {
2802 // Do something on the page
2803 page.evaluate(function() {
2804 $(".phrase").val("abandon abandon ability").trigger("input");
2805 });
2806 waitForGenerate(function() {
2807 // Check the result of doing the thing
2808 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
2809 var actual = page.evaluate(function() {
2810 return $(".address:first").text();
2811 });
2812 if (actual != expected) {
2813 console.log("A specific message about what failed");
2814 console.log("Expected: " + expected);
2815 console.log("Actual: " + actual);
2816 fail();
2817 }
2818 // Run the next test
2819 next();
2820 });
2821});
2822},
2823
2824*/
2825
88e2cdaa
IC
2826];
2827
2828console.log("Running tests...");
fb372687 2829tests = shuffle(tests);
88e2cdaa 2830next();