]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blame - tests.js
Add BCH - Bitcoin Cash network
[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';
ddeb8613 7var testMaxTime = 20000;
88e2cdaa 8
e00964cc
IC
9page.viewportSize = {
10 width: 1024,
11 height: 720
12};
13
88e2cdaa
IC
14page.onResourceError = function(e) {
15 console.log("Error loading " + e.url);
16 phantom.exit();
17}
18
19function fail() {
20 console.log("Failed");
21 phantom.exit();
22}
23
3eef9d0d
IC
24function waitForGenerate(fn, maxTime) {
25 if (!maxTime) {
26 maxTime = testMaxTime;
27 }
28 var start = new Date().getTime();
29 var prevAddressCount = -1;
30 var wait = function keepWaiting() {
31 var now = new Date().getTime();
32 var hasTimedOut = now - start > maxTime;
33 var addressCount = page.evaluate(function() {
34 return $(".address").length;
35 });
36 var hasFinished = addressCount > 0 && addressCount == prevAddressCount;
37 prevAddressCount = addressCount;
38 if (hasFinished) {
39 fn();
40 }
41 else if (hasTimedOut) {
42 console.log("Test timed out");
43 fn();
44 }
45 else {
46 setTimeout(keepWaiting, 100);
47 }
48 }
49 wait();
50}
51
c3719b00
IC
52function waitForFeedback(fn, maxTime) {
53 if (!maxTime) {
54 maxTime = testMaxTime;
55 }
56 var start = new Date().getTime();
57 var wait = function keepWaiting() {
58 var now = new Date().getTime();
59 var hasTimedOut = now - start > maxTime;
60 if (hasTimedOut) {
61 console.log("Test timed out");
62 fn();
63 return;
64 }
65 var feedback = page.evaluate(function() {
66 var feedback = $(".feedback");
67 if (feedback.css("display") == "none") {
68 return "";
69 }
70 return feedback.text();
71 });
72 var hasFinished = feedback.length > 0 && feedback != "Calculating...";
73 if (hasFinished) {
74 fn();
75 }
76 else {
77 setTimeout(keepWaiting, 100);
78 }
79 }
80 wait();
81}
82
057722b0
IC
83function waitForEntropyFeedback(fn, maxTime) {
84 if (!maxTime) {
85 maxTime = testMaxTime;
86 }
87 var origFeedback = page.evaluate(function() {
dd944906 88 return $(".entropy-container").text();
057722b0
IC
89 });
90 var start = new Date().getTime();
91 var wait = function keepWaiting() {
92 var now = new Date().getTime();
93 var hasTimedOut = now - start > maxTime;
94 if (hasTimedOut) {
95 console.log("Test timed out");
96 fn();
97 return;
98 }
99 var feedback = page.evaluate(function() {
dd944906 100 return $(".entropy-container").text();
057722b0
IC
101 });
102 var hasFinished = feedback != origFeedback;
103 if (hasFinished) {
104 fn();
105 }
106 else {
107 setTimeout(keepWaiting, 100);
108 }
109 }
110 wait();
111}
112
88e2cdaa
IC
113function next() {
114 if (tests.length > 0) {
115 var testsStr = tests.length == 1 ? "test" : "tests";
116 console.log(tests.length + " " + testsStr + " remaining");
117 tests.shift()();
118 }
119 else {
120 console.log("Finished with 0 failures");
121 phantom.exit();
122 }
123}
124
fb372687
IC
125/**
126 * Randomize array element order in-place.
127 * Using Durstenfeld shuffle algorithm.
128 * See http://stackoverflow.com/a/12646864
129 */
130function shuffle(array) {
131 for (var i = array.length - 1; i > 0; i--) {
132 var j = Math.floor(Math.random() * (i + 1));
133 var temp = array[i];
134 array[i] = array[j];
135 array[j] = temp;
136 }
137 return array;
138}
139
88e2cdaa
IC
140tests = [
141
142// Page loads with status of 'success'
143function() {
144page.open(url, function(status) {
145 if (status != "success") {
146 console.log("Page did not load with status 'success'");
147 fail();
148 }
149 next();
150});
151},
152
153// Page has text
154function() {
155page.open(url, function(status) {
156 var content = page.evaluate(function() {
157 return document.body.textContent.trim();
158 });
159 if (!content) {
160 console.log("Page does not have text");
161 fail();
162 }
163 next();
164});
165},
166
167// Entering mnemonic generates addresses
168function() {
169page.open(url, function(status) {
88e2cdaa
IC
170 // set the phrase
171 page.evaluate(function() {
172 $(".phrase").val("abandon abandon ability").trigger("input");
173 });
174 // get the address
3eef9d0d 175 waitForGenerate(function() {
06c4c6e3
IC
176 var addressCount = page.evaluate(function() {
177 return $(".address").length;
88e2cdaa 178 });
06c4c6e3
IC
179 if (addressCount != 20) {
180 console.log("Mnemonic did not generate addresses");
88e2cdaa
IC
181 console.log("Expected: " + expected);
182 console.log("Got: " + actual);
183 fail();
184 }
185 next();
3eef9d0d 186 });
88e2cdaa
IC
187});
188},
189
190// Random button generates random mnemonic
191function() {
192page.open(url, function(status) {
193 // check initial phrase is empty
194 var phrase = page.evaluate(function() {
195 return $(".phrase").text();
196 });
197 if (phrase != "") {
198 console.log("Initial phrase is not blank");
199 fail();
200 }
201 // press the 'generate' button
202 page.evaluate(function() {
203 $(".generate").click();
204 });
205 // get the new phrase
3eef9d0d 206 waitForGenerate(function() {
88e2cdaa
IC
207 var phrase = page.evaluate(function() {
208 return $(".phrase").val();
209 });
210 if (phrase.length <= 0) {
211 console.log("Phrase not generated by pressing button");
212 fail();
213 }
214 next();
3eef9d0d 215 });
88e2cdaa
IC
216});
217},
218
219// Mnemonic length can be customized
220function() {
221page.open(url, function(status) {
222 // set the length to 6
54563907 223 var expectedLength = "6";
88e2cdaa 224 page.evaluate(function() {
54563907
IC
225 $(".strength option[selected]").removeAttr("selected");
226 $(".strength option[value=6]").prop("selected", true);
88e2cdaa
IC
227 });
228 // press the 'generate' button
229 page.evaluate(function() {
230 $(".generate").click();
231 });
232 // check the new phrase is six words long
3eef9d0d 233 waitForGenerate(function() {
88e2cdaa
IC
234 var actualLength = page.evaluate(function() {
235 var words = $(".phrase").val().split(" ");
236 return words.length;
237 });
238 if (actualLength != expectedLength) {
239 console.log("Phrase not generated with correct length");
240 console.log("Expected: " + expectedLength);
241 console.log("Actual: " + actualLength);
242 fail();
243 }
54563907 244 next();
3eef9d0d 245 });
88e2cdaa
IC
246});
247},
248
88e2cdaa 249// Passphrase can be set
54563907
IC
250function() {
251page.open(url, function(status) {
252 // set the phrase and passphrase
253 var expected = "15pJzUWPGzR7avffV9nY5by4PSgSKG9rba";
254 page.evaluate(function() {
255 $(".phrase").val("abandon abandon ability");
256 $(".passphrase").val("secure_passphrase").trigger("input");
257 });
258 // check the address is generated correctly
3eef9d0d 259 waitForGenerate(function() {
54563907
IC
260 var actual = page.evaluate(function() {
261 return $(".address:first").text();
262 });
263 if (actual != expected) {
264 console.log("Passphrase results in wrong address");
265 console.log("Expected: " + expected);
266 console.log("Actual: " + actual);
267 fail();
268 }
269 next();
3eef9d0d 270 });
54563907
IC
271});
272},
273
88e2cdaa 274// Network can be set to bitcoin testnet
54563907
IC
275function() {
276page.open(url, function(status) {
59193779 277 // set the phrase and coin
54563907
IC
278 var expected = "mucaU5iiDaJDb69BHLeDv8JFfGiyg2nJKi";
279 page.evaluate(function() {
280 $(".phrase").val("abandon abandon ability");
281 $(".phrase").trigger("input");
282 $(".network option[selected]").removeAttr("selected");
52d589ea 283 $(".network option").filter(function() {
534481b6 284 return $(this).html() == "BTC - Bitcoin Testnet";
52d589ea 285 }).prop("selected", true);
54563907
IC
286 $(".network").trigger("change");
287 });
288 // check the address is generated correctly
3eef9d0d 289 waitForGenerate(function() {
54563907
IC
290 var actual = page.evaluate(function() {
291 return $(".address:first").text();
292 });
293 if (actual != expected) {
294 console.log("Bitcoin testnet address is incorrect");
295 console.log("Expected: " + expected);
296 console.log("Actual: " + actual);
297 fail();
298 }
299 next();
3eef9d0d 300 });
54563907
IC
301});
302},
303
88e2cdaa 304// Network can be set to litecoin
59193779
IC
305function() {
306page.open(url, function(status) {
307 // set the phrase and coin
308 var expected = "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn";
309 page.evaluate(function() {
310 $(".phrase").val("abandon abandon ability");
311 $(".phrase").trigger("input");
312 $(".network option[selected]").removeAttr("selected");
52d589ea 313 $(".network option").filter(function() {
534481b6 314 return $(this).html() == "LTC - Litecoin";
52d589ea 315 }).prop("selected", true);
59193779
IC
316 $(".network").trigger("change");
317 });
318 // check the address is generated correctly
3eef9d0d 319 waitForGenerate(function() {
59193779
IC
320 var actual = page.evaluate(function() {
321 return $(".address:first").text();
322 });
323 if (actual != expected) {
324 console.log("Litecoin address is incorrect");
325 console.log("Expected: " + expected);
326 console.log("Actual: " + actual);
327 fail();
328 }
329 next();
3eef9d0d 330 });
59193779
IC
331});
332},
333
a96cfdf3 334// Network can be set to ripple
335function() {
336page.open(url, function(status) {
337 // set the phrase and coin
338 var expected = "rLTFnqbmCVPGx6VfaygdtuKWJgcN4v1zRS";
339 page.evaluate(function() {
340 $(".phrase").val("ill clump only blind unit burden thing track silver cloth review awake useful craft whale all satisfy else trophy sunset walk vanish hope valve");
341 $(".phrase").trigger("input");
342 $(".network option[selected]").removeAttr("selected");
343 $(".network option").filter(function() {
534481b6 344 return $(this).html() == "XRP - Ripple";
a96cfdf3 345 }).prop("selected", true);
346 $(".network").trigger("change");
59193779
IC
347 });
348 // check the address is generated correctly
3eef9d0d 349 waitForGenerate(function() {
59193779
IC
350 var actual = page.evaluate(function() {
351 return $(".address:first").text();
352 });
353 if (actual != expected) {
56b1275f 354 console.log("Ripple address is incorrect");
59193779
IC
355 console.log("Expected: " + expected);
356 console.log("Actual: " + actual);
357 fail();
358 }
359 next();
3eef9d0d 360 });
59193779
IC
361});
362},
363
88e2cdaa 364// Network can be set to dogecoin
59193779
IC
365function() {
366page.open(url, function(status) {
367 // set the phrase and coin
368 var expected = "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA";
369 page.evaluate(function() {
370 $(".phrase").val("abandon abandon ability");
371 $(".phrase").trigger("input");
372 $(".network option[selected]").removeAttr("selected");
52d589ea 373 $(".network option").filter(function() {
534481b6 374 return $(this).html() == "DOGE - Dogecoin";
52d589ea 375 }).prop("selected", true);
59193779
IC
376 $(".network").trigger("change");
377 });
378 // check the address is generated correctly
3eef9d0d 379 waitForGenerate(function() {
59193779
IC
380 var actual = page.evaluate(function() {
381 return $(".address:first").text();
382 });
383 if (actual != expected) {
384 console.log("Dogecoin address is incorrect");
385 console.log("Expected: " + expected);
386 console.log("Actual: " + actual);
387 fail();
388 }
389 next();
3eef9d0d 390 });
59193779
IC
391});
392},
393
88e2cdaa 394// Network can be set to shadowcash
59193779
IC
395function() {
396page.open(url, function(status) {
397 // set the phrase and coin
398 var expected = "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG";
399 page.evaluate(function() {
400 $(".phrase").val("abandon abandon ability");
401 $(".phrase").trigger("input");
402 $(".network option[selected]").removeAttr("selected");
52d589ea 403 $(".network option").filter(function() {
534481b6 404 return $(this).html() == "SDC - ShadowCash";
52d589ea 405 }).prop("selected", true);
59193779
IC
406 $(".network").trigger("change");
407 });
408 // check the address is generated correctly
3eef9d0d 409 waitForGenerate(function() {
59193779
IC
410 var actual = page.evaluate(function() {
411 return $(".address:first").text();
412 });
413 if (actual != expected) {
414 console.log("Shadowcash address is incorrect");
415 console.log("Expected: " + expected);
416 console.log("Actual: " + actual);
417 fail();
418 }
419 next();
3eef9d0d 420 });
59193779
IC
421});
422},
423
88e2cdaa 424// Network can be set to shadowcash testnet
59193779
IC
425function() {
426page.open(url, function(status) {
427 // set the phrase and coin
428 var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
429 page.evaluate(function() {
430 $(".phrase").val("abandon abandon ability");
431 $(".phrase").trigger("input");
432 $(".network option[selected]").removeAttr("selected");
52d589ea 433 $(".network option").filter(function() {
534481b6 434 return $(this).html() == "SDC - ShadowCash Testnet";
52d589ea 435 }).prop("selected", true);
59193779
IC
436 $(".network").trigger("change");
437 });
438 // check the address is generated correctly
3eef9d0d 439 waitForGenerate(function() {
59193779
IC
440 var actual = page.evaluate(function() {
441 return $(".address:first").text();
442 });
443 if (actual != expected) {
444 console.log("Shadowcash testnet address is incorrect");
445 console.log("Expected: " + expected);
446 console.log("Actual: " + actual);
447 fail();
448 }
449 next();
3eef9d0d 450 });
59193779
IC
451});
452},
453
88e2cdaa 454// Network can be set to viacoin
59193779
IC
455function() {
456page.open(url, function(status) {
457 // set the phrase and coin
458 var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
459 page.evaluate(function() {
460 $(".phrase").val("abandon abandon ability");
461 $(".phrase").trigger("input");
462 $(".network option[selected]").removeAttr("selected");
52d589ea 463 $(".network option").filter(function() {
534481b6 464 return $(this).html() == "VIA - Viacoin";
52d589ea 465 }).prop("selected", true);
59193779
IC
466 $(".network").trigger("change");
467 });
468 // check the address is generated correctly
3eef9d0d 469 waitForGenerate(function() {
59193779
IC
470 var actual = page.evaluate(function() {
471 return $(".address:first").text();
472 });
473 if (actual != expected) {
474 console.log("Viacoin address is incorrect");
475 console.log("Expected: " + expected);
476 console.log("Actual: " + actual);
477 fail();
478 }
479 next();
3eef9d0d 480 });
59193779
IC
481});
482},
483
88e2cdaa 484// Network can be set to viacoin testnet
59193779
IC
485function() {
486page.open(url, function(status) {
487 // set the phrase and coin
488 var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
489 page.evaluate(function() {
490 $(".phrase").val("abandon abandon ability");
491 $(".phrase").trigger("input");
492 $(".network option[selected]").removeAttr("selected");
52d589ea 493 $(".network option").filter(function() {
534481b6 494 return $(this).html() == "VIA - Viacoin Testnet";
52d589ea 495 }).prop("selected", true);
59193779
IC
496 $(".network").trigger("change");
497 });
498 // check the address is generated correctly
3eef9d0d 499 waitForGenerate(function() {
59193779
IC
500 var actual = page.evaluate(function() {
501 return $(".address:first").text();
502 });
503 if (actual != expected) {
504 console.log("Viacoin testnet address is incorrect");
505 console.log("Expected: " + expected);
506 console.log("Actual: " + actual);
507 fail();
508 }
509 next();
3eef9d0d 510 });
59193779
IC
511});
512},
513
88e2cdaa 514// Network can be set to jumbucks
59193779
IC
515function() {
516page.open(url, function(status) {
517 // set the phrase and coin
518 var expected = "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew";
519 page.evaluate(function() {
520 $(".phrase").val("abandon abandon ability");
521 $(".phrase").trigger("input");
522 $(".network option[selected]").removeAttr("selected");
52d589ea 523 $(".network option").filter(function() {
534481b6 524 return $(this).html() == "JBS - Jumbucks";
52d589ea 525 }).prop("selected", true);
59193779
IC
526 $(".network").trigger("change");
527 });
528 // check the address is generated correctly
3eef9d0d 529 waitForGenerate(function() {
59193779
IC
530 var actual = page.evaluate(function() {
531 return $(".address:first").text();
532 });
533 if (actual != expected) {
534 console.log("Jumbucks address is incorrect");
535 console.log("Expected: " + expected);
536 console.log("Actual: " + actual);
537 fail();
538 }
539 next();
3eef9d0d 540 });
59193779
IC
541});
542},
543
88e2cdaa 544// Network can be set to clam
59193779
IC
545function() {
546page.open(url, function(status) {
547 // set the phrase and coin
548 var expected = "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y";
549 page.evaluate(function() {
550 $(".phrase").val("abandon abandon ability");
551 $(".phrase").trigger("input");
552 $(".network option[selected]").removeAttr("selected");
52d589ea 553 $(".network option").filter(function() {
534481b6 554 return $(this).html() == "CLAM - Clams";
52d589ea 555 }).prop("selected", true);
59193779
IC
556 $(".network").trigger("change");
557 });
558 // check the address is generated correctly
3eef9d0d 559 waitForGenerate(function() {
59193779
IC
560 var actual = page.evaluate(function() {
561 return $(".address:first").text();
562 });
563 if (actual != expected) {
564 console.log("CLAM address is incorrect");
565 console.log("Expected: " + expected);
566 console.log("Actual: " + actual);
567 fail();
f3fad1b5
IC
568 }
569 next();
570 });
571});
572},
573
0921f370 574// Network can be set to crown
575function() {
576page.open(url, function(status) {
577 // set the phrase and coin
578 var expected = "18pWSwSUAQdiwMHUfFZB1fM2xue9X1FqE5";
579 page.evaluate(function() {
580 $(".phrase").val("abandon abandon ability");
581 $(".phrase").trigger("input");
582 $(".network option[selected]").removeAttr("selected");
583 $(".network option").filter(function() {
584 return $(this).html() == "CRW - Crown";
585 }).prop("selected", true);
586 $(".network").trigger("change");
587 });
588 // check the address is generated correctly
589 waitForGenerate(function() {
590 var actual = page.evaluate(function() {
591 return $(".address:first").text();
592 });
593 if (actual != expected) {
594 console.log("CRW address is incorrect");
595 console.log("Expected: " + expected);
596 console.log("Actual: " + actual);
597 fail();
598 }
599 next();
600 });
601});
602},
603
f3fad1b5
IC
604// Network can be set to dash
605function() {
606page.open(url, function(status) {
607 // set the phrase and coin
608 var expected = "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT";
609 page.evaluate(function() {
610 $(".phrase").val("abandon abandon ability");
611 $(".phrase").trigger("input");
612 $(".network option[selected]").removeAttr("selected");
52d589ea 613 $(".network option").filter(function() {
534481b6 614 return $(this).html() == "DASH - Dash";
52d589ea 615 }).prop("selected", true);
f3fad1b5
IC
616 $(".network").trigger("change");
617 });
618 // check the address is generated correctly
619 waitForGenerate(function() {
620 var actual = page.evaluate(function() {
621 return $(".address:first").text();
622 });
623 if (actual != expected) {
624 console.log("DASH address is incorrect");
625 console.log("Expected: " + expected);
626 console.log("Actual: " + actual);
627 fail();
00fd1a03 628 }
629 next();
630 });
631});
632},
633
c0386f3b
KR
634function() {
635page.open(url, function(status) {
636 // set the phrase and coin
637 var expected = "yaR52EN4oojdJfBgzWJTymC4uuCLPT29Gw";
638 page.evaluate(function() {
639 $(".phrase").val("abandon abandon ability");
640 $(".phrase").trigger("input");
641 $(".network option[selected]").removeAttr("selected");
642 $(".network option").filter(function() {
534481b6 643 return $(this).html() == "DASH - Dash Testnet";
c0386f3b
KR
644 }).prop("selected", true);
645 $(".network").trigger("change");
646 });
647 // check the address is generated correctly
648 waitForGenerate(function() {
649 var actual = page.evaluate(function() {
650 return $(".address:first").text();
651 });
652 if (actual != expected) {
653 console.log("DASH Testnet address is incorrect");
654 console.log("Expected: " + expected);
655 console.log("Actual: " + actual);
656 fail();
657 }
658 next();
659 });
660});
661},
662
00fd1a03 663// Network can be set to game
664function() {
665page.open(url, function(status) {
666 // set the phrase and coin
667 var expected = "GSMY9bAp36cMR4zyT4uGVS7GFjpdXbao5Q";
668 page.evaluate(function() {
669 $(".phrase").val("abandon abandon ability");
670 $(".phrase").trigger("input");
671 $(".network option[selected]").removeAttr("selected");
672 $(".network option").filter(function() {
534481b6 673 return $(this).html() == "GAME - GameCredits";
00fd1a03 674 }).prop("selected", true);
675 $(".network").trigger("change");
676 });
677 // check the address is generated correctly
678 waitForGenerate(function() {
679 var actual = page.evaluate(function() {
680 return $(".address:first").text();
681 });
682 if (actual != expected) {
683 console.log("GAME address is incorrect");
684 console.log("Expected: " + expected);
685 console.log("Actual: " + actual);
686 fail();
59193779 687 }
bfb3dab6
IC
688 next();
689 });
690});
691},
692
693// Network can be set to namecoin
694function() {
695page.open(url, function(status) {
696 // set the phrase and coin
697 var expected = "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2";
698 page.evaluate(function() {
699 $(".phrase").val("abandon abandon ability");
700 $(".phrase").trigger("input");
701 $(".network option[selected]").removeAttr("selected");
52d589ea 702 $(".network option").filter(function() {
534481b6 703 return $(this).html() == "NMC - Namecoin";
52d589ea 704 }).prop("selected", true);
bfb3dab6
IC
705 $(".network").trigger("change");
706 });
707 // check the address is generated correctly
708 waitForGenerate(function() {
709 var actual = page.evaluate(function() {
710 return $(".address:first").text();
711 });
712 if (actual != expected) {
713 console.log("Namecoin address is incorrect");
714 console.log("Expected: " + expected);
715 console.log("Actual: " + actual);
716 fail();
717 }
718 next();
719 });
720});
721},
722
723// Network can be set to peercoin
724function() {
725page.open(url, function(status) {
726 // set the phrase and coin
727 var expected = "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm";
728 page.evaluate(function() {
729 $(".phrase").val("abandon abandon ability");
730 $(".phrase").trigger("input");
731 $(".network option[selected]").removeAttr("selected");
52d589ea 732 $(".network option").filter(function() {
534481b6 733 return $(this).html() == "PPC - Peercoin";
52d589ea 734 }).prop("selected", true);
bfb3dab6
IC
735 $(".network").trigger("change");
736 });
737 // check the address is generated correctly
738 waitForGenerate(function() {
739 var actual = page.evaluate(function() {
740 return $(".address:first").text();
741 });
742 if (actual != expected) {
743 console.log("Peercoin address is incorrect");
744 console.log("Expected: " + expected);
745 console.log("Actual: " + actual);
746 fail();
747 }
59193779 748 next();
3eef9d0d 749 });
59193779
IC
750});
751},
752
24137d96
IC
753// Network can be set to ethereum
754function() {
755
756page.open(url, function(status) {
757
758 // set the phrase and coin
759 page.evaluate(function() {
760 $(".phrase").val("abandon abandon ability");
761 $(".phrase").trigger("input");
762 $(".network option[selected]").removeAttr("selected");
52d589ea 763 $(".network option").filter(function() {
534481b6 764 return $(this).html() == "ETH - Ethereum";
52d589ea 765 }).prop("selected", true);
24137d96
IC
766 $(".network").trigger("change");
767 });
768 waitForGenerate(function() {
769 // check the address is generated correctly
770 // this value comes from
771 // https://www.myetherwallet.com/#view-wallet-info
49b21f12 772 // Unusual capitalization is due to checksum
24137d96
IC
773 var expected = "0xe5815d5902Ad612d49283DEdEc02100Bd44C2772";
774 var actual = page.evaluate(function() {
775 return $(".address:first").text();
776 });
49b21f12 777 if (actual != expected) {
24137d96
IC
778 console.log("Ethereum address is incorrect");
779 console.log("Expected: " + expected);
780 console.log("Actual: " + actual);
781 fail();
782 }
783 // check the private key is correct
784 // this private key can be imported into
785 // https://www.myetherwallet.com/#view-wallet-info
786 // and it should correlate to the address above
0163fe6a 787 var expected = "0x8f253078b73d7498302bb78c171b23ce7a8fb511987d2b2702b731638a4a15e7";
24137d96
IC
788 var actual = page.evaluate(function() {
789 return $(".privkey:first").text();
790 });
791 if (actual != expected) {
792 console.log("Ethereum privkey is incorrect");
793 console.log("Expected: " + expected);
794 console.log("Actual: " + actual);
795 fail();
796 }
797 // check the public key is correct
798 // TODO
799 // don't have any third-party source to generate the expected value
800 //var expected = "?";
801 //var actual = page.evaluate(function() {
802 // return $(".pubkey:first").text();
803 //});
804 //if (actual != expected) {
805 // console.log("Ethereum privkey is incorrect");
806 // console.log("Expected: " + expected);
807 // console.log("Actual: " + actual);
808 // fail();
809 //}
810 next();
811 });
812});
813},
814
7a5a87a0
GH
815// Network can be set to Slimcoin
816function() {
817page.open(url, function(status) {
818 // set the phrase and coin
819 var expected = "SNzPi1CafHFm3WWjRo43aMgiaEEj3ogjww";
820 page.evaluate(function() {
821 $(".phrase").val("abandon abandon ability");
822 $(".phrase").trigger("input");
823 $(".network option[selected]").removeAttr("selected");
824 $(".network option").filter(function() {
534481b6 825 return $(this).html() == "SLM - Slimcoin";
7a5a87a0
GH
826 }).prop("selected", true);
827 $(".network").trigger("change");
828 });
829 // check the address is generated correctly
830 waitForGenerate(function() {
831 var actual = page.evaluate(function() {
832 return $(".address:first").text();
833 });
834 if (actual != expected) {
835 console.log("Slimcoin address is incorrect");
836 console.log("Expected: " + expected);
837 console.log("Actual: " + actual);
838 fail();
839 }
840 next();
841 });
842});
843},
844
845// Network can be set to Slimcointn
846function() {
847page.open(url, function(status) {
848 // set the phrase and coin
849 var expected = "n3nMgWufTek5QQAr6uwMhg5xbzj8xqc4Dq";
850 page.evaluate(function() {
851 $(".phrase").val("abandon abandon ability");
852 $(".phrase").trigger("input");
853 $(".network option[selected]").removeAttr("selected");
854 $(".network option").filter(function() {
534481b6 855 return $(this).html() == "SLM - Slimcoin Testnet";
7a5a87a0
GH
856 }).prop("selected", true);
857 $(".network").trigger("change");
858 });
859 // check the address is generated correctly
860 waitForGenerate(function() {
861 var actual = page.evaluate(function() {
862 return $(".address:first").text();
863 });
864 if (actual != expected) {
865 console.log("Slimcoin testnet address is incorrect");
866 console.log("Expected: " + expected);
867 console.log("Actual: " + actual);
868 fail();
869 }
870 next();
871 });
872});
873},
874
daab55dc
IC
875// Network can be set to bitcoin cash
876function() {
877page.open(url, function(status) {
878 // set the phrase and coin
879 var expected = "1JKvb6wKtsjNoCRxpZ4DGrbniML7z5U16A";
880 page.evaluate(function() {
881 $(".phrase").val("abandon abandon ability");
882 $(".phrase").trigger("input");
883 $(".network option[selected]").removeAttr("selected");
884 $(".network option").filter(function() {
885 return $(this).html() == "BCH - Bitcoin Cash";
886 }).prop("selected", true);
887 $(".network").trigger("change");
888 });
889 // check the address is generated correctly
890 waitForGenerate(function() {
891 var actual = page.evaluate(function() {
892 return $(".address:first").text();
893 });
894 if (actual != expected) {
895 console.log("Bitcoin Cash address is incorrect");
896 console.log("Expected: " + expected);
897 console.log("Actual: " + actual);
898 fail();
899 }
900 next();
901 });
902});
903},
904
88e2cdaa 905// BIP39 seed is set from phrase
c196ad55
IC
906function() {
907page.open(url, function(status) {
908 // set the phrase
909 var expected = "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
910 page.evaluate(function() {
911 $(".phrase").val("abandon abandon ability");
912 $(".phrase").trigger("input");
913 });
914 // check the address is generated correctly
3eef9d0d 915 waitForGenerate(function() {
c196ad55
IC
916 var actual = page.evaluate(function() {
917 return $(".seed").val();
918 });
919 if (actual != expected) {
920 console.log("BIP39 seed is incorrectly generated from mnemonic");
921 console.log("Expected: " + expected);
922 console.log("Actual: " + actual);
923 fail();
924 }
925 next();
3eef9d0d 926 });
c196ad55
IC
927});
928},
929
88e2cdaa 930// BIP32 root key is set from phrase
ec60b662
IC
931function() {
932page.open(url, function(status) {
933 // set the phrase
934 var expected = "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi";
935 page.evaluate(function() {
936 $(".phrase").val("abandon abandon ability");
937 $(".phrase").trigger("input");
938 });
939 // check the address is generated correctly
3eef9d0d 940 waitForGenerate(function() {
ec60b662
IC
941 var actual = page.evaluate(function() {
942 return $(".root-key").val();
943 });
944 if (actual != expected) {
945 console.log("Root key is incorrectly generated from mnemonic");
946 console.log("Expected: " + expected);
947 console.log("Actual: " + actual);
948 fail();
949 }
950 next();
3eef9d0d 951 });
ec60b662
IC
952});
953},
954
88e2cdaa 955// Tabs show correct addresses when changed
cf7258fd
IC
956function() {
957page.open(url, function(status) {
958 // set the phrase
959 var expected = "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz";
960 page.evaluate(function() {
961 $(".phrase").val("abandon abandon ability");
962 $(".phrase").trigger("input");
963 });
964 // change tabs
965 waitForGenerate(function() {
966 page.evaluate(function() {
967 $("#bip32-tab a").click();
968 });
969 // check the address is generated correctly
970 waitForGenerate(function() {
971 var actual = page.evaluate(function() {
972 return $(".address:first").text();
973 });
974 if (actual != expected) {
975 console.log("Clicking tab generates incorrect address");
976 console.log("Expected: " + expected);
977 console.log("Actual: " + actual);
978 fail();
979 }
980 next();
981 });
982 });
983});
984},
88e2cdaa
IC
985
986// BIP44 derivation path is shown
d077e1e7
IC
987function() {
988page.open(url, function(status) {
989 // set the phrase
990 var expected = "m/44'/0'/0'/0";
991 page.evaluate(function() {
992 $(".phrase").val("abandon abandon ability");
993 $(".phrase").trigger("input");
994 });
995 // check the derivation path of the first address
996 waitForGenerate(function() {
997 var actual = page.evaluate(function() {
998 return $("#bip44 .path").val();
999 });
1000 if (actual != expected) {
1001 console.log("BIP44 derivation path is incorrect");
1002 console.log("Expected: " + expected);
1003 console.log("Actual: " + actual);
1004 fail();
1005 }
1006 next();
1007 });
1008});
1009},
1010
88e2cdaa 1011// BIP44 extended private key is shown
4fd2925d
IC
1012function() {
1013page.open(url, function(status) {
1014 // set the phrase
1015 var expected = "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG";
1016 page.evaluate(function() {
1017 $(".phrase").val("abandon abandon ability");
1018 $(".phrase").trigger("input");
1019 });
06286adb 1020 // check the BIP44 extended private key
4fd2925d
IC
1021 waitForGenerate(function() {
1022 var actual = page.evaluate(function() {
1023 return $(".extended-priv-key").val();
1024 });
1025 if (actual != expected) {
1026 console.log("BIP44 extended private key is incorrect");
1027 console.log("Expected: " + expected);
1028 console.log("Actual: " + actual);
1029 fail();
1030 }
1031 next();
1032 });
1033});
1034},
1035
88e2cdaa 1036// BIP44 extended public key is shown
39fd45bb
IC
1037function() {
1038page.open(url, function(status) {
1039 // set the phrase
1040 var expected = "xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM";
1041 page.evaluate(function() {
1042 $(".phrase").val("abandon abandon ability");
1043 $(".phrase").trigger("input");
1044 });
06286adb 1045 // check the BIP44 extended public key
39fd45bb
IC
1046 waitForGenerate(function() {
1047 var actual = page.evaluate(function() {
1048 return $(".extended-pub-key").val();
1049 });
1050 if (actual != expected) {
1051 console.log("BIP44 extended public key is incorrect");
1052 console.log("Expected: " + expected);
1053 console.log("Actual: " + actual);
1054 fail();
1055 }
1056 next();
1057 });
1058});
1059},
1060
88e2cdaa 1061// BIP44 account field changes address list
048bc3e0
IC
1062function() {
1063page.open(url, function(status) {
1064 // set the phrase
1065 var expected = "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1066 page.evaluate(function() {
1067 $(".phrase").val("abandon abandon ability");
1068 $(".phrase").trigger("input");
1069 });
1070 waitForGenerate(function() {
1071 // change the bip44 purpose field to 45
1072 page.evaluate(function() {
1073 $("#bip44 .account").val("1");
1074 $("#bip44 .account").trigger("input");
1075 });
1076 waitForGenerate(function() {
1077 // check the address for the new derivation path
1078 var actual = page.evaluate(function() {
1079 return $(".address:first").text();
1080 });
1081 if (actual != expected) {
1082 console.log("BIP44 account field generates incorrect address");
1083 console.log("Expected: " + expected);
1084 console.log("Actual: " + actual);
1085 fail();
1086 }
1087 next();
1088 });
1089 });
1090});
1091},
1092
fa4da086
IC
1093// BIP44 change field changes address list
1094function() {
1095page.open(url, function(status) {
1096 // set the phrase
1097 var expected = "1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo";
1098 page.evaluate(function() {
1099 $(".phrase").val("abandon abandon ability");
1100 $(".phrase").trigger("input");
1101 });
1102 waitForGenerate(function() {
1103 // change the bip44 purpose field to 45
1104 page.evaluate(function() {
1105 $("#bip44 .change").val("1");
1106 $("#bip44 .change").trigger("input");
1107 });
1108 waitForGenerate(function() {
1109 // check the address for the new derivation path
1110 var actual = page.evaluate(function() {
1111 return $(".address:first").text();
1112 });
1113 if (actual != expected) {
1114 console.log("BIP44 change field generates incorrect address");
1115 console.log("Expected: " + expected);
1116 console.log("Actual: " + actual);
1117 fail();
1118 }
1119 next();
1120 });
1121 });
1122});
1123},
048bc3e0 1124
88e2cdaa 1125// BIP32 derivation path can be set
651382a3
IC
1126function() {
1127page.open(url, function(status) {
1128 // set the phrase
1129 var expected = "16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L";
1130 page.evaluate(function() {
1131 $(".phrase").val("abandon abandon ability");
1132 $(".phrase").trigger("input");
1133 });
1134 // change tabs
1135 waitForGenerate(function() {
1136 page.evaluate(function() {
1137 $("#bip32-tab a").click();
1138 });
1139 // set the derivation path to m/1
1140 waitForGenerate(function() {
1141 page.evaluate(function() {
1142 $("#bip32 .path").val("m/1");
1143 $("#bip32 .path").trigger("input");
1144 });
1145 // check the address is generated correctly
1146 waitForGenerate(function() {
1147 var actual = page.evaluate(function() {
1148 return $(".address:first").text();
1149 });
1150 if (actual != expected) {
1151 console.log("Custom BIP32 path generates incorrect address");
1152 console.log("Expected: " + expected);
1153 console.log("Actual: " + actual);
1154 fail();
1155 }
1156 next();
1157 });
1158 });
1159 });
1160});
1161},
1162
88e2cdaa 1163// BIP32 can use hardened derivation paths
651382a3
IC
1164function() {
1165page.open(url, function(status) {
1166 // set the phrase
1167 var expected = "14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4";
1168 page.evaluate(function() {
1169 $(".phrase").val("abandon abandon ability");
1170 $(".phrase").trigger("input");
1171 });
1172 // change tabs
1173 waitForGenerate(function() {
1174 page.evaluate(function() {
1175 $("#bip32-tab a").click();
1176 });
1177 // set the derivation path to m/0'
1178 waitForGenerate(function() {
1179 page.evaluate(function() {
1180 $("#bip32 .path").val("m/0'");
1181 $("#bip32 .path").trigger("input");
1182 });
1183 // check the address is generated correctly
1184 waitForGenerate(function() {
1185 var actual = page.evaluate(function() {
1186 return $(".address:first").text();
1187 });
1188 if (actual != expected) {
1189 console.log("Hardened BIP32 path generates incorrect address");
1190 console.log("Expected: " + expected);
1191 console.log("Actual: " + actual);
1192 fail();
1193 }
1194 next();
1195 });
1196 });
1197 });
1198});
1199},
1200
88e2cdaa 1201// BIP32 extended private key is shown
9e9dcfda
IC
1202function() {
1203page.open(url, function(status) {
1204 // set the phrase
1205 var expected = "xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe";
1206 page.evaluate(function() {
1207 $(".phrase").val("abandon abandon ability");
1208 $(".phrase").trigger("input");
1209 });
1210 // change tabs
1211 waitForGenerate(function() {
1212 page.evaluate(function() {
1213 $("#bip32-tab a").click();
1214 });
1215 // check the extended private key is generated correctly
1216 waitForGenerate(function() {
1217 var actual = page.evaluate(function() {
1218 return $(".extended-priv-key").val();
1219 });
1220 if (actual != expected) {
1221 console.log("BIP32 extended private key is incorrect");
1222 console.log("Expected: " + expected);
1223 console.log("Actual: " + actual);
1224 fail();
1225 }
1226 next();
1227 });
1228 });
1229});
1230},
1231
88e2cdaa 1232// BIP32 extended public key is shown
9e9dcfda
IC
1233function() {
1234page.open(url, function(status) {
1235 // set the phrase
1236 var expected = "xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P";
1237 page.evaluate(function() {
1238 $(".phrase").val("abandon abandon ability");
1239 $(".phrase").trigger("input");
1240 });
1241 // change tabs
1242 waitForGenerate(function() {
1243 page.evaluate(function() {
1244 $("#bip32-tab a").click();
1245 });
1246 // check the extended public key is generated correctly
1247 waitForGenerate(function() {
1248 var actual = page.evaluate(function() {
1249 return $(".extended-pub-key").val();
1250 });
1251 if (actual != expected) {
1252 console.log("BIP32 extended public key is incorrect");
1253 console.log("Expected: " + expected);
1254 console.log("Actual: " + actual);
1255 fail();
1256 }
1257 next();
1258 });
1259 });
1260});
1261},
88e2cdaa
IC
1262
1263// Derivation path is shown in table
5f844c62
IC
1264function() {
1265page.open(url, function(status) {
1266 // set the phrase
1267 var expected = "m/44'/0'/0'/0/0";
1268 page.evaluate(function() {
1269 $(".phrase").val("abandon abandon ability");
1270 $(".phrase").trigger("input");
1271 });
1272 // check for derivation path in table
1273 waitForGenerate(function() {
1274 var actual = page.evaluate(function() {
1275 return $(".index:first").text();
1276 });
1277 if (actual != expected) {
1278 console.log("Derivation path shown incorrectly in table");
1279 console.log("Expected: " + expected);
1280 console.log("Actual: " + actual);
1281 fail();
1282 }
1283 next();
1284 });
1285});
1286},
1287
88e2cdaa 1288// Derivation path for address can be hardened
4974fd7f
IC
1289function() {
1290page.open(url, function(status) {
1291 // set the phrase
1292 var expected = "18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd";
1293 page.evaluate(function() {
1294 $(".phrase").val("abandon abandon ability");
1295 $(".phrase").trigger("input");
1296 });
1297 // change tabs
1298 waitForGenerate(function() {
1299 page.evaluate(function() {
1300 $("#bip32-tab a").click();
1301 });
1302 waitForGenerate(function() {
1303 // select the hardened addresses option
1304 page.evaluate(function() {
1305 $(".hardened-addresses").prop("checked", true);
1306 $(".hardened-addresses").trigger("change");
1307 });
1308 waitForGenerate(function() {
1309 // check the generated address is hardened
1310 var actual = page.evaluate(function() {
1311 return $(".address:first").text();
1312 });
1313 if (actual != expected) {
1314 console.log("Hardened address is incorrect");
1315 console.log("Expected: " + expected);
1316 console.log("Actual: " + actual);
1317 fail();
1318 }
1319 next();
1320 });
1321 });
1322 });
1323});
1324},
1325
88e2cdaa 1326// Derivation path visibility can be toggled
a775b5c6
IC
1327function() {
1328page.open(url, function(status) {
1329 // set the phrase
1330 page.evaluate(function() {
1331 $(".phrase").val("abandon abandon ability");
1332 $(".phrase").trigger("input");
1333 });
a775b5c6
IC
1334 waitForGenerate(function() {
1335 // toggle path visibility
1336 page.evaluate(function() {
1337 $(".index-toggle").click();
1338 });
1339 // check the path is not visible
1340 var isInvisible = page.evaluate(function() {
1341 return $(".index:first span").hasClass("invisible");
1342 });
1343 if (!isInvisible) {
1344 console.log("Toggled derivation path is visible");
1345 fail();
1346 }
1347 next();
1348 });
1349});
1350},
1351
88e2cdaa 1352// Address is shown
06c4c6e3
IC
1353function() {
1354page.open(url, function(status) {
1355 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1356 // set the phrase
1357 page.evaluate(function() {
1358 $(".phrase").val("abandon abandon ability").trigger("input");
1359 });
1360 // get the address
1361 waitForGenerate(function() {
1362 var actual = page.evaluate(function() {
1363 return $(".address:first").text();
1364 });
1365 if (actual != expected) {
1366 console.log("Address is not shown");
1367 console.log("Expected: " + expected);
1368 console.log("Got: " + actual);
1369 fail();
1370 }
1371 next();
1372 });
1373});
1374},
1375
88e2cdaa 1376// Addresses are shown in order of derivation path
3f1faa4d
IC
1377function() {
1378page.open(url, function(status) {
1379 // set the phrase
1380 page.evaluate(function() {
1381 $(".phrase").val("abandon abandon ability").trigger("input");
1382 });
1383 // get the derivation paths
1384 waitForGenerate(function() {
1385 var paths = page.evaluate(function() {
1386 return $(".index").map(function(i, e) {
1387 return $(e).text();
1388 });
1389 });
1390 if (paths.length != 20) {
1391 console.log("Total paths is less than expected: " + paths.length);
1392 fail();
1393 }
1394 for (var i=0; i<paths.length; i++) {
1395 var expected = "m/44'/0'/0'/0/" + i;
1396 var actual = paths[i];
1397 if (actual != expected) {
1398 console.log("Path " + i + " is incorrect");
1399 console.log("Expected: " + expected);
1400 console.log("Actual: " + actual);
1401 fail();
1402 }
1403 }
1404 next();
1405 });
1406});
1407},
21372fab 1408
88e2cdaa 1409// Address visibility can be toggled
21372fab
IC
1410function() {
1411page.open(url, function(status) {
1412 // set the phrase
1413 page.evaluate(function() {
1414 $(".phrase").val("abandon abandon ability");
1415 $(".phrase").trigger("input");
1416 });
1417 waitForGenerate(function() {
1418 // toggle address visibility
1419 page.evaluate(function() {
1420 $(".address-toggle").click();
1421 });
1422 // check the address is not visible
1423 var isInvisible = page.evaluate(function() {
1424 return $(".address:first span").hasClass("invisible");
1425 });
1426 if (!isInvisible) {
1427 console.log("Toggled address is visible");
1428 fail();
1429 }
1430 next();
1431 });
1432});
1433},
1434
1b12b2f5
IC
1435// Public key is shown
1436function() {
1437page.open(url, function(status) {
1438 var expected = "033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3";
1439 // set the phrase
1440 page.evaluate(function() {
1441 $(".phrase").val("abandon abandon ability").trigger("input");
1442 });
1443 // get the address
1444 waitForGenerate(function() {
1445 var actual = page.evaluate(function() {
1446 return $(".pubkey:first").text();
1447 });
1448 if (actual != expected) {
1449 console.log("Public key is not shown");
1450 console.log("Expected: " + expected);
1451 console.log("Got: " + actual);
1452 fail();
1453 }
1454 next();
1455 });
1456});
1457},
1458
1459// Public key visibility can be toggled
1460function() {
1461page.open(url, function(status) {
1462 // set the phrase
1463 page.evaluate(function() {
1464 $(".phrase").val("abandon abandon ability");
1465 $(".phrase").trigger("input");
1466 });
1467 waitForGenerate(function() {
1468 // toggle public key visibility
1469 page.evaluate(function() {
1470 $(".public-key-toggle").click();
1471 });
1472 // check the public key is not visible
1473 var isInvisible = page.evaluate(function() {
1474 return $(".pubkey:first span").hasClass("invisible");
1475 });
1476 if (!isInvisible) {
1477 console.log("Toggled public key is visible");
1478 fail();
1479 }
1480 next();
1481 });
1482});
1483},
1484
88e2cdaa 1485// Private key is shown
8cd5e231
IC
1486function() {
1487page.open(url, function(status) {
1488 var expected = "L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE";
1489 // set the phrase
1490 page.evaluate(function() {
1491 $(".phrase").val("abandon abandon ability").trigger("input");
1492 });
1493 // get the address
1494 waitForGenerate(function() {
1495 var actual = page.evaluate(function() {
1496 return $(".privkey:first").text();
1497 });
1498 if (actual != expected) {
1499 console.log("Private key is not shown");
1500 console.log("Expected: " + expected);
1501 console.log("Got: " + actual);
1502 fail();
1503 }
1504 next();
1505 });
1506});
1507},
1508
88e2cdaa 1509// Private key visibility can be toggled
6848d03b
IC
1510function() {
1511page.open(url, function(status) {
1512 // set the phrase
1513 page.evaluate(function() {
1514 $(".phrase").val("abandon abandon ability");
1515 $(".phrase").trigger("input");
1516 });
1517 waitForGenerate(function() {
1518 // toggle private key visibility
1519 page.evaluate(function() {
1520 $(".private-key-toggle").click();
1521 });
1522 // check the private key is not visible
1523 var isInvisible = page.evaluate(function() {
1524 return $(".privkey:first span").hasClass("invisible");
1525 });
1526 if (!isInvisible) {
1527 console.log("Toggled private key is visible");
1528 fail();
1529 }
1530 next();
1531 });
1532});
1533},
88e2cdaa
IC
1534
1535// More addresses can be generated
35631659
IC
1536function() {
1537page.open(url, function(status) {
1538 // set the phrase
1539 page.evaluate(function() {
1540 $(".phrase").val("abandon abandon ability");
1541 $(".phrase").trigger("input");
1542 });
1543 waitForGenerate(function() {
1544 // generate more addresses
1545 page.evaluate(function() {
1546 $(".more").click();
1547 });
1548 waitForGenerate(function() {
1549 // check there are more addresses
1550 var addressCount = page.evaluate(function() {
1551 return $(".address").length;
1552 });
1553 if (addressCount != 40) {
1554 console.log("More addresses cannot be generated");
1555 fail();
1556 }
1557 next();
1558 });
1559 });
1560});
1561},
1562
88e2cdaa 1563// A custom number of additional addresses can be generated
8a89b9da
IC
1564function() {
1565page.open(url, function(status) {
1566 // set the phrase
1567 page.evaluate(function() {
1568 $(".phrase").val("abandon abandon ability");
1569 $(".phrase").trigger("input");
1570 });
1571 waitForGenerate(function() {
1572 // get the current number of addresses
1573 var oldAddressCount = page.evaluate(function() {
1574 return $(".address").length;
1575 });
1576 // set a custom number of additional addresses
1577 page.evaluate(function() {
1578 $(".rows-to-add").val(1);
1579 });
1580 // generate more addresses
1581 page.evaluate(function() {
1582 $(".more").click();
1583 });
1584 waitForGenerate(function() {
1585 // check there are the correct number of addresses
1586 var newAddressCount = page.evaluate(function() {
1587 return $(".address").length;
1588 });
1589 if (newAddressCount - oldAddressCount != 1) {
1590 console.log("Number of additional addresses cannot be customized");
1591 console.log(newAddressCount)
1592 console.log(oldAddressCount)
1593 fail();
1594 }
1595 next();
1596 });
1597 });
1598});
1599},
1600
88e2cdaa 1601// Additional addresses are shown in order of derivation path
4d387bf5
IC
1602function() {
1603page.open(url, function(status) {
1604 // set the phrase
1605 page.evaluate(function() {
1606 $(".phrase").val("abandon abandon ability").trigger("input");
1607 });
1608 waitForGenerate(function() {
1609 // generate more addresses
1610 page.evaluate(function() {
1611 $(".more").click();
1612 });
1613 // get the derivation paths
1614 waitForGenerate(function() {
1615 var paths = page.evaluate(function() {
1616 return $(".index").map(function(i, e) {
1617 return $(e).text();
1618 });
1619 });
1620 if (paths.length != 40) {
1621 console.log("Total additional paths is less than expected: " + paths.length);
1622 fail();
1623 }
1624 for (var i=0; i<paths.length; i++) {
1625 var expected = "m/44'/0'/0'/0/" + i;
1626 var actual = paths[i];
1627 if (actual != expected) {
1628 console.log("Path " + i + " is not in correct order");
1629 console.log("Expected: " + expected);
1630 console.log("Actual: " + actual);
1631 fail();
1632 }
1633 }
1634 next();
1635 });
1636 });
1637});
1638},
88e2cdaa
IC
1639
1640// BIP32 root key can be set by the user
61ed16a9
IC
1641function() {
1642page.open(url, function(status) {
1643 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1644 // set the root key
1645 page.evaluate(function() {
1646 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1647 });
1648 waitForGenerate(function() {
1649 var actual = page.evaluate(function() {
1650 return $(".address:first").text();
1651 });
1652 if (actual != expected) {
bc324fd2 1653 console.log("Setting BIP32 root key results in wrong address");
61ed16a9
IC
1654 console.log("Expected: " + expected);
1655 console.log("Actual: " + actual);
1656 fail();
1657 }
1658 next();
1659 });
1660});
1661},
1662
54563907 1663// Setting BIP32 root key clears the existing phrase, passphrase and seed
bc324fd2
IC
1664function() {
1665page.open(url, function(status) {
1666 var expected = "";
1667 // set a mnemonic
1668 page.evaluate(function() {
1669 $(".phrase").val("A non-blank but invalid value");
1670 });
1671 // Accept any confirm dialogs
1672 page.onConfirm = function() {
1673 return true;
1674 };
1675 // set the root key
1676 page.evaluate(function() {
1677 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1678 });
1679 waitForGenerate(function() {
1680 var actual = page.evaluate(function() {
1681 return $(".phrase").val();
1682 });
1683 if (actual != expected) {
1684 console.log("Phrase not cleared when setting BIP32 root key");
1685 console.log("Expected: " + expected);
1686 console.log("Actual: " + actual);
1687 fail();
1688 }
1689 next();
1690 });
1691});
1692},
1693
54563907 1694// Clearing of phrase, passphrase and seed can be cancelled by user
abfbe450
IC
1695function() {
1696page.open(url, function(status) {
1697 var expected = "abandon abandon ability";
1698 // set a mnemonic
1699 page.evaluate(function() {
1700 $(".phrase").val("abandon abandon ability");
1701 });
1702 // Cancel any confirm dialogs
1703 page.onConfirm = function() {
1704 return false;
1705 };
1706 // set the root key
1707 page.evaluate(function() {
1708 $(".root-key").val("xprv9s21ZrQH143K3d3vzEDD3KpSKmxsZ3y7CqhAL1tinwtP6wqK4TKEKjpBuo6P2hUhB6ZENo7TTSRytiP857hBZVpBdk8PooFuRspE1eywwNZ").trigger("input");
1709 });
1710 var actual = page.evaluate(function() {
1711 return $(".phrase").val();
1712 });
1713 if (actual != expected) {
1714 console.log("Phrase not retained when cancelling changes to BIP32 root key");
1715 console.log("Expected: " + expected);
1716 console.log("Actual: " + actual);
1717 fail();
1718 }
1719 next();
1720});
1721},
7ff86d4c 1722
88e2cdaa 1723// Custom BIP32 root key is used when changing the derivation path
7ff86d4c
IC
1724function() {
1725page.open(url, function(status) {
1726 var expected = "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1727 // set the root key
1728 page.evaluate(function() {
1729 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1730 });
1731 waitForGenerate(function() {
1732 // change the derivation path
1733 page.evaluate(function() {
1734 $("#account").val("1").trigger("input");
1735 });
1736 // check the bip32 root key is used for derivation, not the blank phrase
1737 waitForGenerate(function() {
1738 var actual = page.evaluate(function() {
1739 return $(".address:first").text();
1740 });
1741 if (actual != expected) {
1742 console.log("Changing the derivation path does not use BIP32 root key");
1743 console.log("Expected: " + expected);
1744 console.log("Actual: " + actual);
1745 fail();
1746 }
1747 next();
1748 });
1749 });
1750});
1751},
88e2cdaa
IC
1752
1753// Incorrect mnemonic shows error
c3719b00
IC
1754function() {
1755page.open(url, function(status) {
1756 // set the root key
1757 page.evaluate(function() {
1758 $(".phrase").val("abandon abandon abandon").trigger("input");
1759 });
1760 waitForFeedback(function() {
1761 // check there is an error shown
1762 var feedback = page.evaluate(function() {
1763 return $(".feedback").text();
1764 });
1765 if (feedback.length <= 0) {
1766 console.log("Invalid mnemonic does not show error");
1767 fail();
1768 }
1769 next();
1770 });
1771});
1772},
1773
88e2cdaa 1774// Incorrect word shows suggested replacement
20f80cfa
IC
1775function() {
1776page.open(url, function(status) {
1777 // set the root key
1778 page.evaluate(function() {
1779 $(".phrase").val("abandon abandon abiliti").trigger("input");
1780 });
1781 // check there is a suggestion shown
1782 waitForFeedback(function() {
1783 var feedback = page.evaluate(function() {
1784 return $(".feedback").text();
1785 });
1786 if (feedback.indexOf("did you mean ability?") < 0) {
1787 console.log("Incorrect word does not show suggested replacement");
1788 console.log("Error: " + error);
1789 fail();
6ea15134
IC
1790 }
1791 next();
1792 });
1793});
1794},
1795
1796// Github pull request 48
1797// First four letters of word shows that word, not closest
1798// since first four letters gives unique word in BIP39 wordlist
1799// eg ille should show illegal, not idle
1800function() {
1801page.open(url, function(status) {
1802 // set the incomplete word
1803 page.evaluate(function() {
1804 $(".phrase").val("ille").trigger("input");
1805 });
1806 // check there is a suggestion shown
1807 waitForFeedback(function() {
1808 var feedback = page.evaluate(function() {
1809 return $(".feedback").text();
1810 });
1811 if (feedback.indexOf("did you mean illegal?") < 0) {
1812 console.log("Start of word does not show correct suggestion");
1813 console.log("Error: " + error);
1814 fail();
20f80cfa
IC
1815 }
1816 next();
1817 });
1818});
1819},
1820
88e2cdaa 1821// Incorrect BIP32 root key shows error
02f4a90e
IC
1822function() {
1823page.open(url, function(status) {
1824 // set the root key
1825 page.evaluate(function() {
1826 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj").trigger("input");
1827 });
1828 // check there is an error shown
1829 waitForFeedback(function() {
1830 var feedback = page.evaluate(function() {
1831 return $(".feedback").text();
1832 });
1833 if (feedback != "Invalid root key") {
1834 console.log("Invalid root key does not show error");
1835 console.log("Error: " + error);
1836 fail();
1837 }
1838 next();
1839 });
1840});
1841},
1842
88e2cdaa 1843// Derivation path not starting with m shows error
f976b541
IC
1844function() {
1845page.open(url, function(status) {
1846 // set the mnemonic phrase
1847 page.evaluate(function() {
1848 $(".phrase").val("abandon abandon ability").trigger("input");
1849 });
1850 waitForGenerate(function() {
1851 // select the bip32 tab so custom derivation path can be set
1852 page.evaluate(function() {
1853 $("#bip32-tab a").click();
1854 });
1855 waitForGenerate(function() {
1856 // set the incorrect derivation path
1857 page.evaluate(function() {
1858 $("#bip32 .path").val("n/0").trigger("input");
1859 });
1860 waitForFeedback(function() {
1861 var feedback = page.evaluate(function() {
1862 return $(".feedback").text();
1863 });
1864 if (feedback != "First character must be 'm'") {
1865 console.log("Derivation path not starting with m should show error");
1866 console.log("Error: " + error);
1867 fail();
1868 }
1869 next();
1870 });
1871 });
1872 });
1873});
1874},
1875
88e2cdaa 1876// Derivation path containing invalid characters shows useful error
55c0ffcb
IC
1877function() {
1878page.open(url, function(status) {
1879 // set the mnemonic phrase
1880 page.evaluate(function() {
1881 $(".phrase").val("abandon abandon ability").trigger("input");
1882 });
1883 waitForGenerate(function() {
1884 // select the bip32 tab so custom derivation path can be set
1885 page.evaluate(function() {
1886 $("#bip32-tab a").click();
1887 });
1888 waitForGenerate(function() {
1889 // set the incorrect derivation path
1890 page.evaluate(function() {
1891 $("#bip32 .path").val("m/1/0wrong1/1").trigger("input");
1892 });
1893 waitForFeedback(function() {
1894 var feedback = page.evaluate(function() {
1895 return $(".feedback").text();
1896 });
1897 if (feedback != "Invalid characters 0wrong1 found at depth 2") {
1898 console.log("Derivation path with invalid characters should show error");
1899 console.log("Error: " + error);
1900 fail();
1901 }
1902 next();
1903 });
1904 });
1905 });
1906});
1907},
88e2cdaa
IC
1908
1909// Github Issue 11: Default word length is 15
b630f83d 1910// https://github.com/iancoleman/bip39/issues/11
ca976aa9
IC
1911function() {
1912page.open(url, function(status) {
1913 // get the word length
1914 var defaultLength = page.evaluate(function() {
1915 return $(".strength").val();
1916 });
1917 if (defaultLength != 15) {
1918 console.log("Default word length is not 15");
1919 fail();
1920 }
1921 next();
1922});
1923},
1924
88e2cdaa
IC
1925
1926// Github Issue 12: Generate more rows with private keys hidden
b630f83d 1927// https://github.com/iancoleman/bip39/issues/12
c97627fa
IC
1928function() {
1929page.open(url, function(status) {
1930 // set the phrase
1931 page.evaluate(function() {
1932 $(".phrase").val("abandon abandon ability");
1933 $(".phrase").trigger("input");
1934 });
1935 waitForGenerate(function() {
1936 // toggle private keys hidden, then generate more addresses
1937 page.evaluate(function() {
1938 $(".private-key-toggle").click();
1939 $(".more").click();
1940 });
1941 waitForGenerate(function() {
1942 // check more have been generated
1943 var expected = 40;
1944 var numPrivKeys = page.evaluate(function() {
1945 return $(".privkey").length;
1946 });
1947 if (numPrivKeys != expected) {
1948 console.log("Wrong number of addresses when clicking 'more' with hidden privkeys");
1949 console.log("Expected: " + expected);
1950 console.log("Actual: " + numPrivKeys);
1951 fail();
1952 }
1953 // check no private keys are shown
1954 var numHiddenPrivKeys = page.evaluate(function() {
1955 return $(".privkey span[class=invisible]").length;
1956 });
1957 if (numHiddenPrivKeys != expected) {
1958 console.log("Generating more does not retain hidden state of privkeys");
1959 console.log("Expected: " + expected);
1960 console.log("Actual: " + numHiddenPrivKeys);
1961 fail();
1962 }
1963 next();
1964 });
1965 });
1966});
1967},
88e2cdaa
IC
1968
1969// Github Issue 19: Mnemonic is not sensitive to whitespace
b630f83d 1970// https://github.com/iancoleman/bip39/issues/19
a7becc43
IC
1971function() {
1972page.open(url, function(status) {
1973 // set the phrase
1974 var expected = "xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC";
1975 page.evaluate(function() {
1976 var doubleSpace = " ";
1977 $(".phrase").val("urge cat" + doubleSpace + "bid");
1978 $(".phrase").trigger("input");
1979 });
1980 waitForGenerate(function() {
1981 // Check the bip32 root key is correct
1982 var actual = page.evaluate(function() {
1983 return $(".root-key").val();
1984 });
1985 if (actual != expected) {
1986 console.log("Mnemonic is sensitive to whitespace");
1987 console.log("Expected: " + expected);
1988 console.log("Actual: " + actual);
1989 fail();
1990 }
1991 next();
1992 });
1993});
1994},
88e2cdaa 1995
e3001539 1996// Github Issue 23: Part 1: Use correct derivation path when changing tabs
b630f83d 1997// https://github.com/iancoleman/bip39/issues/23
e3001539
IC
1998function() {
1999page.open(url, function(status) {
2000 // 1) and 2) set the phrase
2001 page.evaluate(function() {
2002 $(".phrase").val("abandon abandon ability").trigger("input");
2003 });
2004 waitForGenerate(function() {
2005 // 3) select bip32 tab
2006 page.evaluate(function() {
2007 $("#bip32-tab a").click();
2008 });
2009 waitForGenerate(function() {
2010 // 4) switch from bitcoin to litecoin
2011 page.evaluate(function() {
52d589ea 2012 $(".network option").filter(function() {
534481b6 2013 return $(this).html() == "LTC - Litecoin";
52d589ea
IC
2014 }).prop("selected", true);
2015 $(".network").trigger("change");
e3001539
IC
2016 });
2017 waitForGenerate(function() {
2018 // 5) Check derivation path is displayed correctly
2019 var expected = "m/0/0";
2020 var actual = page.evaluate(function() {
2021 return $(".index:first").text();
2022 });
2023 if (actual != expected) {
2024 console.log("Github Issue 23 Part 1: derivation path display error");
2025 console.log("Expected: " + expected);
2026 console.log("Actual: " + actual);
2027 fail();
2028 }
2029 // 5) Check address is displayed correctly
2030 var expected = "LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5";
2031 var actual = page.evaluate(function() {
2032 return $(".address:first").text();
2033 });
2034 if (actual != expected) {
2035 console.log("Github Issue 23 Part 1: address display error");
2036 console.log("Expected: " + expected);
2037 console.log("Actual: " + actual);
2038 fail();
2039 }
2040 next();
2041 });
2042 });
2043 });
2044});
2045},
2046
2047// Github Issue 23 Part 2: Coin selection in derivation path
b630f83d 2048// https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
af4fd3a2
IC
2049function() {
2050page.open(url, function(status) {
2051 // set the phrase
2052 page.evaluate(function() {
2053 $(".phrase").val("abandon abandon ability").trigger("input");
2054 });
2055 waitForGenerate(function() {
2056 // switch from bitcoin to clam
2057 page.evaluate(function() {
52d589ea 2058 $(".network option").filter(function() {
534481b6 2059 return $(this).html() == "CLAM - Clams";
52d589ea
IC
2060 }).prop("selected", true);
2061 $(".network").trigger("change");
af4fd3a2
IC
2062 });
2063 waitForGenerate(function() {
2064 // check derivation path is displayed correctly
2065 var expected = "m/44'/23'/0'/0/0";
2066 var actual = page.evaluate(function() {
2067 return $(".index:first").text();
2068 });
2069 if (actual != expected) {
2070 console.log("Github Issue 23 Part 2: Coin in BIP44 derivation path is incorrect");
2071 console.log("Expected: " + expected);
2072 console.log("Actual: " + actual);
2073 fail();
2074 }
2075 next();
2076 });
2077 });
2078});
2079},
88e2cdaa 2080
f3d0aca1 2081// Github Issue 26: When using a Root key derrived altcoins are incorrect
b630f83d 2082// https://github.com/iancoleman/bip39/issues/26
558ef9ac
IC
2083function() {
2084page.open(url, function(status) {
2085 // 1) 2) and 3) set the root key
2086 page.evaluate(function() {
2087 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
2088 });
2089 waitForGenerate(function() {
2090 // 4) switch from bitcoin to viacoin
2091 page.evaluate(function() {
52d589ea 2092 $(".network option").filter(function() {
534481b6 2093 return $(this).html() == "VIA - Viacoin";
52d589ea
IC
2094 }).prop("selected", true);
2095 $(".network").trigger("change");
558ef9ac
IC
2096 });
2097 waitForGenerate(function() {
2098 // 5) ensure the derived address is correct
2099 var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
2100 var actual = page.evaluate(function() {
2101 return $(".address:first").text();
2102 });
2103 if (actual != expected) {
2104 console.log("Github Issue 26: address is incorrect when changing networks and using root-key to derive");
2105 console.log("Expected: " + expected);
2106 console.log("Actual: " + actual);
2107 fail();
2108 }
2109 next();
2110 });
2111 });
2112});
2113},
f3d0aca1 2114
e1bae843
IC
2115// Selecting a language with no existing phrase should generate a phrase in
2116// that language.
2117function() {
2118page.open(url, function(status) {
2119 // Select a language
2120 // Need to manually simulate hash being set due to quirk between
2121 // 'click' event triggered by javascript vs triggered by mouse.
2122 // Perhaps look into page.sendEvent
2123 // http://phantomjs.org/api/webpage/method/send-event.html
2124 page.evaluate(function() {
2125 window.location.hash = "#japanese";
2126 $("a[href='#japanese']").trigger("click");
2127 });
2128 waitForGenerate(function() {
2129 // Check the mnemonic is in Japanese
2130 var phrase = page.evaluate(function() {
2131 return $(".phrase").val();
2132 });
2133 if (phrase.length <= 0) {
2134 console.log("No Japanese phrase generated");
2135 fail();
2136 }
2137 if (phrase.charCodeAt(0) < 128) {
2138 console.log("First character of Japanese phrase is ascii");
2139 console.log("Phrase: " + phrase);
2140 fail();
2141 }
2142 next();
2143 });
2144});
2145},
2146
2147// Selecting a language with existing phrase should update the phrase to use
2148// that language.
2149function() {
2150page.open(url, function(status) {
2151 // Set the phrase to an English phrase.
2152 page.evaluate(function() {
2153 $(".phrase").val("abandon abandon ability").trigger("input");
2154 });
2155 waitForGenerate(function() {
2156 // Change to Italian
2157 // Need to manually simulate hash being set due to quirk between
2158 // 'click' event triggered by javascript vs triggered by mouse.
2159 // Perhaps look into page.sendEvent
2160 // http://phantomjs.org/api/webpage/method/send-event.html
2161 page.evaluate(function() {
2162 window.location.hash = "#italian";
2163 $("a[href='#italian']").trigger("click");
2164 });
2165 waitForGenerate(function() {
2166 // Check only the language changes, not the phrase
2167 var expected = "abaco abaco abbaglio";
2168 var actual = page.evaluate(function() {
2169 return $(".phrase").val();
2170 });
2171 if (actual != expected) {
2172 console.log("Changing language with existing phrase");
2173 console.log("Expected: " + expected);
2174 console.log("Actual: " + actual);
2175 fail();
2176 }
2177 // Check the address is correct
2178 var expected = "1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV";
2179 var actual = page.evaluate(function() {
2180 return $(".address:first").text();
2181 });
2182 if (actual != expected) {
2183 console.log("Changing language generates incorrect address");
2184 console.log("Expected: " + expected);
2185 console.log("Actual: " + actual);
2186 fail();
2187 }
2188 next();
2189 });
2190 });
2191});
2192},
2193
2194// Suggested replacement for erroneous word in non-English language
2195function() {
2196page.open(url, function(status) {
2197 // Set an incorrect phrase in Italian
2198 page.evaluate(function() {
2199 $(".phrase").val("abaco abaco zbbaglio").trigger("input");
2200 });
2201 waitForFeedback(function() {
2202 // Check the suggestion is correct
2203 var feedback = page.evaluate(function() {
2204 return $(".feedback").text();
2205 });
2206 if (feedback.indexOf("did you mean abbaglio?") < 0) {
2207 console.log("Incorrect Italian word does not show suggested replacement");
2208 console.log("Error: " + error);
2209 fail();
2210 }
2211 next();
2212 });
2213});
2214},
2215
2216
2217// Japanese word does not break across lines.
2218// Point 2 from
2219// https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
2220function() {
2221page.open(url, function(status) {
2222 hasWordBreakCss = page.content.indexOf("word-break: keep-all;") > -1;
2223 if (!hasWordBreakCss) {
2224 console.log("Japanese words can break across lines mid-word");
2225 console.log("Check CSS for '.phrase { word-break: keep-all; }'");
2226 fail();
2227 }
2228 // Run the next test
2229 next();
2230});
2231},
2232
2233// Language can be specified at page load using hash value in url
2234function() {
2235page.open(url, function(status) {
2236 // Set the page hash as if it were on a fresh page load
2237 page.evaluate(function() {
2238 window.location.hash = "#japanese";
2239 });
2240 // Generate a random phrase
2241 page.evaluate(function() {
2242 $(".generate").trigger("click");
2243 });
2244 waitForGenerate(function() {
2245 // Check the phrase is in Japanese
2246 var phrase = page.evaluate(function() {
2247 return $(".phrase").val();
2248 });
2249 if (phrase.length <= 0) {
2250 console.log("No phrase generated using url hash");
2251 fail();
2252 }
2253 if (phrase.charCodeAt(0) < 128) {
2254 console.log("Language not detected from url hash on page load.");
2255 console.log("Phrase: " + phrase);
2256 fail();
2257 }
2258 next();
2259 });
2260});
2261},
2262
c6624d51
IC
2263// Entropy unit tests
2264function() {
2265page.open(url, function(status) {
adc8ce12 2266 var response = page.evaluate(function() {
c6624d51
IC
2267 var e;
2268 // binary entropy is detected
adc8ce12
IC
2269 try {
2270 e = Entropy.fromString("01010101");
2271 if (e.base.str != "binary") {
2272 return "Binary entropy not detected correctly";
2273 }
2274 }
2275 catch (e) {
2276 return e.message;
c6624d51
IC
2277 }
2278 // base6 entropy is detected
adc8ce12
IC
2279 try {
2280 e = Entropy.fromString("012345012345");
2281 if (e.base.str != "base 6") {
2282 return "base6 entropy not detected correctly";
2283 }
2284 }
2285 catch (e) {
2286 return e.message;
c6624d51
IC
2287 }
2288 // dice entropy is detected
adc8ce12
IC
2289 try {
2290 e = Entropy.fromString("123456123456");
2291 if (e.base.str != "base 6 (dice)") {
2292 return "dice entropy not detected correctly";
2293 }
2294 }
2295 catch (e) {
2296 return e.message;
c6624d51
IC
2297 }
2298 // base10 entropy is detected
adc8ce12
IC
2299 try {
2300 e = Entropy.fromString("0123456789");
2301 if (e.base.str != "base 10") {
2302 return "base10 entropy not detected correctly";
2303 }
2304 }
2305 catch (e) {
2306 return e.message;
c6624d51
IC
2307 }
2308 // hex entropy is detected
adc8ce12
IC
2309 try {
2310 e = Entropy.fromString("0123456789ABCDEF");
2311 if (e.base.str != "hexadecimal") {
2312 return "hexadecimal entropy not detected correctly";
2313 }
2314 }
2315 catch (e) {
2316 return e.message;
2317 }
2318 // card entropy is detected
2319 try {
2320 e = Entropy.fromString("AC4DTHKS");
2321 if (e.base.str != "card") {
2322 return "card entropy not detected correctly";
2323 }
2324 }
2325 catch (e) {
2326 return e.message;
c6624d51
IC
2327 }
2328 // entropy is case insensitive
adc8ce12
IC
2329 try {
2330 e = Entropy.fromString("aBcDeF");
2331 if (e.cleanStr != "aBcDeF") {
2332 return "Entropy should not be case sensitive";
2333 }
2334 }
2335 catch (e) {
2336 return e.message;
c6624d51
IC
2337 }
2338 // dice entropy is converted to base6
adc8ce12
IC
2339 try {
2340 e = Entropy.fromString("123456");
425b75a9 2341 if (e.cleanStr != "123450") {
adc8ce12
IC
2342 return "Dice entropy is not automatically converted to base6";
2343 }
2344 }
2345 catch (e) {
2346 return e.message;
c6624d51
IC
2347 }
2348 // dice entropy is preferred to base6 if ambiguous
adc8ce12
IC
2349 try {
2350 e = Entropy.fromString("12345");
2351 if (e.base.str != "base 6 (dice)") {
2352 return "dice not used as default over base 6";
2353 }
2354 }
2355 catch (e) {
2356 return e.message;
c6624d51
IC
2357 }
2358 // unused characters are ignored
adc8ce12
IC
2359 try {
2360 e = Entropy.fromString("fghijkl");
2361 if (e.cleanStr != "f") {
2362 return "additional characters are not ignored";
2363 }
2364 }
2365 catch (e) {
2366 return e.message;
c6624d51
IC
2367 }
2368 // the lowest base is used by default
2369 // 7 could be decimal or hexadecimal, but should be detected as decimal
adc8ce12
IC
2370 try {
2371 e = Entropy.fromString("7");
2372 if (e.base.str != "base 10") {
2373 return "lowest base is not used";
2374 }
c6624d51 2375 }
adc8ce12
IC
2376 catch (e) {
2377 return e.message;
c6624d51
IC
2378 }
2379 // Leading zeros are retained
adc8ce12
IC
2380 try {
2381 e = Entropy.fromString("000A");
2382 if (e.cleanStr != "000A") {
2383 return "Leading zeros are not retained";
2384 }
2385 }
2386 catch (e) {
2387 return e.message;
c6624d51
IC
2388 }
2389 // Leading zeros are correctly preserved for hex in binary string
adc8ce12
IC
2390 try {
2391 e = Entropy.fromString("2A");
2392 if (e.binaryStr != "00101010") {
2393 return "Hex leading zeros are not correct in binary";
2394 }
2395 }
2396 catch (e) {
2397 return e.message;
2398 }
1cf1bbaf
IC
2399 // Leading zeros for base 6 as binary string
2400 // 20 = 2 events at 2.58 bits per event = 5 bits
2401 // 20 in base 6 = 12 in base 10 = 1100 in base 2
2402 // so it needs 1 bit of padding to be the right bit length
adc8ce12 2403 try {
1cf1bbaf
IC
2404 e = Entropy.fromString("20");
2405 if (e.binaryStr != "01100") {
0d0f07f9
IC
2406 return "Base 6 as binary has leading zeros";
2407 }
2408 }
2409 catch (e) {
2410 return e.message;
2411 }
1cf1bbaf 2412 // Leading zeros for base 10 as binary string
0d0f07f9 2413 try {
1cf1bbaf
IC
2414 e = Entropy.fromString("17");
2415 if (e.binaryStr != "010001") {
0d0f07f9
IC
2416 return "Base 10 as binary has leading zeros";
2417 }
2418 }
2419 catch (e) {
2420 return e.message;
2421 }
87ad2c6e 2422 // Leading zeros for card entropy as binary string.
9d33c892 2423 // Card entropy is hashed so 2c does not necessarily produce leading zeros.
0d0f07f9 2424 try {
9d33c892
IC
2425 e = Entropy.fromString("2c");
2426 if (e.binaryStr != "0010") {
0d0f07f9 2427 return "Card entropy as binary has leading zeros";
adc8ce12
IC
2428 }
2429 }
2430 catch (e) {
2431 return e.message;
c6624d51
IC
2432 }
2433 // Keyboard mashing results in weak entropy
2434 // Despite being a long string, it's less than 30 bits of entropy
adc8ce12
IC
2435 try {
2436 e = Entropy.fromString("aj;se ifj; ask,dfv js;ifj");
2437 if (e.binaryStr.length >= 30) {
2438 return "Keyboard mashing should produce weak entropy";
2439 }
c6624d51 2440 }
adc8ce12
IC
2441 catch (e) {
2442 return e.message;
2443 }
2444 // Card entropy is used if every pair could be a card
2445 try {
2446 e = Entropy.fromString("4c3c2c");
2447 if (e.base.str != "card") {
2448 return "Card entropy not used if all pairs are cards";
2449 }
2450 }
2451 catch (e) {
2452 return e.message;
2453 }
2454 // Card entropy uses base 52
2455 // [ cards, binary ]
2456 try {
2457 var cards = [
9d33c892
IC
2458 [ "ac", "0101" ],
2459 [ "acqs", "11011100" ],
2460 [ "acks", "01011100" ],
2461 [ "2cac", "11111000" ],
2462 [ "2c", "0010" ],
2463 [ "3d", "0001" ],
2464 [ "4h", "1001" ],
87ad2c6e 2465 [ "5s", "1001" ],
9d33c892
IC
2466 [ "6c", "0000" ],
2467 [ "7d", "0001" ],
87ad2c6e 2468 [ "8h", "1011" ],
9d33c892
IC
2469 [ "9s", "0010" ],
2470 [ "tc", "1001" ],
2471 [ "jd", "1111" ],
2472 [ "qh", "0010" ],
2473 [ "ks", "0101" ],
2474 [ "ks2c", "01010100" ],
2475 [ "KS2C", "01010100" ],
adc8ce12
IC
2476 ];
2477 for (var i=0; i<cards.length; i++) {
2478 var card = cards[i][0];
2479 var result = cards[i][1];
2480 e = Entropy.fromString(card);
2481 console.log(e.binary + " " + result);
2482 if (e.binaryStr !== result) {
886f06ee 2483 return "card entropy " + card + " not parsed correctly: " + result + " != " + e.binaryStr;
adc8ce12
IC
2484 }
2485 }
2486 }
2487 catch (e) {
2488 return e.message;
2489 }
2490 return "PASS";
c6624d51 2491 });
adc8ce12 2492 if (response != "PASS") {
c6624d51 2493 console.log("Entropy unit tests");
adc8ce12 2494 console.log(response);
c6624d51
IC
2495 fail();
2496 };
2497 next();
2498});
2499},
2500
2501// Entropy can be entered by the user
2502function() {
2503page.open(url, function(status) {
2504 expected = {
2505 mnemonic: "abandon abandon ability",
2506 address: "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug",
2507 }
2508 // use entropy
2509 page.evaluate(function() {
2510 $(".use-entropy").prop("checked", true).trigger("change");
2511 $(".entropy").val("00000000 00000000 00000000 00000000").trigger("input");
2512 });
2513 // check the mnemonic is set and address is correct
2514 waitForGenerate(function() {
2515 var actual = page.evaluate(function() {
2516 return {
2517 address: $(".address:first").text(),
2518 mnemonic: $(".phrase").val(),
2519 }
2520 });
2521 if (actual.mnemonic != expected.mnemonic) {
2522 console.log("Entropy does not generate correct mnemonic");
2523 console.log("Expected: " + expected.mnemonic);
2524 console.log("Got: " + actual.mnemonic);
2525 fail();
2526 }
2527 if (actual.address != expected.address) {
2528 console.log("Entropy does not generate correct address");
2529 console.log("Expected: " + expected.address);
2530 console.log("Got: " + actual.address);
2531 fail();
2532 }
2533 next();
2534 });
2535});
2536},
2537
2538// A warning about entropy is shown to the user, with additional information
2539function() {
2540page.open(url, function(status) {
2541 // get text content from entropy sections of page
2542 var hasWarning = page.evaluate(function() {
2543 var entropyText = $(".entropy-container").text();
2544 var warning = "mnemonic may be insecure";
2545 if (entropyText.indexOf(warning) == -1) {
2546 return false;
2547 }
2548 var readMoreText = $("#entropy-notes").parent().text();
2549 var goodSources = "flipping a fair coin, rolling a fair dice, noise measurements etc";
2550 if (readMoreText.indexOf(goodSources) == -1) {
2551 return false;
2552 }
2553 return true;
2554 });
2555 // check the warnings and information are shown
2556 if (!hasWarning) {
2557 console.log("Page does not contain warning about using own entropy");
2558 fail();
2559 }
2560 next();
2561});
2562},
2563
2564// The types of entropy available are described to the user
2565function() {
2566page.open(url, function(status) {
2567 // get placeholder text for entropy field
2568 var placeholder = page.evaluate(function() {
2569 return $(".entropy").attr("placeholder");
2570 });
2571 var options = [
2572 "binary",
2573 "base 6",
2574 "dice",
2575 "base 10",
2576 "hexadecimal",
439f0e25 2577 "cards",
c6624d51
IC
2578 ];
2579 for (var i=0; i<options.length; i++) {
2580 var option = options[i];
2581 if (placeholder.indexOf(option) == -1) {
2582 console.log("Available entropy type is not shown to user: " + option);
2583 fail();
2584 }
2585 }
2586 next();
2587});
2588},
2589
2590// The actual entropy used is shown to the user
2591function() {
2592page.open(url, function(status) {
2593 // use entropy
2594 var badEntropySource = page.evaluate(function() {
2595 var entropy = "Not A Very Good Entropy Source At All";
2596 $(".use-entropy").prop("checked", true).trigger("change");
2597 $(".entropy").val(entropy).trigger("input");
2598 });
2599 // check the actual entropy being used is shown
057722b0 2600 waitForEntropyFeedback(function() {
c6624d51
IC
2601 var expectedText = "AedEceAA";
2602 var entropyText = page.evaluate(function() {
2603 return $(".entropy-container").text();
2604 });
2605 if (entropyText.indexOf(expectedText) == -1) {
2606 console.log("Actual entropy used is not shown");
2607 fail();
2608 }
2609 next();
2610 });
2611});
2612},
2613
2614// Binary entropy can be entered
2615function() {
2616page.open(url, function(status) {
2617 // use entropy
2618 page.evaluate(function() {
2619 $(".use-entropy").prop("checked", true).trigger("change");
2620 $(".entropy").val("01").trigger("input");
2621 });
2622 // check the entropy is shown to be the correct type
057722b0 2623 waitForEntropyFeedback(function() {
c6624d51
IC
2624 var entropyText = page.evaluate(function() {
2625 return $(".entropy-container").text();
2626 });
2627 if (entropyText.indexOf("binary") == -1) {
2628 console.log("Binary entropy is not detected and presented to user");
2629 fail();
2630 }
2631 next();
2632 });
2633});
2634},
2635
2636// Base 6 entropy can be entered
2637function() {
2638page.open(url, function(status) {
2639 // use entropy
2640 page.evaluate(function() {
2641 $(".use-entropy").prop("checked", true).trigger("change");
2642 $(".entropy").val("012345").trigger("input");
2643 });
2644 // check the entropy is shown to be the correct type
057722b0 2645 waitForEntropyFeedback(function() {
c6624d51
IC
2646 var entropyText = page.evaluate(function() {
2647 return $(".entropy-container").text();
2648 });
2649 if (entropyText.indexOf("base 6") == -1) {
2650 console.log("Base 6 entropy is not detected and presented to user");
2651 fail();
2652 }
2653 next();
2654 });
2655});
2656},
2657
2658// Base 6 dice entropy can be entered
2659function() {
2660page.open(url, function(status) {
2661 // use entropy
2662 page.evaluate(function() {
2663 $(".use-entropy").prop("checked", true).trigger("change");
2664 $(".entropy").val("123456").trigger("input");
2665 });
2666 // check the entropy is shown to be the correct type
057722b0 2667 waitForEntropyFeedback(function() {
c6624d51
IC
2668 var entropyText = page.evaluate(function() {
2669 return $(".entropy-container").text();
2670 });
2671 if (entropyText.indexOf("dice") == -1) {
2672 console.log("Dice entropy is not detected and presented to user");
2673 fail();
2674 }
2675 next();
2676 });
2677});
2678},
2679
2680// Base 10 entropy can be entered
2681function() {
2682page.open(url, function(status) {
2683 // use entropy
2684 page.evaluate(function() {
2685 $(".use-entropy").prop("checked", true).trigger("change");
2686 $(".entropy").val("789").trigger("input");
2687 });
2688 // check the entropy is shown to be the correct type
057722b0 2689 waitForEntropyFeedback(function() {
c6624d51
IC
2690 var entropyText = page.evaluate(function() {
2691 return $(".entropy-container").text();
2692 });
2693 if (entropyText.indexOf("base 10") == -1) {
2694 console.log("Base 10 entropy is not detected and presented to user");
2695 fail();
2696 }
2697 next();
2698 });
2699});
2700},
2701
2702// Hexadecimal entropy can be entered
2703function() {
2704page.open(url, function(status) {
2705 // use entropy
2706 page.evaluate(function() {
2707 $(".use-entropy").prop("checked", true).trigger("change");
2708 $(".entropy").val("abcdef").trigger("input");
2709 });
2710 // check the entropy is shown to be the correct type
057722b0 2711 waitForEntropyFeedback(function() {
c6624d51
IC
2712 var entropyText = page.evaluate(function() {
2713 return $(".entropy-container").text();
2714 });
2715 if (entropyText.indexOf("hexadecimal") == -1) {
2716 console.log("Hexadecimal entropy is not detected and presented to user");
2717 fail();
2718 }
2719 next();
2720 });
2721});
2722},
2723
2724// Dice entropy value is shown as the converted base 6 value
2725function() {
2726page.open(url, function(status) {
2727 // use entropy
2728 page.evaluate(function() {
2729 $(".use-entropy").prop("checked", true).trigger("change");
2730 $(".entropy").val("123456").trigger("input");
2731 });
2732 // check the entropy is shown as base 6, not as the original dice value
057722b0 2733 waitForEntropyFeedback(function() {
c6624d51
IC
2734 var entropyText = page.evaluate(function() {
2735 return $(".entropy-container").text();
2736 });
425b75a9 2737 if (entropyText.indexOf("123450") == -1) {
c6624d51
IC
2738 console.log("Dice entropy is not shown to user as base 6 value");
2739 fail();
2740 }
2741 if (entropyText.indexOf("123456") > -1) {
2742 console.log("Dice entropy value is shown instead of true base 6 value");
2743 fail();
2744 }
2745 next();
2746 });
2747});
2748},
2749
2750// The number of bits of entropy accumulated is shown
2751function() {
2752page.open(url, function(status) {
057722b0
IC
2753 //[ entropy, bits ]
2754 var tests = [
2755 [ "0000 0000 0000 0000 0000", "20" ],
2756 [ "0", "1" ],
2757 [ "0000", "4" ],
0d0f07f9
IC
2758 [ "6", "2" ], // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
2759 [ "7", "3" ], // 7 in base 10 is 111 in base 2, no leading zeros
057722b0
IC
2760 [ "8", "4" ],
2761 [ "F", "4" ],
1cf1bbaf 2762 [ "29", "6" ],
057722b0
IC
2763 [ "0A", "8" ],
2764 [ "1A", "8" ], // hex is always multiple of 4 bits of entropy
2765 [ "2A", "8" ],
2766 [ "4A", "8" ],
2767 [ "8A", "8" ],
2768 [ "FA", "8" ],
2769 [ "000A", "16" ],
0d0f07f9
IC
2770 [ "5555", "11" ],
2771 [ "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)
1cf1bbaf 2772 [ "2227", "13" ], // Uses base 10, which is 4 lots of 3.32 bits, which is 13.3 bits (rounded down to 13)
057722b0
IC
2773 [ "222F", "16" ],
2774 [ "FFFF", "16" ],
1cf1bbaf 2775 [ "0000101017", "33" ], // 10 events at 3.32 bits per event
6422c1cd 2776 [ "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", "225" ], // cards are not replaced, so a full deck is not 52^52 entropy which is 296 bits, it's 52!, which is 225 bits
057722b0 2777 ]
c6624d51
IC
2778 // use entropy
2779 page.evaluate(function(e) {
2780 $(".use-entropy").prop("checked", true).trigger("change");
2781 });
2782 // Run each test
2783 var nextTest = function runNextTest(i) {
057722b0
IC
2784 var entropy = tests[i][0];
2785 var expected = tests[i][1];
c6624d51
IC
2786 // set entropy
2787 page.evaluate(function(e) {
c6624d51
IC
2788 $(".entropy").val(e).trigger("input");
2789 }, entropy);
2790 // check the number of bits of entropy is shown
057722b0 2791 waitForEntropyFeedback(function() {
c6624d51 2792 var entropyText = page.evaluate(function() {
dd944906 2793 return $(".entropy-container").text();
c6624d51 2794 });
1cf1bbaf 2795 if (entropyText.replace(/\s/g,"").indexOf("Bits" + expected) == -1) {
c6624d51
IC
2796 console.log("Accumulated entropy is not shown correctly for " + entropy);
2797 fail();
2798 }
057722b0 2799 var isLastTest = i == tests.length - 1;
c6624d51
IC
2800 if (isLastTest) {
2801 next();
2802 }
2803 else {
2804 runNextTest(i+1);
2805 }
2806 });
2807 }
2808 nextTest(0);
2809});
2810},
2811
fb353f9d 2812// There is feedback provided about the supplied entropy
c6624d51
IC
2813function() {
2814page.open(url, function(status) {
2815 var tests = [
2816 {
2817 entropy: "A",
fb353f9d
IC
2818 filtered: "A",
2819 type: "hexadecimal",
2820 events: 1,
2821 bits: 4,
c6624d51 2822 words: 0,
20f459ce 2823 strength: "less than a second",
c6624d51
IC
2824 },
2825 {
2826 entropy: "AAAAAAAA",
fb353f9d
IC
2827 filtered: "AAAAAAAA",
2828 type: "hexadecimal",
2829 events: 8,
2830 bits: 32,
c6624d51 2831 words: 3,
20f459ce 2832 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
c6624d51
IC
2833 },
2834 {
2835 entropy: "AAAAAAAA B",
fb353f9d
IC
2836 filtered: "AAAAAAAAB",
2837 type: "hexadecimal",
2838 events: 9,
2839 bits: 36,
c6624d51 2840 words: 3,
20f459ce 2841 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
c6624d51
IC
2842 },
2843 {
2844 entropy: "AAAAAAAA BBBBBBBB",
fb353f9d
IC
2845 filtered: "AAAAAAAABBBBBBBB",
2846 type: "hexadecimal",
2847 events: 16,
2848 bits: 64,
c6624d51 2849 words: 6,
20f459ce 2850 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
c6624d51
IC
2851 },
2852 {
2853 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
fb353f9d
IC
2854 filtered: "AAAAAAAABBBBBBBBCCCCCCCC",
2855 type: "hexadecimal",
2856 events: 24,
2857 bits: 96,
c6624d51 2858 words: 9,
20f459ce 2859 strength: "less than a second",
c6624d51
IC
2860 },
2861 {
2862 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
fb353f9d
IC
2863 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD",
2864 type: "hexadecimal",
2865 events: 32,
2866 bits: 128,
c6624d51 2867 words: 12,
20f459ce 2868 strength: "2 minutes",
e6a799cc
IC
2869 },
2870 {
2871 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA",
2872 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA",
2873 type: "hexadecimal",
2874 events: 32,
2875 bits: 128,
2876 words: 12,
20f459ce 2877 strength: "2 days",
c6624d51
IC
2878 },
2879 {
e6a799cc
IC
2880 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE",
2881 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE",
fb353f9d
IC
2882 type: "hexadecimal",
2883 events: 40,
2884 bits: 160,
c6624d51 2885 words: 15,
20f459ce 2886 strength: "3 years",
1cf1bbaf
IC
2887 },
2888 {
e6a799cc
IC
2889 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF",
2890 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF",
fb353f9d
IC
2891 type: "hexadecimal",
2892 events: 48,
2893 bits: 192,
1cf1bbaf 2894 words: 18,
20f459ce 2895 strength: "centuries",
fb353f9d 2896 },
391c7f26
IC
2897 {
2898 entropy: "7d",
2899 type: "card",
2900 events: 1,
2901 bits: 5,
2902 words: 0,
20f459ce 2903 strength: "less than a second",
391c7f26
IC
2904 },
2905 {
2906 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2907 type: "card (full deck)",
2908 events: 52,
6422c1cd
IC
2909 bits: 225,
2910 words: 21,
20f459ce 2911 strength: "centuries",
391c7f26
IC
2912 },
2913 {
2914 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d",
2915 type: "card (full deck, 1 duplicate: 3d)",
2916 events: 53,
87ad2c6e 2917 bits: 254,
6422c1cd 2918 words: 21,
20f459ce 2919 strength: "centuries",
391c7f26
IC
2920 },
2921 {
2922 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d",
bbc29c80 2923 type: "card (2 duplicates: 3d 4d, 1 missing: KS)",
391c7f26 2924 events: 53,
87ad2c6e 2925 bits: 254,
6422c1cd 2926 words: 21,
20f459ce 2927 strength: "centuries",
391c7f26
IC
2928 },
2929 {
2930 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d",
bbc29c80 2931 type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)",
391c7f26 2932 events: 53,
87ad2c6e
IC
2933 bits: 264,
2934 words: 24,
20f459ce 2935 strength: "centuries",
391c7f26 2936 },
9bc39377 2937 // Next test was throwing uncaught error in zxcvbn
6422c1cd 2938 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
9bc39377
IC
2939 {
2940 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2941 type: "card (full deck, 52 duplicates: ac 2c 3c...)",
2942 events: 104,
87ad2c6e
IC
2943 bits: 499,
2944 words: 45,
20f459ce 2945 strength: "centuries",
9bc39377 2946 },
5c653a12
IC
2947 // Case insensitivity to duplicate cards
2948 {
2949 entropy: "asAS",
2950 type: "card (1 duplicate: AS)",
2951 events: 2,
87ad2c6e 2952 bits: 9,
5c653a12 2953 words: 0,
20f459ce 2954 strength: "less than a second",
5c653a12
IC
2955 },
2956 {
2957 entropy: "ASas",
2958 type: "card (1 duplicate: as)",
2959 events: 2,
87ad2c6e 2960 bits: 9,
5c653a12 2961 words: 0,
20f459ce 2962 strength: "less than a second",
5c653a12 2963 },
bbc29c80
IC
2964 // Missing cards are detected
2965 {
2966 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2967 type: "card (1 missing: 9C)",
2968 events: 51,
87ad2c6e
IC
2969 bits: 221,
2970 words: 18,
20f459ce 2971 strength: "centuries",
bbc29c80
IC
2972 },
2973 {
2974 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2975 type: "card (2 missing: 9C 5D)",
2976 events: 50,
87ad2c6e
IC
2977 bits: 216,
2978 words: 18,
20f459ce 2979 strength: "centuries",
bbc29c80
IC
2980 },
2981 {
2982 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2983 type: "card (4 missing: 9C 5D QD...)",
2984 events: 48,
87ad2c6e 2985 bits: 208,
6422c1cd 2986 words: 18,
20f459ce 2987 strength: "centuries",
bbc29c80
IC
2988 },
2989 // More than six missing cards does not show message
2990 {
2991 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks",
2992 type: "card",
2993 events: 45,
87ad2c6e 2994 bits: 195,
6422c1cd 2995 words: 18,
20f459ce 2996 strength: "centuries",
bbc29c80 2997 },
fc7c248f
IC
2998 // Multiple decks of cards increases bits per event
2999 {
3000 entropy: "3d",
3001 events: 1,
3002 bits: 4,
3003 bitsPerEvent: 4.34,
3004 },
3005 {
3006 entropy: "3d3d",
3007 events: 2,
3008 bits: 9,
3009 bitsPerEvent: 4.80,
3010 },
3011 {
3012 entropy: "3d3d3d",
3013 events: 3,
3014 bits: 15,
3015 bitsPerEvent: 5.01,
3016 },
3017 {
3018 entropy: "3d3d3d3d",
3019 events: 4,
3020 bits: 20,
3021 bitsPerEvent: 5.14,
3022 },
3023 {
3024 entropy: "3d3d3d3d3d",
3025 events: 5,
3026 bits: 26,
3027 bitsPerEvent: 5.22,
3028 },
3029 {
3030 entropy: "3d3d3d3d3d3d",
3031 events: 6,
3032 bits: 31,
3033 bitsPerEvent: 5.28,
3034 },
3035 {
3036 entropy: "3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d",
3037 events: 33,
3038 bits: 184,
3039 bitsPerEvent: 5.59,
20f459ce 3040 strength: 'less than a second - Repeats like "abcabcabc" are only slightly harder to guess than "abc"',
fc7c248f 3041 },
c6624d51
IC
3042 ];
3043 // use entropy
3044 page.evaluate(function() {
3045 $(".use-entropy").prop("checked", true).trigger("change");
3046 });
3047 var nextTest = function runNextTest(i) {
fb353f9d 3048 function getFeedbackError(expected, actual) {
391c7f26 3049 if ("filtered" in expected && actual.indexOf(expected.filtered) == -1) {
fb353f9d
IC
3050 return "Filtered value not in feedback";
3051 }
fc7c248f 3052 if ("type" in expected && actual.indexOf(expected.type) == -1) {
fb353f9d
IC
3053 return "Entropy type not in feedback";
3054 }
fc7c248f 3055 if ("events" in expected && actual.indexOf(expected.events) == -1) {
fb353f9d
IC
3056 return "Event count not in feedback";
3057 }
fc7c248f 3058 if ("bits" in expected && actual.indexOf(expected.bits) == -1) {
fb353f9d
IC
3059 return "Bit count not in feedback";
3060 }
fc7c248f 3061 if ("strength" in expected && actual.indexOf(expected.strength) == -1) {
fb353f9d
IC
3062 return "Strength not in feedback";
3063 }
fc7c248f
IC
3064 if ("bitsPerEvent" in expected && actual.indexOf(expected.bitsPerEvent) == -1) {
3065 return "bitsPerEvent not in feedback";
3066 }
fb353f9d
IC
3067 return false;
3068 }
c6624d51
IC
3069 test = tests[i];
3070 page.evaluate(function(e) {
3071 $(".addresses").empty();
057722b0 3072 $(".phrase").val("");
c6624d51
IC
3073 $(".entropy").val(e).trigger("input");
3074 }, test.entropy);
fb353f9d 3075 waitForEntropyFeedback(function() {
c6624d51
IC
3076 var mnemonic = page.evaluate(function() {
3077 return $(".phrase").val();
3078 });
fb353f9d 3079 // Check mnemonic length
fc7c248f 3080 if ("words" in test && test.words == 0) {
fb353f9d
IC
3081 if (mnemonic.length > 0) {
3082 console.log("Mnemonic length for " + test.strength + " strength is not " + test.words);
87ad2c6e 3083 console.log("Entropy: " + test.entropy);
fb353f9d
IC
3084 console.log("Mnemonic: " + mnemonic);
3085 fail();
3086 }
3087 }
fc7c248f 3088 else if ("words" in test) {
fb353f9d
IC
3089 if (mnemonic.split(" ").length != test.words) {
3090 console.log("Mnemonic length for " + test.strength + " strength is not " + test.words);
87ad2c6e 3091 console.log("Entropy: " + test.entropy);
fb353f9d
IC
3092 console.log("Mnemonic: " + mnemonic);
3093 fail();
3094 }
3095 }
3096 // check feedback
3097 var feedback = page.evaluate(function() {
dd944906 3098 return $(".entropy-container").text();
fb353f9d
IC
3099 });
3100 var feedbackError = getFeedbackError(test, feedback);
3101 if (feedbackError) {
3102 console.log("Entropy feedback for " + test.entropy + " returned error");
3103 console.log(feedbackError);
c6624d51
IC
3104 fail();
3105 }
fb353f9d 3106 // Run next test
c6624d51
IC
3107 var isLastTest = i == tests.length - 1;
3108 if (isLastTest) {
3109 next();
3110 }
3111 else {
3112 runNextTest(i+1);
3113 }
fb353f9d 3114 });
c6624d51
IC
3115 }
3116 nextTest(0);
3117});
3118},
3119
d6fd8ebf 3120// Entropy is truncated from the left
c6624d51
IC
3121function() {
3122page.open(url, function(status) {
d6fd8ebf 3123 var expected = "avocado zoo zone";
c6624d51
IC
3124 // use entropy
3125 page.evaluate(function() {
3126 $(".use-entropy").prop("checked", true).trigger("change");
3127 var entropy = "00000000 00000000 00000000 00000000";
d6fd8ebf 3128 entropy += "11111111 11111111 11111111 1111"; // Missing last byte
c6624d51
IC
3129 $(".entropy").val(entropy).trigger("input");
3130 });
3131 // check the entropy is truncated from the right
3132 waitForGenerate(function() {
3133 var actual = page.evaluate(function() {
3134 return $(".phrase").val();
3135 });
3136 if (actual != expected) {
3137 console.log("Entropy is not truncated from the right");
3138 console.log("Expected: " + expected);
3139 console.log("Got: " + actual);
3140 fail();
3141 }
3142 next();
3143 });
3144});
3145},
3146
3147// Very large entropy results in very long mnemonics
3148function() {
3149page.open(url, function(status) {
3150 // use entropy
3151 page.evaluate(function() {
3152 $(".use-entropy").prop("checked", true).trigger("change");
3153 var entropy = "";
3154 // Generate a very long entropy string
3155 for (var i=0; i<33; i++) {
3156 entropy += "AAAAAAAA"; // 3 words * 33 iterations = 99 words
3157 }
3158 $(".entropy").val(entropy).trigger("input");
3159 });
3160 // check the mnemonic is very long
3161 waitForGenerate(function() {
3162 var wordCount = page.evaluate(function() {
3163 return $(".phrase").val().split(" ").length;
3164 });
3165 if (wordCount != 99) {
3166 console.log("Large entropy does not generate long mnemonic");
3167 console.log("Expected 99 words, got " + wordCount);
3168 fail();
3169 }
3170 next();
3171 });
3172});
3173},
3174
3175// Is compatible with bip32jp entropy
3176// https://bip32jp.github.io/english/index.html
3177// NOTES:
3178// Is incompatible with:
c6624d51
IC
3179// base 20
3180function() {
3181page.open(url, function(status) {
0d0f07f9 3182 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
3183 // use entropy
3184 page.evaluate(function() {
3185 $(".use-entropy").prop("checked", true).trigger("change");
0d0f07f9 3186 var entropy = "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543";
c6624d51
IC
3187 $(".entropy").val(entropy).trigger("input");
3188 });
3189 // check the mnemonic matches the expected value from bip32jp
3190 waitForGenerate(function() {
3191 var actual = page.evaluate(function() {
3192 return $(".phrase").val();
3193 });
3194 if (actual != expected) {
3195 console.log("Mnemonic does not match bip32jp for base 6 entropy");
3196 console.log("Expected: " + expected);
3197 console.log("Got: " + actual);
3198 fail();
3199 }
3200 next();
3201 });
3202});
3203},
3204
057722b0
IC
3205// Blank entropy does not generate mnemonic or addresses
3206function() {
3207page.open(url, function(status) {
3208 // use entropy
3209 page.evaluate(function() {
3210 $(".use-entropy").prop("checked", true).trigger("change");
3211 $(".entropy").val("").trigger("input");
3212 });
3213 waitForFeedback(function() {
3214 // check there is no mnemonic
3215 var phrase = page.evaluate(function() {
3216 return $(".phrase").val();
3217 });
3218 if (phrase != "") {
3219 console.log("Blank entropy does not result in blank mnemonic");
3220 console.log("Got: " + phrase);
3221 fail();
3222 }
3223 // check there are no addresses displayed
3224 var addresses = page.evaluate(function() {
3225 return $(".address").length;
3226 });
3227 if (addresses != 0) {
3228 console.log("Blank entropy does not result in zero addresses");
3229 fail();
3230 }
3231 // Check the feedback says 'blank entropy'
3232 var feedback = page.evaluate(function() {
3233 return $(".feedback").text();
3234 });
3235 if (feedback != "Blank entropy") {
3236 console.log("Blank entropy does not show feedback message");
3237 fail();
3238 }
3239 next();
3240 });
3241});
3242},
3243
3599674d
IC
3244// Mnemonic length can be selected even for weak entropy
3245function() {
3246page.open(url, function(status) {
3247 // use entropy
3248 page.evaluate(function() {
3249 $(".use-entropy").prop("checked", true).trigger("change");
3250 $(".entropy").val("012345");
3251 $(".mnemonic-length").val("18").trigger("change");
3252 });
3253 // check the mnemonic is the correct length
3254 waitForGenerate(function() {
3255 var phrase = page.evaluate(function() {
3256 return $(".phrase").val();
3257 });
3258 var numberOfWords = phrase.split(/\s/g).length;
3259 if (numberOfWords != 18) {
3260 console.log("Weak entropy cannot be overridden to give 18 word mnemonic");
3261 console.log(phrase);
3262 fail();
3263 }
3264 next();
3265 });
3266});
3267},
3268
886f06ee
IC
3269// Github issue 33
3270// https://github.com/iancoleman/bip39/issues/33
3271// Final cards should contribute entropy
3272function() {
3273page.open(url, function(status) {
3274 // use entropy
3275 page.evaluate(function() {
3276 $(".use-entropy").prop("checked", true).trigger("change");
3277 $(".entropy").val("7S 9H 9S QH 8C KS AS 7D 7C QD 4S 4D TC 2D 5S JS 3D 8S 8H 4C 3C AC 3S QC 9C JC 7H AD TD JD 6D KH 5C QS 2S 6S 6H JH KD 9D-6C TS TH 4H KC 5H 2H AH 2C 8D 3H 5D").trigger("input");
3278 });
3279 // get the mnemonic
3280 waitForGenerate(function() {
3281 var originalPhrase = page.evaluate(function() {
3282 return $(".phrase").val();
3283 });
3284 // Set the last 12 cards to be AS
3285 page.evaluate(function() {
3286 $(".addresses").empty();
3287 $(".entropy").val("7S 9H 9S QH 8C KS AS 7D 7C QD 4S 4D TC 2D 5S JS 3D 8S 8H 4C 3C AC 3S QC 9C JC 7H AD TD JD 6D KH 5C QS 2S 6S 6H JH KD 9D-AS AS AS AS AS AS AS AS AS AS AS AS").trigger("input");
3288 });
3289 // get the new mnemonic
3290 waitForGenerate(function() {
3291 var newPhrase = page.evaluate(function() {
3292 return $(".phrase").val();
3293 });
92e73fd9 3294 // check the phrase has changed
886f06ee
IC
3295 if (newPhrase == originalPhrase) {
3296 console.log("Changing last 12 cards does not change mnemonic");
3297 console.log("Original:");
3298 console.log(originalPhrase);
3299 console.log("New:");
3300 console.log(newPhrase);
3301 fail();
3302 }
3303 next();
3304 });
3305 });
3306});
3307},
3308
e00964cc
IC
3309// Github issue 35
3310// https://github.com/iancoleman/bip39/issues/35
3311// QR Code support
3312function() {
3313page.open(url, function(status) {
3314 // use entropy
3315 page.evaluate(function() {
3316 $(".generate").click();
3317 });
3318 waitForGenerate(function() {
3319 var p = page.evaluate(function() {
3320 // get position of mnemonic element
3321 return $(".phrase").offset();
3322 });
3323 p.top = Math.ceil(p.top);
3324 p.left = Math.ceil(p.left);
3325 // check the qr code shows
3326 page.sendEvent("mousemove", p.left+4, p.top+4);
3327 var qrShowing = page.evaluate(function() {
3328 return $(".qr-container").find("canvas").length > 0;
3329 });
3330 if (!qrShowing) {
3331 console.log("QR Code does not show");
3332 fail();
3333 }
3334 // check the qr code hides
3335 page.sendEvent("mousemove", p.left-4, p.top-4);
3336 var qrHidden = page.evaluate(function() {
3337 return $(".qr-container").find("canvas").length == 0;
3338 });
3339 if (!qrHidden) {
3340 console.log("QR Code does not hide");
3341 fail();
3342 }
3343 next();
3344 });
3345});
3346},
3347
c554e6ff
IC
3348// BIP44 account extendend private key is shown
3349// github issue 37 - compatibility with electrum
3350function() {
3351page.open(url, function(status) {
3352 // set the phrase
3353 var expected = "xprv9yzrnt4zWVJUr1k2VxSPy9ettKz5PpeDMgaVG7UKedhqnw1tDkxP2UyYNhuNSumk2sLE5ctwKZs9vwjsq3e1vo9egCK6CzP87H2cVYXpfwQ";
3354 page.evaluate(function() {
3355 $(".phrase").val("abandon abandon ability");
3356 $(".phrase").trigger("input");
3357 });
3358 // check the BIP44 account extended private key
3359 waitForGenerate(function() {
3360 var actual = page.evaluate(function() {
684624b5 3361 return $("#bip44 .account-xprv").val();
c554e6ff
IC
3362 });
3363 if (actual != expected) {
3364 console.log("BIP44 account extended private key is incorrect");
3365 console.log("Expected: " + expected);
3366 console.log("Actual: " + actual);
3367 fail();
3368 }
3369 next();
3370 });
3371});
3372},
3373
3374// BIP44 account extendend public key is shown
3375// github issue 37 - compatibility with electrum
3376function() {
3377page.open(url, function(status) {
3378 // set the phrase
3379 var expected = "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3380 page.evaluate(function() {
3381 $(".phrase").val("abandon abandon ability");
3382 $(".phrase").trigger("input");
3383 });
3384 // check the BIP44 account extended public key
3385 waitForGenerate(function() {
3386 var actual = page.evaluate(function() {
684624b5 3387 return $("#bip44 .account-xpub").val();
c554e6ff
IC
3388 });
3389 if (actual != expected) {
3390 console.log("BIP44 account extended public key is incorrect");
3391 console.log("Expected: " + expected);
ba3cb9ec
IC
3392 console.log("Actual: " + actual);
3393 fail();
3394 }
3395 next();
3396 });
3397});
3398},
3399
3400// github issue 40
3401// BIP32 root key can be set as an xpub
3402function() {
3403page.open(url, function(status) {
3404 // set the phrase
3405 page.evaluate(function() {
3406 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3407 var bip44AccountXpub = "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3408 $("#root-key").val(bip44AccountXpub);
3409 $("#root-key").trigger("input");
3410 });
3411 waitForFeedback(function() {
3412 page.evaluate(function() {
3413 // Use bip32 tab
3414 $("#bip32-tab a").click();
3415 });
3416 waitForGenerate(function() {
3417 page.evaluate(function() {
3418 // derive external addresses for this xpub
3419 var firstAccountDerivationPath = "m/0";
3420 $("#bip32-path").val(firstAccountDerivationPath);
3421 $("#bip32-path").trigger("input");
3422 });
3423 waitForGenerate(function() {
3424 // check the addresses are generated
3425 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3426 var actual = page.evaluate(function() {
3427 return $(".address:first").text();
3428 });
3429 if (actual != expected) {
3430 console.log("xpub key does not generate addresses in table");
3431 console.log("Expected: " + expected);
3432 console.log("Actual: " + actual);
3433 fail();
3434 }
3435 // check the xprv key is not set
3436 var expected = "NA";
3437 var actual = page.evaluate(function() {
3438 return $(".extended-priv-key").val();
3439 });
3440 if (actual != expected) {
3441 console.log("xpub key as root shows derived bip32 xprv key");
3442 console.log("Expected: " + expected);
3443 console.log("Actual: " + actual);
3444 fail();
3445 }
3446 // check the private key is not set
3447 var expected = "NA";
3448 var actual = page.evaluate(function() {
3449 return $(".privkey:first").text();
3450 });
3451 if (actual != expected) {
3452 console.log("xpub key generates private key in addresses table");
3453 console.log("Expected: " + expected);
3454 console.log("Actual: " + actual);
3455 fail();
3456 }
3457 next();
3458 });
3459 });
3460 });
3461});
3462},
3463
3464// github issue 40
3465// xpub for bip32 root key will not work with hardened derivation paths
3466function() {
3467page.open(url, function(status) {
3468 // set the phrase
3469 page.evaluate(function() {
3470 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3471 var bip44AccountXpub = "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3472 $("#root-key").val(bip44AccountXpub);
3473 $("#root-key").trigger("input");
3474 });
3475 waitForFeedback(function() {
3476 // Check feedback is correct
3477 var expected = "Hardened derivation path is invalid with xpub key";
3478 var actual = page.evaluate(function() {
3479 return $(".feedback").text();
3480 });
3481 if (actual != expected) {
3482 console.log("xpub key with hardened derivation path does not show feedback");
3483 console.log("Expected: " + expected);
3484 console.log("Actual: " + actual);
3485 fail();
3486 }
3487 // Check no addresses are shown
3488 var expected = 0;
3489 var actual = page.evaluate(function() {
3490 return $(".addresses tr").length;
3491 });
3492 if (actual != expected) {
3493 console.log("addresses still show after setting xpub key with hardened derivation path");
3494 console.log("Expected: " + expected);
c554e6ff
IC
3495 console.log("Actual: " + actual);
3496 fail();
3497 }
3498 next();
3499 });
3500});
3501},
3502
0a1f0259
IC
3503// github issue 39
3504// no root key shows feedback
3505function() {
3506page.open(url, function(status) {
3507 // click the bip32 tab on fresh page
3508 page.evaluate(function() {
3509 $("#bip32-tab a").click();
3510 });
3511 waitForFeedback(function() {
3512 // Check feedback is correct
3513 var expected = "No root key";
3514 var actual = page.evaluate(function() {
3515 return $(".feedback").text();
3516 });
3517 if (actual != expected) {
3518 console.log("Blank root key not detected");
3519 console.log("Expected: " + expected);
3520 console.log("Actual: " + actual);
3521 fail();
3522 }
3523 next();
3524 });
3525});
3526},
886f06ee 3527
40892aba
IC
3528// Github issue 44
3529// display error switching tabs while addresses are generating
3530function() {
3531page.open(url, function(status) {
3532 // set the phrase
3533 page.evaluate(function() {
3534 $(".phrase").val("abandon abandon ability").trigger("input");
3535 });
3536 waitForGenerate(function() {
3537 // set to generate 500 more addresses
3538 // generate more addresses
3539 // change tabs which should cancel the previous generating
3540 page.evaluate(function() {
3541 $(".rows-to-add").val("100");
3542 $(".more").click();
3543 $("#bip32-tab a").click();
3544 });
3545 // check the derivation paths are in order and of the right quantity
3546 waitForGenerate(function() {
3547 var paths = page.evaluate(function() {
3548 return $(".index").map(function(i, e) {
3549 return $(e).text();
3550 });
3551 });
3552 for (var i=0; i<paths.length; i++) {
3553 var expected = "m/0/" + i;
3554 var actual = paths[i];
3555 if (actual != expected) {
3556 console.log("Path " + i + " is not in correct order");
3557 console.log("Expected: " + expected);
3558 console.log("Actual: " + actual);
3559 fail();
3560 }
3561 }
3562 if (paths.length != 20) {
3563 console.log("Generation was not cancelled by new action");
3564 fail();
3565 }
3566 next();
3567 });
3568 });
3569});
3570},
3571
53aaab27
IC
3572// Github issue 49
3573// padding for binary should give length with multiple of 256
3574// hashed entropy 1111 is length 252, so requires 4 leading zeros
3575// prior to issue 49 it would only generate 2 leading zeros, ie missing 2
3576function() {
3577page.open(url, function(status) {
3578 expected = "avocado valid quantum cross link predict excuse edit street able flame large galaxy ginger nuclear"
3579 // use entropy
3580 page.evaluate(function() {
3581 $(".use-entropy").prop("checked", true).trigger("change");
5ed50bd9 3582 $(".mnemonic-length").val("15");
53aaab27
IC
3583 $(".entropy").val("1111").trigger("input");
3584 });
3585 waitForGenerate(function() {
3586 // get the mnemonic
3587 var actual = page.evaluate(function() {
3588 return $(".phrase").val();
3589 });
3590 // check the mnemonic is correct
3591 if (actual != expected) {
3592 console.log("Left padding error for entropy");
3593 console.log("Expected: " + expected);
3594 console.log("Actual: " + actual);
3595 fail();
3596 }
3597 next();
3598 });
3599});
3600},
3601
cdfaaf00
IC
3602// Github pull request 55
3603// https://github.com/iancoleman/bip39/pull/55
3604// Client select
3605function() {
3606page.open(url, function(status) {
3607 // set mnemonic and select bip32 tab
3608 page.evaluate(function() {
3609 $("#bip32-tab a").click();
3610 $(".phrase").val("abandon abandon ability").trigger("input");
3611 });
3612 waitForGenerate(function() {
3613 // BITCOIN CORE
3614 // set bip32 client to bitcoin core
3615 page.evaluate(function() {
3616 var bitcoinCoreIndex = "0";
3617 $("#bip32-client").val(bitcoinCoreIndex).trigger("change");
3618 });
3619 waitForGenerate(function() {
3620 // get the derivation path
3621 var actual = page.evaluate(function() {
3622 return $("#bip32-path").val();
3623 });
3624 // check the derivation path is correct
3625 expected = "m/0'/0'"
3626 if (actual != expected) {
3627 console.log("Selecting Bitcoin Core client does not set correct derivation path");
3628 console.log("Expected: " + expected);
3629 console.log("Actual: " + actual);
3630 fail();
3631 }
3632 // get hardened addresses
3633 var usesHardenedAddresses = page.evaluate(function() {
3634 return $(".hardened-addresses").prop("checked");
3635 });
3636 // check hardened addresses is selected
3637 if(!usesHardenedAddresses) {
3638 console.log("Selecting Bitcoin Core client does not use hardened addresses");
3639 fail();
3640 }
3641 // check input is readonly
3642 var pathIsReadonly = page.evaluate(function() {
3643 return $("#bip32-path").prop("readonly");
3644 });
3645 if (!pathIsReadonly) {
3646 console.log("Selecting Bitcoin Core client does not set derivation path to readonly");
3647 fail();
3648 }
3649 // MULTIBIT
3650 // set bip32 client to multibit
3651 page.evaluate(function() {
3652 var multibitIndex = "2";
3653 $("#bip32-client").val(multibitIndex).trigger("change");
3654 });
3655 waitForGenerate(function() {
3656 // get the derivation path
3657 var actual = page.evaluate(function() {
3658 return $("#bip32-path").val();
3659 });
3660 // check the derivation path is correct
3661 expected = "m/0'/0"
3662 if (actual != expected) {
3663 console.log("Selecting Multibit client does not set correct derivation path");
3664 console.log("Expected: " + expected);
3665 console.log("Actual: " + actual);
3666 fail();
3667 }
3668 // get hardened addresses
3669 var usesHardenedAddresses = page.evaluate(function() {
3670 return $(".hardened-addresses").prop("checked");
3671 });
3672 // check hardened addresses is selected
3673 if(usesHardenedAddresses) {
3674 console.log("Selecting Multibit client does not uncheck hardened addresses");
3675 fail();
3676 }
3677 // CUSTOM DERIVATION PATH
3678 // check input is not readonly
3679 page.evaluate(function() {
3680 $("#bip32-client").val("custom").trigger("change");
3681 });
3682 // do not wait for generate, since there is no change to the
3683 // derivation path there is no new generation performed
3684 var pathIsReadonly = page.evaluate(function() {
3685 return $("#bip32-path").prop("readonly");
3686 });
3687 if (pathIsReadonly) {
3688 console.log("Selecting Custom Derivation Path does not allow derivation path input");
3689 fail();
3690 }
3691 next();
3692 });
3693 });
3694 });
3695});
3696},
3697
e6f08c6a
IC
3698// github issue 58
3699// https://github.com/iancoleman/bip39/issues/58
3700// bip32 derivation is correct, does not drop leading zeros
3701// see also
3702// https://medium.com/@alexberegszaszi/why-do-my-bip32-wallets-disagree-6f3254cc5846
3703function() {
3704page.open(url, function(status) {
3705 // set the phrase and passphrase
3706 var expected = "17rxURoF96VhmkcEGCj5LNQkmN9HVhWb7F";
3707 // Note that bitcore generates an incorrect address
3708 // 13EuKhffWkBE2KUwcbkbELZb1MpzbimJ3Y
3709 // see the medium.com link above for more details
3710 page.evaluate(function() {
3711 $(".phrase").val("fruit wave dwarf banana earth journey tattoo true farm silk olive fence");
3712 $(".passphrase").val("banana").trigger("input");
3713 });
3714 // check the address is generated correctly
3715 waitForGenerate(function() {
3716 var actual = page.evaluate(function() {
3717 return $(".address:first").text();
3718 });
3719 if (actual != expected) {
3720 console.log("BIP32 derivation is incorrect");
3721 console.log("Expected: " + expected);
d2d98ef7
IC
3722 console.log("Actual: " + actual);
3723 fail();
3724 }
3725 next();
3726 });
3727});
3728},
3729
3730
3731// github issue 60
3732// Japanese mnemonics generate incorrect bip32 seed
3733// BIP39 seed is set from phrase
3734function() {
3735page.open(url, function(status) {
3736 // set the phrase
3737 var expected = "a262d6fb6122ecf45be09c50492b31f92e9beb7d9a845987a02cefda57a15f9c467a17872029a9e92299b5cbdf306e3a0ee620245cbd508959b6cb7ca637bd55";
3738 page.evaluate(function() {
3739 $(".phrase").val("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら");
3740 $("#passphrase").val("メートルガバヴァぱばぐゞちぢ十人十色");
3741 $("#passphrase").trigger("input");
3742 });
3743 // check the seed is generated correctly
3744 waitForGenerate(function() {
3745 var actual = page.evaluate(function() {
3746 return $(".seed").val();
3747 });
3748 if (actual != expected) {
3749 console.log("BIP39 seed is incorrectly generated from Japanese mnemonic");
3750 console.log("Expected: " + expected);
e6f08c6a
IC
3751 console.log("Actual: " + actual);
3752 fail();
3753 }
3754 next();
3755 });
3756});
3757},
3758
6c08f364
IC
3759// BIP49 official test vectors
3760// https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki#test-vectors
3761function() {
3762page.open(url, function(status) {
3763 // set the phrase and select bitcoin testnet
3764 var expected = "2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2";
3765 page.evaluate(function() {
3766 $("#bip49-tab a").click();
3767 $(".phrase").val("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about");
3768 $(".network option[selected]").removeAttr("selected");
3769 $(".network option").filter(function() {
3770 return $(this).html() == "BTC - Bitcoin Testnet";
3771 }).prop("selected", true);
3772 $(".network").trigger("change");
3773 $(".phrase").trigger("input");
3774 });
3775 // check the first address
3776 waitForGenerate(function() {
3777 var actual = page.evaluate(function() {
3778 return $(".address:first").text();
3779 });
3780 if (actual != expected) {
3781 console.log("BIP49 address is incorrect");
3782 console.log("Expected: " + expected);
3783 console.log("Actual: " + actual);
3784 fail();
3785 }
3786 next();
3787 });
3788});
3789},
3790
3791// BIP49 derivation path is shown
3792function() {
3793page.open(url, function(status) {
3794 // set the phrase
3795 var expected = "m/49'/0'/0'/0";
3796 page.evaluate(function() {
3797 $("#bip49-tab a").click();
3798 $(".phrase").val("abandon abandon ability").trigger("input");
3799 });
3800 // check the derivation path of the first address
3801 waitForGenerate(function() {
3802 var actual = page.evaluate(function() {
3803 return $("#bip49 .path").val();
3804 });
3805 if (actual != expected) {
3806 console.log("BIP49 derivation path is incorrect");
3807 console.log("Expected: " + expected);
3808 console.log("Actual: " + actual);
3809 fail();
3810 }
3811 next();
3812 });
3813});
3814},
3815
3816// BIP49 extended private key is shown
3817function() {
3818page.open(url, function(status) {
3819 // set the phrase
3820 var expected = "xprvA1hukYsW7QfX9CVsaDAKde4eryajKa4DKWb6m9YjSnqkiZHrahFwwTJfEQTwBQ5kptWT5pZMkkusT1oK8dc1efQ8VFfq4SLSPAWd7Cpt423";
3821 page.evaluate(function() {
3822 $("#bip49-tab a").click();
3823 $(".phrase").val("abandon abandon ability").trigger("input");
3824 });
3825 // check the BIP49 extended private key
3826 waitForGenerate(function() {
3827 var actual = page.evaluate(function() {
3828 return $(".extended-priv-key").val();
3829 });
3830 if (actual != expected) {
3831 console.log("BIP49 extended private key is incorrect");
3832 console.log("Expected: " + expected);
3833 console.log("Actual: " + actual);
3834 fail();
3835 }
3836 next();
3837 });
3838});
3839},
3840
3841// BIP49 extended public key is shown
3842function() {
3843page.open(url, function(status) {
3844 // set the phrase
3845 var expected = "xpub6EhGA4QPwnDpMgaLgEhKzn1PR1RDj2n4gjWhZXxM18NjbMd18EaCVFd95gkLARJaBD2rXAYJED2gdkUbGn1KkrSzCKR554AdABUELoainnt";
3846 page.evaluate(function() {
3847 $("#bip49-tab a").click();
3848 $(".phrase").val("abandon abandon ability").trigger("input");
3849 });
3850 // check the BIP49 extended public key
3851 waitForGenerate(function() {
3852 var actual = page.evaluate(function() {
3853 return $(".extended-pub-key").val();
3854 });
3855 if (actual != expected) {
3856 console.log("BIP49 extended public key is incorrect");
3857 console.log("Expected: " + expected);
3858 console.log("Actual: " + actual);
3859 fail();
3860 }
3861 next();
3862 });
3863});
3864},
3865
3866// BIP49 account field changes address list
3867function() {
3868page.open(url, function(status) {
3869 // set the phrase
3870 var expected = "381wg1GGN4rP88rNC9v7QWsiww63yLVPsn";
3871 page.evaluate(function() {
3872 $("#bip49-tab a").click();
3873 $(".phrase").val("abandon abandon ability").trigger("input");
3874 });
3875 waitForGenerate(function() {
3876 // change the bip49 account field to 1
3877 page.evaluate(function() {
3878 $("#bip49 .account").val("1");
3879 $("#bip49 .account").trigger("input");
3880 });
3881 waitForGenerate(function() {
3882 // check the address for the new derivation path
3883 var actual = page.evaluate(function() {
3884 return $(".address:first").text();
3885 });
3886 if (actual != expected) {
3887 console.log("BIP49 account field generates incorrect address");
3888 console.log("Expected: " + expected);
3889 console.log("Actual: " + actual);
3890 fail();
3891 }
3892 next();
3893 });
3894 });
3895});
3896},
3897
3898// BIP49 change field changes address list
3899function() {
3900page.open(url, function(status) {
3901 // set the phrase
3902 var expected = "3PEM7MiKed5konBoN66PQhK8r3hjGhy9dT";
3903 page.evaluate(function() {
3904 $("#bip49-tab a").click();
3905 $(".phrase").val("abandon abandon ability").trigger("input");
3906 });
3907 waitForGenerate(function() {
3908 // change the bip49 change field to 1
3909 page.evaluate(function() {
3910 $("#bip49 .change").val("1");
3911 $("#bip49 .change").trigger("input");
3912 });
3913 waitForGenerate(function() {
3914 // check the address for the new derivation path
3915 var actual = page.evaluate(function() {
3916 return $(".address:first").text();
3917 });
3918 if (actual != expected) {
3919 console.log("BIP49 change field generates incorrect address");
3920 console.log("Expected: " + expected);
3921 console.log("Actual: " + actual);
3922 fail();
3923 }
3924 next();
3925 });
3926 });
3927});
3928},
3929
3930// BIP49 account extendend private key is shown
3931function() {
3932page.open(url, function(status) {
3933 // set the phrase
3934 var expected = "xprv9y3uhgQbfQZbj3o98nfgLDwGGuCJjUn7GKArSAZXjKgMjSdYHjQmTyf78s22g6jsGrxXvHB6HJeFyvFSPkuYZajeTGMZVXV6aNLWw2fagCn";
3935 page.evaluate(function() {
3936 $("#bip49-tab a").click();
3937 $(".phrase").val("abandon abandon ability");
3938 $(".phrase").trigger("input");
3939 });
3940 // check the BIP49 account extended private key
3941 waitForGenerate(function() {
3942 var actual = page.evaluate(function() {
3943 return $("#bip49 .account-xprv").val();
3944 });
3945 if (actual != expected) {
3946 console.log("BIP49 account extended private key is incorrect");
3947 console.log("Expected: " + expected);
3948 console.log("Actual: " + actual);
3949 fail();
3950 }
3951 next();
3952 });
3953});
3954},
3955
3956// BIP49 account extendend public key is shown
3957function() {
3958page.open(url, function(status) {
3959 // set the phrase
3960 var expected = "xpub6C3G7BwVVn7twXscEpCghMszpw2o8wVxdY6TEYy9HfDLcExgqGj21myazAiq6HSmW2F1cBiFqJa3D1cqcDpSh8pbZF5x4iqpd4PyJvd3gjB";
3961 page.evaluate(function() {
3962 $("#bip49-tab a").click();
3963 $(".phrase").val("abandon abandon ability");
3964 $(".phrase").trigger("input");
3965 });
3966 // check the BIP49 account extended public key
3967 waitForGenerate(function() {
3968 var actual = page.evaluate(function() {
3969 return $("#bip49 .account-xpub").val();
3970 });
3971 if (actual != expected) {
3972 console.log("BIP49 account extended public key is incorrect");
3973 console.log("Expected: " + expected);
3974 console.log("Actual: " + actual);
3975 fail();
3976 }
3977 next();
3978 });
3979});
3980},
3981
3982// Test selecting coin where bip49 is unavailable (eg CLAM)
3983function() {
3984page.open(url, function(status) {
3985 // set the phrase
3986 page.evaluate(function() {
3987 $("#bip49-tab a").click();
3988 $(".phrase").val("abandon abandon ability");
3989 $(".phrase").trigger("input");
3990 });
3991 waitForGenerate(function() {
3992 // select non-bip49 network, ie CLAM network
3993 page.evaluate(function() {
3994 $(".network option[selected]").removeAttr("selected");
3995 $(".network option").filter(function() {
3996 return $(this).html() == "CLAM - Clams";
3997 }).prop("selected", true);
3998 $(".network").trigger("change");
3999 });
4000 // check the BIP49 error is shown
4001 var bip49ErrorShown = page.evaluate(function() {
4002 var bip49hidden = $("#bip49 .available").hasClass("hidden");
4003 bip49hidden = bip49hidden && !($("#bip49 .unavailable").hasClass("hidden"));
4004 return bip49hidden;
4005 });
4006 if (!bip49ErrorShown) {
4007 console.log("BIP49 error not shown for non-bip49 network");
4008 fail();
4009 }
4010 // check there are no addresses shown
4011 var addressCount = page.evaluate(function() {
4012 return $(".address").length;
4013 });
4014 if (addressCount != 0) {
4015 console.log("BIP49 address count for non-bip49 network is " + addressCount);
4016 fail();
4017 }
4018 // check the derived keys are blank
4019 var areBlank = page.evaluate(function() {
4020 var prvKeyIsBlank = $(".extended-priv-key").val().length == 0;
4021 var pubKeyIsBlank = $(".extended-pub-key").val().length == 0;
4022 return prvKeyIsBlank && pubKeyIsBlank;
4023 });
4024 if (!areBlank) {
4025 console.log("BIP49 extended keys for non-bip49 network are not blank ");
4026 fail();
4027 }
4028 next();
4029 });
4030});
4031},
4032
b0fb45b9
IC
4033// If you wish to add more tests, do so here...
4034
4035// Here is a blank test template
4036/*
4037
4038function() {
4039page.open(url, function(status) {
4040 // Do something on the page
4041 page.evaluate(function() {
4042 $(".phrase").val("abandon abandon ability").trigger("input");
4043 });
4044 waitForGenerate(function() {
4045 // Check the result of doing the thing
4046 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
4047 var actual = page.evaluate(function() {
4048 return $(".address:first").text();
4049 });
4050 if (actual != expected) {
4051 console.log("A specific message about what failed");
4052 console.log("Expected: " + expected);
4053 console.log("Actual: " + actual);
4054 fail();
4055 }
4056 // Run the next test
4057 next();
4058 });
4059});
4060},
4061
4062*/
4063
88e2cdaa
IC
4064];
4065
4066console.log("Running tests...");
fb372687 4067tests = shuffle(tests);
88e2cdaa 4068next();