]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blame - tests.js
Ethereum address table is populated
[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';
0c6777ee 7var testMaxTime = 10000;
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");
283 $(".network option[value=1]").prop("selected", true);
284 $(".network").trigger("change");
285 });
286 // check the address is generated correctly
3eef9d0d 287 waitForGenerate(function() {
54563907
IC
288 var actual = page.evaluate(function() {
289 return $(".address:first").text();
290 });
291 if (actual != expected) {
292 console.log("Bitcoin testnet address is incorrect");
293 console.log("Expected: " + expected);
294 console.log("Actual: " + actual);
295 fail();
296 }
297 next();
3eef9d0d 298 });
54563907
IC
299});
300},
301
88e2cdaa 302// Network can be set to litecoin
59193779
IC
303function() {
304page.open(url, function(status) {
305 // set the phrase and coin
306 var expected = "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn";
307 page.evaluate(function() {
308 $(".phrase").val("abandon abandon ability");
309 $(".phrase").trigger("input");
310 $(".network option[selected]").removeAttr("selected");
311 $(".network option[value=2]").prop("selected", true);
312 $(".network").trigger("change");
313 });
314 // check the address is generated correctly
3eef9d0d 315 waitForGenerate(function() {
59193779
IC
316 var actual = page.evaluate(function() {
317 return $(".address:first").text();
318 });
319 if (actual != expected) {
320 console.log("Litecoin address is incorrect");
321 console.log("Expected: " + expected);
322 console.log("Actual: " + actual);
323 fail();
324 }
325 next();
3eef9d0d 326 });
59193779
IC
327});
328},
329
88e2cdaa 330// Network can be set to dogecoin
59193779
IC
331function() {
332page.open(url, function(status) {
333 // set the phrase and coin
334 var expected = "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA";
335 page.evaluate(function() {
336 $(".phrase").val("abandon abandon ability");
337 $(".phrase").trigger("input");
338 $(".network option[selected]").removeAttr("selected");
339 $(".network option[value=3]").prop("selected", true);
340 $(".network").trigger("change");
341 });
342 // check the address is generated correctly
3eef9d0d 343 waitForGenerate(function() {
59193779
IC
344 var actual = page.evaluate(function() {
345 return $(".address:first").text();
346 });
347 if (actual != expected) {
348 console.log("Dogecoin address is incorrect");
349 console.log("Expected: " + expected);
350 console.log("Actual: " + actual);
351 fail();
352 }
353 next();
3eef9d0d 354 });
59193779
IC
355});
356},
357
88e2cdaa 358// Network can be set to shadowcash
59193779
IC
359function() {
360page.open(url, function(status) {
361 // set the phrase and coin
362 var expected = "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG";
363 page.evaluate(function() {
364 $(".phrase").val("abandon abandon ability");
365 $(".phrase").trigger("input");
366 $(".network option[selected]").removeAttr("selected");
367 $(".network option[value=4]").prop("selected", true);
368 $(".network").trigger("change");
369 });
370 // check the address is generated correctly
3eef9d0d 371 waitForGenerate(function() {
59193779
IC
372 var actual = page.evaluate(function() {
373 return $(".address:first").text();
374 });
375 if (actual != expected) {
376 console.log("Shadowcash address is incorrect");
377 console.log("Expected: " + expected);
378 console.log("Actual: " + actual);
379 fail();
380 }
381 next();
3eef9d0d 382 });
59193779
IC
383});
384},
385
88e2cdaa 386// Network can be set to shadowcash testnet
59193779
IC
387function() {
388page.open(url, function(status) {
389 // set the phrase and coin
390 var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
391 page.evaluate(function() {
392 $(".phrase").val("abandon abandon ability");
393 $(".phrase").trigger("input");
394 $(".network option[selected]").removeAttr("selected");
395 $(".network option[value=5]").prop("selected", true);
396 $(".network").trigger("change");
397 });
398 // check the address is generated correctly
3eef9d0d 399 waitForGenerate(function() {
59193779
IC
400 var actual = page.evaluate(function() {
401 return $(".address:first").text();
402 });
403 if (actual != expected) {
404 console.log("Shadowcash testnet address is incorrect");
405 console.log("Expected: " + expected);
406 console.log("Actual: " + actual);
407 fail();
408 }
409 next();
3eef9d0d 410 });
59193779
IC
411});
412},
413
88e2cdaa 414// Network can be set to viacoin
59193779
IC
415function() {
416page.open(url, function(status) {
417 // set the phrase and coin
418 var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
419 page.evaluate(function() {
420 $(".phrase").val("abandon abandon ability");
421 $(".phrase").trigger("input");
422 $(".network option[selected]").removeAttr("selected");
423 $(".network option[value=6]").prop("selected", true);
424 $(".network").trigger("change");
425 });
426 // check the address is generated correctly
3eef9d0d 427 waitForGenerate(function() {
59193779
IC
428 var actual = page.evaluate(function() {
429 return $(".address:first").text();
430 });
431 if (actual != expected) {
432 console.log("Viacoin address is incorrect");
433 console.log("Expected: " + expected);
434 console.log("Actual: " + actual);
435 fail();
436 }
437 next();
3eef9d0d 438 });
59193779
IC
439});
440},
441
88e2cdaa 442// Network can be set to viacoin testnet
59193779
IC
443function() {
444page.open(url, function(status) {
445 // set the phrase and coin
446 var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
447 page.evaluate(function() {
448 $(".phrase").val("abandon abandon ability");
449 $(".phrase").trigger("input");
450 $(".network option[selected]").removeAttr("selected");
451 $(".network option[value=7]").prop("selected", true);
452 $(".network").trigger("change");
453 });
454 // check the address is generated correctly
3eef9d0d 455 waitForGenerate(function() {
59193779
IC
456 var actual = page.evaluate(function() {
457 return $(".address:first").text();
458 });
459 if (actual != expected) {
460 console.log("Viacoin testnet address is incorrect");
461 console.log("Expected: " + expected);
462 console.log("Actual: " + actual);
463 fail();
464 }
465 next();
3eef9d0d 466 });
59193779
IC
467});
468},
469
88e2cdaa 470// Network can be set to jumbucks
59193779
IC
471function() {
472page.open(url, function(status) {
473 // set the phrase and coin
474 var expected = "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew";
475 page.evaluate(function() {
476 $(".phrase").val("abandon abandon ability");
477 $(".phrase").trigger("input");
478 $(".network option[selected]").removeAttr("selected");
479 $(".network option[value=8]").prop("selected", true);
480 $(".network").trigger("change");
481 });
482 // check the address is generated correctly
3eef9d0d 483 waitForGenerate(function() {
59193779
IC
484 var actual = page.evaluate(function() {
485 return $(".address:first").text();
486 });
487 if (actual != expected) {
488 console.log("Jumbucks address is incorrect");
489 console.log("Expected: " + expected);
490 console.log("Actual: " + actual);
491 fail();
492 }
493 next();
3eef9d0d 494 });
59193779
IC
495});
496},
497
88e2cdaa 498// Network can be set to clam
59193779
IC
499function() {
500page.open(url, function(status) {
501 // set the phrase and coin
502 var expected = "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y";
503 page.evaluate(function() {
504 $(".phrase").val("abandon abandon ability");
505 $(".phrase").trigger("input");
506 $(".network option[selected]").removeAttr("selected");
507 $(".network option[value=9]").prop("selected", true);
508 $(".network").trigger("change");
509 });
510 // check the address is generated correctly
3eef9d0d 511 waitForGenerate(function() {
59193779
IC
512 var actual = page.evaluate(function() {
513 return $(".address:first").text();
514 });
515 if (actual != expected) {
516 console.log("CLAM address is incorrect");
517 console.log("Expected: " + expected);
518 console.log("Actual: " + actual);
519 fail();
f3fad1b5
IC
520 }
521 next();
522 });
523});
524},
525
526// Network can be set to dash
527function() {
528page.open(url, function(status) {
529 // set the phrase and coin
530 var expected = "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT";
531 page.evaluate(function() {
532 $(".phrase").val("abandon abandon ability");
533 $(".phrase").trigger("input");
534 $(".network option[selected]").removeAttr("selected");
535 $(".network option[value=10]").prop("selected", true);
536 $(".network").trigger("change");
537 });
538 // check the address is generated correctly
539 waitForGenerate(function() {
540 var actual = page.evaluate(function() {
541 return $(".address:first").text();
542 });
543 if (actual != expected) {
544 console.log("DASH address is incorrect");
545 console.log("Expected: " + expected);
546 console.log("Actual: " + actual);
547 fail();
59193779 548 }
bfb3dab6
IC
549 next();
550 });
551});
552},
553
554// Network can be set to namecoin
555function() {
556page.open(url, function(status) {
557 // set the phrase and coin
558 var expected = "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2";
559 page.evaluate(function() {
560 $(".phrase").val("abandon abandon ability");
561 $(".phrase").trigger("input");
562 $(".network option[selected]").removeAttr("selected");
563 $(".network option[value=11]").prop("selected", true);
564 $(".network").trigger("change");
565 });
566 // check the address is generated correctly
567 waitForGenerate(function() {
568 var actual = page.evaluate(function() {
569 return $(".address:first").text();
570 });
571 if (actual != expected) {
572 console.log("Namecoin address is incorrect");
573 console.log("Expected: " + expected);
574 console.log("Actual: " + actual);
575 fail();
576 }
577 next();
578 });
579});
580},
581
582// Network can be set to peercoin
583function() {
584page.open(url, function(status) {
585 // set the phrase and coin
586 var expected = "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm";
587 page.evaluate(function() {
588 $(".phrase").val("abandon abandon ability");
589 $(".phrase").trigger("input");
590 $(".network option[selected]").removeAttr("selected");
591 $(".network option[value=12]").prop("selected", true);
592 $(".network").trigger("change");
593 });
594 // check the address is generated correctly
595 waitForGenerate(function() {
596 var actual = page.evaluate(function() {
597 return $(".address:first").text();
598 });
599 if (actual != expected) {
600 console.log("Peercoin address is incorrect");
601 console.log("Expected: " + expected);
602 console.log("Actual: " + actual);
603 fail();
604 }
59193779 605 next();
3eef9d0d 606 });
59193779
IC
607});
608},
609
88e2cdaa 610// BIP39 seed is set from phrase
c196ad55
IC
611function() {
612page.open(url, function(status) {
613 // set the phrase
614 var expected = "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
615 page.evaluate(function() {
616 $(".phrase").val("abandon abandon ability");
617 $(".phrase").trigger("input");
618 });
619 // check the address is generated correctly
3eef9d0d 620 waitForGenerate(function() {
c196ad55
IC
621 var actual = page.evaluate(function() {
622 return $(".seed").val();
623 });
624 if (actual != expected) {
625 console.log("BIP39 seed is incorrectly generated from mnemonic");
626 console.log("Expected: " + expected);
627 console.log("Actual: " + actual);
628 fail();
629 }
630 next();
3eef9d0d 631 });
c196ad55
IC
632});
633},
634
88e2cdaa 635// BIP32 root key is set from phrase
ec60b662
IC
636function() {
637page.open(url, function(status) {
638 // set the phrase
639 var expected = "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi";
640 page.evaluate(function() {
641 $(".phrase").val("abandon abandon ability");
642 $(".phrase").trigger("input");
643 });
644 // check the address is generated correctly
3eef9d0d 645 waitForGenerate(function() {
ec60b662
IC
646 var actual = page.evaluate(function() {
647 return $(".root-key").val();
648 });
649 if (actual != expected) {
650 console.log("Root key is incorrectly generated from mnemonic");
651 console.log("Expected: " + expected);
652 console.log("Actual: " + actual);
653 fail();
654 }
655 next();
3eef9d0d 656 });
ec60b662
IC
657});
658},
659
88e2cdaa 660// Tabs show correct addresses when changed
cf7258fd
IC
661function() {
662page.open(url, function(status) {
663 // set the phrase
664 var expected = "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz";
665 page.evaluate(function() {
666 $(".phrase").val("abandon abandon ability");
667 $(".phrase").trigger("input");
668 });
669 // change tabs
670 waitForGenerate(function() {
671 page.evaluate(function() {
672 $("#bip32-tab a").click();
673 });
674 // check the address is generated correctly
675 waitForGenerate(function() {
676 var actual = page.evaluate(function() {
677 return $(".address:first").text();
678 });
679 if (actual != expected) {
680 console.log("Clicking tab generates incorrect address");
681 console.log("Expected: " + expected);
682 console.log("Actual: " + actual);
683 fail();
684 }
685 next();
686 });
687 });
688});
689},
88e2cdaa
IC
690
691// BIP44 derivation path is shown
d077e1e7
IC
692function() {
693page.open(url, function(status) {
694 // set the phrase
695 var expected = "m/44'/0'/0'/0";
696 page.evaluate(function() {
697 $(".phrase").val("abandon abandon ability");
698 $(".phrase").trigger("input");
699 });
700 // check the derivation path of the first address
701 waitForGenerate(function() {
702 var actual = page.evaluate(function() {
703 return $("#bip44 .path").val();
704 });
705 if (actual != expected) {
706 console.log("BIP44 derivation path is incorrect");
707 console.log("Expected: " + expected);
708 console.log("Actual: " + actual);
709 fail();
710 }
711 next();
712 });
713});
714},
715
88e2cdaa 716// BIP44 extended private key is shown
4fd2925d
IC
717function() {
718page.open(url, function(status) {
719 // set the phrase
720 var expected = "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG";
721 page.evaluate(function() {
722 $(".phrase").val("abandon abandon ability");
723 $(".phrase").trigger("input");
724 });
06286adb 725 // check the BIP44 extended private key
4fd2925d
IC
726 waitForGenerate(function() {
727 var actual = page.evaluate(function() {
728 return $(".extended-priv-key").val();
729 });
730 if (actual != expected) {
731 console.log("BIP44 extended private key is incorrect");
732 console.log("Expected: " + expected);
733 console.log("Actual: " + actual);
734 fail();
735 }
736 next();
737 });
738});
739},
740
88e2cdaa 741// BIP44 extended public key is shown
39fd45bb
IC
742function() {
743page.open(url, function(status) {
744 // set the phrase
745 var expected = "xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM";
746 page.evaluate(function() {
747 $(".phrase").val("abandon abandon ability");
748 $(".phrase").trigger("input");
749 });
06286adb 750 // check the BIP44 extended public key
39fd45bb
IC
751 waitForGenerate(function() {
752 var actual = page.evaluate(function() {
753 return $(".extended-pub-key").val();
754 });
755 if (actual != expected) {
756 console.log("BIP44 extended public key is incorrect");
757 console.log("Expected: " + expected);
758 console.log("Actual: " + actual);
759 fail();
760 }
761 next();
762 });
763});
764},
765
06286adb
IC
766// BIP44 purpose field changes address list
767function() {
768page.open(url, function(status) {
769 // set the phrase
770 var expected = "1JbDzRJ2cDT8aat2xwKd6Pb2zzavow5MhF";
771 page.evaluate(function() {
772 $(".phrase").val("abandon abandon ability");
773 $(".phrase").trigger("input");
774 });
775 waitForGenerate(function() {
776 // change the bip44 purpose field to 45
777 page.evaluate(function() {
778 $("#bip44 .purpose").val("45");
779 $("#bip44 .purpose").trigger("input");
780 });
781 waitForGenerate(function() {
782 // check the address for the new derivation path
783 var actual = page.evaluate(function() {
784 return $(".address:first").text();
785 });
786 if (actual != expected) {
787 console.log("BIP44 purpose field generates incorrect address");
788 console.log("Expected: " + expected);
789 console.log("Actual: " + actual);
790 fail();
791 }
792 next();
793 });
794 });
795});
796},
797
88e2cdaa 798// BIP44 coin field changes address list
9eb72cdf
IC
799function() {
800page.open(url, function(status) {
801 // set the phrase
802 var expected = "1F6dB2djQYrxoyfZZmfr6D5voH8GkJTghk";
803 page.evaluate(function() {
804 $(".phrase").val("abandon abandon ability");
805 $(".phrase").trigger("input");
806 });
807 waitForGenerate(function() {
808 // change the bip44 purpose field to 45
809 page.evaluate(function() {
810 $("#bip44 .coin").val("1");
811 $("#bip44 .coin").trigger("input");
812 });
813 waitForGenerate(function() {
814 // check the address for the new derivation path
815 var actual = page.evaluate(function() {
816 return $(".address:first").text();
817 });
818 if (actual != expected) {
819 console.log("BIP44 coin field generates incorrect address");
820 console.log("Expected: " + expected);
821 console.log("Actual: " + actual);
822 fail();
823 }
824 next();
825 });
826 });
827});
828},
829
88e2cdaa 830// BIP44 account field changes address list
048bc3e0
IC
831function() {
832page.open(url, function(status) {
833 // set the phrase
834 var expected = "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
835 page.evaluate(function() {
836 $(".phrase").val("abandon abandon ability");
837 $(".phrase").trigger("input");
838 });
839 waitForGenerate(function() {
840 // change the bip44 purpose field to 45
841 page.evaluate(function() {
842 $("#bip44 .account").val("1");
843 $("#bip44 .account").trigger("input");
844 });
845 waitForGenerate(function() {
846 // check the address for the new derivation path
847 var actual = page.evaluate(function() {
848 return $(".address:first").text();
849 });
850 if (actual != expected) {
851 console.log("BIP44 account field generates incorrect address");
852 console.log("Expected: " + expected);
853 console.log("Actual: " + actual);
854 fail();
855 }
856 next();
857 });
858 });
859});
860},
861
fa4da086
IC
862// BIP44 change field changes address list
863function() {
864page.open(url, function(status) {
865 // set the phrase
866 var expected = "1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo";
867 page.evaluate(function() {
868 $(".phrase").val("abandon abandon ability");
869 $(".phrase").trigger("input");
870 });
871 waitForGenerate(function() {
872 // change the bip44 purpose field to 45
873 page.evaluate(function() {
874 $("#bip44 .change").val("1");
875 $("#bip44 .change").trigger("input");
876 });
877 waitForGenerate(function() {
878 // check the address for the new derivation path
879 var actual = page.evaluate(function() {
880 return $(".address:first").text();
881 });
882 if (actual != expected) {
883 console.log("BIP44 change field generates incorrect address");
884 console.log("Expected: " + expected);
885 console.log("Actual: " + actual);
886 fail();
887 }
888 next();
889 });
890 });
891});
892},
048bc3e0 893
88e2cdaa 894// BIP32 derivation path can be set
651382a3
IC
895function() {
896page.open(url, function(status) {
897 // set the phrase
898 var expected = "16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L";
899 page.evaluate(function() {
900 $(".phrase").val("abandon abandon ability");
901 $(".phrase").trigger("input");
902 });
903 // change tabs
904 waitForGenerate(function() {
905 page.evaluate(function() {
906 $("#bip32-tab a").click();
907 });
908 // set the derivation path to m/1
909 waitForGenerate(function() {
910 page.evaluate(function() {
911 $("#bip32 .path").val("m/1");
912 $("#bip32 .path").trigger("input");
913 });
914 // check the address is generated correctly
915 waitForGenerate(function() {
916 var actual = page.evaluate(function() {
917 return $(".address:first").text();
918 });
919 if (actual != expected) {
920 console.log("Custom BIP32 path generates incorrect address");
921 console.log("Expected: " + expected);
922 console.log("Actual: " + actual);
923 fail();
924 }
925 next();
926 });
927 });
928 });
929});
930},
931
88e2cdaa 932// BIP32 can use hardened derivation paths
651382a3
IC
933function() {
934page.open(url, function(status) {
935 // set the phrase
936 var expected = "14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4";
937 page.evaluate(function() {
938 $(".phrase").val("abandon abandon ability");
939 $(".phrase").trigger("input");
940 });
941 // change tabs
942 waitForGenerate(function() {
943 page.evaluate(function() {
944 $("#bip32-tab a").click();
945 });
946 // set the derivation path to m/0'
947 waitForGenerate(function() {
948 page.evaluate(function() {
949 $("#bip32 .path").val("m/0'");
950 $("#bip32 .path").trigger("input");
951 });
952 // check the address is generated correctly
953 waitForGenerate(function() {
954 var actual = page.evaluate(function() {
955 return $(".address:first").text();
956 });
957 if (actual != expected) {
958 console.log("Hardened BIP32 path generates incorrect address");
959 console.log("Expected: " + expected);
960 console.log("Actual: " + actual);
961 fail();
962 }
963 next();
964 });
965 });
966 });
967});
968},
969
88e2cdaa 970// BIP32 extended private key is shown
9e9dcfda
IC
971function() {
972page.open(url, function(status) {
973 // set the phrase
974 var expected = "xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe";
975 page.evaluate(function() {
976 $(".phrase").val("abandon abandon ability");
977 $(".phrase").trigger("input");
978 });
979 // change tabs
980 waitForGenerate(function() {
981 page.evaluate(function() {
982 $("#bip32-tab a").click();
983 });
984 // check the extended private key is generated correctly
985 waitForGenerate(function() {
986 var actual = page.evaluate(function() {
987 return $(".extended-priv-key").val();
988 });
989 if (actual != expected) {
990 console.log("BIP32 extended private key is incorrect");
991 console.log("Expected: " + expected);
992 console.log("Actual: " + actual);
993 fail();
994 }
995 next();
996 });
997 });
998});
999},
1000
88e2cdaa 1001// BIP32 extended public key is shown
9e9dcfda
IC
1002function() {
1003page.open(url, function(status) {
1004 // set the phrase
1005 var expected = "xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P";
1006 page.evaluate(function() {
1007 $(".phrase").val("abandon abandon ability");
1008 $(".phrase").trigger("input");
1009 });
1010 // change tabs
1011 waitForGenerate(function() {
1012 page.evaluate(function() {
1013 $("#bip32-tab a").click();
1014 });
1015 // check the extended public key is generated correctly
1016 waitForGenerate(function() {
1017 var actual = page.evaluate(function() {
1018 return $(".extended-pub-key").val();
1019 });
1020 if (actual != expected) {
1021 console.log("BIP32 extended public key is incorrect");
1022 console.log("Expected: " + expected);
1023 console.log("Actual: " + actual);
1024 fail();
1025 }
1026 next();
1027 });
1028 });
1029});
1030},
88e2cdaa
IC
1031
1032// Derivation path is shown in table
5f844c62
IC
1033function() {
1034page.open(url, function(status) {
1035 // set the phrase
1036 var expected = "m/44'/0'/0'/0/0";
1037 page.evaluate(function() {
1038 $(".phrase").val("abandon abandon ability");
1039 $(".phrase").trigger("input");
1040 });
1041 // check for derivation path in table
1042 waitForGenerate(function() {
1043 var actual = page.evaluate(function() {
1044 return $(".index:first").text();
1045 });
1046 if (actual != expected) {
1047 console.log("Derivation path shown incorrectly in table");
1048 console.log("Expected: " + expected);
1049 console.log("Actual: " + actual);
1050 fail();
1051 }
1052 next();
1053 });
1054});
1055},
1056
88e2cdaa 1057// Derivation path for address can be hardened
4974fd7f
IC
1058function() {
1059page.open(url, function(status) {
1060 // set the phrase
1061 var expected = "18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd";
1062 page.evaluate(function() {
1063 $(".phrase").val("abandon abandon ability");
1064 $(".phrase").trigger("input");
1065 });
1066 // change tabs
1067 waitForGenerate(function() {
1068 page.evaluate(function() {
1069 $("#bip32-tab a").click();
1070 });
1071 waitForGenerate(function() {
1072 // select the hardened addresses option
1073 page.evaluate(function() {
1074 $(".hardened-addresses").prop("checked", true);
1075 $(".hardened-addresses").trigger("change");
1076 });
1077 waitForGenerate(function() {
1078 // check the generated address is hardened
1079 var actual = page.evaluate(function() {
1080 return $(".address:first").text();
1081 });
1082 if (actual != expected) {
1083 console.log("Hardened address is incorrect");
1084 console.log("Expected: " + expected);
1085 console.log("Actual: " + actual);
1086 fail();
1087 }
1088 next();
1089 });
1090 });
1091 });
1092});
1093},
1094
88e2cdaa 1095// Derivation path visibility can be toggled
a775b5c6
IC
1096function() {
1097page.open(url, function(status) {
1098 // set the phrase
1099 page.evaluate(function() {
1100 $(".phrase").val("abandon abandon ability");
1101 $(".phrase").trigger("input");
1102 });
a775b5c6
IC
1103 waitForGenerate(function() {
1104 // toggle path visibility
1105 page.evaluate(function() {
1106 $(".index-toggle").click();
1107 });
1108 // check the path is not visible
1109 var isInvisible = page.evaluate(function() {
1110 return $(".index:first span").hasClass("invisible");
1111 });
1112 if (!isInvisible) {
1113 console.log("Toggled derivation path is visible");
1114 fail();
1115 }
1116 next();
1117 });
1118});
1119},
1120
88e2cdaa 1121// Address is shown
06c4c6e3
IC
1122function() {
1123page.open(url, function(status) {
1124 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1125 // set the phrase
1126 page.evaluate(function() {
1127 $(".phrase").val("abandon abandon ability").trigger("input");
1128 });
1129 // get the address
1130 waitForGenerate(function() {
1131 var actual = page.evaluate(function() {
1132 return $(".address:first").text();
1133 });
1134 if (actual != expected) {
1135 console.log("Address is not shown");
1136 console.log("Expected: " + expected);
1137 console.log("Got: " + actual);
1138 fail();
1139 }
1140 next();
1141 });
1142});
1143},
1144
88e2cdaa 1145// Addresses are shown in order of derivation path
3f1faa4d
IC
1146function() {
1147page.open(url, function(status) {
1148 // set the phrase
1149 page.evaluate(function() {
1150 $(".phrase").val("abandon abandon ability").trigger("input");
1151 });
1152 // get the derivation paths
1153 waitForGenerate(function() {
1154 var paths = page.evaluate(function() {
1155 return $(".index").map(function(i, e) {
1156 return $(e).text();
1157 });
1158 });
1159 if (paths.length != 20) {
1160 console.log("Total paths is less than expected: " + paths.length);
1161 fail();
1162 }
1163 for (var i=0; i<paths.length; i++) {
1164 var expected = "m/44'/0'/0'/0/" + i;
1165 var actual = paths[i];
1166 if (actual != expected) {
1167 console.log("Path " + i + " is incorrect");
1168 console.log("Expected: " + expected);
1169 console.log("Actual: " + actual);
1170 fail();
1171 }
1172 }
1173 next();
1174 });
1175});
1176},
21372fab 1177
88e2cdaa 1178// Address visibility can be toggled
21372fab
IC
1179function() {
1180page.open(url, function(status) {
1181 // set the phrase
1182 page.evaluate(function() {
1183 $(".phrase").val("abandon abandon ability");
1184 $(".phrase").trigger("input");
1185 });
1186 waitForGenerate(function() {
1187 // toggle address visibility
1188 page.evaluate(function() {
1189 $(".address-toggle").click();
1190 });
1191 // check the address is not visible
1192 var isInvisible = page.evaluate(function() {
1193 return $(".address:first span").hasClass("invisible");
1194 });
1195 if (!isInvisible) {
1196 console.log("Toggled address is visible");
1197 fail();
1198 }
1199 next();
1200 });
1201});
1202},
1203
1b12b2f5
IC
1204// Public key is shown
1205function() {
1206page.open(url, function(status) {
1207 var expected = "033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3";
1208 // set the phrase
1209 page.evaluate(function() {
1210 $(".phrase").val("abandon abandon ability").trigger("input");
1211 });
1212 // get the address
1213 waitForGenerate(function() {
1214 var actual = page.evaluate(function() {
1215 return $(".pubkey:first").text();
1216 });
1217 if (actual != expected) {
1218 console.log("Public key is not shown");
1219 console.log("Expected: " + expected);
1220 console.log("Got: " + actual);
1221 fail();
1222 }
1223 next();
1224 });
1225});
1226},
1227
1228// Public key visibility can be toggled
1229function() {
1230page.open(url, function(status) {
1231 // set the phrase
1232 page.evaluate(function() {
1233 $(".phrase").val("abandon abandon ability");
1234 $(".phrase").trigger("input");
1235 });
1236 waitForGenerate(function() {
1237 // toggle public key visibility
1238 page.evaluate(function() {
1239 $(".public-key-toggle").click();
1240 });
1241 // check the public key is not visible
1242 var isInvisible = page.evaluate(function() {
1243 return $(".pubkey:first span").hasClass("invisible");
1244 });
1245 if (!isInvisible) {
1246 console.log("Toggled public key is visible");
1247 fail();
1248 }
1249 next();
1250 });
1251});
1252},
1253
88e2cdaa 1254// Private key is shown
8cd5e231
IC
1255function() {
1256page.open(url, function(status) {
1257 var expected = "L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE";
1258 // set the phrase
1259 page.evaluate(function() {
1260 $(".phrase").val("abandon abandon ability").trigger("input");
1261 });
1262 // get the address
1263 waitForGenerate(function() {
1264 var actual = page.evaluate(function() {
1265 return $(".privkey:first").text();
1266 });
1267 if (actual != expected) {
1268 console.log("Private key is not shown");
1269 console.log("Expected: " + expected);
1270 console.log("Got: " + actual);
1271 fail();
1272 }
1273 next();
1274 });
1275});
1276},
1277
88e2cdaa 1278// Private key visibility can be toggled
6848d03b
IC
1279function() {
1280page.open(url, function(status) {
1281 // set the phrase
1282 page.evaluate(function() {
1283 $(".phrase").val("abandon abandon ability");
1284 $(".phrase").trigger("input");
1285 });
1286 waitForGenerate(function() {
1287 // toggle private key visibility
1288 page.evaluate(function() {
1289 $(".private-key-toggle").click();
1290 });
1291 // check the private key is not visible
1292 var isInvisible = page.evaluate(function() {
1293 return $(".privkey:first span").hasClass("invisible");
1294 });
1295 if (!isInvisible) {
1296 console.log("Toggled private key is visible");
1297 fail();
1298 }
1299 next();
1300 });
1301});
1302},
88e2cdaa
IC
1303
1304// More addresses can be generated
35631659
IC
1305function() {
1306page.open(url, function(status) {
1307 // set the phrase
1308 page.evaluate(function() {
1309 $(".phrase").val("abandon abandon ability");
1310 $(".phrase").trigger("input");
1311 });
1312 waitForGenerate(function() {
1313 // generate more addresses
1314 page.evaluate(function() {
1315 $(".more").click();
1316 });
1317 waitForGenerate(function() {
1318 // check there are more addresses
1319 var addressCount = page.evaluate(function() {
1320 return $(".address").length;
1321 });
1322 if (addressCount != 40) {
1323 console.log("More addresses cannot be generated");
1324 fail();
1325 }
1326 next();
1327 });
1328 });
1329});
1330},
1331
88e2cdaa 1332// A custom number of additional addresses can be generated
8a89b9da
IC
1333function() {
1334page.open(url, function(status) {
1335 // set the phrase
1336 page.evaluate(function() {
1337 $(".phrase").val("abandon abandon ability");
1338 $(".phrase").trigger("input");
1339 });
1340 waitForGenerate(function() {
1341 // get the current number of addresses
1342 var oldAddressCount = page.evaluate(function() {
1343 return $(".address").length;
1344 });
1345 // set a custom number of additional addresses
1346 page.evaluate(function() {
1347 $(".rows-to-add").val(1);
1348 });
1349 // generate more addresses
1350 page.evaluate(function() {
1351 $(".more").click();
1352 });
1353 waitForGenerate(function() {
1354 // check there are the correct number of addresses
1355 var newAddressCount = page.evaluate(function() {
1356 return $(".address").length;
1357 });
1358 if (newAddressCount - oldAddressCount != 1) {
1359 console.log("Number of additional addresses cannot be customized");
1360 console.log(newAddressCount)
1361 console.log(oldAddressCount)
1362 fail();
1363 }
1364 next();
1365 });
1366 });
1367});
1368},
1369
88e2cdaa 1370// Additional addresses are shown in order of derivation path
4d387bf5
IC
1371function() {
1372page.open(url, function(status) {
1373 // set the phrase
1374 page.evaluate(function() {
1375 $(".phrase").val("abandon abandon ability").trigger("input");
1376 });
1377 waitForGenerate(function() {
1378 // generate more addresses
1379 page.evaluate(function() {
1380 $(".more").click();
1381 });
1382 // get the derivation paths
1383 waitForGenerate(function() {
1384 var paths = page.evaluate(function() {
1385 return $(".index").map(function(i, e) {
1386 return $(e).text();
1387 });
1388 });
1389 if (paths.length != 40) {
1390 console.log("Total additional paths is less than expected: " + paths.length);
1391 fail();
1392 }
1393 for (var i=0; i<paths.length; i++) {
1394 var expected = "m/44'/0'/0'/0/" + i;
1395 var actual = paths[i];
1396 if (actual != expected) {
1397 console.log("Path " + i + " is not in correct order");
1398 console.log("Expected: " + expected);
1399 console.log("Actual: " + actual);
1400 fail();
1401 }
1402 }
1403 next();
1404 });
1405 });
1406});
1407},
88e2cdaa
IC
1408
1409// BIP32 root key can be set by the user
61ed16a9
IC
1410function() {
1411page.open(url, function(status) {
1412 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1413 // set the root key
1414 page.evaluate(function() {
1415 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1416 });
1417 waitForGenerate(function() {
1418 var actual = page.evaluate(function() {
1419 return $(".address:first").text();
1420 });
1421 if (actual != expected) {
bc324fd2 1422 console.log("Setting BIP32 root key results in wrong address");
61ed16a9
IC
1423 console.log("Expected: " + expected);
1424 console.log("Actual: " + actual);
1425 fail();
1426 }
1427 next();
1428 });
1429});
1430},
1431
54563907 1432// Setting BIP32 root key clears the existing phrase, passphrase and seed
bc324fd2
IC
1433function() {
1434page.open(url, function(status) {
1435 var expected = "";
1436 // set a mnemonic
1437 page.evaluate(function() {
1438 $(".phrase").val("A non-blank but invalid value");
1439 });
1440 // Accept any confirm dialogs
1441 page.onConfirm = function() {
1442 return true;
1443 };
1444 // set the root key
1445 page.evaluate(function() {
1446 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1447 });
1448 waitForGenerate(function() {
1449 var actual = page.evaluate(function() {
1450 return $(".phrase").val();
1451 });
1452 if (actual != expected) {
1453 console.log("Phrase not cleared when setting BIP32 root key");
1454 console.log("Expected: " + expected);
1455 console.log("Actual: " + actual);
1456 fail();
1457 }
1458 next();
1459 });
1460});
1461},
1462
54563907 1463// Clearing of phrase, passphrase and seed can be cancelled by user
abfbe450
IC
1464function() {
1465page.open(url, function(status) {
1466 var expected = "abandon abandon ability";
1467 // set a mnemonic
1468 page.evaluate(function() {
1469 $(".phrase").val("abandon abandon ability");
1470 });
1471 // Cancel any confirm dialogs
1472 page.onConfirm = function() {
1473 return false;
1474 };
1475 // set the root key
1476 page.evaluate(function() {
1477 $(".root-key").val("xprv9s21ZrQH143K3d3vzEDD3KpSKmxsZ3y7CqhAL1tinwtP6wqK4TKEKjpBuo6P2hUhB6ZENo7TTSRytiP857hBZVpBdk8PooFuRspE1eywwNZ").trigger("input");
1478 });
1479 var actual = page.evaluate(function() {
1480 return $(".phrase").val();
1481 });
1482 if (actual != expected) {
1483 console.log("Phrase not retained when cancelling changes to BIP32 root key");
1484 console.log("Expected: " + expected);
1485 console.log("Actual: " + actual);
1486 fail();
1487 }
1488 next();
1489});
1490},
7ff86d4c 1491
88e2cdaa 1492// Custom BIP32 root key is used when changing the derivation path
7ff86d4c
IC
1493function() {
1494page.open(url, function(status) {
1495 var expected = "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1496 // set the root key
1497 page.evaluate(function() {
1498 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1499 });
1500 waitForGenerate(function() {
1501 // change the derivation path
1502 page.evaluate(function() {
1503 $("#account").val("1").trigger("input");
1504 });
1505 // check the bip32 root key is used for derivation, not the blank phrase
1506 waitForGenerate(function() {
1507 var actual = page.evaluate(function() {
1508 return $(".address:first").text();
1509 });
1510 if (actual != expected) {
1511 console.log("Changing the derivation path does not use BIP32 root key");
1512 console.log("Expected: " + expected);
1513 console.log("Actual: " + actual);
1514 fail();
1515 }
1516 next();
1517 });
1518 });
1519});
1520},
88e2cdaa
IC
1521
1522// Incorrect mnemonic shows error
c3719b00
IC
1523function() {
1524page.open(url, function(status) {
1525 // set the root key
1526 page.evaluate(function() {
1527 $(".phrase").val("abandon abandon abandon").trigger("input");
1528 });
1529 waitForFeedback(function() {
1530 // check there is an error shown
1531 var feedback = page.evaluate(function() {
1532 return $(".feedback").text();
1533 });
1534 if (feedback.length <= 0) {
1535 console.log("Invalid mnemonic does not show error");
1536 fail();
1537 }
1538 next();
1539 });
1540});
1541},
1542
88e2cdaa 1543// Incorrect word shows suggested replacement
20f80cfa
IC
1544function() {
1545page.open(url, function(status) {
1546 // set the root key
1547 page.evaluate(function() {
1548 $(".phrase").val("abandon abandon abiliti").trigger("input");
1549 });
1550 // check there is a suggestion shown
1551 waitForFeedback(function() {
1552 var feedback = page.evaluate(function() {
1553 return $(".feedback").text();
1554 });
1555 if (feedback.indexOf("did you mean ability?") < 0) {
1556 console.log("Incorrect word does not show suggested replacement");
1557 console.log("Error: " + error);
1558 fail();
6ea15134
IC
1559 }
1560 next();
1561 });
1562});
1563},
1564
1565// Github pull request 48
1566// First four letters of word shows that word, not closest
1567// since first four letters gives unique word in BIP39 wordlist
1568// eg ille should show illegal, not idle
1569function() {
1570page.open(url, function(status) {
1571 // set the incomplete word
1572 page.evaluate(function() {
1573 $(".phrase").val("ille").trigger("input");
1574 });
1575 // check there is a suggestion shown
1576 waitForFeedback(function() {
1577 var feedback = page.evaluate(function() {
1578 return $(".feedback").text();
1579 });
1580 if (feedback.indexOf("did you mean illegal?") < 0) {
1581 console.log("Start of word does not show correct suggestion");
1582 console.log("Error: " + error);
1583 fail();
20f80cfa
IC
1584 }
1585 next();
1586 });
1587});
1588},
1589
88e2cdaa 1590// Incorrect BIP32 root key shows error
02f4a90e
IC
1591function() {
1592page.open(url, function(status) {
1593 // set the root key
1594 page.evaluate(function() {
1595 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj").trigger("input");
1596 });
1597 // check there is an error shown
1598 waitForFeedback(function() {
1599 var feedback = page.evaluate(function() {
1600 return $(".feedback").text();
1601 });
1602 if (feedback != "Invalid root key") {
1603 console.log("Invalid root key does not show error");
1604 console.log("Error: " + error);
1605 fail();
1606 }
1607 next();
1608 });
1609});
1610},
1611
88e2cdaa 1612// Derivation path not starting with m shows error
f976b541
IC
1613function() {
1614page.open(url, function(status) {
1615 // set the mnemonic phrase
1616 page.evaluate(function() {
1617 $(".phrase").val("abandon abandon ability").trigger("input");
1618 });
1619 waitForGenerate(function() {
1620 // select the bip32 tab so custom derivation path can be set
1621 page.evaluate(function() {
1622 $("#bip32-tab a").click();
1623 });
1624 waitForGenerate(function() {
1625 // set the incorrect derivation path
1626 page.evaluate(function() {
1627 $("#bip32 .path").val("n/0").trigger("input");
1628 });
1629 waitForFeedback(function() {
1630 var feedback = page.evaluate(function() {
1631 return $(".feedback").text();
1632 });
1633 if (feedback != "First character must be 'm'") {
1634 console.log("Derivation path not starting with m should show error");
1635 console.log("Error: " + error);
1636 fail();
1637 }
1638 next();
1639 });
1640 });
1641 });
1642});
1643},
1644
88e2cdaa 1645// Derivation path containing invalid characters shows useful error
55c0ffcb
IC
1646function() {
1647page.open(url, function(status) {
1648 // set the mnemonic phrase
1649 page.evaluate(function() {
1650 $(".phrase").val("abandon abandon ability").trigger("input");
1651 });
1652 waitForGenerate(function() {
1653 // select the bip32 tab so custom derivation path can be set
1654 page.evaluate(function() {
1655 $("#bip32-tab a").click();
1656 });
1657 waitForGenerate(function() {
1658 // set the incorrect derivation path
1659 page.evaluate(function() {
1660 $("#bip32 .path").val("m/1/0wrong1/1").trigger("input");
1661 });
1662 waitForFeedback(function() {
1663 var feedback = page.evaluate(function() {
1664 return $(".feedback").text();
1665 });
1666 if (feedback != "Invalid characters 0wrong1 found at depth 2") {
1667 console.log("Derivation path with invalid characters should show error");
1668 console.log("Error: " + error);
1669 fail();
1670 }
1671 next();
1672 });
1673 });
1674 });
1675});
1676},
88e2cdaa
IC
1677
1678// Github Issue 11: Default word length is 15
b630f83d 1679// https://github.com/iancoleman/bip39/issues/11
ca976aa9
IC
1680function() {
1681page.open(url, function(status) {
1682 // get the word length
1683 var defaultLength = page.evaluate(function() {
1684 return $(".strength").val();
1685 });
1686 if (defaultLength != 15) {
1687 console.log("Default word length is not 15");
1688 fail();
1689 }
1690 next();
1691});
1692},
1693
88e2cdaa
IC
1694
1695// Github Issue 12: Generate more rows with private keys hidden
b630f83d 1696// https://github.com/iancoleman/bip39/issues/12
c97627fa
IC
1697function() {
1698page.open(url, function(status) {
1699 // set the phrase
1700 page.evaluate(function() {
1701 $(".phrase").val("abandon abandon ability");
1702 $(".phrase").trigger("input");
1703 });
1704 waitForGenerate(function() {
1705 // toggle private keys hidden, then generate more addresses
1706 page.evaluate(function() {
1707 $(".private-key-toggle").click();
1708 $(".more").click();
1709 });
1710 waitForGenerate(function() {
1711 // check more have been generated
1712 var expected = 40;
1713 var numPrivKeys = page.evaluate(function() {
1714 return $(".privkey").length;
1715 });
1716 if (numPrivKeys != expected) {
1717 console.log("Wrong number of addresses when clicking 'more' with hidden privkeys");
1718 console.log("Expected: " + expected);
1719 console.log("Actual: " + numPrivKeys);
1720 fail();
1721 }
1722 // check no private keys are shown
1723 var numHiddenPrivKeys = page.evaluate(function() {
1724 return $(".privkey span[class=invisible]").length;
1725 });
1726 if (numHiddenPrivKeys != expected) {
1727 console.log("Generating more does not retain hidden state of privkeys");
1728 console.log("Expected: " + expected);
1729 console.log("Actual: " + numHiddenPrivKeys);
1730 fail();
1731 }
1732 next();
1733 });
1734 });
1735});
1736},
88e2cdaa
IC
1737
1738// Github Issue 19: Mnemonic is not sensitive to whitespace
b630f83d 1739// https://github.com/iancoleman/bip39/issues/19
a7becc43
IC
1740function() {
1741page.open(url, function(status) {
1742 // set the phrase
1743 var expected = "xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC";
1744 page.evaluate(function() {
1745 var doubleSpace = " ";
1746 $(".phrase").val("urge cat" + doubleSpace + "bid");
1747 $(".phrase").trigger("input");
1748 });
1749 waitForGenerate(function() {
1750 // Check the bip32 root key is correct
1751 var actual = page.evaluate(function() {
1752 return $(".root-key").val();
1753 });
1754 if (actual != expected) {
1755 console.log("Mnemonic is sensitive to whitespace");
1756 console.log("Expected: " + expected);
1757 console.log("Actual: " + actual);
1758 fail();
1759 }
1760 next();
1761 });
1762});
1763},
88e2cdaa 1764
e3001539 1765// Github Issue 23: Part 1: Use correct derivation path when changing tabs
b630f83d 1766// https://github.com/iancoleman/bip39/issues/23
e3001539
IC
1767function() {
1768page.open(url, function(status) {
1769 // 1) and 2) set the phrase
1770 page.evaluate(function() {
1771 $(".phrase").val("abandon abandon ability").trigger("input");
1772 });
1773 waitForGenerate(function() {
1774 // 3) select bip32 tab
1775 page.evaluate(function() {
1776 $("#bip32-tab a").click();
1777 });
1778 waitForGenerate(function() {
1779 // 4) switch from bitcoin to litecoin
1780 page.evaluate(function() {
1781 $(".network").val("2").trigger("change");
1782 });
1783 waitForGenerate(function() {
1784 // 5) Check derivation path is displayed correctly
1785 var expected = "m/0/0";
1786 var actual = page.evaluate(function() {
1787 return $(".index:first").text();
1788 });
1789 if (actual != expected) {
1790 console.log("Github Issue 23 Part 1: derivation path display error");
1791 console.log("Expected: " + expected);
1792 console.log("Actual: " + actual);
1793 fail();
1794 }
1795 // 5) Check address is displayed correctly
1796 var expected = "LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5";
1797 var actual = page.evaluate(function() {
1798 return $(".address:first").text();
1799 });
1800 if (actual != expected) {
1801 console.log("Github Issue 23 Part 1: address display error");
1802 console.log("Expected: " + expected);
1803 console.log("Actual: " + actual);
1804 fail();
1805 }
1806 next();
1807 });
1808 });
1809 });
1810});
1811},
1812
1813// Github Issue 23 Part 2: Coin selection in derivation path
b630f83d 1814// https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
af4fd3a2
IC
1815function() {
1816page.open(url, function(status) {
1817 // set the phrase
1818 page.evaluate(function() {
1819 $(".phrase").val("abandon abandon ability").trigger("input");
1820 });
1821 waitForGenerate(function() {
1822 // switch from bitcoin to clam
1823 page.evaluate(function() {
1824 $(".network").val("9").trigger("change");
1825 });
1826 waitForGenerate(function() {
1827 // check derivation path is displayed correctly
1828 var expected = "m/44'/23'/0'/0/0";
1829 var actual = page.evaluate(function() {
1830 return $(".index:first").text();
1831 });
1832 if (actual != expected) {
1833 console.log("Github Issue 23 Part 2: Coin in BIP44 derivation path is incorrect");
1834 console.log("Expected: " + expected);
1835 console.log("Actual: " + actual);
1836 fail();
1837 }
1838 next();
1839 });
1840 });
1841});
1842},
88e2cdaa 1843
f3d0aca1 1844// Github Issue 26: When using a Root key derrived altcoins are incorrect
b630f83d 1845// https://github.com/iancoleman/bip39/issues/26
558ef9ac
IC
1846function() {
1847page.open(url, function(status) {
1848 // 1) 2) and 3) set the root key
1849 page.evaluate(function() {
1850 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1851 });
1852 waitForGenerate(function() {
1853 // 4) switch from bitcoin to viacoin
1854 page.evaluate(function() {
1855 $(".network").val("6").trigger("change");
1856 });
1857 waitForGenerate(function() {
1858 // 5) ensure the derived address is correct
1859 var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
1860 var actual = page.evaluate(function() {
1861 return $(".address:first").text();
1862 });
1863 if (actual != expected) {
1864 console.log("Github Issue 26: address is incorrect when changing networks and using root-key to derive");
1865 console.log("Expected: " + expected);
1866 console.log("Actual: " + actual);
1867 fail();
1868 }
1869 next();
1870 });
1871 });
1872});
1873},
f3d0aca1 1874
e1bae843
IC
1875// Selecting a language with no existing phrase should generate a phrase in
1876// that language.
1877function() {
1878page.open(url, function(status) {
1879 // Select a language
1880 // Need to manually simulate hash being set due to quirk between
1881 // 'click' event triggered by javascript vs triggered by mouse.
1882 // Perhaps look into page.sendEvent
1883 // http://phantomjs.org/api/webpage/method/send-event.html
1884 page.evaluate(function() {
1885 window.location.hash = "#japanese";
1886 $("a[href='#japanese']").trigger("click");
1887 });
1888 waitForGenerate(function() {
1889 // Check the mnemonic is in Japanese
1890 var phrase = page.evaluate(function() {
1891 return $(".phrase").val();
1892 });
1893 if (phrase.length <= 0) {
1894 console.log("No Japanese phrase generated");
1895 fail();
1896 }
1897 if (phrase.charCodeAt(0) < 128) {
1898 console.log("First character of Japanese phrase is ascii");
1899 console.log("Phrase: " + phrase);
1900 fail();
1901 }
1902 next();
1903 });
1904});
1905},
1906
1907// Selecting a language with existing phrase should update the phrase to use
1908// that language.
1909function() {
1910page.open(url, function(status) {
1911 // Set the phrase to an English phrase.
1912 page.evaluate(function() {
1913 $(".phrase").val("abandon abandon ability").trigger("input");
1914 });
1915 waitForGenerate(function() {
1916 // Change to Italian
1917 // Need to manually simulate hash being set due to quirk between
1918 // 'click' event triggered by javascript vs triggered by mouse.
1919 // Perhaps look into page.sendEvent
1920 // http://phantomjs.org/api/webpage/method/send-event.html
1921 page.evaluate(function() {
1922 window.location.hash = "#italian";
1923 $("a[href='#italian']").trigger("click");
1924 });
1925 waitForGenerate(function() {
1926 // Check only the language changes, not the phrase
1927 var expected = "abaco abaco abbaglio";
1928 var actual = page.evaluate(function() {
1929 return $(".phrase").val();
1930 });
1931 if (actual != expected) {
1932 console.log("Changing language with existing phrase");
1933 console.log("Expected: " + expected);
1934 console.log("Actual: " + actual);
1935 fail();
1936 }
1937 // Check the address is correct
1938 var expected = "1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV";
1939 var actual = page.evaluate(function() {
1940 return $(".address:first").text();
1941 });
1942 if (actual != expected) {
1943 console.log("Changing language generates incorrect address");
1944 console.log("Expected: " + expected);
1945 console.log("Actual: " + actual);
1946 fail();
1947 }
1948 next();
1949 });
1950 });
1951});
1952},
1953
1954// Suggested replacement for erroneous word in non-English language
1955function() {
1956page.open(url, function(status) {
1957 // Set an incorrect phrase in Italian
1958 page.evaluate(function() {
1959 $(".phrase").val("abaco abaco zbbaglio").trigger("input");
1960 });
1961 waitForFeedback(function() {
1962 // Check the suggestion is correct
1963 var feedback = page.evaluate(function() {
1964 return $(".feedback").text();
1965 });
1966 if (feedback.indexOf("did you mean abbaglio?") < 0) {
1967 console.log("Incorrect Italian word does not show suggested replacement");
1968 console.log("Error: " + error);
1969 fail();
1970 }
1971 next();
1972 });
1973});
1974},
1975
1976
1977// Japanese word does not break across lines.
1978// Point 2 from
1979// https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
1980function() {
1981page.open(url, function(status) {
1982 hasWordBreakCss = page.content.indexOf("word-break: keep-all;") > -1;
1983 if (!hasWordBreakCss) {
1984 console.log("Japanese words can break across lines mid-word");
1985 console.log("Check CSS for '.phrase { word-break: keep-all; }'");
1986 fail();
1987 }
1988 // Run the next test
1989 next();
1990});
1991},
1992
1993// Language can be specified at page load using hash value in url
1994function() {
1995page.open(url, function(status) {
1996 // Set the page hash as if it were on a fresh page load
1997 page.evaluate(function() {
1998 window.location.hash = "#japanese";
1999 });
2000 // Generate a random phrase
2001 page.evaluate(function() {
2002 $(".generate").trigger("click");
2003 });
2004 waitForGenerate(function() {
2005 // Check the phrase is in Japanese
2006 var phrase = page.evaluate(function() {
2007 return $(".phrase").val();
2008 });
2009 if (phrase.length <= 0) {
2010 console.log("No phrase generated using url hash");
2011 fail();
2012 }
2013 if (phrase.charCodeAt(0) < 128) {
2014 console.log("Language not detected from url hash on page load.");
2015 console.log("Phrase: " + phrase);
2016 fail();
2017 }
2018 next();
2019 });
2020});
2021},
2022
c6624d51
IC
2023// Entropy unit tests
2024function() {
2025page.open(url, function(status) {
adc8ce12 2026 var response = page.evaluate(function() {
c6624d51
IC
2027 var e;
2028 // binary entropy is detected
adc8ce12
IC
2029 try {
2030 e = Entropy.fromString("01010101");
2031 if (e.base.str != "binary") {
2032 return "Binary entropy not detected correctly";
2033 }
2034 }
2035 catch (e) {
2036 return e.message;
c6624d51
IC
2037 }
2038 // base6 entropy is detected
adc8ce12
IC
2039 try {
2040 e = Entropy.fromString("012345012345");
2041 if (e.base.str != "base 6") {
2042 return "base6 entropy not detected correctly";
2043 }
2044 }
2045 catch (e) {
2046 return e.message;
c6624d51
IC
2047 }
2048 // dice entropy is detected
adc8ce12
IC
2049 try {
2050 e = Entropy.fromString("123456123456");
2051 if (e.base.str != "base 6 (dice)") {
2052 return "dice entropy not detected correctly";
2053 }
2054 }
2055 catch (e) {
2056 return e.message;
c6624d51
IC
2057 }
2058 // base10 entropy is detected
adc8ce12
IC
2059 try {
2060 e = Entropy.fromString("0123456789");
2061 if (e.base.str != "base 10") {
2062 return "base10 entropy not detected correctly";
2063 }
2064 }
2065 catch (e) {
2066 return e.message;
c6624d51
IC
2067 }
2068 // hex entropy is detected
adc8ce12
IC
2069 try {
2070 e = Entropy.fromString("0123456789ABCDEF");
2071 if (e.base.str != "hexadecimal") {
2072 return "hexadecimal entropy not detected correctly";
2073 }
2074 }
2075 catch (e) {
2076 return e.message;
2077 }
2078 // card entropy is detected
2079 try {
2080 e = Entropy.fromString("AC4DTHKS");
2081 if (e.base.str != "card") {
2082 return "card entropy not detected correctly";
2083 }
2084 }
2085 catch (e) {
2086 return e.message;
c6624d51
IC
2087 }
2088 // entropy is case insensitive
adc8ce12
IC
2089 try {
2090 e = Entropy.fromString("aBcDeF");
2091 if (e.cleanStr != "aBcDeF") {
2092 return "Entropy should not be case sensitive";
2093 }
2094 }
2095 catch (e) {
2096 return e.message;
c6624d51
IC
2097 }
2098 // dice entropy is converted to base6
adc8ce12
IC
2099 try {
2100 e = Entropy.fromString("123456");
425b75a9 2101 if (e.cleanStr != "123450") {
adc8ce12
IC
2102 return "Dice entropy is not automatically converted to base6";
2103 }
2104 }
2105 catch (e) {
2106 return e.message;
c6624d51
IC
2107 }
2108 // dice entropy is preferred to base6 if ambiguous
adc8ce12
IC
2109 try {
2110 e = Entropy.fromString("12345");
2111 if (e.base.str != "base 6 (dice)") {
2112 return "dice not used as default over base 6";
2113 }
2114 }
2115 catch (e) {
2116 return e.message;
c6624d51
IC
2117 }
2118 // unused characters are ignored
adc8ce12
IC
2119 try {
2120 e = Entropy.fromString("fghijkl");
2121 if (e.cleanStr != "f") {
2122 return "additional characters are not ignored";
2123 }
2124 }
2125 catch (e) {
2126 return e.message;
c6624d51
IC
2127 }
2128 // the lowest base is used by default
2129 // 7 could be decimal or hexadecimal, but should be detected as decimal
adc8ce12
IC
2130 try {
2131 e = Entropy.fromString("7");
2132 if (e.base.str != "base 10") {
2133 return "lowest base is not used";
2134 }
c6624d51 2135 }
adc8ce12
IC
2136 catch (e) {
2137 return e.message;
c6624d51
IC
2138 }
2139 // Leading zeros are retained
adc8ce12
IC
2140 try {
2141 e = Entropy.fromString("000A");
2142 if (e.cleanStr != "000A") {
2143 return "Leading zeros are not retained";
2144 }
2145 }
2146 catch (e) {
2147 return e.message;
c6624d51
IC
2148 }
2149 // Leading zeros are correctly preserved for hex in binary string
adc8ce12
IC
2150 try {
2151 e = Entropy.fromString("2A");
2152 if (e.binaryStr != "00101010") {
2153 return "Hex leading zeros are not correct in binary";
2154 }
2155 }
2156 catch (e) {
2157 return e.message;
2158 }
1cf1bbaf
IC
2159 // Leading zeros for base 6 as binary string
2160 // 20 = 2 events at 2.58 bits per event = 5 bits
2161 // 20 in base 6 = 12 in base 10 = 1100 in base 2
2162 // so it needs 1 bit of padding to be the right bit length
adc8ce12 2163 try {
1cf1bbaf
IC
2164 e = Entropy.fromString("20");
2165 if (e.binaryStr != "01100") {
0d0f07f9
IC
2166 return "Base 6 as binary has leading zeros";
2167 }
2168 }
2169 catch (e) {
2170 return e.message;
2171 }
1cf1bbaf 2172 // Leading zeros for base 10 as binary string
0d0f07f9 2173 try {
1cf1bbaf
IC
2174 e = Entropy.fromString("17");
2175 if (e.binaryStr != "010001") {
0d0f07f9
IC
2176 return "Base 10 as binary has leading zeros";
2177 }
2178 }
2179 catch (e) {
2180 return e.message;
2181 }
87ad2c6e 2182 // Leading zeros for card entropy as binary string.
9d33c892 2183 // Card entropy is hashed so 2c does not necessarily produce leading zeros.
0d0f07f9 2184 try {
9d33c892
IC
2185 e = Entropy.fromString("2c");
2186 if (e.binaryStr != "0010") {
0d0f07f9 2187 return "Card entropy as binary has leading zeros";
adc8ce12
IC
2188 }
2189 }
2190 catch (e) {
2191 return e.message;
c6624d51
IC
2192 }
2193 // Keyboard mashing results in weak entropy
2194 // Despite being a long string, it's less than 30 bits of entropy
adc8ce12
IC
2195 try {
2196 e = Entropy.fromString("aj;se ifj; ask,dfv js;ifj");
2197 if (e.binaryStr.length >= 30) {
2198 return "Keyboard mashing should produce weak entropy";
2199 }
c6624d51 2200 }
adc8ce12
IC
2201 catch (e) {
2202 return e.message;
2203 }
2204 // Card entropy is used if every pair could be a card
2205 try {
2206 e = Entropy.fromString("4c3c2c");
2207 if (e.base.str != "card") {
2208 return "Card entropy not used if all pairs are cards";
2209 }
2210 }
2211 catch (e) {
2212 return e.message;
2213 }
2214 // Card entropy uses base 52
2215 // [ cards, binary ]
2216 try {
2217 var cards = [
9d33c892
IC
2218 [ "ac", "0101" ],
2219 [ "acqs", "11011100" ],
2220 [ "acks", "01011100" ],
2221 [ "2cac", "11111000" ],
2222 [ "2c", "0010" ],
2223 [ "3d", "0001" ],
2224 [ "4h", "1001" ],
87ad2c6e 2225 [ "5s", "1001" ],
9d33c892
IC
2226 [ "6c", "0000" ],
2227 [ "7d", "0001" ],
87ad2c6e 2228 [ "8h", "1011" ],
9d33c892
IC
2229 [ "9s", "0010" ],
2230 [ "tc", "1001" ],
2231 [ "jd", "1111" ],
2232 [ "qh", "0010" ],
2233 [ "ks", "0101" ],
2234 [ "ks2c", "01010100" ],
2235 [ "KS2C", "01010100" ],
adc8ce12
IC
2236 ];
2237 for (var i=0; i<cards.length; i++) {
2238 var card = cards[i][0];
2239 var result = cards[i][1];
2240 e = Entropy.fromString(card);
2241 console.log(e.binary + " " + result);
2242 if (e.binaryStr !== result) {
886f06ee 2243 return "card entropy " + card + " not parsed correctly: " + result + " != " + e.binaryStr;
adc8ce12
IC
2244 }
2245 }
2246 }
2247 catch (e) {
2248 return e.message;
2249 }
2250 return "PASS";
c6624d51 2251 });
adc8ce12 2252 if (response != "PASS") {
c6624d51 2253 console.log("Entropy unit tests");
adc8ce12 2254 console.log(response);
c6624d51
IC
2255 fail();
2256 };
2257 next();
2258});
2259},
2260
2261// Entropy can be entered by the user
2262function() {
2263page.open(url, function(status) {
2264 expected = {
2265 mnemonic: "abandon abandon ability",
2266 address: "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug",
2267 }
2268 // use entropy
2269 page.evaluate(function() {
2270 $(".use-entropy").prop("checked", true).trigger("change");
2271 $(".entropy").val("00000000 00000000 00000000 00000000").trigger("input");
2272 });
2273 // check the mnemonic is set and address is correct
2274 waitForGenerate(function() {
2275 var actual = page.evaluate(function() {
2276 return {
2277 address: $(".address:first").text(),
2278 mnemonic: $(".phrase").val(),
2279 }
2280 });
2281 if (actual.mnemonic != expected.mnemonic) {
2282 console.log("Entropy does not generate correct mnemonic");
2283 console.log("Expected: " + expected.mnemonic);
2284 console.log("Got: " + actual.mnemonic);
2285 fail();
2286 }
2287 if (actual.address != expected.address) {
2288 console.log("Entropy does not generate correct address");
2289 console.log("Expected: " + expected.address);
2290 console.log("Got: " + actual.address);
2291 fail();
2292 }
2293 next();
2294 });
2295});
2296},
2297
2298// A warning about entropy is shown to the user, with additional information
2299function() {
2300page.open(url, function(status) {
2301 // get text content from entropy sections of page
2302 var hasWarning = page.evaluate(function() {
2303 var entropyText = $(".entropy-container").text();
2304 var warning = "mnemonic may be insecure";
2305 if (entropyText.indexOf(warning) == -1) {
2306 return false;
2307 }
2308 var readMoreText = $("#entropy-notes").parent().text();
2309 var goodSources = "flipping a fair coin, rolling a fair dice, noise measurements etc";
2310 if (readMoreText.indexOf(goodSources) == -1) {
2311 return false;
2312 }
2313 return true;
2314 });
2315 // check the warnings and information are shown
2316 if (!hasWarning) {
2317 console.log("Page does not contain warning about using own entropy");
2318 fail();
2319 }
2320 next();
2321});
2322},
2323
2324// The types of entropy available are described to the user
2325function() {
2326page.open(url, function(status) {
2327 // get placeholder text for entropy field
2328 var placeholder = page.evaluate(function() {
2329 return $(".entropy").attr("placeholder");
2330 });
2331 var options = [
2332 "binary",
2333 "base 6",
2334 "dice",
2335 "base 10",
2336 "hexadecimal",
439f0e25 2337 "cards",
c6624d51
IC
2338 ];
2339 for (var i=0; i<options.length; i++) {
2340 var option = options[i];
2341 if (placeholder.indexOf(option) == -1) {
2342 console.log("Available entropy type is not shown to user: " + option);
2343 fail();
2344 }
2345 }
2346 next();
2347});
2348},
2349
2350// The actual entropy used is shown to the user
2351function() {
2352page.open(url, function(status) {
2353 // use entropy
2354 var badEntropySource = page.evaluate(function() {
2355 var entropy = "Not A Very Good Entropy Source At All";
2356 $(".use-entropy").prop("checked", true).trigger("change");
2357 $(".entropy").val(entropy).trigger("input");
2358 });
2359 // check the actual entropy being used is shown
057722b0 2360 waitForEntropyFeedback(function() {
c6624d51
IC
2361 var expectedText = "AedEceAA";
2362 var entropyText = page.evaluate(function() {
2363 return $(".entropy-container").text();
2364 });
2365 if (entropyText.indexOf(expectedText) == -1) {
2366 console.log("Actual entropy used is not shown");
2367 fail();
2368 }
2369 next();
2370 });
2371});
2372},
2373
2374// Binary entropy can be entered
2375function() {
2376page.open(url, function(status) {
2377 // use entropy
2378 page.evaluate(function() {
2379 $(".use-entropy").prop("checked", true).trigger("change");
2380 $(".entropy").val("01").trigger("input");
2381 });
2382 // check the entropy is shown to be the correct type
057722b0 2383 waitForEntropyFeedback(function() {
c6624d51
IC
2384 var entropyText = page.evaluate(function() {
2385 return $(".entropy-container").text();
2386 });
2387 if (entropyText.indexOf("binary") == -1) {
2388 console.log("Binary entropy is not detected and presented to user");
2389 fail();
2390 }
2391 next();
2392 });
2393});
2394},
2395
2396// Base 6 entropy can be entered
2397function() {
2398page.open(url, function(status) {
2399 // use entropy
2400 page.evaluate(function() {
2401 $(".use-entropy").prop("checked", true).trigger("change");
2402 $(".entropy").val("012345").trigger("input");
2403 });
2404 // check the entropy is shown to be the correct type
057722b0 2405 waitForEntropyFeedback(function() {
c6624d51
IC
2406 var entropyText = page.evaluate(function() {
2407 return $(".entropy-container").text();
2408 });
2409 if (entropyText.indexOf("base 6") == -1) {
2410 console.log("Base 6 entropy is not detected and presented to user");
2411 fail();
2412 }
2413 next();
2414 });
2415});
2416},
2417
2418// Base 6 dice entropy can be entered
2419function() {
2420page.open(url, function(status) {
2421 // use entropy
2422 page.evaluate(function() {
2423 $(".use-entropy").prop("checked", true).trigger("change");
2424 $(".entropy").val("123456").trigger("input");
2425 });
2426 // check the entropy is shown to be the correct type
057722b0 2427 waitForEntropyFeedback(function() {
c6624d51
IC
2428 var entropyText = page.evaluate(function() {
2429 return $(".entropy-container").text();
2430 });
2431 if (entropyText.indexOf("dice") == -1) {
2432 console.log("Dice entropy is not detected and presented to user");
2433 fail();
2434 }
2435 next();
2436 });
2437});
2438},
2439
2440// Base 10 entropy can be entered
2441function() {
2442page.open(url, function(status) {
2443 // use entropy
2444 page.evaluate(function() {
2445 $(".use-entropy").prop("checked", true).trigger("change");
2446 $(".entropy").val("789").trigger("input");
2447 });
2448 // check the entropy is shown to be the correct type
057722b0 2449 waitForEntropyFeedback(function() {
c6624d51
IC
2450 var entropyText = page.evaluate(function() {
2451 return $(".entropy-container").text();
2452 });
2453 if (entropyText.indexOf("base 10") == -1) {
2454 console.log("Base 10 entropy is not detected and presented to user");
2455 fail();
2456 }
2457 next();
2458 });
2459});
2460},
2461
2462// Hexadecimal entropy can be entered
2463function() {
2464page.open(url, function(status) {
2465 // use entropy
2466 page.evaluate(function() {
2467 $(".use-entropy").prop("checked", true).trigger("change");
2468 $(".entropy").val("abcdef").trigger("input");
2469 });
2470 // check the entropy is shown to be the correct type
057722b0 2471 waitForEntropyFeedback(function() {
c6624d51
IC
2472 var entropyText = page.evaluate(function() {
2473 return $(".entropy-container").text();
2474 });
2475 if (entropyText.indexOf("hexadecimal") == -1) {
2476 console.log("Hexadecimal entropy is not detected and presented to user");
2477 fail();
2478 }
2479 next();
2480 });
2481});
2482},
2483
2484// Dice entropy value is shown as the converted base 6 value
2485function() {
2486page.open(url, function(status) {
2487 // use entropy
2488 page.evaluate(function() {
2489 $(".use-entropy").prop("checked", true).trigger("change");
2490 $(".entropy").val("123456").trigger("input");
2491 });
2492 // check the entropy is shown as base 6, not as the original dice value
057722b0 2493 waitForEntropyFeedback(function() {
c6624d51
IC
2494 var entropyText = page.evaluate(function() {
2495 return $(".entropy-container").text();
2496 });
425b75a9 2497 if (entropyText.indexOf("123450") == -1) {
c6624d51
IC
2498 console.log("Dice entropy is not shown to user as base 6 value");
2499 fail();
2500 }
2501 if (entropyText.indexOf("123456") > -1) {
2502 console.log("Dice entropy value is shown instead of true base 6 value");
2503 fail();
2504 }
2505 next();
2506 });
2507});
2508},
2509
2510// The number of bits of entropy accumulated is shown
2511function() {
2512page.open(url, function(status) {
057722b0
IC
2513 //[ entropy, bits ]
2514 var tests = [
2515 [ "0000 0000 0000 0000 0000", "20" ],
2516 [ "0", "1" ],
2517 [ "0000", "4" ],
0d0f07f9
IC
2518 [ "6", "2" ], // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
2519 [ "7", "3" ], // 7 in base 10 is 111 in base 2, no leading zeros
057722b0
IC
2520 [ "8", "4" ],
2521 [ "F", "4" ],
1cf1bbaf 2522 [ "29", "6" ],
057722b0
IC
2523 [ "0A", "8" ],
2524 [ "1A", "8" ], // hex is always multiple of 4 bits of entropy
2525 [ "2A", "8" ],
2526 [ "4A", "8" ],
2527 [ "8A", "8" ],
2528 [ "FA", "8" ],
2529 [ "000A", "16" ],
0d0f07f9
IC
2530 [ "5555", "11" ],
2531 [ "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 2532 [ "2227", "13" ], // Uses base 10, which is 4 lots of 3.32 bits, which is 13.3 bits (rounded down to 13)
057722b0
IC
2533 [ "222F", "16" ],
2534 [ "FFFF", "16" ],
1cf1bbaf 2535 [ "0000101017", "33" ], // 10 events at 3.32 bits per event
6422c1cd 2536 [ "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 2537 ]
c6624d51
IC
2538 // use entropy
2539 page.evaluate(function(e) {
2540 $(".use-entropy").prop("checked", true).trigger("change");
2541 });
2542 // Run each test
2543 var nextTest = function runNextTest(i) {
057722b0
IC
2544 var entropy = tests[i][0];
2545 var expected = tests[i][1];
c6624d51
IC
2546 // set entropy
2547 page.evaluate(function(e) {
c6624d51
IC
2548 $(".entropy").val(e).trigger("input");
2549 }, entropy);
2550 // check the number of bits of entropy is shown
057722b0 2551 waitForEntropyFeedback(function() {
c6624d51 2552 var entropyText = page.evaluate(function() {
dd944906 2553 return $(".entropy-container").text();
c6624d51 2554 });
1cf1bbaf 2555 if (entropyText.replace(/\s/g,"").indexOf("Bits" + expected) == -1) {
c6624d51
IC
2556 console.log("Accumulated entropy is not shown correctly for " + entropy);
2557 fail();
2558 }
057722b0 2559 var isLastTest = i == tests.length - 1;
c6624d51
IC
2560 if (isLastTest) {
2561 next();
2562 }
2563 else {
2564 runNextTest(i+1);
2565 }
2566 });
2567 }
2568 nextTest(0);
2569});
2570},
2571
fb353f9d 2572// There is feedback provided about the supplied entropy
c6624d51
IC
2573function() {
2574page.open(url, function(status) {
2575 var tests = [
2576 {
2577 entropy: "A",
fb353f9d
IC
2578 filtered: "A",
2579 type: "hexadecimal",
2580 events: 1,
2581 bits: 4,
c6624d51 2582 words: 0,
1cf1bbaf 2583 strength: "extremely weak",
c6624d51
IC
2584 },
2585 {
2586 entropy: "AAAAAAAA",
fb353f9d
IC
2587 filtered: "AAAAAAAA",
2588 type: "hexadecimal",
2589 events: 8,
2590 bits: 32,
c6624d51 2591 words: 3,
1cf1bbaf 2592 strength: "extremely weak",
c6624d51
IC
2593 },
2594 {
2595 entropy: "AAAAAAAA B",
fb353f9d
IC
2596 filtered: "AAAAAAAAB",
2597 type: "hexadecimal",
2598 events: 9,
2599 bits: 36,
c6624d51 2600 words: 3,
1cf1bbaf 2601 strength: "extremely weak",
c6624d51
IC
2602 },
2603 {
2604 entropy: "AAAAAAAA BBBBBBBB",
fb353f9d
IC
2605 filtered: "AAAAAAAABBBBBBBB",
2606 type: "hexadecimal",
2607 events: 16,
2608 bits: 64,
c6624d51 2609 words: 6,
1cf1bbaf 2610 strength: "very weak",
c6624d51
IC
2611 },
2612 {
2613 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
fb353f9d
IC
2614 filtered: "AAAAAAAABBBBBBBBCCCCCCCC",
2615 type: "hexadecimal",
2616 events: 24,
2617 bits: 96,
c6624d51 2618 words: 9,
1cf1bbaf 2619 strength: "weak",
c6624d51
IC
2620 },
2621 {
2622 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
fb353f9d
IC
2623 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD",
2624 type: "hexadecimal",
2625 events: 32,
2626 bits: 128,
c6624d51 2627 words: 12,
e6a799cc
IC
2628 strength: "easily cracked",
2629 },
2630 {
2631 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA",
2632 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA",
2633 type: "hexadecimal",
2634 events: 32,
2635 bits: 128,
2636 words: 12,
1cf1bbaf 2637 strength: "strong",
c6624d51
IC
2638 },
2639 {
e6a799cc
IC
2640 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE",
2641 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE",
fb353f9d
IC
2642 type: "hexadecimal",
2643 events: 40,
2644 bits: 160,
c6624d51 2645 words: 15,
1cf1bbaf
IC
2646 strength: "very strong",
2647 },
2648 {
e6a799cc
IC
2649 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF",
2650 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF",
fb353f9d
IC
2651 type: "hexadecimal",
2652 events: 48,
2653 bits: 192,
1cf1bbaf
IC
2654 words: 18,
2655 strength: "extremely strong",
fb353f9d 2656 },
391c7f26
IC
2657 {
2658 entropy: "7d",
2659 type: "card",
2660 events: 1,
2661 bits: 5,
2662 words: 0,
2663 strength: "extremely weak",
2664 },
2665 {
2666 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2667 type: "card (full deck)",
2668 events: 52,
6422c1cd
IC
2669 bits: 225,
2670 words: 21,
391c7f26
IC
2671 strength: "extremely strong",
2672 },
2673 {
2674 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d",
2675 type: "card (full deck, 1 duplicate: 3d)",
2676 events: 53,
87ad2c6e 2677 bits: 254,
6422c1cd 2678 words: 21,
391c7f26
IC
2679 strength: "extremely strong",
2680 },
2681 {
2682 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d",
bbc29c80 2683 type: "card (2 duplicates: 3d 4d, 1 missing: KS)",
391c7f26 2684 events: 53,
87ad2c6e 2685 bits: 254,
6422c1cd 2686 words: 21,
391c7f26
IC
2687 strength: "extremely strong",
2688 },
2689 {
2690 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d",
bbc29c80 2691 type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)",
391c7f26 2692 events: 53,
87ad2c6e
IC
2693 bits: 264,
2694 words: 24,
391c7f26
IC
2695 strength: "extremely strong",
2696 },
9bc39377 2697 // Next test was throwing uncaught error in zxcvbn
6422c1cd 2698 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
9bc39377
IC
2699 {
2700 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2701 type: "card (full deck, 52 duplicates: ac 2c 3c...)",
2702 events: 104,
87ad2c6e
IC
2703 bits: 499,
2704 words: 45,
9bc39377
IC
2705 strength: "extremely strong",
2706 },
5c653a12
IC
2707 // Case insensitivity to duplicate cards
2708 {
2709 entropy: "asAS",
2710 type: "card (1 duplicate: AS)",
2711 events: 2,
87ad2c6e 2712 bits: 9,
5c653a12
IC
2713 words: 0,
2714 strength: "extremely weak",
2715 },
2716 {
2717 entropy: "ASas",
2718 type: "card (1 duplicate: as)",
2719 events: 2,
87ad2c6e 2720 bits: 9,
5c653a12
IC
2721 words: 0,
2722 strength: "extremely weak",
2723 },
bbc29c80
IC
2724 // Missing cards are detected
2725 {
2726 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2727 type: "card (1 missing: 9C)",
2728 events: 51,
87ad2c6e
IC
2729 bits: 221,
2730 words: 18,
bbc29c80
IC
2731 strength: "extremely strong",
2732 },
2733 {
2734 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2735 type: "card (2 missing: 9C 5D)",
2736 events: 50,
87ad2c6e
IC
2737 bits: 216,
2738 words: 18,
bbc29c80
IC
2739 strength: "extremely strong",
2740 },
2741 {
2742 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2743 type: "card (4 missing: 9C 5D QD...)",
2744 events: 48,
87ad2c6e 2745 bits: 208,
6422c1cd 2746 words: 18,
bbc29c80
IC
2747 strength: "extremely strong",
2748 },
2749 // More than six missing cards does not show message
2750 {
2751 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks",
2752 type: "card",
2753 events: 45,
87ad2c6e 2754 bits: 195,
6422c1cd 2755 words: 18,
bbc29c80
IC
2756 strength: "extremely strong",
2757 },
fc7c248f
IC
2758 // Multiple decks of cards increases bits per event
2759 {
2760 entropy: "3d",
2761 events: 1,
2762 bits: 4,
2763 bitsPerEvent: 4.34,
2764 },
2765 {
2766 entropy: "3d3d",
2767 events: 2,
2768 bits: 9,
2769 bitsPerEvent: 4.80,
2770 },
2771 {
2772 entropy: "3d3d3d",
2773 events: 3,
2774 bits: 15,
2775 bitsPerEvent: 5.01,
2776 },
2777 {
2778 entropy: "3d3d3d3d",
2779 events: 4,
2780 bits: 20,
2781 bitsPerEvent: 5.14,
2782 },
2783 {
2784 entropy: "3d3d3d3d3d",
2785 events: 5,
2786 bits: 26,
2787 bitsPerEvent: 5.22,
2788 },
2789 {
2790 entropy: "3d3d3d3d3d3d",
2791 events: 6,
2792 bits: 31,
2793 bitsPerEvent: 5.28,
2794 },
2795 {
2796 entropy: "3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d",
2797 events: 33,
2798 bits: 184,
2799 bitsPerEvent: 5.59,
2800 strength: 'easily cracked - Repeats like "abcabcabc" are only slightly harder to guess than "abc"',
2801 },
c6624d51
IC
2802 ];
2803 // use entropy
2804 page.evaluate(function() {
2805 $(".use-entropy").prop("checked", true).trigger("change");
2806 });
2807 var nextTest = function runNextTest(i) {
fb353f9d 2808 function getFeedbackError(expected, actual) {
391c7f26 2809 if ("filtered" in expected && actual.indexOf(expected.filtered) == -1) {
fb353f9d
IC
2810 return "Filtered value not in feedback";
2811 }
fc7c248f 2812 if ("type" in expected && actual.indexOf(expected.type) == -1) {
fb353f9d
IC
2813 return "Entropy type not in feedback";
2814 }
fc7c248f 2815 if ("events" in expected && actual.indexOf(expected.events) == -1) {
fb353f9d
IC
2816 return "Event count not in feedback";
2817 }
fc7c248f 2818 if ("bits" in expected && actual.indexOf(expected.bits) == -1) {
fb353f9d
IC
2819 return "Bit count not in feedback";
2820 }
fc7c248f 2821 if ("strength" in expected && actual.indexOf(expected.strength) == -1) {
fb353f9d
IC
2822 return "Strength not in feedback";
2823 }
fc7c248f
IC
2824 if ("bitsPerEvent" in expected && actual.indexOf(expected.bitsPerEvent) == -1) {
2825 return "bitsPerEvent not in feedback";
2826 }
fb353f9d
IC
2827 return false;
2828 }
c6624d51
IC
2829 test = tests[i];
2830 page.evaluate(function(e) {
2831 $(".addresses").empty();
057722b0 2832 $(".phrase").val("");
c6624d51
IC
2833 $(".entropy").val(e).trigger("input");
2834 }, test.entropy);
fb353f9d 2835 waitForEntropyFeedback(function() {
c6624d51
IC
2836 var mnemonic = page.evaluate(function() {
2837 return $(".phrase").val();
2838 });
fb353f9d 2839 // Check mnemonic length
fc7c248f 2840 if ("words" in test && test.words == 0) {
fb353f9d
IC
2841 if (mnemonic.length > 0) {
2842 console.log("Mnemonic length for " + test.strength + " strength is not " + test.words);
87ad2c6e 2843 console.log("Entropy: " + test.entropy);
fb353f9d
IC
2844 console.log("Mnemonic: " + mnemonic);
2845 fail();
2846 }
2847 }
fc7c248f 2848 else if ("words" in test) {
fb353f9d
IC
2849 if (mnemonic.split(" ").length != test.words) {
2850 console.log("Mnemonic length for " + test.strength + " strength is not " + test.words);
87ad2c6e 2851 console.log("Entropy: " + test.entropy);
fb353f9d
IC
2852 console.log("Mnemonic: " + mnemonic);
2853 fail();
2854 }
2855 }
2856 // check feedback
2857 var feedback = page.evaluate(function() {
dd944906 2858 return $(".entropy-container").text();
fb353f9d
IC
2859 });
2860 var feedbackError = getFeedbackError(test, feedback);
2861 if (feedbackError) {
2862 console.log("Entropy feedback for " + test.entropy + " returned error");
2863 console.log(feedbackError);
c6624d51
IC
2864 fail();
2865 }
fb353f9d 2866 // Run next test
c6624d51
IC
2867 var isLastTest = i == tests.length - 1;
2868 if (isLastTest) {
2869 next();
2870 }
2871 else {
2872 runNextTest(i+1);
2873 }
fb353f9d 2874 });
c6624d51
IC
2875 }
2876 nextTest(0);
2877});
2878},
2879
d6fd8ebf 2880// Entropy is truncated from the left
c6624d51
IC
2881function() {
2882page.open(url, function(status) {
d6fd8ebf 2883 var expected = "avocado zoo zone";
c6624d51
IC
2884 // use entropy
2885 page.evaluate(function() {
2886 $(".use-entropy").prop("checked", true).trigger("change");
2887 var entropy = "00000000 00000000 00000000 00000000";
d6fd8ebf 2888 entropy += "11111111 11111111 11111111 1111"; // Missing last byte
c6624d51
IC
2889 $(".entropy").val(entropy).trigger("input");
2890 });
2891 // check the entropy is truncated from the right
2892 waitForGenerate(function() {
2893 var actual = page.evaluate(function() {
2894 return $(".phrase").val();
2895 });
2896 if (actual != expected) {
2897 console.log("Entropy is not truncated from the right");
2898 console.log("Expected: " + expected);
2899 console.log("Got: " + actual);
2900 fail();
2901 }
2902 next();
2903 });
2904});
2905},
2906
2907// Very large entropy results in very long mnemonics
2908function() {
2909page.open(url, function(status) {
2910 // use entropy
2911 page.evaluate(function() {
2912 $(".use-entropy").prop("checked", true).trigger("change");
2913 var entropy = "";
2914 // Generate a very long entropy string
2915 for (var i=0; i<33; i++) {
2916 entropy += "AAAAAAAA"; // 3 words * 33 iterations = 99 words
2917 }
2918 $(".entropy").val(entropy).trigger("input");
2919 });
2920 // check the mnemonic is very long
2921 waitForGenerate(function() {
2922 var wordCount = page.evaluate(function() {
2923 return $(".phrase").val().split(" ").length;
2924 });
2925 if (wordCount != 99) {
2926 console.log("Large entropy does not generate long mnemonic");
2927 console.log("Expected 99 words, got " + wordCount);
2928 fail();
2929 }
2930 next();
2931 });
2932});
2933},
2934
2935// Is compatible with bip32jp entropy
2936// https://bip32jp.github.io/english/index.html
2937// NOTES:
2938// Is incompatible with:
c6624d51
IC
2939// base 20
2940function() {
2941page.open(url, function(status) {
0d0f07f9 2942 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
2943 // use entropy
2944 page.evaluate(function() {
2945 $(".use-entropy").prop("checked", true).trigger("change");
0d0f07f9 2946 var entropy = "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543";
c6624d51
IC
2947 $(".entropy").val(entropy).trigger("input");
2948 });
2949 // check the mnemonic matches the expected value from bip32jp
2950 waitForGenerate(function() {
2951 var actual = page.evaluate(function() {
2952 return $(".phrase").val();
2953 });
2954 if (actual != expected) {
2955 console.log("Mnemonic does not match bip32jp for base 6 entropy");
2956 console.log("Expected: " + expected);
2957 console.log("Got: " + actual);
2958 fail();
2959 }
2960 next();
2961 });
2962});
2963},
2964
057722b0
IC
2965// Blank entropy does not generate mnemonic or addresses
2966function() {
2967page.open(url, function(status) {
2968 // use entropy
2969 page.evaluate(function() {
2970 $(".use-entropy").prop("checked", true).trigger("change");
2971 $(".entropy").val("").trigger("input");
2972 });
2973 waitForFeedback(function() {
2974 // check there is no mnemonic
2975 var phrase = page.evaluate(function() {
2976 return $(".phrase").val();
2977 });
2978 if (phrase != "") {
2979 console.log("Blank entropy does not result in blank mnemonic");
2980 console.log("Got: " + phrase);
2981 fail();
2982 }
2983 // check there are no addresses displayed
2984 var addresses = page.evaluate(function() {
2985 return $(".address").length;
2986 });
2987 if (addresses != 0) {
2988 console.log("Blank entropy does not result in zero addresses");
2989 fail();
2990 }
2991 // Check the feedback says 'blank entropy'
2992 var feedback = page.evaluate(function() {
2993 return $(".feedback").text();
2994 });
2995 if (feedback != "Blank entropy") {
2996 console.log("Blank entropy does not show feedback message");
2997 fail();
2998 }
2999 next();
3000 });
3001});
3002},
3003
3599674d
IC
3004// Mnemonic length can be selected even for weak entropy
3005function() {
3006page.open(url, function(status) {
3007 // use entropy
3008 page.evaluate(function() {
3009 $(".use-entropy").prop("checked", true).trigger("change");
3010 $(".entropy").val("012345");
3011 $(".mnemonic-length").val("18").trigger("change");
3012 });
3013 // check the mnemonic is the correct length
3014 waitForGenerate(function() {
3015 var phrase = page.evaluate(function() {
3016 return $(".phrase").val();
3017 });
3018 var numberOfWords = phrase.split(/\s/g).length;
3019 if (numberOfWords != 18) {
3020 console.log("Weak entropy cannot be overridden to give 18 word mnemonic");
3021 console.log(phrase);
3022 fail();
3023 }
3024 next();
3025 });
3026});
3027},
3028
886f06ee
IC
3029// Github issue 33
3030// https://github.com/iancoleman/bip39/issues/33
3031// Final cards should contribute entropy
3032function() {
3033page.open(url, function(status) {
3034 // use entropy
3035 page.evaluate(function() {
3036 $(".use-entropy").prop("checked", true).trigger("change");
3037 $(".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");
3038 });
3039 // get the mnemonic
3040 waitForGenerate(function() {
3041 var originalPhrase = page.evaluate(function() {
3042 return $(".phrase").val();
3043 });
3044 // Set the last 12 cards to be AS
3045 page.evaluate(function() {
3046 $(".addresses").empty();
3047 $(".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");
3048 });
3049 // get the new mnemonic
3050 waitForGenerate(function() {
3051 var newPhrase = page.evaluate(function() {
3052 return $(".phrase").val();
3053 });
92e73fd9 3054 // check the phrase has changed
886f06ee
IC
3055 if (newPhrase == originalPhrase) {
3056 console.log("Changing last 12 cards does not change mnemonic");
3057 console.log("Original:");
3058 console.log(originalPhrase);
3059 console.log("New:");
3060 console.log(newPhrase);
3061 fail();
3062 }
3063 next();
3064 });
3065 });
3066});
3067},
3068
e00964cc
IC
3069// Github issue 35
3070// https://github.com/iancoleman/bip39/issues/35
3071// QR Code support
3072function() {
3073page.open(url, function(status) {
3074 // use entropy
3075 page.evaluate(function() {
3076 $(".generate").click();
3077 });
3078 waitForGenerate(function() {
3079 var p = page.evaluate(function() {
3080 // get position of mnemonic element
3081 return $(".phrase").offset();
3082 });
3083 p.top = Math.ceil(p.top);
3084 p.left = Math.ceil(p.left);
3085 // check the qr code shows
3086 page.sendEvent("mousemove", p.left+4, p.top+4);
3087 var qrShowing = page.evaluate(function() {
3088 return $(".qr-container").find("canvas").length > 0;
3089 });
3090 if (!qrShowing) {
3091 console.log("QR Code does not show");
3092 fail();
3093 }
3094 // check the qr code hides
3095 page.sendEvent("mousemove", p.left-4, p.top-4);
3096 var qrHidden = page.evaluate(function() {
3097 return $(".qr-container").find("canvas").length == 0;
3098 });
3099 if (!qrHidden) {
3100 console.log("QR Code does not hide");
3101 fail();
3102 }
3103 next();
3104 });
3105});
3106},
3107
c554e6ff
IC
3108// BIP44 account extendend private key is shown
3109// github issue 37 - compatibility with electrum
3110function() {
3111page.open(url, function(status) {
3112 // set the phrase
3113 var expected = "xprv9yzrnt4zWVJUr1k2VxSPy9ettKz5PpeDMgaVG7UKedhqnw1tDkxP2UyYNhuNSumk2sLE5ctwKZs9vwjsq3e1vo9egCK6CzP87H2cVYXpfwQ";
3114 page.evaluate(function() {
3115 $(".phrase").val("abandon abandon ability");
3116 $(".phrase").trigger("input");
3117 });
3118 // check the BIP44 account extended private key
3119 waitForGenerate(function() {
3120 var actual = page.evaluate(function() {
3121 return $(".account-xprv").val();
3122 });
3123 if (actual != expected) {
3124 console.log("BIP44 account extended private key is incorrect");
3125 console.log("Expected: " + expected);
3126 console.log("Actual: " + actual);
3127 fail();
3128 }
3129 next();
3130 });
3131});
3132},
3133
3134// BIP44 account extendend public key is shown
3135// github issue 37 - compatibility with electrum
3136function() {
3137page.open(url, function(status) {
3138 // set the phrase
3139 var expected = "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3140 page.evaluate(function() {
3141 $(".phrase").val("abandon abandon ability");
3142 $(".phrase").trigger("input");
3143 });
3144 // check the BIP44 account extended public key
3145 waitForGenerate(function() {
3146 var actual = page.evaluate(function() {
3147 return $(".account-xpub").val();
3148 });
3149 if (actual != expected) {
3150 console.log("BIP44 account extended public key is incorrect");
3151 console.log("Expected: " + expected);
ba3cb9ec
IC
3152 console.log("Actual: " + actual);
3153 fail();
3154 }
3155 next();
3156 });
3157});
3158},
3159
3160// github issue 40
3161// BIP32 root key can be set as an xpub
3162function() {
3163page.open(url, function(status) {
3164 // set the phrase
3165 page.evaluate(function() {
3166 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3167 var bip44AccountXpub = "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3168 $("#root-key").val(bip44AccountXpub);
3169 $("#root-key").trigger("input");
3170 });
3171 waitForFeedback(function() {
3172 page.evaluate(function() {
3173 // Use bip32 tab
3174 $("#bip32-tab a").click();
3175 });
3176 waitForGenerate(function() {
3177 page.evaluate(function() {
3178 // derive external addresses for this xpub
3179 var firstAccountDerivationPath = "m/0";
3180 $("#bip32-path").val(firstAccountDerivationPath);
3181 $("#bip32-path").trigger("input");
3182 });
3183 waitForGenerate(function() {
3184 // check the addresses are generated
3185 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3186 var actual = page.evaluate(function() {
3187 return $(".address:first").text();
3188 });
3189 if (actual != expected) {
3190 console.log("xpub key does not generate addresses in table");
3191 console.log("Expected: " + expected);
3192 console.log("Actual: " + actual);
3193 fail();
3194 }
3195 // check the xprv key is not set
3196 var expected = "NA";
3197 var actual = page.evaluate(function() {
3198 return $(".extended-priv-key").val();
3199 });
3200 if (actual != expected) {
3201 console.log("xpub key as root shows derived bip32 xprv key");
3202 console.log("Expected: " + expected);
3203 console.log("Actual: " + actual);
3204 fail();
3205 }
3206 // check the private key is not set
3207 var expected = "NA";
3208 var actual = page.evaluate(function() {
3209 return $(".privkey:first").text();
3210 });
3211 if (actual != expected) {
3212 console.log("xpub key generates private key in addresses table");
3213 console.log("Expected: " + expected);
3214 console.log("Actual: " + actual);
3215 fail();
3216 }
3217 next();
3218 });
3219 });
3220 });
3221});
3222},
3223
3224// github issue 40
3225// xpub for bip32 root key will not work with hardened derivation paths
3226function() {
3227page.open(url, function(status) {
3228 // set the phrase
3229 page.evaluate(function() {
3230 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3231 var bip44AccountXpub = "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3232 $("#root-key").val(bip44AccountXpub);
3233 $("#root-key").trigger("input");
3234 });
3235 waitForFeedback(function() {
3236 // Check feedback is correct
3237 var expected = "Hardened derivation path is invalid with xpub key";
3238 var actual = page.evaluate(function() {
3239 return $(".feedback").text();
3240 });
3241 if (actual != expected) {
3242 console.log("xpub key with hardened derivation path does not show feedback");
3243 console.log("Expected: " + expected);
3244 console.log("Actual: " + actual);
3245 fail();
3246 }
3247 // Check no addresses are shown
3248 var expected = 0;
3249 var actual = page.evaluate(function() {
3250 return $(".addresses tr").length;
3251 });
3252 if (actual != expected) {
3253 console.log("addresses still show after setting xpub key with hardened derivation path");
3254 console.log("Expected: " + expected);
c554e6ff
IC
3255 console.log("Actual: " + actual);
3256 fail();
3257 }
3258 next();
3259 });
3260});
3261},
3262
0a1f0259
IC
3263// github issue 39
3264// no root key shows feedback
3265function() {
3266page.open(url, function(status) {
3267 // click the bip32 tab on fresh page
3268 page.evaluate(function() {
3269 $("#bip32-tab a").click();
3270 });
3271 waitForFeedback(function() {
3272 // Check feedback is correct
3273 var expected = "No root key";
3274 var actual = page.evaluate(function() {
3275 return $(".feedback").text();
3276 });
3277 if (actual != expected) {
3278 console.log("Blank root key not detected");
3279 console.log("Expected: " + expected);
3280 console.log("Actual: " + actual);
3281 fail();
3282 }
3283 next();
3284 });
3285});
3286},
886f06ee 3287
40892aba
IC
3288// Github issue 44
3289// display error switching tabs while addresses are generating
3290function() {
3291page.open(url, function(status) {
3292 // set the phrase
3293 page.evaluate(function() {
3294 $(".phrase").val("abandon abandon ability").trigger("input");
3295 });
3296 waitForGenerate(function() {
3297 // set to generate 500 more addresses
3298 // generate more addresses
3299 // change tabs which should cancel the previous generating
3300 page.evaluate(function() {
3301 $(".rows-to-add").val("100");
3302 $(".more").click();
3303 $("#bip32-tab a").click();
3304 });
3305 // check the derivation paths are in order and of the right quantity
3306 waitForGenerate(function() {
3307 var paths = page.evaluate(function() {
3308 return $(".index").map(function(i, e) {
3309 return $(e).text();
3310 });
3311 });
3312 for (var i=0; i<paths.length; i++) {
3313 var expected = "m/0/" + i;
3314 var actual = paths[i];
3315 if (actual != expected) {
3316 console.log("Path " + i + " is not in correct order");
3317 console.log("Expected: " + expected);
3318 console.log("Actual: " + actual);
3319 fail();
3320 }
3321 }
3322 if (paths.length != 20) {
3323 console.log("Generation was not cancelled by new action");
3324 fail();
3325 }
3326 next();
3327 });
3328 });
3329});
3330},
3331
53aaab27
IC
3332// Github issue 49
3333// padding for binary should give length with multiple of 256
3334// hashed entropy 1111 is length 252, so requires 4 leading zeros
3335// prior to issue 49 it would only generate 2 leading zeros, ie missing 2
3336function() {
3337page.open(url, function(status) {
3338 expected = "avocado valid quantum cross link predict excuse edit street able flame large galaxy ginger nuclear"
3339 // use entropy
3340 page.evaluate(function() {
3341 $(".use-entropy").prop("checked", true).trigger("change");
5ed50bd9 3342 $(".mnemonic-length").val("15");
53aaab27
IC
3343 $(".entropy").val("1111").trigger("input");
3344 });
3345 waitForGenerate(function() {
3346 // get the mnemonic
3347 var actual = page.evaluate(function() {
3348 return $(".phrase").val();
3349 });
3350 // check the mnemonic is correct
3351 if (actual != expected) {
3352 console.log("Left padding error for entropy");
3353 console.log("Expected: " + expected);
3354 console.log("Actual: " + actual);
3355 fail();
3356 }
3357 next();
3358 });
3359});
3360},
3361
b0fb45b9
IC
3362// If you wish to add more tests, do so here...
3363
3364// Here is a blank test template
3365/*
3366
3367function() {
3368page.open(url, function(status) {
3369 // Do something on the page
3370 page.evaluate(function() {
3371 $(".phrase").val("abandon abandon ability").trigger("input");
3372 });
3373 waitForGenerate(function() {
3374 // Check the result of doing the thing
3375 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3376 var actual = page.evaluate(function() {
3377 return $(".address:first").text();
3378 });
3379 if (actual != expected) {
3380 console.log("A specific message about what failed");
3381 console.log("Expected: " + expected);
3382 console.log("Actual: " + actual);
3383 fail();
3384 }
3385 // Run the next test
3386 next();
3387 });
3388});
3389},
3390
3391*/
3392
88e2cdaa
IC
3393];
3394
3395console.log("Running tests...");
fb372687 3396tests = shuffle(tests);
88e2cdaa 3397next();