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