]>
git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blob - tests.js
2 // $ phantomjs tests.js
5 var page
= require('webpage').create();
6 var url
= 'src/index.html';
7 var testMaxTime
= 5000;
9 page
.onResourceError = function(e
) {
10 console
.log("Error loading " + e
.url
);
15 console
.log("Failed");
19 function waitForGenerate(fn
, maxTime
) {
21 maxTime
= testMaxTime
;
23 var start
= new Date().getTime();
24 var prevAddressCount
= -1;
25 var wait
= function keepWaiting() {
26 var now
= new Date().getTime();
27 var hasTimedOut
= now
- start
> maxTime
;
28 var addressCount
= page
.evaluate(function() {
29 return $(".address").length
;
31 var hasFinished
= addressCount
> 0 && addressCount
== prevAddressCount
;
32 prevAddressCount
= addressCount
;
36 else if (hasTimedOut
) {
37 console
.log("Test timed out");
41 setTimeout(keepWaiting
, 100);
48 if (tests
.length
> 0) {
49 var testsStr
= tests
.length
== 1 ? "test" : "tests";
50 console
.log(tests
.length
+ " " + testsStr
+ " remaining");
54 console
.log("Finished with 0 failures");
60 * Randomize array element order in-place.
61 * Using Durstenfeld shuffle algorithm.
62 * See http://stackoverflow.com/a/12646864
64 function shuffle(array
) {
65 for (var i
= array
.length
- 1; i
> 0; i
--) {
66 var j
= Math
.floor(Math
.random() * (i
+ 1));
76 // Page loads with status of 'success'
78 page
.open(url
, function(status
) {
79 if (status
!= "success") {
80 console
.log("Page did not load with status 'success'");
89 page
.open(url
, function(status
) {
90 var content
= page
.evaluate(function() {
91 return document
.body
.textContent
.trim();
94 console
.log("Page does not have text");
101 // Entering mnemonic generates addresses
103 page
.open(url
, function(status
) {
104 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
106 page
.evaluate(function() {
107 $(".phrase").val("abandon abandon ability").trigger("input");
110 waitForGenerate(function() {
111 var actual
= page
.evaluate(function() {
112 return $(".address:first").text();
114 if (actual
!= expected
) {
115 console
.log("Mnemonic did not generate address");
116 console
.log("Expected: " + expected
);
117 console
.log("Got: " + actual
);
125 // Random button generates random mnemonic
127 page
.open(url
, function(status
) {
128 // check initial phrase is empty
129 var phrase
= page
.evaluate(function() {
130 return $(".phrase").text();
133 console
.log("Initial phrase is not blank");
136 // press the 'generate' button
137 page
.evaluate(function() {
138 $(".generate").click();
140 // get the new phrase
141 waitForGenerate(function() {
142 var phrase
= page
.evaluate(function() {
143 return $(".phrase").val();
145 if (phrase
.length
<= 0) {
146 console
.log("Phrase not generated by pressing button");
154 // Mnemonic length can be customized
156 page
.open(url
, function(status
) {
157 // set the length to 6
158 var expectedLength
= "6";
159 page
.evaluate(function() {
160 $(".strength option[selected]").removeAttr("selected");
161 $(".strength option[value=6]").prop("selected", true);
163 // press the 'generate' button
164 page
.evaluate(function() {
165 $(".generate").click();
167 // check the new phrase is six words long
168 waitForGenerate(function() {
169 var actualLength
= page
.evaluate(function() {
170 var words
= $(".phrase").val().split(" ");
173 if (actualLength
!= expectedLength
) {
174 console
.log("Phrase not generated with correct length");
175 console
.log("Expected: " + expectedLength
);
176 console
.log("Actual: " + actualLength
);
184 // Passphrase can be set
186 page
.open(url
, function(status
) {
187 // set the phrase and passphrase
188 var expected
= "15pJzUWPGzR7avffV9nY5by4PSgSKG9rba";
189 page
.evaluate(function() {
190 $(".phrase").val("abandon abandon ability");
191 $(".passphrase").val("secure_passphrase").trigger("input");
193 // check the address is generated correctly
194 waitForGenerate(function() {
195 var actual
= page
.evaluate(function() {
196 return $(".address:first").text();
198 if (actual
!= expected
) {
199 console
.log("Passphrase results in wrong address");
200 console
.log("Expected: " + expected
);
201 console
.log("Actual: " + actual
);
209 // Network can be set to bitcoin testnet
211 page
.open(url
, function(status
) {
212 // set the phrase and coin
213 var expected
= "mucaU5iiDaJDb69BHLeDv8JFfGiyg2nJKi";
214 page
.evaluate(function() {
215 $(".phrase").val("abandon abandon ability");
216 $(".phrase").trigger("input");
217 $(".network option[selected]").removeAttr("selected");
218 $(".network option[value=1]").prop("selected", true);
219 $(".network").trigger("change");
221 // check the address is generated correctly
222 waitForGenerate(function() {
223 var actual
= page
.evaluate(function() {
224 return $(".address:first").text();
226 if (actual
!= expected
) {
227 console
.log("Bitcoin testnet address is incorrect");
228 console
.log("Expected: " + expected
);
229 console
.log("Actual: " + actual
);
237 // Network can be set to litecoin
239 page
.open(url
, function(status
) {
240 // set the phrase and coin
241 var expected
= "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn";
242 page
.evaluate(function() {
243 $(".phrase").val("abandon abandon ability");
244 $(".phrase").trigger("input");
245 $(".network option[selected]").removeAttr("selected");
246 $(".network option[value=2]").prop("selected", true);
247 $(".network").trigger("change");
249 // check the address is generated correctly
250 waitForGenerate(function() {
251 var actual
= page
.evaluate(function() {
252 return $(".address:first").text();
254 if (actual
!= expected
) {
255 console
.log("Litecoin address is incorrect");
256 console
.log("Expected: " + expected
);
257 console
.log("Actual: " + actual
);
265 // Network can be set to dogecoin
267 page
.open(url
, function(status
) {
268 // set the phrase and coin
269 var expected
= "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA";
270 page
.evaluate(function() {
271 $(".phrase").val("abandon abandon ability");
272 $(".phrase").trigger("input");
273 $(".network option[selected]").removeAttr("selected");
274 $(".network option[value=3]").prop("selected", true);
275 $(".network").trigger("change");
277 // check the address is generated correctly
278 waitForGenerate(function() {
279 var actual
= page
.evaluate(function() {
280 return $(".address:first").text();
282 if (actual
!= expected
) {
283 console
.log("Dogecoin address is incorrect");
284 console
.log("Expected: " + expected
);
285 console
.log("Actual: " + actual
);
293 // Network can be set to shadowcash
295 page
.open(url
, function(status
) {
296 // set the phrase and coin
297 var expected
= "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG";
298 page
.evaluate(function() {
299 $(".phrase").val("abandon abandon ability");
300 $(".phrase").trigger("input");
301 $(".network option[selected]").removeAttr("selected");
302 $(".network option[value=4]").prop("selected", true);
303 $(".network").trigger("change");
305 // check the address is generated correctly
306 waitForGenerate(function() {
307 var actual
= page
.evaluate(function() {
308 return $(".address:first").text();
310 if (actual
!= expected
) {
311 console
.log("Shadowcash address is incorrect");
312 console
.log("Expected: " + expected
);
313 console
.log("Actual: " + actual
);
321 // Network can be set to shadowcash testnet
323 page
.open(url
, function(status
) {
324 // set the phrase and coin
325 var expected
= "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
326 page
.evaluate(function() {
327 $(".phrase").val("abandon abandon ability");
328 $(".phrase").trigger("input");
329 $(".network option[selected]").removeAttr("selected");
330 $(".network option[value=5]").prop("selected", true);
331 $(".network").trigger("change");
333 // check the address is generated correctly
334 waitForGenerate(function() {
335 var actual
= page
.evaluate(function() {
336 return $(".address:first").text();
338 if (actual
!= expected
) {
339 console
.log("Shadowcash testnet address is incorrect");
340 console
.log("Expected: " + expected
);
341 console
.log("Actual: " + actual
);
349 // Network can be set to viacoin
351 page
.open(url
, function(status
) {
352 // set the phrase and coin
353 var expected
= "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
354 page
.evaluate(function() {
355 $(".phrase").val("abandon abandon ability");
356 $(".phrase").trigger("input");
357 $(".network option[selected]").removeAttr("selected");
358 $(".network option[value=6]").prop("selected", true);
359 $(".network").trigger("change");
361 // check the address is generated correctly
362 waitForGenerate(function() {
363 var actual
= page
.evaluate(function() {
364 return $(".address:first").text();
366 if (actual
!= expected
) {
367 console
.log("Viacoin address is incorrect");
368 console
.log("Expected: " + expected
);
369 console
.log("Actual: " + actual
);
377 // Network can be set to viacoin testnet
379 page
.open(url
, function(status
) {
380 // set the phrase and coin
381 var expected
= "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
382 page
.evaluate(function() {
383 $(".phrase").val("abandon abandon ability");
384 $(".phrase").trigger("input");
385 $(".network option[selected]").removeAttr("selected");
386 $(".network option[value=7]").prop("selected", true);
387 $(".network").trigger("change");
389 // check the address is generated correctly
390 waitForGenerate(function() {
391 var actual
= page
.evaluate(function() {
392 return $(".address:first").text();
394 if (actual
!= expected
) {
395 console
.log("Viacoin testnet address is incorrect");
396 console
.log("Expected: " + expected
);
397 console
.log("Actual: " + actual
);
405 // Network can be set to jumbucks
407 page
.open(url
, function(status
) {
408 // set the phrase and coin
409 var expected
= "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew";
410 page
.evaluate(function() {
411 $(".phrase").val("abandon abandon ability");
412 $(".phrase").trigger("input");
413 $(".network option[selected]").removeAttr("selected");
414 $(".network option[value=8]").prop("selected", true);
415 $(".network").trigger("change");
417 // check the address is generated correctly
418 waitForGenerate(function() {
419 var actual
= page
.evaluate(function() {
420 return $(".address:first").text();
422 if (actual
!= expected
) {
423 console
.log("Jumbucks address is incorrect");
424 console
.log("Expected: " + expected
);
425 console
.log("Actual: " + actual
);
433 // Network can be set to clam
435 page
.open(url
, function(status
) {
436 // set the phrase and coin
437 var expected
= "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y";
438 page
.evaluate(function() {
439 $(".phrase").val("abandon abandon ability");
440 $(".phrase").trigger("input");
441 $(".network option[selected]").removeAttr("selected");
442 $(".network option[value=9]").prop("selected", true);
443 $(".network").trigger("change");
445 // check the address is generated correctly
446 waitForGenerate(function() {
447 var actual
= page
.evaluate(function() {
448 return $(".address:first").text();
450 if (actual
!= expected
) {
451 console
.log("CLAM address is incorrect");
452 console
.log("Expected: " + expected
);
453 console
.log("Actual: " + actual
);
461 // BIP39 seed is set from phrase
463 page
.open(url
, function(status
) {
465 var expected
= "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
466 page
.evaluate(function() {
467 $(".phrase").val("abandon abandon ability");
468 $(".phrase").trigger("input");
470 // check the address is generated correctly
471 waitForGenerate(function() {
472 var actual
= page
.evaluate(function() {
473 return $(".seed").val();
475 if (actual
!= expected
) {
476 console
.log("BIP39 seed is incorrectly generated from mnemonic");
477 console
.log("Expected: " + expected
);
478 console
.log("Actual: " + actual
);
486 // BIP32 root key is set from phrase
488 page
.open(url
, function(status
) {
490 var expected
= "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi";
491 page
.evaluate(function() {
492 $(".phrase").val("abandon abandon ability");
493 $(".phrase").trigger("input");
495 // check the address is generated correctly
496 waitForGenerate(function() {
497 var actual
= page
.evaluate(function() {
498 return $(".root-key").val();
500 if (actual
!= expected
) {
501 console
.log("Root key is incorrectly generated from mnemonic");
502 console
.log("Expected: " + expected
);
503 console
.log("Actual: " + actual
);
511 // Tabs show correct addresses when changed
513 page
.open(url
, function(status
) {
515 var expected
= "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz";
516 page
.evaluate(function() {
517 $(".phrase").val("abandon abandon ability");
518 $(".phrase").trigger("input");
521 waitForGenerate(function() {
522 page
.evaluate(function() {
523 $("#bip32-tab a").click();
525 // check the address is generated correctly
526 waitForGenerate(function() {
527 var actual
= page
.evaluate(function() {
528 return $(".address:first").text();
530 if (actual
!= expected
) {
531 console
.log("Clicking tab generates incorrect address");
532 console
.log("Expected: " + expected
);
533 console
.log("Actual: " + actual
);
542 // BIP44 derivation path is shown
544 page
.open(url
, function(status
) {
546 var expected
= "m/44'/0'/0'/0";
547 page
.evaluate(function() {
548 $(".phrase").val("abandon abandon ability");
549 $(".phrase").trigger("input");
551 // check the derivation path of the first address
552 waitForGenerate(function() {
553 var actual
= page
.evaluate(function() {
554 return $("#bip44 .path").val();
556 if (actual
!= expected
) {
557 console
.log("BIP44 derivation path is incorrect");
558 console
.log("Expected: " + expected
);
559 console
.log("Actual: " + actual
);
567 // BIP44 extended private key is shown
569 page
.open(url
, function(status
) {
571 var expected
= "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG";
572 page
.evaluate(function() {
573 $(".phrase").val("abandon abandon ability");
574 $(".phrase").trigger("input");
576 // check the derivation path of the first address
577 waitForGenerate(function() {
578 var actual
= page
.evaluate(function() {
579 return $(".extended-priv-key").val();
581 if (actual
!= expected
) {
582 console
.log("BIP44 extended private key is incorrect");
583 console
.log("Expected: " + expected
);
584 console
.log("Actual: " + actual
);
592 // BIP44 extended public key is shown
593 // BIP44 purpose field changes address list
594 // BIP44 coin field changes address list
595 // BIP44 account field changes address list
596 // BIP44 external/internal field changes address list
598 // BIP32 derivation path can be set
599 // BIP32 can use hardened derivation paths
600 // BIP32 extended private key is shown
601 // BIP32 extended public key is shown
603 // Derivation path is shown in table
604 // Derivation path for address can be hardened
605 // Derivation path visibility can be toggled
607 // Addresses are shown in order of derivation path
608 // Address visibility can be toggled
609 // Private key is shown
610 // Private key visibility can be toggled
612 // More addresses can be generated
613 // A custom number of additional addresses can be generated
614 // Additional addresses are shown in order of derivation path
616 // BIP32 root key can be set by the user
617 // Setting BIP32 root key clears the existing phrase, passphrase and seed
618 // Clearing of phrase, passphrase and seed can be cancelled by user
619 // Custom BIP32 root key is used when changing the derivation path
621 // Incorrect mnemonic shows error
622 // Incorrect word shows suggested replacement
623 // Incorrect BIP32 root key shows error
624 // Derivation path not starting with m shows error
625 // Derivation path containing invalid characters shows useful error
627 // Github Issue 11: Default word length is 15
628 // https://github.com/dcpos/bip39/issues/11
630 // Github Issue 12: Generate more rows with private keys hidden
631 // https://github.com/dcpos/bip39/issues/12
633 // Github Issue 19: Mnemonic is not sensitive to whitespace
634 // https://github.com/dcpos/bip39/issues/19
636 // Github Issue 23: Use correct derivation path when changing tabs
637 // https://github.com/dcpos/bip39/issues/23
641 console
.log("Running tests...");
642 tests
= shuffle(tests
);