]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blob - tests/spec/tests.js
Increase allowed rendering time tests
[perso/Immae/Projets/Cryptomonnaies/BIP39.git] / tests / spec / tests.js
1 // Usage:
2 // cd /path/to/repo/tests
3 // jasmine spec/tests.js
4 //
5 // Dependencies:
6 // nodejs
7 // selenium
8 // jasmine
9 // see https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode#Automated_testing_with_headless_mode
10
11 // USER SPECIFIED OPTIONS
12 var browser = process.env.BROWSER; //"firefox"; // or "chrome"
13 if (!browser) {
14 console.log("Browser can be set via environment variable, eg");
15 console.log("BROWSER=firefox jasmine spec/tests.js");
16 console.log("Options for BROWSER are firefox chrome");
17 console.log("Using default browser: chrome");
18 browser = "chrome";
19 }
20 else {
21 console.log("Using browser: " + browser);
22 }
23
24 // Globals
25
26 var webdriver = require('selenium-webdriver');
27 var By = webdriver.By;
28 var Key = webdriver.Key;
29 var until = webdriver.until;
30 var newDriver = null;
31 var driver = null;
32 // Delays in ms
33 var generateDelay = 1500;
34 var feedbackDelay = 500;
35 var entropyFeedbackDelay = 500;
36
37 // url uses file:// scheme
38 var path = require('path')
39 var parentDir = path.resolve(process.cwd(), '..', 'src', 'index.html');
40 var url = "file://" + parentDir;
41 if (browser == "firefox") {
42 // TODO loading local html in firefox is broken
43 console.log("Loading local html in firefox is broken, see https://stackoverflow.com/q/46367054");
44 console.log("You must run a server in this case, ie do this:");
45 console.log("$ cd /path/to/bip39/src");
46 console.log("$ python -m http.server");
47 url = "http://localhost:8000";
48 }
49
50 // Variables dependent on specific browser selection
51
52 if (browser == "firefox") {
53 var firefox = require('selenium-webdriver/firefox');
54 var binary = new firefox.Binary(firefox.Channel.NIGHTLY);
55 binary.addArguments("-headless");
56 newDriver = function() {
57 return new webdriver.Builder()
58 .forBrowser('firefox')
59 .setFirefoxOptions(new firefox.Options().setBinary(binary))
60 .build();
61 }
62 }
63 else if (browser == "chrome") {
64 var chrome = require('selenium-webdriver/chrome');
65 newDriver = function() {
66 return new webdriver.Builder()
67 .forBrowser('chrome')
68 .setChromeOptions(new chrome.Options().addArguments("headless"))
69 .build();
70 }
71 }
72
73 // Helper functions
74
75 function testNetwork(done, params) {
76 var phrase = params.phrase || 'abandon abandon ability';
77 driver.findElement(By.css('.phrase'))
78 .sendKeys(phrase);
79 selectNetwork(params.selectText);
80 driver.sleep(generateDelay).then(function() {
81 getFirstAddress(function(address) {
82 expect(address).toBe(params.firstAddress);
83 done();
84 });
85 });
86 }
87
88 function getFirstRowValue(handler, selector) {
89 driver.findElements(By.css(selector))
90 .then(function(els) {
91 els[0].getText()
92 .then(handler);
93 })
94 }
95
96 function getFirstAddress(handler) {
97 getFirstRowValue(handler, ".address");
98 }
99
100 function getFirstPath(handler) {
101 getFirstRowValue(handler, ".index");
102 }
103
104 function testColumnValuesAreInvisible(done, columnClassName) {
105 var selector = "." + columnClassName + " span";
106 driver.findElements(By.css(selector))
107 .then(function(els) {
108 els[0].getAttribute("class")
109 .then(function(classes) {
110 expect(classes).toContain("invisible");
111 done();
112 });
113 })
114 }
115
116 function testRowsAreInCorrectOrder(done) {
117 driver.findElements(By.css('.index'))
118 .then(function(els) {
119 var testRowAtIndex = function(i) {
120 if (i >= els.length) {
121 done();
122 }
123 else {
124 els[i].getText()
125 .then(function(actualPath) {
126 var noHardened = actualPath.replace(/'/g, "");
127 var pathBits = noHardened.split("/")
128 var lastBit = pathBits[pathBits.length-1];
129 var actualIndex = parseInt(lastBit);
130 expect(actualIndex).toBe(i);
131 testRowAtIndex(i+1);
132 });
133 }
134 }
135 testRowAtIndex(0);
136 });
137 }
138
139 function selectNetwork(name) {
140 driver.executeScript(function() {
141 var selectText = arguments[0];
142 $(".network option[selected]").removeAttr("selected");
143 $(".network option").filter(function(i,e) {
144 return $(e).html() == selectText;
145 }).prop("selected", true);
146 $(".network").trigger("change");
147 }, name);
148 }
149
150 function testEntropyType(done, entropyText, entropyTypeUnsafe) {
151 // entropy type is compiled into regexp so needs escaping
152 // see https://stackoverflow.com/a/2593661
153 var entropyType = (entropyTypeUnsafe+'').replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&");
154 driver.findElement(By.css('.use-entropy'))
155 .click();
156 driver.findElement(By.css('.entropy'))
157 .sendKeys(entropyText);
158 driver.sleep(generateDelay).then(function() {
159 driver.findElement(By.css('.entropy-container'))
160 .getText()
161 .then(function(text) {
162 var re = new RegExp("Entropy Type\\s+" + entropyType);
163 expect(text).toMatch(re);
164 done();
165 });
166 });
167 }
168
169 function testEntropyBits(done, entropyText, entropyBits) {
170 driver.findElement(By.css('.use-entropy'))
171 .click();
172 driver.findElement(By.css('.entropy'))
173 .sendKeys(entropyText);
174 driver.sleep(generateDelay).then(function() {
175 driver.findElement(By.css('.entropy-container'))
176 .getText()
177 .then(function(text) {
178 var re = new RegExp("Total Bits\\s+" + entropyBits);
179 expect(text).toMatch(re);
180 done();
181 });
182 });
183 }
184
185 function testEntropyFeedback(done, entropyDetail) {
186 // entropy type is compiled into regexp so needs escaping
187 // see https://stackoverflow.com/a/2593661
188 if ("type" in entropyDetail) {
189 entropyDetail.type = (entropyDetail.type+'').replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&");
190 }
191 driver.findElement(By.css('.use-entropy'))
192 .click();
193 driver.findElement(By.css('.entropy'))
194 .sendKeys(entropyDetail.entropy);
195 driver.sleep(entropyFeedbackDelay).then(function() {
196 driver.findElement(By.css('.entropy-container'))
197 .getText()
198 .then(function(text) {
199 driver.findElement(By.css('.phrase'))
200 .getAttribute("value")
201 .then(function(phrase) {
202 if ("filtered" in entropyDetail) {
203 var key = "Filtered Entropy";
204 var value = entropyDetail.filtered;
205 var reText = key + "\\s+" + value;
206 var re = new RegExp(reText);
207 expect(text).toMatch(re);
208 }
209 if ("type" in entropyDetail) {
210 var key = "Entropy Type";
211 var value = entropyDetail.type;
212 var reText = key + "\\s+" + value;
213 var re = new RegExp(reText);
214 expect(text).toMatch(re);
215 }
216 if ("events" in entropyDetail) {
217 var key = "Event Count";
218 var value = entropyDetail.events;
219 var reText = key + "\\s+" + value;
220 var re = new RegExp(reText);
221 expect(text).toMatch(re);
222 }
223 if ("bits" in entropyDetail) {
224 var key = "Total Bits";
225 var value = entropyDetail.bits;
226 var reText = key + "\\s+" + value;
227 var re = new RegExp(reText);
228 expect(text).toMatch(re);
229 }
230 if ("bitsPerEvent" in entropyDetail) {
231 var key = "Bits Per Event";
232 var value = entropyDetail.bitsPerEvent;
233 var reText = key + "\\s+" + value;
234 var re = new RegExp(reText);
235 expect(text).toMatch(re);
236 }
237 if ("words" in entropyDetail) {
238 var actualWords = phrase.split(/\s+/)
239 .filter(function(w) { return w.length > 0 })
240 .length;
241 expect(actualWords).toBe(entropyDetail.words);
242 }
243 if ("strength" in entropyDetail) {
244 var key = "Time To Crack";
245 var value = entropyDetail.strength;
246 var reText = key + "\\s+" + value;
247 var re = new RegExp(reText);
248 expect(text).toMatch(re);
249 }
250 done();
251 });
252 });
253 });
254 }
255
256 function testClientSelect(done, params) {
257 // set mnemonic and select bip32 tab
258 driver.findElement(By.css('#bip32-tab a'))
259 .click()
260 driver.findElement(By.css('.phrase'))
261 .sendKeys("abandon abandon ability");
262 driver.sleep(generateDelay).then(function() {
263 // BITCOIN CORE
264 // set bip32 client to bitcoin core
265 driver.executeScript(function() {
266 $("#bip32-client").val(arguments[0]).trigger("change");
267 }, params.selectValue);
268 driver.sleep(generateDelay).then(function() {
269 // check the derivation path is correct
270 driver.findElement(By.css("#bip32-path"))
271 .getAttribute("value")
272 .then(function(path) {
273 expect(path).toBe(params.bip32path);
274 // check hardened addresses is selected
275 driver.findElement(By.css(".hardened-addresses"))
276 .getAttribute("checked")
277 .then(function(isChecked) {
278 expect(isChecked).toBe(params.useHardenedAddresses);
279 // check input is readonly
280 driver.findElement(By.css("#bip32-path"))
281 .getAttribute("readonly")
282 .then(function(isReadonly) {
283 expect(isReadonly).toBe("true");
284 done();
285 });
286 });
287 });
288 });
289 });
290 }
291
292 // Tests
293
294 describe('BIP39 Tool Tests', function() {
295
296 beforeEach(function(done) {
297 driver = newDriver();
298 driver.get(url).then(done);
299 });
300
301 // Close the website after each test is run (so that it is opened fresh each time)
302 afterEach(function(done) {
303 driver.quit().then(done);
304 });
305
306 // BEGIN TESTS
307
308 // Page initially loads with blank phrase
309 it('Should load the page', function(done) {
310 driver.findElement(By.css('.phrase'))
311 .getAttribute('value').then(function(value) {
312 expect(value).toBe('');
313 done();
314 });
315 });
316
317 // Page has text
318 it('Should have text on the page', function(done) {
319 driver.findElement(By.css('body'))
320 .getText()
321 .then(function(text) {
322 var textToFind = "You can enter an existing BIP39 mnemonic";
323 expect(text).toContain(textToFind);
324 done();
325 });
326 });
327
328 // Entering mnemonic generates addresses
329 it('Should have a list of addresses', function(done) {
330 driver.findElement(By.css('.phrase'))
331 .sendKeys('abandon abandon ability');
332 driver.sleep(generateDelay).then(function() {
333 driver.findElements(By.css('.address'))
334 .then(function(els) {
335 expect(els.length).toBe(20);
336 done();
337 })
338 });
339 });
340
341 // Generate button generates random mnemonic
342 it('Should be able to generate a random mnemonic', function(done) {
343 // initial phrase is blank
344 driver.findElement(By.css('.phrase'))
345 .getAttribute("value")
346 .then(function(phrase) {
347 expect(phrase.length).toBe(0);
348 // press generate
349 driver.findElement(By.css('.generate')).click();
350 driver.sleep(generateDelay).then(function() {
351 // new phrase is not blank
352 driver.findElement(By.css('.phrase'))
353 .getAttribute("value")
354 .then(function(phrase) {
355 expect(phrase.length).toBeGreaterThan(0);
356 done();
357 });
358 });
359 });
360 });
361
362 // Mnemonic length can be customized
363 it('Should allow custom length mnemonics', function(done) {
364 // set strength to 6
365 driver.executeScript(function() {
366 $(".strength option[selected]").removeAttr("selected");
367 $(".strength option[value=6]").prop("selected", true);
368 });
369 driver.findElement(By.css('.generate')).click();
370 driver.sleep(generateDelay).then(function() {
371 driver.findElement(By.css('.phrase'))
372 .getAttribute("value")
373 .then(function(phrase) {
374 var words = phrase.split(" ");
375 expect(words.length).toBe(6);
376 done();
377 });
378 });
379 });
380
381 // Passphrase can be set
382 it('Allows a passphrase to be set', function(done) {
383 driver.findElement(By.css('.phrase'))
384 .sendKeys('abandon abandon ability');
385 driver.findElement(By.css('.passphrase'))
386 .sendKeys('secure_passphrase');
387 driver.sleep(generateDelay).then(function() {
388 getFirstAddress(function(address) {
389 expect(address).toBe("15pJzUWPGzR7avffV9nY5by4PSgSKG9rba");
390 done();
391 })
392 });
393 });
394
395 // Network can be set to networks other than bitcoin
396 it('Allows selection of bitcoin testnet', function(done) {
397 var params = {
398 selectText: "BTC - Bitcoin Testnet",
399 firstAddress: "mucaU5iiDaJDb69BHLeDv8JFfGiyg2nJKi",
400 };
401 testNetwork(done, params);
402 });
403 it('Allows selection of litecoin', function(done) {
404 var params = {
405 selectText: "LTC - Litecoin",
406 firstAddress: "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn",
407 };
408 testNetwork(done, params);
409 });
410 it('Allows selection of ripple', function(done) {
411 var params = {
412 selectText: "XRP - Ripple",
413 firstAddress: "rLTFnqbmCVPGx6VfaygdtuKWJgcN4v1zRS",
414 phrase: "ill clump only blind unit burden thing track silver cloth review awake useful craft whale all satisfy else trophy sunset walk vanish hope valve",
415 };
416 testNetwork(done, params);
417 });
418 it('Allows selection of dogecoin', function(done) {
419 var params = {
420 selectText: "DOGE - Dogecoin",
421 firstAddress: "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA",
422 };
423 testNetwork(done, params);
424 });
425 it('Allows selection of shadowcash', function(done) {
426 var params = {
427 selectText: "SDC - ShadowCash",
428 firstAddress: "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG",
429 };
430 testNetwork(done, params);
431 });
432 it('Allows selection of shadowcash testnet', function(done) {
433 var params = {
434 selectText: "SDC - ShadowCash Testnet",
435 firstAddress: "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe",
436 };
437 testNetwork(done, params);
438 });
439 it('Allows selection of viacoin', function(done) {
440 var params = {
441 selectText: "VIA - Viacoin",
442 firstAddress: "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT",
443 };
444 testNetwork(done, params);
445 });
446 it('Allows selection of viacoin testnet', function(done) {
447 var params = {
448 selectText: "VIA - Viacoin Testnet",
449 firstAddress: "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe",
450 };
451 testNetwork(done, params);
452 });
453 it('Allows selection of jumbucks', function(done) {
454 var params = {
455 selectText: "JBS - Jumbucks",
456 firstAddress: "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew",
457 };
458 testNetwork(done, params);
459 });
460 it('Allows selection of clam', function(done) {
461 var params = {
462 selectText: "CLAM - Clams",
463 firstAddress: "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y",
464 };
465 testNetwork(done, params);
466 });
467 it('Allows selection of crown', function(done) {
468 var params = {
469 selectText: "CRW - Crown",
470 firstAddress: "18pWSwSUAQdiwMHUfFZB1fM2xue9X1FqE5",
471 };
472 testNetwork(done, params);
473 });
474 it('Allows selection of dash', function(done) {
475 var params = {
476 selectText: "DASH - Dash",
477 firstAddress: "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT",
478 };
479 testNetwork(done, params);
480 });
481 it('Allows selection of dash testnet', function(done) {
482 var params = {
483 selectText: "DASH - Dash Testnet",
484 firstAddress: "yaR52EN4oojdJfBgzWJTymC4uuCLPT29Gw",
485 };
486 testNetwork(done, params);
487 });
488 it('Allows selection of game', function(done) {
489 var params = {
490 selectText: "GAME - GameCredits",
491 firstAddress: "GSMY9bAp36cMR4zyT4uGVS7GFjpdXbao5Q",
492 };
493 testNetwork(done, params);
494 });
495 it('Allows selection of komodo', function(done) {
496 var params = {
497 selectText: "KMD - Komodo",
498 firstAddress: "RMPPzJwAjPVZZAwJvXivHJGGjdCx6WBD2t",
499 };
500 testNetwork(done, params);
501 });
502 it('Allows selection of namecoin', function(done) {
503 var params = {
504 selectText: "NMC - Namecoin",
505 firstAddress: "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2",
506 };
507 testNetwork(done, params);
508 });
509 it('Allows selection of onixcoin', function(done) {
510 var params = {
511 selectText: "ONX - Onixcoin",
512 firstAddress: "XGwMqddeKjT3ddgX73QokjVbCL3aK6Yxfk",
513 };
514 testNetwork(done, params);
515 });
516 it('Allows selection of peercoin', function(done) {
517 var params = {
518 selectText: "PPC - Peercoin",
519 firstAddress: "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm",
520 };
521 testNetwork(done, params);
522 });
523 it('Allows selection of ethereum', function(done) {
524 var params = {
525 selectText: "ETH - Ethereum",
526 firstAddress: "0xe5815d5902Ad612d49283DEdEc02100Bd44C2772",
527 };
528 testNetwork(done, params);
529 // TODO test private key and public key
530 });
531 it('Allows selection of slimcoin', function(done) {
532 var params = {
533 selectText: "SLM - Slimcoin",
534 firstAddress: "SNzPi1CafHFm3WWjRo43aMgiaEEj3ogjww",
535 };
536 testNetwork(done, params);
537 });
538 it('Allows selection of slimcoin testnet', function(done) {
539 var params = {
540 selectText: "SLM - Slimcoin Testnet",
541 firstAddress: "n3nMgWufTek5QQAr6uwMhg5xbzj8xqc4Dq",
542 };
543 testNetwork(done, params);
544 });
545 it('Allows selection of bitcoin cash', function(done) {
546 var params = {
547 selectText: "BCH - Bitcoin Cash",
548 firstAddress: "1JKvb6wKtsjNoCRxpZ4DGrbniML7z5U16A",
549 };
550 testNetwork(done, params);
551 });
552 it('Allows selection of myriadcoin', function(done) {
553 var params = {
554 selectText: "XMY - Myriadcoin",
555 firstAddress: "MJEswvRR46wh9BoiVj9DzKYMBkCramhoBV",
556 };
557 testNetwork(done, params);
558 });
559 it('Allows selection of pivx', function(done) {
560 var params = {
561 selectText: "PIVX - PIVX",
562 firstAddress: "DBxgT7faCuno7jmtKuu6KWCiwqsVPqh1tS",
563 };
564 testNetwork(done, params);
565 });
566 it('Allows selection of pivx testnet', function(done) {
567 var params = {
568 selectText: "PIVX - PIVX Testnet",
569 firstAddress: "yB5U384n6dGkVE3by5y9VdvHHPwPg68fQj",
570 };
571 testNetwork(done, params);
572 });
573 it('Allows selection of maza', function(done) {
574 var params = {
575 selectText: "MAZA - Maza",
576 firstAddress: "MGW4Bmi2NEm4PxSjgeFwhP9vg18JHoRnfw",
577 };
578 testNetwork(done, params);
579 });
580 it('Allows selection of fujicoin', function(done) {
581 var params = {
582 selectText: "FJC - Fujicoin",
583 firstAddress: "FgiaLpG7C99DyR4WnPxXedRVHXSfKzUDhF",
584 };
585 testNetwork(done, params);
586 });
587 it('Allows selection of nubits', function(done) {
588 var params = {
589 selectText: "USNBT - NuBits",
590 firstAddress: "BLxkabXuZSJSdesLD7KxZdqovd4YwyBTU6",
591 };
592 testNetwork(done, params);
593 });
594 it('Allows selection of bitcoin gold', function(done) {
595 var params = {
596 selectText: "BTG - Bitcoin Gold",
597 firstAddress: "GWYxuwSqANWGV3WT7Gpr6HE91euYXBqtwQ",
598 };
599 testNetwork(done, params);
600 });
601 it('Allows selection of monacoin', function(done) {
602 var params = {
603 selectText: "MONA - Monacoin",
604 firstAddress: "MKMiMr7MyjDKjJbCBzgF6u4ByqTS4NkRB1",
605 };
606 testNetwork(done, params);
607 });
608
609 // BIP39 seed is set from phrase
610 it('Sets the bip39 seed from the prhase', function(done) {
611 driver.findElement(By.css('.phrase'))
612 .sendKeys('abandon abandon ability');
613 driver.sleep(generateDelay).then(function() {
614 driver.findElement(By.css('.seed'))
615 .getAttribute("value")
616 .then(function(seed) {
617 expect(seed).toBe("20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3");
618 done();
619 })
620 });
621 });
622
623 // BIP32 root key is set from phrase
624 it('Sets the bip39 root key from the prhase', function(done) {
625 driver.findElement(By.css('.phrase'))
626 .sendKeys('abandon abandon ability');
627 driver.sleep(generateDelay).then(function() {
628 driver.findElement(By.css('.root-key'))
629 .getAttribute("value")
630 .then(function(seed) {
631 expect(seed).toBe("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi");
632 done();
633 })
634 });
635 });
636
637 // Tabs show correct addresses when changed
638 it('Shows the correct address when tab is changed', function(done) {
639 driver.findElement(By.css('.phrase'))
640 .sendKeys('abandon abandon ability');
641 driver.sleep(generateDelay).then(function() {
642 driver.findElement(By.css('#bip32-tab a'))
643 .click();
644 driver.sleep(generateDelay).then(function() {
645 getFirstAddress(function(address) {
646 expect(address).toBe("17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz");
647 done();
648 });
649 });
650 });
651 });
652
653 // BIP44 derivation path is shown
654 it('Shows the derivation path for bip44 tab', function(done) {
655 driver.findElement(By.css('.phrase'))
656 .sendKeys('abandon abandon ability');
657 driver.sleep(generateDelay).then(function() {
658 driver.findElement(By.css('#bip44 .path'))
659 .getAttribute("value")
660 .then(function(path) {
661 expect(path).toBe("m/44'/0'/0'/0");
662 done();
663 })
664 });
665 });
666
667 // BIP44 extended private key is shown
668 it('Shows the extended private key for bip44 tab', function(done) {
669 driver.findElement(By.css('.phrase'))
670 .sendKeys('abandon abandon ability');
671 driver.sleep(generateDelay).then(function() {
672 driver.findElement(By.css('.extended-priv-key'))
673 .getAttribute("value")
674 .then(function(path) {
675 expect(path).toBe("xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG");
676 done();
677 })
678 });
679 });
680
681 // BIP44 extended public key is shown
682 it('Shows the extended public key for bip44 tab', function(done) {
683 driver.findElement(By.css('.phrase'))
684 .sendKeys('abandon abandon ability');
685 driver.sleep(generateDelay).then(function() {
686 driver.findElement(By.css('.extended-pub-key'))
687 .getAttribute("value")
688 .then(function(path) {
689 expect(path).toBe("xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM");
690 done();
691 })
692 });
693 });
694
695 // BIP44 account field changes address list
696 it('Changes the address list if bip44 account is changed', function(done) {
697 driver.findElement(By.css('#bip44 .account'))
698 .sendKeys('1');
699 driver.findElement(By.css('.phrase'))
700 .sendKeys('abandon abandon ability');
701 driver.sleep(generateDelay).then(function() {
702 getFirstAddress(function(address) {
703 expect(address).toBe("1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H");
704 done();
705 });
706 });
707 });
708
709 // BIP44 change field changes address list
710 it('Changes the address list if bip44 change is changed', function(done) {
711 driver.findElement(By.css('#bip44 .change'))
712 .sendKeys('1');
713 driver.findElement(By.css('.phrase'))
714 .sendKeys('abandon abandon ability');
715 driver.sleep(generateDelay).then(function() {
716 getFirstAddress(function(address) {
717 expect(address).toBe("1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo");
718 done();
719 });
720 });
721 });
722
723 // BIP32 derivation path can be set
724 it('Can use a custom bip32 derivation path', function(done) {
725 driver.findElement(By.css('#bip32-tab a'))
726 .click();
727 driver.findElement(By.css('#bip32 .path'))
728 .clear();
729 driver.findElement(By.css('#bip32 .path'))
730 .sendKeys('m/1');
731 driver.findElement(By.css('.phrase'))
732 .sendKeys('abandon abandon ability');
733 driver.sleep(generateDelay).then(function() {
734 getFirstAddress(function(address) {
735 expect(address).toBe("16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L");
736 done();
737 });
738 });
739 });
740
741 // BIP32 can use hardened derivation paths
742 it('Can use a hardened derivation paths', function(done) {
743 driver.findElement(By.css('#bip32-tab a'))
744 .click();
745 driver.findElement(By.css('#bip32 .path'))
746 .clear();
747 driver.findElement(By.css('#bip32 .path'))
748 .sendKeys("m/0'");
749 driver.findElement(By.css('.phrase'))
750 .sendKeys('abandon abandon ability');
751 driver.sleep(generateDelay).then(function() {
752 getFirstAddress(function(address) {
753 expect(address).toBe("14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4");
754 done();
755 });
756 });
757 });
758
759 // BIP32 extended private key is shown
760 it('Shows the BIP32 extended private key', function(done) {
761 driver.findElement(By.css('#bip32-tab a'))
762 .click();
763 driver.findElement(By.css('.phrase'))
764 .sendKeys('abandon abandon ability');
765 driver.sleep(generateDelay).then(function() {
766 driver.findElement(By.css('.extended-priv-key'))
767 .getAttribute("value")
768 .then(function(privKey) {
769 expect(privKey).toBe("xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe");
770 done();
771 });
772 });
773 });
774
775 // BIP32 extended public key is shown
776 it('Shows the BIP32 extended public key', function(done) {
777 driver.findElement(By.css('#bip32-tab a'))
778 .click();
779 driver.findElement(By.css('.phrase'))
780 .sendKeys('abandon abandon ability');
781 driver.sleep(generateDelay).then(function() {
782 driver.findElement(By.css('.extended-pub-key'))
783 .getAttribute("value")
784 .then(function(pubKey) {
785 expect(pubKey).toBe("xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P");
786 done();
787 });
788 });
789 });
790
791 // Derivation path is shown in table
792 it('Shows the derivation path in the table', function(done) {
793 driver.findElement(By.css('.phrase'))
794 .sendKeys('abandon abandon ability');
795 driver.sleep(generateDelay).then(function() {
796 getFirstPath(function(path) {
797 expect(path).toBe("m/44'/0'/0'/0/0");
798 done();
799 });
800 });
801 });
802
803 // Derivation path for address can be hardened
804 it('Can derive hardened addresses', function(done) {
805 driver.findElement(By.css('#bip32-tab a'))
806 .click();
807 driver.executeScript(function() {
808 $(".hardened-addresses").prop("checked", true);
809 });
810 driver.findElement(By.css('.phrase'))
811 .sendKeys('abandon abandon ability');
812 driver.sleep(generateDelay).then(function() {
813 getFirstAddress(function(address) {
814 expect(address).toBe("18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd");
815 done();
816 });
817 });
818 });
819
820 // Derivation path visibility can be toggled
821 it('Can toggle visibility of the derivation path column', function(done) {
822 driver.findElement(By.css('.phrase'))
823 .sendKeys('abandon abandon ability');
824 driver.sleep(generateDelay).then(function() {
825 driver.findElement(By.css('.index-toggle'))
826 .click();
827 testColumnValuesAreInvisible(done, "index");
828 });
829 });
830
831 // Address is shown
832 it('Shows the address in the table', function(done) {
833 driver.findElement(By.css('.phrase'))
834 .sendKeys('abandon abandon ability');
835 driver.sleep(generateDelay).then(function() {
836 getFirstAddress(function(address) {
837 expect(address).toBe("1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug");
838 done();
839 });
840 });
841 });
842
843 // Addresses are shown in order of derivation path
844 it('Shows the address in order of derivation path', function(done) {
845 driver.findElement(By.css('.phrase'))
846 .sendKeys('abandon abandon ability');
847 driver.sleep(generateDelay).then(function() {
848 testRowsAreInCorrectOrder(done);
849 });
850 });
851
852 // Address visibility can be toggled
853 it('Can toggle visibility of the address column', function(done) {
854 driver.findElement(By.css('.phrase'))
855 .sendKeys('abandon abandon ability');
856 driver.sleep(generateDelay).then(function() {
857 driver.findElement(By.css('.address-toggle'))
858 .click();
859 testColumnValuesAreInvisible(done, "address");
860 });
861 });
862
863 // Public key is shown in table
864 it('Shows the public key in the table', function(done) {
865 driver.findElement(By.css('.phrase'))
866 .sendKeys('abandon abandon ability');
867 driver.sleep(generateDelay).then(function() {
868 driver.findElements(By.css('.pubkey'))
869 .then(function(els) {
870 els[0].getText()
871 .then(function(pubkey) {
872 expect(pubkey).toBe("033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3");
873 done();
874 });
875 });
876 });
877 });
878
879 // Public key visibility can be toggled
880 it('Can toggle visibility of the public key column', function(done) {
881 driver.findElement(By.css('.phrase'))
882 .sendKeys('abandon abandon ability');
883 driver.sleep(generateDelay).then(function() {
884 driver.findElement(By.css('.public-key-toggle'))
885 .click();
886 testColumnValuesAreInvisible(done, "pubkey");
887 });
888 });
889
890 // Private key is shown in table
891 it('Shows the private key in the table', function(done) {
892 driver.findElement(By.css('.phrase'))
893 .sendKeys('abandon abandon ability');
894 driver.sleep(generateDelay).then(function() {
895 driver.findElements(By.css('.privkey'))
896 .then(function(els) {
897 els[0].getText()
898 .then(function(pubkey) {
899 expect(pubkey).toBe("L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE");
900 done();
901 });
902 });
903 });
904 });
905
906 // Private key visibility can be toggled
907 it('Can toggle visibility of the private key column', function(done) {
908 driver.findElement(By.css('.phrase'))
909 .sendKeys('abandon abandon ability');
910 driver.sleep(generateDelay).then(function() {
911 driver.findElement(By.css('.private-key-toggle'))
912 .click();
913 testColumnValuesAreInvisible(done, "privkey");
914 });
915 });
916
917 // More addresses can be generated
918 it('Can generate more rows in the table', function(done) {
919 driver.findElement(By.css('.phrase'))
920 .sendKeys('abandon abandon ability');
921 driver.sleep(generateDelay).then(function() {
922 driver.findElement(By.css('.more'))
923 .click();
924 driver.sleep(generateDelay).then(function() {
925 driver.findElements(By.css('.address'))
926 .then(function(els) {
927 expect(els.length).toBe(40);
928 done();
929 });
930 });
931 });
932 });
933
934 // A custom number of additional addresses can be generated
935 it('Can generate more rows in the table', function(done) {
936 driver.findElement(By.css('.rows-to-add'))
937 .clear();
938 driver.findElement(By.css('.rows-to-add'))
939 .sendKeys('1');
940 driver.findElement(By.css('.phrase'))
941 .sendKeys('abandon abandon ability');
942 driver.sleep(generateDelay).then(function() {
943 driver.findElement(By.css('.more'))
944 .click();
945 driver.sleep(generateDelay).then(function() {
946 driver.findElements(By.css('.address'))
947 .then(function(els) {
948 expect(els.length).toBe(21);
949 done();
950 });
951 });
952 });
953 });
954
955 // Additional addresses are shown in order of derivation path
956 it('Shows additional addresses in order of derivation path', function(done) {
957 driver.findElement(By.css('.phrase'))
958 .sendKeys('abandon abandon ability');
959 driver.sleep(generateDelay).then(function() {
960 driver.findElement(By.css('.more'))
961 .click();
962 driver.sleep(generateDelay).then(function() {
963 testRowsAreInCorrectOrder(done);
964 });
965 });
966 });
967
968 // BIP32 root key can be set by the user
969 it('Allows the user to set the BIP32 root key', function(done) {
970 driver.findElement(By.css('.root-key'))
971 .sendKeys('xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi');
972 driver.sleep(generateDelay).then(function() {
973 getFirstAddress(function(address) {
974 expect(address).toBe("1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug");
975 done();
976 });
977 });
978 });
979
980 // Setting BIP32 root key clears the existing phrase, passphrase and seed
981 // TODO this doesn't work in selenium with chrome
982 it('Confirms the existing phrase should be cleared', function(done) {
983 if (browser == "chrome") {
984 pending("Selenium + Chrome headless bug for alert, see https://stackoverflow.com/q/45242264");
985 }
986 driver.findElement(By.css('.phrase'))
987 .sendKeys('A non-blank but invalid value');
988 driver.findElement(By.css('.root-key'))
989 .sendKeys('xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi');
990 driver.switchTo().alert().accept();
991 driver.findElement(By.css('.phrase'))
992 .getAttribute("value").then(function(value) {
993 expect(value).toBe("");
994 done();
995 });
996 });
997
998 // Clearing of phrase, passphrase and seed can be cancelled by user
999 // TODO this doesn't work in selenium with chrome
1000 it('Allows the clearing of the phrase to be cancelled', function(done) {
1001 if (browser == "chrome") {
1002 pending("Selenium + Chrome headless bug for alert, see https://stackoverflow.com/q/45242264");
1003 }
1004 driver.findElement(By.css('.phrase'))
1005 .sendKeys('abandon abandon ability');
1006 driver.sleep(generateDelay).then(function() {
1007 driver.findElement(By.css('.root-key'))
1008 .clear();
1009 driver.findElement(By.css('.root-key'))
1010 .sendKeys('x');
1011 driver.switchTo().alert().dismiss();
1012 driver.findElement(By.css('.phrase'))
1013 .getAttribute("value").then(function(value) {
1014 expect(value).toBe("abandon abandon ability");
1015 done();
1016 });
1017 });
1018 });
1019
1020 // Custom BIP32 root key is used when changing the derivation path
1021 it('Can set derivation path for root key instead of phrase', function(done) {
1022 driver.findElement(By.css('#bip44 .account'))
1023 .sendKeys('1');
1024 driver.findElement(By.css('.root-key'))
1025 .sendKeys('xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi');
1026 driver.sleep(generateDelay).then(function() {
1027 getFirstAddress(function(address) {
1028 expect(address).toBe("1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H");
1029 done();
1030 });
1031 });
1032 });
1033
1034 // Incorrect mnemonic shows error
1035 it('Shows an error for incorrect mnemonic', function(done) {
1036 driver.findElement(By.css('.phrase'))
1037 .sendKeys('abandon abandon abandon');
1038 driver.sleep(feedbackDelay).then(function() {
1039 driver.findElement(By.css('.feedback'))
1040 .getText()
1041 .then(function(feedback) {
1042 expect(feedback).toBe("Invalid mnemonic");
1043 done();
1044 });
1045 });
1046 });
1047
1048 // Incorrect word shows suggested replacement
1049 it('Shows word suggestion for incorrect word', function(done) {
1050 driver.findElement(By.css('.phrase'))
1051 .sendKeys('abandon abandon abiliti');
1052 driver.sleep(feedbackDelay).then(function() {
1053 driver.findElement(By.css('.feedback'))
1054 .getText()
1055 .then(function(feedback) {
1056 var msg = "abiliti not in wordlist, did you mean ability?";
1057 expect(feedback).toBe(msg);
1058 done();
1059 });
1060 });
1061 });
1062
1063 // Github pull request 48
1064 // First four letters of word shows that word, not closest
1065 // since first four letters gives unique word in BIP39 wordlist
1066 // eg ille should show illegal, not idle
1067 it('Shows word suggestion based on first four chars', function(done) {
1068 driver.findElement(By.css('.phrase'))
1069 .sendKeys('ille');
1070 driver.sleep(feedbackDelay).then(function() {
1071 driver.findElement(By.css('.feedback'))
1072 .getText()
1073 .then(function(feedback) {
1074 var msg = "ille not in wordlist, did you mean illegal?";
1075 expect(feedback).toBe(msg);
1076 done();
1077 });
1078 });
1079 });
1080
1081 // Incorrect BIP32 root key shows error
1082 it('Shows error for incorrect root key', function(done) {
1083 driver.findElement(By.css('.root-key'))
1084 .sendKeys('xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj');
1085 driver.sleep(feedbackDelay).then(function() {
1086 driver.findElement(By.css('.feedback'))
1087 .getText()
1088 .then(function(feedback) {
1089 var msg = "Invalid root key";
1090 expect(feedback).toBe(msg);
1091 done();
1092 });
1093 });
1094 });
1095
1096 // Derivation path not starting with m shows error
1097 it('Shows error for derivation path not starting with m', function(done) {
1098 driver.findElement(By.css('#bip32-tab a'))
1099 .click();
1100 driver.findElement(By.css('#bip32 .path'))
1101 .clear();
1102 driver.findElement(By.css('#bip32 .path'))
1103 .sendKeys('n/0');
1104 driver.findElement(By.css('.phrase'))
1105 .sendKeys('abandon abandon ability');
1106 driver.sleep(feedbackDelay).then(function() {
1107 driver.findElement(By.css('.feedback'))
1108 .getText()
1109 .then(function(feedback) {
1110 var msg = "First character must be 'm'";
1111 expect(feedback).toBe(msg);
1112 done();
1113 });
1114 });
1115 });
1116
1117 // Derivation path containing invalid characters shows useful error
1118 it('Shows error for derivation path not starting with m', function(done) {
1119 driver.findElement(By.css('#bip32-tab a'))
1120 .click();
1121 driver.findElement(By.css('#bip32 .path'))
1122 .clear();
1123 driver.findElement(By.css('#bip32 .path'))
1124 .sendKeys('m/1/0wrong1/1');
1125 driver.findElement(By.css('.phrase'))
1126 .sendKeys('abandon abandon ability');
1127 driver.sleep(feedbackDelay).then(function() {
1128 driver.findElement(By.css('.feedback'))
1129 .getText()
1130 .then(function(feedback) {
1131 var msg = "Invalid characters 0wrong1 found at depth 2";
1132 expect(feedback).toBe(msg);
1133 done();
1134 });
1135 });
1136 });
1137
1138 // Github Issue 11: Default word length is 15
1139 // https://github.com/iancoleman/bip39/issues/11
1140 it('Sets the default word length to 15', function(done) {
1141 driver.findElement(By.css('.strength'))
1142 .getAttribute("value")
1143 .then(function(strength) {
1144 expect(strength).toBe("15");
1145 done();
1146 });
1147 });
1148
1149 // Github Issue 12: Generate more rows with private keys hidden
1150 // https://github.com/iancoleman/bip39/issues/12
1151 it('Sets the correct hidden column state on new rows', function(done) {
1152 driver.findElement(By.css('.phrase'))
1153 .sendKeys("abandon abandon ability");
1154 driver.sleep(generateDelay).then(function() {
1155 driver.findElement(By.css('.private-key-toggle'))
1156 .click();
1157 driver.findElement(By.css('.more'))
1158 .click();
1159 driver.sleep(generateDelay).then(function() {
1160 driver.findElements(By.css('.privkey'))
1161 .then(function(els) {
1162 expect(els.length).toBe(40);
1163 });
1164 testColumnValuesAreInvisible(done, "privkey");
1165 });
1166 });
1167 });
1168
1169 // Github Issue 19: Mnemonic is not sensitive to whitespace
1170 // https://github.com/iancoleman/bip39/issues/19
1171 it('Ignores excess whitespace in the mnemonic', function(done) {
1172 var doublespace = " ";
1173 var mnemonic = "urge cat" + doublespace + "bid";
1174 driver.findElement(By.css('.phrase'))
1175 .sendKeys(mnemonic);
1176 driver.sleep(generateDelay).then(function() {
1177 driver.findElement(By.css('.root-key'))
1178 .getAttribute("value")
1179 .then(function(seed) {
1180 expect(seed).toBe("xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC");
1181 done();
1182 });
1183 });
1184 });
1185
1186 // Github Issue 23: Part 1: Use correct derivation path when changing tabs
1187 // https://github.com/iancoleman/bip39/issues/23
1188 it('Uses the correct derivation path when changing tabs', function(done) {
1189 // 1) and 2) set the phrase
1190 driver.findElement(By.css('.phrase'))
1191 .sendKeys("abandon abandon ability");
1192 driver.sleep(generateDelay).then(function() {
1193 // 3) select bip32 tab
1194 driver.findElement(By.css('#bip32-tab a'))
1195 .click();
1196 driver.sleep(generateDelay).then(function() {
1197 // 4) switch from bitcoin to litecoin
1198 selectNetwork("LTC - Litecoin");
1199 driver.sleep(generateDelay).then(function() {
1200 // 5) Check address is displayed correctly
1201 getFirstAddress(function(address) {
1202 expect(address).toBe("LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5");
1203 // 5) Check derivation path is displayed correctly
1204 getFirstPath(function(path) {
1205 expect(path).toBe("m/0/0");
1206 done();
1207 });
1208 });
1209 });
1210 });
1211 });
1212 });
1213
1214 // Github Issue 23 Part 2: Coin selection in derivation path
1215 // https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
1216 it('Uses the correct derivation path when changing coins', function(done) {
1217 // set the phrase
1218 driver.findElement(By.css('.phrase'))
1219 .sendKeys("abandon abandon ability");
1220 driver.sleep(generateDelay).then(function() {
1221 // switch from bitcoin to clam
1222 selectNetwork("CLAM - Clams");
1223 driver.sleep(generateDelay).then(function() {
1224 // check derivation path is displayed correctly
1225 getFirstPath(function(path) {
1226 expect(path).toBe("m/44'/23'/0'/0/0");
1227 done();
1228 });
1229 });
1230 });
1231 });
1232
1233 // Github Issue 26: When using a Root key derrived altcoins are incorrect
1234 // https://github.com/iancoleman/bip39/issues/26
1235 it('Uses the correct derivation for altcoins with root keys', function(done) {
1236 // 1) 2) and 3) set the root key
1237 driver.findElement(By.css('.root-key'))
1238 .sendKeys("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi");
1239 driver.sleep(generateDelay).then(function() {
1240 // 4) switch from bitcoin to viacoin
1241 selectNetwork("VIA - Viacoin");
1242 driver.sleep(generateDelay).then(function() {
1243 // 5) ensure the derived address is correct
1244 getFirstAddress(function(address) {
1245 expect(address).toBe("Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT");
1246 done();
1247 });
1248 });
1249 });
1250 });
1251
1252 // Selecting a language with no existing phrase should generate a phrase in
1253 // that language.
1254 it('Generate a random phrase when language is selected and no current phrase', function(done) {
1255 driver.findElement(By.css("a[href='#japanese']"))
1256 .click();
1257 driver.sleep(generateDelay).then(function() {
1258 driver.findElement(By.css(".phrase"))
1259 .getAttribute("value").then(function(phrase) {
1260 expect(phrase.search(/[a-z]/)).toBe(-1);
1261 expect(phrase.length).toBeGreaterThan(0);
1262 done();
1263 });
1264 });
1265 });
1266
1267 // Selecting a language with existing phrase should update the phrase to use
1268 // that language.
1269 it('Updates existing phrases when the language is changed', function(done) {
1270 driver.findElement(By.css(".phrase"))
1271 .sendKeys("abandon abandon ability");
1272 driver.sleep(generateDelay).then(function() {
1273 driver.findElement(By.css("a[href='#italian']"))
1274 .click();
1275 driver.sleep(generateDelay).then(function() {
1276 driver.findElement(By.css(".phrase"))
1277 .getAttribute("value").then(function(phrase) {
1278 // Check only the language changes, not the phrase
1279 expect(phrase).toBe("abaco abaco abbaglio");
1280 getFirstAddress(function(address) {
1281 // Check the address is correct
1282 expect(address).toBe("1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV");
1283 done();
1284 });
1285 });
1286 });
1287 });
1288 });
1289
1290 // Suggested replacement for erroneous word in non-English language
1291 it('Shows word suggestion for incorrect word in non-English language', function(done) {
1292 driver.findElement(By.css('.phrase'))
1293 .sendKeys('abaco abaco zbbaglio');
1294 driver.sleep(feedbackDelay).then(function() {
1295 driver.findElement(By.css('.feedback'))
1296 .getText()
1297 .then(function(feedback) {
1298 var msg = "zbbaglio not in wordlist, did you mean abbaglio?";
1299 expect(feedback).toBe(msg);
1300 done();
1301 });
1302 });
1303 });
1304
1305 // Japanese word does not break across lines.
1306 // Point 2 from
1307 // https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
1308 it('Does not break Japanese words across lines', function(done) {
1309 driver.findElement(By.css('.phrase'))
1310 .getCssValue("word-break")
1311 .then(function(value) {
1312 expect(value).toBe("keep-all");
1313 done();
1314 });
1315 });
1316
1317 // Language can be specified at page load using hash value in url
1318 it('Can set the language from the url hash', function(done) {
1319 driver.get(url + "#japanese").then(function() {
1320 driver.findElement(By.css('.generate')).click();
1321 driver.sleep(generateDelay).then(function() {
1322 driver.findElement(By.css(".phrase"))
1323 .getAttribute("value").then(function(phrase) {
1324 expect(phrase.search(/[a-z]/)).toBe(-1);
1325 expect(phrase.length).toBeGreaterThan(0);
1326 done();
1327 });
1328 });
1329 });
1330 });
1331
1332 // Entropy can be entered by the user
1333 it('Allows entropy to be entered', function(done) {
1334 driver.findElement(By.css('.use-entropy'))
1335 .click();
1336 driver.findElement(By.css('.entropy'))
1337 .sendKeys('00000000 00000000 00000000 00000000');
1338 driver.sleep(generateDelay).then(function() {
1339 driver.findElement(By.css(".phrase"))
1340 .getAttribute("value").then(function(phrase) {
1341 expect(phrase).toBe("abandon abandon ability");
1342 getFirstAddress(function(address) {
1343 expect(address).toBe("1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug");
1344 done();
1345 })
1346 });
1347 });
1348 });
1349
1350 // A warning about entropy is shown to the user, with additional information
1351 it('Shows a warning about using entropy', function(done) {
1352 driver.findElement(By.css('.use-entropy'))
1353 .click();
1354 driver.findElement(By.css('.entropy-container'))
1355 .getText()
1356 .then(function(containerText) {
1357 var warning = "mnemonic may be insecure";
1358 expect(containerText).toContain(warning);
1359 driver.findElement(By.css('#entropy-notes'))
1360 .findElement(By.xpath("parent::*"))
1361 .getText()
1362 .then(function(notesText) {
1363 var detail = "flipping a fair coin, rolling a fair dice, noise measurements etc";
1364 expect(notesText).toContain(detail);
1365 done();
1366 });
1367 });
1368 });
1369
1370 // The types of entropy available are described to the user
1371 it('Shows the types of entropy available', function(done) {
1372 driver.findElement(By.css('.entropy'))
1373 .getAttribute("placeholder")
1374 .then(function(placeholderText) {
1375 var options = [
1376 "binary",
1377 "base 6",
1378 "dice",
1379 "base 10",
1380 "hexadecimal",
1381 "cards",
1382 ];
1383 for (var i=0; i<options.length; i++) {
1384 var option = options[i];
1385 expect(placeholderText).toContain(option);
1386 }
1387 done();
1388 });
1389 });
1390
1391 // The actual entropy used is shown to the user
1392 it('Shows the actual entropy used', function(done) {
1393 driver.findElement(By.css('.use-entropy'))
1394 .click();
1395 driver.findElement(By.css('.entropy'))
1396 .sendKeys('Not A Very Good Entropy Source At All');
1397 driver.sleep(generateDelay).then(function() {
1398 driver.findElement(By.css('.entropy-container'))
1399 .getText()
1400 .then(function(text) {
1401 expect(text).toMatch(/Filtered Entropy\s+AedEceAA/);
1402 done();
1403 });
1404 });
1405 });
1406
1407 // Binary entropy can be entered
1408 it('Allows binary entropy to be entered', function(done) {
1409 testEntropyType(done, "01", "binary");
1410 });
1411
1412 // Base 6 entropy can be entered
1413 it('Allows base 6 entropy to be entered', function(done) {
1414 testEntropyType(done, "012345", "base 6");
1415 });
1416
1417 // Base 6 dice entropy can be entered
1418 it('Allows base 6 dice entropy to be entered', function(done) {
1419 testEntropyType(done, "123456", "base 6 (dice)");
1420 });
1421
1422 // Base 10 entropy can be entered
1423 it('Allows base 10 entropy to be entered', function(done) {
1424 testEntropyType(done, "789", "base 10");
1425 });
1426
1427 // Hexadecimal entropy can be entered
1428 it('Allows hexadecimal entropy to be entered', function(done) {
1429 testEntropyType(done, "abcdef", "hexadecimal");
1430 });
1431
1432 // Dice entropy value is shown as the converted base 6 value
1433 // ie 123456 is converted to 123450
1434 it('Shows dice entropy as base 6', function(done) {
1435 driver.findElement(By.css('.use-entropy'))
1436 .click();
1437 driver.findElement(By.css('.entropy'))
1438 .sendKeys("123456");
1439 driver.sleep(generateDelay).then(function() {
1440 driver.findElement(By.css('.entropy-container'))
1441 .getText()
1442 .then(function(text) {
1443 expect(text).toMatch(/Filtered Entropy\s+123450/);
1444 done();
1445 });
1446 });
1447 });
1448
1449 // The number of bits of entropy accumulated is shown
1450 it("Shows the number of bits of entropy for 20 bits of binary", function(done) {
1451 testEntropyBits(done, "0000 0000 0000 0000 0000", "20");
1452 });
1453 it("Shows the number of bits of entropy for 1 bit of binary", function(done) {
1454 testEntropyBits(done, "0", "1");
1455 });
1456 it("Shows the number of bits of entropy for 4 bits of binary", function(done) {
1457 testEntropyBits(done, "0000", "4");
1458 });
1459 it("Shows the number of bits of entropy for 1 character of base 6 (dice)", function(done) {
1460 // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
1461 testEntropyBits(done, "6", "2");
1462 });
1463 it("Shows the number of bits of entropy for 1 character of base 10 with 3 bits", function(done) {
1464 // 7 in base 10 is 111 in base 2, no leading zeros
1465 testEntropyBits(done, "7", "3");
1466 });
1467 it("Shows the number of bits of entropy for 1 character of base 10 with 4 bis", function(done) {
1468 testEntropyBits(done, "8", "4");
1469 });
1470 it("Shows the number of bits of entropy for 1 character of hex", function(done) {
1471 testEntropyBits(done, "F", "4");
1472 });
1473 it("Shows the number of bits of entropy for 2 characters of base 10", function(done) {
1474 testEntropyBits(done, "29", "6");
1475 });
1476 it("Shows the number of bits of entropy for 2 characters of hex", function(done) {
1477 testEntropyBits(done, "0A", "8");
1478 });
1479 it("Shows the number of bits of entropy for 2 characters of hex with 3 leading zeros", function(done) {
1480 // hex is always multiple of 4 bits of entropy
1481 testEntropyBits(done, "1A", "8");
1482 });
1483 it("Shows the number of bits of entropy for 2 characters of hex with 2 leading zeros", function(done) {
1484 testEntropyBits(done, "2A", "8");
1485 });
1486 it("Shows the number of bits of entropy for 2 characters of hex with 1 leading zero", function(done) {
1487 testEntropyBits(done, "4A", "8");
1488 });
1489 it("Shows the number of bits of entropy for 2 characters of hex with no leading zeros", function(done) {
1490 testEntropyBits(done, "8A", "8");
1491 });
1492 it("Shows the number of bits of entropy for 2 characters of hex starting with F", function(done) {
1493 testEntropyBits(done, "FA", "8");
1494 });
1495 it("Shows the number of bits of entropy for 4 characters of hex with leading zeros", function(done) {
1496 testEntropyBits(done, "000A", "16");
1497 });
1498 it("Shows the number of bits of entropy for 4 characters of base 6", function(done) {
1499 testEntropyBits(done, "5555", "11");
1500 });
1501 it("Shows the number of bits of entropy for 4 characters of base 6 dice", function(done) {
1502 // uses dice, so entropy is actually 0000 in base 6, which is 4 lots of
1503 // 2.58 bits, which is 10.32 bits (rounded down to 10 bits)
1504 testEntropyBits(done, "6666", "10");
1505 });
1506 it("Shows the number of bits of entropy for 4 charactes of base 10", function(done) {
1507 // Uses base 10, which is 4 lots of 3.32 bits, which is 13.3 bits (rounded
1508 // down to 13)
1509 testEntropyBits(done, "2227", "13");
1510 });
1511 it("Shows the number of bits of entropy for 4 characters of hex with 2 leading zeros", function(done) {
1512 testEntropyBits(done, "222F", "16");
1513 });
1514 it("Shows the number of bits of entropy for 4 characters of hex starting with F", function(done) {
1515 testEntropyBits(done, "FFFF", "16");
1516 });
1517 it("Shows the number of bits of entropy for 10 characters of base 10", function(done) {
1518 // 10 events at 3.32 bits per event
1519 testEntropyBits(done, "0000101017", "33");
1520 });
1521 it("Shows the number of bits of entropy for a full deck of cards", function(done) {
1522 // cards are not replaced, so a full deck is not 52^52 entropy which is 296
1523 // bits, it's 52!, which is 225 bits
1524 testEntropyBits(done, "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", "225");
1525 });
1526
1527 it("Shows details about the entered entropy", function(done) {
1528 testEntropyFeedback(done,
1529 {
1530 entropy: "A",
1531 filtered: "A",
1532 type: "hexadecimal",
1533 events: "1",
1534 bits: "4",
1535 words: 0,
1536 strength: "less than a second",
1537 }
1538 );
1539 });
1540 it("Shows details about the entered entropy", function(done) {
1541 testEntropyFeedback(done,
1542 {
1543 entropy: "AAAAAAAA",
1544 filtered: "AAAAAAAA",
1545 type: "hexadecimal",
1546 events: "8",
1547 bits: "32",
1548 words: 3,
1549 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
1550 }
1551 );
1552 });
1553 it("Shows details about the entered entropy", function(done) {
1554 testEntropyFeedback(done,
1555 {
1556 entropy: "AAAAAAAA B",
1557 filtered: "AAAAAAAAB",
1558 type: "hexadecimal",
1559 events: "9",
1560 bits: "36",
1561 words: 3,
1562 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
1563 }
1564 );
1565 });
1566 it("Shows details about the entered entropy", function(done) {
1567 testEntropyFeedback(done,
1568 {
1569 entropy: "AAAAAAAA BBBBBBBB",
1570 filtered: "AAAAAAAABBBBBBBB",
1571 type: "hexadecimal",
1572 events: "16",
1573 bits: "64",
1574 words: 6,
1575 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
1576 }
1577 );
1578 });
1579 it("Shows details about the entered entropy", function(done) {
1580 testEntropyFeedback(done,
1581 {
1582 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
1583 filtered: "AAAAAAAABBBBBBBBCCCCCCCC",
1584 type: "hexadecimal",
1585 events: "24",
1586 bits: "96",
1587 words: 9,
1588 strength: "less than a second",
1589 }
1590 );
1591 });
1592 it("Shows details about the entered entropy", function(done) {
1593 testEntropyFeedback(done,
1594 {
1595 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
1596 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD",
1597 type: "hexadecimal",
1598 events: "32",
1599 bits: "128",
1600 words: 12,
1601 strength: "2 minutes",
1602 }
1603 );
1604 });
1605 it("Shows details about the entered entropy", function(done) {
1606 testEntropyFeedback(done,
1607 {
1608 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA",
1609 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA",
1610 type: "hexadecimal",
1611 events: "32",
1612 bits: "128",
1613 words: 12,
1614 strength: "2 days",
1615 }
1616 );
1617 });
1618 it("Shows details about the entered entropy", function(done) {
1619 testEntropyFeedback(done,
1620 {
1621 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE",
1622 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE",
1623 type: "hexadecimal",
1624 events: "40",
1625 bits: "160",
1626 words: 15,
1627 strength: "3 years",
1628 }
1629 );
1630 });
1631 it("Shows details about the entered entropy", function(done) {
1632 testEntropyFeedback(done,
1633 {
1634 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF",
1635 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF",
1636 type: "hexadecimal",
1637 events: "48",
1638 bits: "192",
1639 words: 18,
1640 strength: "centuries",
1641 }
1642 );
1643 });
1644 it("Shows details about the entered entropy", function(done) {
1645 testEntropyFeedback(done,
1646 {
1647 entropy: "7d",
1648 type: "card",
1649 events: "1",
1650 bits: "4",
1651 words: 0,
1652 strength: "less than a second",
1653 }
1654 );
1655 });
1656 it("Shows details about the entered entropy", function(done) {
1657 testEntropyFeedback(done,
1658 {
1659 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1660 type: "card (full deck)",
1661 events: "52",
1662 bits: "225",
1663 words: 21,
1664 strength: "centuries",
1665 }
1666 );
1667 });
1668 it("Shows details about the entered entropy", function(done) {
1669 testEntropyFeedback(done,
1670 {
1671 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d",
1672 type: "card (full deck, 1 duplicate: 3d)",
1673 events: "53",
1674 bits: "254",
1675 words: 21,
1676 strength: "centuries",
1677 }
1678 );
1679 });
1680 it("Shows details about the entered entropy", function(done) {
1681 testEntropyFeedback(done,
1682 {
1683 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d",
1684 type: "card (2 duplicates: 3d 4d, 1 missing: KS)",
1685 events: "53",
1686 bits: "254",
1687 words: 21,
1688 strength: "centuries",
1689 }
1690 );
1691 });
1692 it("Shows details about the entered entropy", function(done) {
1693 testEntropyFeedback(done,
1694 {
1695 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d",
1696 type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)",
1697 events: "55",
1698 bits: "264",
1699 words: 24,
1700 strength: "centuries",
1701 }
1702 );
1703 });
1704 it("Shows details about the entered entropy", function(done) {
1705 testEntropyFeedback(done,
1706 // Next test was throwing uncaught error in zxcvbn
1707 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
1708 {
1709 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1710 type: "card (full deck, 52 duplicates: ac 2c 3c...)",
1711 events: "104",
1712 bits: "499",
1713 words: 45,
1714 strength: "centuries",
1715 }
1716 );
1717 });
1718 it("Shows details about the entered entropy", function(done) {
1719 testEntropyFeedback(done,
1720 // Case insensitivity to duplicate cards
1721 {
1722 entropy: "asAS",
1723 type: "card (1 duplicate: AS)",
1724 events: "2",
1725 bits: "9",
1726 words: 0,
1727 strength: "less than a second",
1728 }
1729 );
1730 });
1731 it("Shows details about the entered entropy", function(done) {
1732 testEntropyFeedback(done,
1733 {
1734 entropy: "ASas",
1735 type: "card (1 duplicate: as)",
1736 events: "2",
1737 bits: "9",
1738 words: 0,
1739 strength: "less than a second",
1740 }
1741 );
1742 });
1743 it("Shows details about the entered entropy", function(done) {
1744 testEntropyFeedback(done,
1745 // Missing cards are detected
1746 {
1747 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1748 type: "card (1 missing: 9C)",
1749 events: "51",
1750 bits: "221",
1751 words: 18,
1752 strength: "centuries",
1753 }
1754 );
1755 });
1756 it("Shows details about the entered entropy", function(done) {
1757 testEntropyFeedback(done,
1758 {
1759 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1760 type: "card (2 missing: 9C 5D)",
1761 events: "50",
1762 bits: "216",
1763 words: 18,
1764 strength: "centuries",
1765 }
1766 );
1767 });
1768 it("Shows details about the entered entropy", function(done) {
1769 testEntropyFeedback(done,
1770 {
1771 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1772 type: "card (4 missing: 9C 5D QD...)",
1773 events: "48",
1774 bits: "208",
1775 words: 18,
1776 strength: "centuries",
1777 }
1778 );
1779 });
1780 it("Shows details about the entered entropy", function(done) {
1781 testEntropyFeedback(done,
1782 // More than six missing cards does not show message
1783 {
1784 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks",
1785 type: "card",
1786 events: "45",
1787 bits: "195",
1788 words: 18,
1789 strength: "centuries",
1790 }
1791 );
1792 });
1793 it("Shows details about the entered entropy", function(done) {
1794 testEntropyFeedback(done,
1795 // Multiple decks of cards increases bits per event
1796 {
1797 entropy: "3d",
1798 events: "1",
1799 bits: "4",
1800 bitsPerEvent: "4.34",
1801 }
1802 );
1803 });
1804 it("Shows details about the entered entropy", function(done) {
1805 testEntropyFeedback(done,
1806 {
1807 entropy: "3d3d",
1808 events: "2",
1809 bits: "9",
1810 bitsPerEvent: "4.80",
1811 }
1812 );
1813 });
1814 it("Shows details about the entered entropy", function(done) {
1815 testEntropyFeedback(done,
1816 {
1817 entropy: "3d3d3d",
1818 events: "3",
1819 bits: "15",
1820 bitsPerEvent: "5.01",
1821 }
1822 );
1823 });
1824 it("Shows details about the entered entropy", function(done) {
1825 testEntropyFeedback(done,
1826 {
1827 entropy: "3d3d3d3d",
1828 events: "4",
1829 bits: "20",
1830 bitsPerEvent: "5.14",
1831 }
1832 );
1833 });
1834 it("Shows details about the entered entropy", function(done) {
1835 testEntropyFeedback(done,
1836 {
1837 entropy: "3d3d3d3d3d",
1838 events: "5",
1839 bits: "26",
1840 bitsPerEvent: "5.22",
1841 }
1842 );
1843 });
1844 it("Shows details about the entered entropy", function(done) {
1845 testEntropyFeedback(done,
1846 {
1847 entropy: "3d3d3d3d3d3d",
1848 events: "6",
1849 bits: "31",
1850 bitsPerEvent: "5.28",
1851 }
1852 );
1853 });
1854 it("Shows details about the entered entropy", function(done) {
1855 testEntropyFeedback(done,
1856 {
1857 entropy: "3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d",
1858 events: "33",
1859 bits: "184",
1860 bitsPerEvent: "5.59",
1861 strength: 'less than a second - Repeats like "abcabcabc" are only slightly harder to guess than "abc"',
1862 }
1863 );
1864 });
1865
1866 // Entropy is truncated from the left
1867 it('Truncates entropy from the left', function(done) {
1868 // Truncate from left means 0000 is removed from the start
1869 // which gives mnemonic 'avocado zoo zone'
1870 // not 1111 removed from the end
1871 // which gives the mnemonic 'abstract zoo zoo'
1872 var entropy = "00000000 00000000 00000000 00000000";
1873 entropy += "11111111 11111111 11111111 1111"; // Missing last byte
1874 driver.findElement(By.css('.use-entropy'))
1875 .click();
1876 driver.findElement(By.css('.entropy'))
1877 .sendKeys(entropy);
1878 driver.sleep(generateDelay).then(function() {
1879 driver.findElement(By.css(".phrase"))
1880 .getAttribute("value").then(function(phrase) {
1881 expect(phrase).toBe("avocado zoo zone");
1882 done();
1883 });
1884 });
1885 });
1886
1887 // Very large entropy results in very long mnemonics
1888 it('Converts very long entropy to very long mnemonics', function(done) {
1889 var entropy = "";
1890 for (var i=0; i<33; i++) {
1891 entropy += "AAAAAAAA"; // 3 words * 33 iterations = 99 words
1892 }
1893 driver.findElement(By.css('.use-entropy'))
1894 .click();
1895 driver.findElement(By.css('.entropy'))
1896 .sendKeys(entropy);
1897 driver.sleep(generateDelay).then(function() {
1898 driver.findElement(By.css(".phrase"))
1899 .getAttribute("value").then(function(phrase) {
1900 var wordCount = phrase.split(/\s+/g).length;
1901 expect(wordCount).toBe(99);
1902 done();
1903 });
1904 });
1905 });
1906
1907 // Is compatible with bip32jp entropy
1908 // https://bip32jp.github.io/english/index.html
1909 // NOTES:
1910 // Is incompatible with:
1911 // base 20
1912 it('Is compatible with bip32jp.github.io', function(done) {
1913 var entropy = "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543";
1914 var expectedPhrase = "train then jungle barely whip fiber purpose puppy eagle cloud clump hospital robot brave balcony utility detect estate old green desk skill multiply virus";
1915 driver.findElement(By.css('.use-entropy'))
1916 .click();
1917 driver.findElement(By.css('.entropy'))
1918 .sendKeys(entropy);
1919 driver.sleep(generateDelay).then(function() {
1920 driver.findElement(By.css(".phrase"))
1921 .getAttribute("value").then(function(phrase) {
1922 expect(phrase).toBe(expectedPhrase);
1923 done();
1924 });
1925 });
1926 });
1927
1928 // Blank entropy does not generate mnemonic or addresses
1929 it('Does not generate mnemonic for blank entropy', function(done) {
1930 driver.findElement(By.css('.use-entropy'))
1931 .click();
1932 driver.findElement(By.css('.entropy'))
1933 .clear();
1934 // check there is no mnemonic
1935 driver.sleep(generateDelay).then(function() {
1936 driver.findElement(By.css(".phrase"))
1937 .getAttribute("value").then(function(phrase) {
1938 expect(phrase).toBe("");
1939 // check there is no mnemonic
1940 driver.findElements(By.css(".address"))
1941 .then(function(addresses) {
1942 expect(addresses.length).toBe(0);
1943 // Check the feedback says 'blank entropy'
1944 driver.findElement(By.css(".feedback"))
1945 .getText()
1946 .then(function(feedbackText) {
1947 expect(feedbackText).toBe("Blank entropy");
1948 done();
1949 });
1950 })
1951 });
1952 });
1953 });
1954
1955 // Mnemonic length can be selected even for weak entropy
1956 it('Allows selection of mnemonic length even for weak entropy', function(done) {
1957 driver.findElement(By.css('.use-entropy'))
1958 .click();
1959 driver.executeScript(function() {
1960 $(".mnemonic-length").val("18").trigger("change");
1961 });
1962 driver.findElement(By.css('.entropy'))
1963 .sendKeys("012345");
1964 driver.sleep(generateDelay).then(function() {
1965 driver.findElement(By.css(".phrase"))
1966 .getAttribute("value").then(function(phrase) {
1967 var wordCount = phrase.split(/\s+/g).length;
1968 expect(wordCount).toBe(18);
1969 done();
1970 });
1971 });
1972 });
1973
1974 // Github issue 33
1975 // https://github.com/iancoleman/bip39/issues/33
1976 // Final cards should contribute entropy
1977 it('Uses as much entropy as possible for the mnemonic', function(done) {
1978 driver.findElement(By.css('.use-entropy'))
1979 .click();
1980 driver.findElement(By.css('.entropy'))
1981 .sendKeys("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");
1982 driver.sleep(generateDelay).then(function() {
1983 // Get mnemonic
1984 driver.findElement(By.css(".phrase"))
1985 .getAttribute("value").then(function(originalPhrase) {
1986 // Set the last 12 cards to be AS
1987 driver.findElement(By.css('.entropy'))
1988 .clear();
1989 driver.findElement(By.css('.entropy'))
1990 .sendKeys("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");
1991 driver.sleep(generateDelay).then(function() {
1992 // Get new mnemonic
1993 driver.findElement(By.css(".phrase"))
1994 .getAttribute("value").then(function(newPhrase) {
1995 expect(originalPhrase).not.toEqual(newPhrase);
1996 done();
1997 });
1998 });
1999 });
2000 });
2001 });
2002
2003 // Github issue 35
2004 // https://github.com/iancoleman/bip39/issues/35
2005 // QR Code support
2006 // TODO this doesn't work in selenium with firefox
2007 // see https://stackoverflow.com/q/40360223
2008 it('Shows a qr code on hover for the phrase', function(done) {
2009 if (browser == "firefox") {
2010 pending("Selenium + Firefox bug for mouseMove, see https://stackoverflow.com/q/40360223");
2011 }
2012 // generate a random mnemonic
2013 var generateEl = driver.findElement(By.css('.generate'));
2014 generateEl.click();
2015 // toggle qr to show (hidden by default)
2016 var phraseEl = driver.findElement(By.css(".phrase"));
2017 phraseEl.click();
2018 var rootKeyEl = driver.findElement(By.css(".root-key"));
2019 driver.sleep(generateDelay).then(function() {
2020 // hover over the root key
2021 driver.actions().mouseMove(rootKeyEl).perform().then(function() {
2022 // check the qr code shows
2023 driver.executeScript(function() {
2024 return $(".qr-container").find("canvas").length > 0;
2025 })
2026 .then(function(qrShowing) {
2027 expect(qrShowing).toBe(true);
2028 // hover away from the phrase
2029 driver.actions().mouseMove(generateEl).perform().then(function() {;
2030 // check the qr code hides
2031 driver.executeScript(function() {
2032 return $(".qr-container").find("canvas").length == 0;
2033 })
2034 .then(function(qrHidden) {
2035 expect(qrHidden).toBe(true);
2036 done();
2037 });
2038 });
2039 });
2040 });
2041 });
2042 });
2043
2044 // BIP44 account extendend private key is shown
2045 // github issue 37 - compatibility with electrum
2046 it('Shows the bip44 account extended private key', function(done) {
2047 driver.findElement(By.css(".phrase"))
2048 .sendKeys("abandon abandon ability");
2049 driver.sleep(generateDelay).then(function() {
2050 driver.findElement(By.css("#bip44 .account-xprv"))
2051 .getAttribute("value")
2052 .then(function(xprv) {
2053 expect(xprv).toBe("xprv9yzrnt4zWVJUr1k2VxSPy9ettKz5PpeDMgaVG7UKedhqnw1tDkxP2UyYNhuNSumk2sLE5ctwKZs9vwjsq3e1vo9egCK6CzP87H2cVYXpfwQ");
2054 done();
2055 });
2056 });
2057 });
2058
2059 // BIP44 account extendend public key is shown
2060 // github issue 37 - compatibility with electrum
2061 it('Shows the bip44 account extended public key', function(done) {
2062 driver.findElement(By.css(".phrase"))
2063 .sendKeys("abandon abandon ability");
2064 driver.sleep(generateDelay).then(function() {
2065 driver.findElement(By.css("#bip44 .account-xpub"))
2066 .getAttribute("value")
2067 .then(function(xprv) {
2068 expect(xprv).toBe("xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf");
2069 done();
2070 });
2071 });
2072 });
2073
2074 // github issue 40
2075 // BIP32 root key can be set as an xpub
2076 it('Generates addresses from xpub as bip32 root key', function(done) {
2077 driver.findElement(By.css('#bip32-tab a'))
2078 .click();
2079 // set xpub for account 0 of bip44 for 'abandon abandon ability'
2080 driver.findElement(By.css("#root-key"))
2081 .sendKeys("xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf");
2082 driver.sleep(generateDelay).then(function() {
2083 // check the addresses are generated
2084 getFirstAddress(function(address) {
2085 expect(address).toBe("1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug");
2086 // check the xprv key is not set
2087 driver.findElement(By.css(".extended-priv-key"))
2088 .getAttribute("value")
2089 .then(function(xprv) {
2090 expect(xprv).toBe("NA");
2091 // check the private key is not set
2092 driver.findElements(By.css(".privkey"))
2093 .then(function(els) {
2094 els[0]
2095 .getText()
2096 .then(function(privkey) {
2097 expect(xprv).toBe("NA");
2098 done();
2099 });
2100 });
2101 });
2102 });
2103 });
2104 });
2105
2106 // github issue 40
2107 // xpub for bip32 root key will not work with hardened derivation paths
2108 it('Shows error for hardened derivation paths with xpub root key', function(done) {
2109 // set xpub for account 0 of bip44 for 'abandon abandon ability'
2110 driver.findElement(By.css("#root-key"))
2111 .sendKeys("xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf");
2112 driver.sleep(feedbackDelay).then(function() {
2113 // Check feedback is correct
2114 driver.findElement(By.css('.feedback'))
2115 .getText()
2116 .then(function(feedback) {
2117 var msg = "Hardened derivation path is invalid with xpub key";
2118 expect(feedback).toBe(msg);
2119 // Check no addresses are shown
2120 driver.findElements(By.css('.addresses tr'))
2121 .then(function(rows) {
2122 expect(rows.length).toBe(0);
2123 done();
2124 });
2125 });
2126 });
2127 });
2128
2129 // github issue 39
2130 // no root key shows feedback
2131 it('Shows feedback for no root key', function(done) {
2132 // set xpub for account 0 of bip44 for 'abandon abandon ability'
2133 driver.findElement(By.css('#bip32-tab a'))
2134 .click();
2135 driver.sleep(feedbackDelay).then(function() {
2136 // Check feedback is correct
2137 driver.findElement(By.css('.feedback'))
2138 .getText()
2139 .then(function(feedback) {
2140 expect(feedback).toBe("Invalid root key");
2141 done();
2142 });
2143 });
2144 });
2145
2146 // Github issue 44
2147 // display error switching tabs while addresses are generating
2148 it('Can change details while old addresses are still being generated', function(done) {
2149 // Set to generate 199 more addresses.
2150 // This will take a long time allowing a new set of addresses to be
2151 // generated midway through this lot.
2152 // The newly generated addresses should not include any from the old set.
2153 // Any more than 199 will show an alert which needs to be accepted.
2154 driver.findElement(By.css('.rows-to-add'))
2155 .clear();
2156 driver.findElement(By.css('.rows-to-add'))
2157 .sendKeys('199');
2158 // set the prhase
2159 driver.findElement(By.css('.phrase'))
2160 .sendKeys("abandon abandon ability");
2161 driver.sleep(generateDelay).then(function() {
2162 // generate more addresses
2163 driver.findElement(By.css('.more'))
2164 .click();
2165 // change tabs which should cancel the previous generating
2166 driver.findElement(By.css('#bip32-tab a'))
2167 .click()
2168 driver.sleep(generateDelay).then(function() {
2169 driver.findElements(By.css('.index'))
2170 .then(function(els) {
2171 // check the derivation paths have the right quantity
2172 expect(els.length).toBe(20);
2173 // check the derivation paths are in order
2174 testRowsAreInCorrectOrder(done);
2175 });
2176 });
2177 });
2178 });
2179
2180 // Github issue 49
2181 // padding for binary should give length with multiple of 256
2182 // hashed entropy 1111 is length 252, so requires 4 leading zeros
2183 // prior to issue 49 it would only generate 2 leading zeros, ie missing 2
2184 it('Pads hashed entropy with leading zeros', function(done) {
2185 driver.findElement(By.css('.use-entropy'))
2186 .click();
2187 driver.executeScript(function() {
2188 $(".mnemonic-length").val("15").trigger("change");
2189 });
2190 driver.findElement(By.css('.entropy'))
2191 .sendKeys("1111");
2192 driver.sleep(generateDelay).then(function() {
2193 driver.findElement(By.css('.phrase'))
2194 .getAttribute("value")
2195 .then(function(phrase) {
2196 expect(phrase).toBe("avocado valid quantum cross link predict excuse edit street able flame large galaxy ginger nuclear");
2197 done();
2198 });
2199 });
2200 });
2201
2202 // Github pull request 55
2203 // https://github.com/iancoleman/bip39/pull/55
2204 // Client select
2205 it('Can set the derivation path on bip32 tab for bitcoincore', function(done) {
2206 testClientSelect(done, {
2207 selectValue: "0",
2208 bip32path: "m/0'/0'",
2209 useHardenedAddresses: "true",
2210 });
2211 });
2212 it('Can set the derivation path on bip32 tab for multibit', function(done) {
2213 testClientSelect(done, {
2214 selectValue: "2",
2215 bip32path: "m/0'/0",
2216 useHardenedAddresses: null,
2217 });
2218 });
2219
2220 // github issue 58
2221 // https://github.com/iancoleman/bip39/issues/58
2222 // bip32 derivation is correct, does not drop leading zeros
2223 // see also
2224 // https://medium.com/@alexberegszaszi/why-do-my-bip32-wallets-disagree-6f3254cc5846
2225 it('Retains leading zeros for bip32 derivation', function(done) {
2226 driver.findElement(By.css(".phrase"))
2227 .sendKeys("fruit wave dwarf banana earth journey tattoo true farm silk olive fence");
2228 driver.findElement(By.css(".passphrase"))
2229 .sendKeys("banana");
2230 driver.sleep(generateDelay).then(function() {
2231 getFirstAddress(function(address) {
2232 // Note that bitcore generates an incorrect address
2233 // 13EuKhffWkBE2KUwcbkbELZb1MpzbimJ3Y
2234 // see the medium.com link above for more details
2235 expect(address).toBe("17rxURoF96VhmkcEGCj5LNQkmN9HVhWb7F");
2236 done();
2237 });
2238 });
2239 });
2240
2241 // github issue 60
2242 // Japanese mnemonics generate incorrect bip32 seed
2243 // BIP39 seed is set from phrase
2244 it('Generates correct seed for Japanese mnemonics', function(done) {
2245 driver.findElement(By.css(".phrase"))
2246 .sendKeys("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら");
2247 driver.findElement(By.css(".passphrase"))
2248 .sendKeys("メートルガバヴァぱばぐゞちぢ十人十色");
2249 driver.sleep(generateDelay).then(function() {
2250 driver.findElement(By.css(".seed"))
2251 .getAttribute("value")
2252 .then(function(seed) {
2253 expect(seed).toBe("a262d6fb6122ecf45be09c50492b31f92e9beb7d9a845987a02cefda57a15f9c467a17872029a9e92299b5cbdf306e3a0ee620245cbd508959b6cb7ca637bd55");
2254 done();
2255 });
2256 });
2257 });
2258
2259 // BIP49 official test vectors
2260 // https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki#test-vectors
2261 it('Generates BIP49 addresses matching the official test vectors', function(done) {
2262 driver.findElement(By.css('#bip49-tab a'))
2263 .click();
2264 selectNetwork("BTC - Bitcoin Testnet");
2265 driver.findElement(By.css(".phrase"))
2266 .sendKeys("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about");
2267 driver.sleep(generateDelay).then(function() {
2268 getFirstAddress(function(address) {
2269 expect(address).toBe("2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2");
2270 done();
2271 });
2272 });
2273 });
2274
2275 // BIP49 derivation path is shown
2276 it('Shows the bip49 derivation path', function(done) {
2277 driver.findElement(By.css('#bip49-tab a'))
2278 .click();
2279 driver.findElement(By.css(".phrase"))
2280 .sendKeys("abandon abandon ability");
2281 driver.sleep(generateDelay).then(function() {
2282 driver.findElement(By.css('#bip49 .path'))
2283 .getAttribute("value")
2284 .then(function(path) {
2285 expect(path).toBe("m/49'/0'/0'/0");
2286 done();
2287 });
2288 });
2289 });
2290
2291 // BIP49 extended private key is shown
2292 it('Shows the bip49 extended private key', function(done) {
2293 driver.findElement(By.css('#bip49-tab a'))
2294 .click();
2295 driver.findElement(By.css(".phrase"))
2296 .sendKeys("abandon abandon ability");
2297 driver.sleep(generateDelay).then(function() {
2298 driver.findElement(By.css('.extended-priv-key'))
2299 .getAttribute("value")
2300 .then(function(xprv) {
2301 expect(xprv).toBe("yprvALYB4DYRG6CzzVgzQZwwqjAA2wjBGC3iEd7KYYScpoDdmf75qMRWZWxoFcRXBJjgEXdFqJ9vDRGRLJQsrL22Su5jMbNFeM9vetaGVqy9Qy2");
2302 done();
2303 });
2304 });
2305 });
2306
2307 // BIP49 extended public key is shown
2308 it('Shows the bip49 extended public key', function(done) {
2309 driver.findElement(By.css('#bip49-tab a'))
2310 .click();
2311 driver.findElement(By.css(".phrase"))
2312 .sendKeys("abandon abandon ability");
2313 driver.sleep(generateDelay).then(function() {
2314 driver.findElement(By.css('.extended-pub-key'))
2315 .getAttribute("value")
2316 .then(function(xprv) {
2317 expect(xprv).toBe("ypub6ZXXTj5K6TmJCymTWbUxCs6tayZffemZbr2vLvrEP8kceTSENtjm7KHH6thvAKxVar9fGe8rgsPEX369zURLZ68b4f7Vexz7RuXsjQ69YDt");
2318 done();
2319 });
2320 });
2321 });
2322
2323 // BIP49 account field changes address list
2324 it('Can set the bip49 account field', function(done) {
2325 driver.findElement(By.css('#bip49-tab a'))
2326 .click();
2327 driver.findElement(By.css('#bip49 .account'))
2328 .clear();
2329 driver.findElement(By.css('#bip49 .account'))
2330 .sendKeys("1");
2331 driver.findElement(By.css(".phrase"))
2332 .sendKeys("abandon abandon ability");
2333 driver.sleep(generateDelay).then(function() {
2334 getFirstAddress(function(address) {
2335 expect(address).toBe("381wg1GGN4rP88rNC9v7QWsiww63yLVPsn");
2336 done();
2337 });
2338 });
2339 });
2340
2341 // BIP49 change field changes address list
2342 it('Can set the bip49 change field', function(done) {
2343 driver.findElement(By.css('#bip49-tab a'))
2344 .click();
2345 driver.findElement(By.css('#bip49 .change'))
2346 .clear();
2347 driver.findElement(By.css('#bip49 .change'))
2348 .sendKeys("1");
2349 driver.findElement(By.css(".phrase"))
2350 .sendKeys("abandon abandon ability");
2351 driver.sleep(generateDelay).then(function() {
2352 getFirstAddress(function(address) {
2353 expect(address).toBe("3PEM7MiKed5konBoN66PQhK8r3hjGhy9dT");
2354 done();
2355 });
2356 });
2357 });
2358
2359 // BIP49 account extendend private key is shown
2360 it('Shows the bip49 account extended private key', function(done) {
2361 driver.findElement(By.css('#bip49-tab a'))
2362 .click();
2363 driver.findElement(By.css(".phrase"))
2364 .sendKeys("abandon abandon ability");
2365 driver.sleep(generateDelay).then(function() {
2366 driver.findElement(By.css('#bip49 .account-xprv'))
2367 .getAttribute("value")
2368 .then(function(xprv) {
2369 expect(xprv).toBe("yprvAHtB1M5Wp675aLzFy9TJYK2mSsLkg6mcBRh5DZTR7L4EnYSmYPaL63KFA4ycg1PngW5LfkmejxzosCs17TKZMpRFKc3z5SJar6QAKaFcaZL");
2370 done();
2371 });
2372 });
2373 });
2374
2375 // BIP49 account extendend public key is shown
2376 it('Shows the bip49 account extended public key', function(done) {
2377 driver.findElement(By.css('#bip49-tab a'))
2378 .click();
2379 driver.findElement(By.css(".phrase"))
2380 .sendKeys("abandon abandon ability");
2381 driver.sleep(generateDelay).then(function() {
2382 driver.findElement(By.css('#bip49 .account-xpub'))
2383 .getAttribute("value")
2384 .then(function(xprv) {
2385 expect(xprv).toBe("ypub6WsXQrcQeTfNnq4j5AzJuSyVzuBF5ZVTYecg1ws2ffbDfLmv5vtadqdj1NgR6C6gufMpMfJpHxvb6JEQKvETVNWCRanNedfJtnTchZiJtsL");
2386 done();
2387 });
2388 });
2389 });
2390
2391 // Test selecting coin where bip49 is unavailable (eg CLAM)
2392 it('Shows an error on bip49 tab for coins without bip49', function(done) {
2393 driver.findElement(By.css('#bip49-tab a'))
2394 .click();
2395 driver.findElement(By.css(".phrase"))
2396 .sendKeys("abandon abandon ability");
2397 driver.sleep(generateDelay).then(function() {
2398 selectNetwork("CLAM - Clams");
2399 // bip49 available is hidden
2400 driver.findElement(By.css('#bip49 .available'))
2401 .getAttribute("class")
2402 .then(function(classes) {
2403 expect(classes).toContain("hidden");
2404 // bip49 unavailable is shown
2405 driver.findElement(By.css('#bip49 .unavailable'))
2406 .getAttribute("class")
2407 .then(function(classes) {
2408 expect(classes).not.toContain("hidden");
2409 // check there are no addresses shown
2410 driver.findElements(By.css('.addresses tr'))
2411 .then(function(rows) {
2412 expect(rows.length).toBe(0);
2413 // check the derived private key is blank
2414 driver.findElement(By.css('.extended-priv-key'))
2415 .getAttribute("value")
2416 .then(function(xprv) {
2417 expect(xprv).toBe('');
2418 // check the derived public key is blank
2419 driver.findElement(By.css('.extended-pub-key'))
2420 .getAttribute("value")
2421 .then(function(xpub) {
2422 expect(xpub).toBe('');
2423 done();
2424 });
2425 });
2426 })
2427 });
2428 });
2429 });
2430 });
2431
2432 // github issue 43
2433 // Cleared mnemonic and root key still allows addresses to be generated
2434 // https://github.com/iancoleman/bip39/issues/43
2435 it('Clears old root keys from memory when mnemonic is cleared', function(done) {
2436 // set the phrase
2437 driver.findElement(By.css(".phrase"))
2438 .sendKeys("abandon abandon ability");
2439 driver.sleep(generateDelay).then(function() {
2440 // clear the mnemonic and root key
2441 // using selenium .clear() doesn't seem to trigger the 'input' event
2442 // so clear it using keys instead
2443 driver.findElement(By.css('.phrase'))
2444 .sendKeys(Key.CONTROL,"a");
2445 driver.findElement(By.css('.phrase'))
2446 .sendKeys(Key.DELETE);
2447 driver.findElement(By.css('.root-key'))
2448 .sendKeys(Key.CONTROL,"a");
2449 driver.findElement(By.css('.root-key'))
2450 .sendKeys(Key.DELETE);
2451 driver.sleep(generateDelay).then(function() {
2452 // try to generate more addresses
2453 driver.findElement(By.css('.more'))
2454 .click();
2455 driver.sleep(generateDelay).then(function() {
2456 driver.findElements(By.css(".addresses tr"))
2457 .then(function(els) {
2458 // check there are no addresses shown
2459 expect(els.length).toBe(0);
2460 done();
2461 });
2462 });
2463 });
2464 });
2465 });
2466
2467 // Github issue 95
2468 // error trying to generate addresses from xpub with hardened derivation
2469 it('Shows error for hardened addresses with xpub root key', function(done) {
2470 driver.findElement(By.css('#bip32-tab a'))
2471 .click()
2472 driver.executeScript(function() {
2473 $(".hardened-addresses").prop("checked", true);
2474 });
2475 // set xpub for account 0 of bip44 for 'abandon abandon ability'
2476 driver.findElement(By.css("#root-key"))
2477 .sendKeys("xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf");
2478 driver.sleep(feedbackDelay).then(function() {
2479 // Check feedback is correct
2480 driver.findElement(By.css('.feedback'))
2481 .getText()
2482 .then(function(feedback) {
2483 var msg = "Hardened derivation path is invalid with xpub key";
2484 expect(feedback).toBe(msg);
2485 done();
2486 });
2487 });
2488 });
2489
2490 // Litecoin uses ltub by default, and can optionally be set to xprv
2491 // github issue 96
2492 // https://github.com/iancoleman/bip39/issues/96
2493 // Issue with extended keys on Litecoin
2494 it('Uses ltub by default for litecoin, but can be set to xprv', function(done) {
2495 driver.findElement(By.css('.phrase'))
2496 .sendKeys("abandon abandon ability");
2497 selectNetwork("LTC - Litecoin");
2498 driver.sleep(generateDelay).then(function() {
2499 // check the extended key is generated correctly
2500 driver.findElement(By.css('.root-key'))
2501 .getAttribute("value")
2502 .then(function(rootKey) {
2503 expect(rootKey).toBe("Ltpv71G8qDifUiNesiPqf6h5V6eQ8ic77oxQiYtawiACjBEx3sTXNR2HGDGnHETYxESjqkMLFBkKhWVq67ey1B2MKQXannUqNy1RZVHbmrEjnEU");
2504 // set litecoin to use ltub
2505 driver.executeScript(function() {
2506 $(".litecoin-use-ltub").prop("checked", false);
2507 $(".litecoin-use-ltub").trigger("change");
2508 });
2509 driver.sleep(generateDelay).then(function() {
2510 driver.findElement(By.css('.root-key'))
2511 .getAttribute("value")
2512 .then(function(rootKey) {
2513 expect(rootKey).toBe("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi");
2514 done();
2515 });
2516 })
2517 });
2518 });
2519 });
2520
2521 // github issue 99
2522 // https://github.com/iancoleman/bip39/issues/99#issuecomment-327094159
2523 // "warn me emphatically when they have detected invalid input" to the entropy field
2524 // A warning is shown when entropy is filtered and discarded
2525 it('Warns when entropy is filtered and discarded', function(done) {
2526 driver.findElement(By.css('.use-entropy'))
2527 .click();
2528 // set entropy to have no filtered content
2529 driver.findElement(By.css('.entropy'))
2530 .sendKeys("00000000 00000000 00000000 00000000");
2531 driver.sleep(generateDelay).then(function() {
2532 // check the filter warning does not show
2533 driver.findElement(By.css('.entropy-container .filter-warning'))
2534 .getAttribute("class")
2535 .then(function(classes) {
2536 expect(classes).toContain("hidden");
2537 // set entropy to have some filtered content
2538 driver.findElement(By.css('.entropy'))
2539 .sendKeys("10000000 zxcvbn 00000000 00000000 00000000");
2540 driver.sleep(entropyFeedbackDelay).then(function() {
2541 // check the filter warning shows
2542 driver.findElement(By.css('.entropy-container .filter-warning'))
2543 .getAttribute("class")
2544 .then(function(classes) {
2545 expect(classes).not.toContain("hidden");
2546 done();
2547 });
2548 });
2549 });
2550 });
2551 });
2552
2553 // Bitcoin Cash address can be set to use bitpay format
2554 it('Can use bitpay format for bitcoin cash addresses', function(done) {
2555 driver.executeScript(function() {
2556 $(".use-bitpay-addresses").prop("checked", true);
2557 });
2558 driver.findElement(By.css('.phrase'))
2559 .sendKeys("abandon abandon ability");
2560 selectNetwork("BCH - Bitcoin Cash");
2561 driver.sleep(generateDelay).then(function() {
2562 getFirstAddress(function(address) {
2563 expect(address).toBe("CZnpA9HPmvhuhLLPWJP8rNDpLUYXy1LXFk");
2564 done();
2565 });
2566 });
2567 });
2568
2569 // End of tests ported from old suit, so no more comments above each test now
2570
2571 it('Can generate more addresses from a custom index', function(done) {
2572 var expectedIndexes = [
2573 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
2574 40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59
2575 ];
2576 driver.findElement(By.css('.phrase'))
2577 .sendKeys("abandon abandon ability");
2578 driver.sleep(generateDelay).then(function() {
2579 // Set start of next lot of rows to be from index 40
2580 // which means indexes 20-39 will not be in the table.
2581 driver.findElement(By.css('.more-rows-start-index'))
2582 .sendKeys("40");
2583 driver.findElement(By.css('.more'))
2584 .click();
2585 driver.sleep(generateDelay).then(function() {
2586 // Check actual indexes in the table match the expected pattern
2587 driver.findElements(By.css(".index"))
2588 .then(function(els) {
2589 expect(els.length).toBe(expectedIndexes.length);
2590 var testRowAtIndex = function(i) {
2591 if (i >= expectedIndexes.length) {
2592 done();
2593 }
2594 else {
2595 els[i].getText()
2596 .then(function(actualPath) {
2597 var noHardened = actualPath.replace(/'/g, "");
2598 var pathBits = noHardened.split("/")
2599 var lastBit = pathBits[pathBits.length-1];
2600 var actualIndex = parseInt(lastBit);
2601 var expectedIndex = expectedIndexes[i];
2602 expect(actualIndex).toBe(expectedIndex);
2603 testRowAtIndex(i+1);
2604 });
2605 }
2606 }
2607 testRowAtIndex(0);
2608 });
2609 });
2610 });
2611 });
2612
2613 it('Can generate BIP141 addresses with P2WPKH-in-P2SH semanitcs', function(done) {
2614 // Sourced from BIP49 official test specs
2615 driver.findElement(By.css('#bip141-tab a'))
2616 .click();
2617 driver.findElement(By.css('.bip141-path'))
2618 .clear();
2619 driver.findElement(By.css('.bip141-path'))
2620 .sendKeys("m/49'/1'/0'/0");
2621 selectNetwork("BTC - Bitcoin Testnet");
2622 driver.findElement(By.css(".phrase"))
2623 .sendKeys("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about");
2624 driver.sleep(generateDelay).then(function() {
2625 getFirstAddress(function(address) {
2626 expect(address).toBe("2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2");
2627 done();
2628 });
2629 });
2630 });
2631
2632 it('Can generate BIP141 addresses with P2WPKH semanitcs', function(done) {
2633 // This result tested against bitcoinjs-lib test spec for segwit address
2634 // using the first private key of this mnemonic and default path m/0
2635 // https://github.com/bitcoinjs/bitcoinjs-lib/blob/9c8503cab0c6c30a95127042703bc18e8d28c76d/test/integration/addresses.js#L50
2636 // so whilst not directly comparable, substituting the private key produces
2637 // identical results between this tool and the bitcoinjs-lib test.
2638 // Private key generated is:
2639 // L3L8Nu9whawPBNLGtFqDhKut9DKKfG3CQoysupT7BimqVCZsLFNP
2640 driver.findElement(By.css('#bip141-tab a'))
2641 .click();
2642 // Choose P2WPKH
2643 driver.executeScript(function() {
2644 $(".bip141-semantics option[selected]").removeAttr("selected");
2645 $(".bip141-semantics option").filter(function(i,e) {
2646 return $(e).html() == "P2WPKH";
2647 }).prop("selected", true);
2648 $(".bip141-semantics").trigger("change");
2649 });
2650 driver.findElement(By.css(".phrase"))
2651 .sendKeys("abandon abandon ability");
2652 driver.sleep(generateDelay).then(function() {
2653 getFirstAddress(function(address) {
2654 expect(address).toBe("bc1qfwu6a5a3evygrk8zvdxxvz4547lmpyx5vsfxe9");
2655 done();
2656 });
2657 });
2658 });
2659
2660 it('Shows the entropy used by the PRNG when clicking generate', function(done) {
2661 driver.findElement(By.css('.generate')).click();
2662 driver.sleep(generateDelay).then(function() {
2663 driver.findElement(By.css('.entropy'))
2664 .getAttribute("value")
2665 .then(function(entropy) {
2666 expect(entropy).not.toBe("");
2667 done();
2668 });
2669 });
2670 });
2671
2672 it('Shows the index of each word in the mnemonic', function(done) {
2673 driver.findElement(By.css('.phrase'))
2674 .sendKeys("abandon abandon ability");
2675 driver.sleep(generateDelay).then(function() {
2676 driver.findElement(By.css('.use-entropy'))
2677 .click();
2678 driver.findElement(By.css('.word-indexes'))
2679 .getText()
2680 .then(function(indexes) {
2681 expect(indexes).toBe("0, 0, 1");
2682 done();
2683 });
2684 });
2685 });
2686
2687 it('Shows the derivation path for bip84 tab', function(done) {
2688 driver.findElement(By.css('#bip84-tab a'))
2689 .click()
2690 driver.findElement(By.css('.phrase'))
2691 .sendKeys('abandon abandon ability');
2692 driver.sleep(generateDelay).then(function() {
2693 driver.findElement(By.css('#bip84 .path'))
2694 .getAttribute("value")
2695 .then(function(path) {
2696 expect(path).toBe("m/84'/0'/0'/0");
2697 done();
2698 })
2699 });
2700 });
2701
2702 it('Shows the extended private key for bip84 tab', function(done) {
2703 driver.findElement(By.css('#bip84-tab a'))
2704 .click()
2705 driver.findElement(By.css('.phrase'))
2706 .sendKeys('abandon abandon ability');
2707 driver.sleep(generateDelay).then(function() {
2708 driver.findElement(By.css('.extended-priv-key'))
2709 .getAttribute("value")
2710 .then(function(path) {
2711 expect(path).toBe("zprvAev3RKrZ3QVKiUFCfdeMRen1BPDJgdNt1XpxiDy8acSs4kkAGTCvq7HeRYRNNpo8EtEjCFQBWavJwtCUR29y4TUCH4X5RXMcyq48uN8y9BP");
2712 done();
2713 })
2714 });
2715 });
2716
2717 it('Shows the extended public key for bip84 tab', function(done) {
2718 driver.findElement(By.css('#bip84-tab a'))
2719 .click()
2720 driver.findElement(By.css('.phrase'))
2721 .sendKeys('abandon abandon ability');
2722 driver.sleep(generateDelay).then(function() {
2723 driver.findElement(By.css('.extended-pub-key'))
2724 .getAttribute("value")
2725 .then(function(path) {
2726 expect(path).toBe("zpub6suPpqPSsn3cvxKfmfBMnnijjR3o666jNkkZWcNk8wyqwZ5JozXBNuc8Gs7DB3uLwTDvGVTspVEAUQcEjKF3pZHgywVbubdTqbXTUg7usyx");
2727 done();
2728 })
2729 });
2730 });
2731
2732 it('Changes the address list if bip84 account is changed', function(done) {
2733 driver.findElement(By.css('#bip84-tab a'))
2734 .click()
2735 driver.findElement(By.css('#bip84 .account'))
2736 .sendKeys('1');
2737 driver.findElement(By.css('.phrase'))
2738 .sendKeys('abandon abandon ability');
2739 driver.sleep(generateDelay).then(function() {
2740 getFirstAddress(function(address) {
2741 expect(address).toBe("bc1qp7vv669t2fy965jdzvqwrraana89ctd5ewc662");
2742 done();
2743 });
2744 });
2745 });
2746
2747 it('Changes the address list if bip84 change is changed', function(done) {
2748 driver.findElement(By.css('#bip84-tab a'))
2749 .click()
2750 driver.findElement(By.css('#bip84 .change'))
2751 .sendKeys('1');
2752 driver.findElement(By.css('.phrase'))
2753 .sendKeys('abandon abandon ability');
2754 driver.sleep(generateDelay).then(function() {
2755 getFirstAddress(function(address) {
2756 expect(address).toBe("bc1qr39vj6rh06ff05m53uxq8uazehwhccswylhrs2");
2757 done();
2758 });
2759 });
2760 });
2761
2762 it('Passes the official BIP84 test spec for rootpriv', function(done) {
2763 driver.findElement(By.css('#bip84-tab a'))
2764 .click()
2765 driver.findElement(By.css('.phrase'))
2766 .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about');
2767 driver.sleep(generateDelay).then(function() {
2768 driver.findElement(By.css(".root-key"))
2769 .getAttribute("value")
2770 .then(function(rootKey) {
2771 expect(rootKey).toBe("zprvAWgYBBk7JR8Gjrh4UJQ2uJdG1r3WNRRfURiABBE3RvMXYSrRJL62XuezvGdPvG6GFBZduosCc1YP5wixPox7zhZLfiUm8aunE96BBa4Kei5");
2772 done();
2773 })
2774 });
2775 });
2776
2777 it('Passes the official BIP84 test spec for account 0 xprv', function(done) {
2778 driver.findElement(By.css('#bip84-tab a'))
2779 .click()
2780 driver.findElement(By.css('.phrase'))
2781 .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about');
2782 driver.sleep(generateDelay).then(function() {
2783 driver.findElement(By.css("#bip84 .account-xprv"))
2784 .getAttribute("value")
2785 .then(function(rootKey) {
2786 expect(rootKey).toBe("zprvAdG4iTXWBoARxkkzNpNh8r6Qag3irQB8PzEMkAFeTRXxHpbF9z4QgEvBRmfvqWvGp42t42nvgGpNgYSJA9iefm1yYNZKEm7z6qUWCroSQnE");
2787 done();
2788 })
2789 });
2790 });
2791
2792 it('Passes the official BIP84 test spec for account 0 xpub', function(done) {
2793 driver.findElement(By.css('#bip84-tab a'))
2794 .click()
2795 driver.findElement(By.css('.phrase'))
2796 .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about');
2797 driver.sleep(generateDelay).then(function() {
2798 driver.findElement(By.css("#bip84 .account-xpub"))
2799 .getAttribute("value")
2800 .then(function(rootKey) {
2801 expect(rootKey).toBe("zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs");
2802 done();
2803 })
2804 });
2805 });
2806
2807 it('Passes the official BIP84 test spec for account 0 first address', function(done) {
2808 driver.findElement(By.css('#bip84-tab a'))
2809 .click()
2810 driver.findElement(By.css('.phrase'))
2811 .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about');
2812 driver.sleep(generateDelay).then(function() {
2813 getFirstAddress(function(address) {
2814 expect(address).toBe("bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu");
2815 done();
2816 });
2817 });
2818 });
2819
2820 it('Passes the official BIP84 test spec for account 0 first change address', function(done) {
2821 driver.findElement(By.css('#bip84-tab a'))
2822 .click()
2823 driver.findElement(By.css('.phrase'))
2824 .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about');
2825 driver.findElement(By.css('#bip84 .change'))
2826 .sendKeys('1');
2827 driver.sleep(generateDelay).then(function() {
2828 getFirstAddress(function(address) {
2829 expect(address).toBe("bc1q8c6fshw2dlwun7ekn9qwf37cu2rn755upcp6el");
2830 done();
2831 });
2832 });
2833 });
2834
2835 it('Can display the table as csv', function(done) {
2836 var headings = "path,address,public key,private key";
2837 var row1 = "m/44'/0'/0'/0/0,1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug,033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3,L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE";
2838 var row20 = "m/44'/0'/0'/0/19,1KhBy28XLAciXnnRvm71PvQJaETyrxGV55,02b4b3e396434d8cdd20c03ac4aaa07387784d5d867b75987f516f5705ee68cb3a,L4GrDrjReMsCAu5DkLXn79jSb95qR7Zfx7eshybCQZ1qL32MXJab";
2839 driver.findElement(By.css('.phrase'))
2840 .sendKeys('abandon abandon ability');
2841 driver.sleep(generateDelay).then(function() {
2842 driver.findElement(By.css('.csv'))
2843 .getAttribute("value")
2844 .then(function(csv) {
2845 expect(csv).toContain(headings);
2846 expect(csv).toContain(row1);
2847 expect(csv).toContain(row20);
2848 done();
2849 });
2850 });
2851 });
2852
2853 it('LeftPads ethereum keys that are less than 32 bytes', function(done) {
2854 // see https://github.com/iancoleman/bip39/issues/155
2855 selectNetwork("ETH - Ethereum");
2856 driver.findElement(By.css('#bip32-tab a'))
2857 .click()
2858 driver.findElement(By.css('#bip32-path'))
2859 .clear();
2860 driver.findElement(By.css('#bip32-path'))
2861 .sendKeys("m/44'/60'/0'");
2862 driver.findElement(By.css('.phrase'))
2863 .sendKeys('scout sort custom elite radar rare vivid thing trophy gesture cover snake change narrow kite list nation sustain buffalo erode open balance system young');
2864 driver.sleep(generateDelay).then(function() {
2865 driver.findElement(By.css(".address"))
2866 .getText()
2867 .then(function(address) {
2868 expect(address).toBe("0x8943E785B4a5714FC87a3aFAad1eB1FeB602B118");
2869 done();
2870 });
2871 //getFirstAddress(function(address) {
2872 // expect(address).toBe("0x8943E785B4a5714FC87a3aFAad1eB1FeB602B118");
2873 // done();
2874 //});
2875 });
2876 });
2877
2878 });