]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blob - tests/spec/tests.js
Use ltub for litecoin by default instead of xprv
[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 = 1000;
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 namecoin', function(done) {
496 var params = {
497 selectText: "NMC - Namecoin",
498 firstAddress: "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2",
499 };
500 testNetwork(done, params);
501 });
502 it('Allows selection of peercoin', function(done) {
503 var params = {
504 selectText: "PPC - Peercoin",
505 firstAddress: "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm",
506 };
507 testNetwork(done, params);
508 });
509 it('Allows selection of ethereum', function(done) {
510 var params = {
511 selectText: "ETH - Ethereum",
512 firstAddress: "0xe5815d5902Ad612d49283DEdEc02100Bd44C2772",
513 };
514 testNetwork(done, params);
515 // TODO test private key and public key
516 });
517 it('Allows selection of slimcoin', function(done) {
518 var params = {
519 selectText: "SLM - Slimcoin",
520 firstAddress: "SNzPi1CafHFm3WWjRo43aMgiaEEj3ogjww",
521 };
522 testNetwork(done, params);
523 });
524 it('Allows selection of slimcoin testnet', function(done) {
525 var params = {
526 selectText: "SLM - Slimcoin Testnet",
527 firstAddress: "n3nMgWufTek5QQAr6uwMhg5xbzj8xqc4Dq",
528 };
529 testNetwork(done, params);
530 });
531 it('Allows selection of bitcoin cash', function(done) {
532 var params = {
533 selectText: "BCH - Bitcoin Cash",
534 firstAddress: "1JKvb6wKtsjNoCRxpZ4DGrbniML7z5U16A",
535 };
536 testNetwork(done, params);
537 });
538 it('Allows selection of myriadcoin', function(done) {
539 var params = {
540 selectText: "XMY - Myriadcoin",
541 firstAddress: "MJEswvRR46wh9BoiVj9DzKYMBkCramhoBV",
542 };
543 testNetwork(done, params);
544 });
545 it('Allows selection of pivx', function(done) {
546 var params = {
547 selectText: "PIVX - PIVX",
548 firstAddress: "DBxgT7faCuno7jmtKuu6KWCiwqsVPqh1tS",
549 };
550 testNetwork(done, params);
551 });
552 it('Allows selection of pivx testnet', function(done) {
553 var params = {
554 selectText: "PIVX - PIVX Testnet",
555 firstAddress: "yB5U384n6dGkVE3by5y9VdvHHPwPg68fQj",
556 };
557 testNetwork(done, params);
558 });
559 it('Allows selection of maza', function(done) {
560 var params = {
561 selectText: "MAZA - Maza",
562 firstAddress: "MGW4Bmi2NEm4PxSjgeFwhP9vg18JHoRnfw",
563 };
564 testNetwork(done, params);
565 });
566 it('Allows selection of fujicoin', function(done) {
567 var params = {
568 selectText: "FJC - Fujicoin",
569 firstAddress: "FgiaLpG7C99DyR4WnPxXedRVHXSfKzUDhF",
570 };
571 testNetwork(done, params);
572 });
573 it('Allows selection of nubits', function(done) {
574 var params = {
575 selectText: "USNBT - NuBits",
576 firstAddress: "BLxkabXuZSJSdesLD7KxZdqovd4YwyBTU6",
577 };
578 testNetwork(done, params);
579 });
580 it('Allows selection of bitcoin gold', function(done) {
581 var params = {
582 selectText: "BTG - Bitcoin Gold",
583 firstAddress: "GWYxuwSqANWGV3WT7Gpr6HE91euYXBqtwQ",
584 };
585 testNetwork(done, params);
586 });
587 it('Allows selection of monacoin', function(done) {
588 var params = {
589 selectText: "MONA - Monacoin",
590 firstAddress: "MKMiMr7MyjDKjJbCBzgF6u4ByqTS4NkRB1",
591 };
592 testNetwork(done, params);
593 });
594
595 // BIP39 seed is set from phrase
596 it('Sets the bip39 seed from the prhase', function(done) {
597 driver.findElement(By.css('.phrase'))
598 .sendKeys('abandon abandon ability');
599 driver.sleep(generateDelay).then(function() {
600 driver.findElement(By.css('.seed'))
601 .getAttribute("value")
602 .then(function(seed) {
603 expect(seed).toBe("20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3");
604 done();
605 })
606 });
607 });
608
609 // BIP32 root key is set from phrase
610 it('Sets the bip39 root key 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('.root-key'))
615 .getAttribute("value")
616 .then(function(seed) {
617 expect(seed).toBe("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi");
618 done();
619 })
620 });
621 });
622
623 // Tabs show correct addresses when changed
624 it('Shows the correct address when tab is changed', function(done) {
625 driver.findElement(By.css('.phrase'))
626 .sendKeys('abandon abandon ability');
627 driver.sleep(generateDelay).then(function() {
628 driver.findElement(By.css('#bip32-tab a'))
629 .click();
630 driver.sleep(generateDelay).then(function() {
631 getFirstAddress(function(address) {
632 expect(address).toBe("17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz");
633 done();
634 });
635 });
636 });
637 });
638
639 // BIP44 derivation path is shown
640 it('Shows the derivation path for bip44 tab', function(done) {
641 driver.findElement(By.css('.phrase'))
642 .sendKeys('abandon abandon ability');
643 driver.sleep(generateDelay).then(function() {
644 driver.findElement(By.css('#bip44 .path'))
645 .getAttribute("value")
646 .then(function(path) {
647 expect(path).toBe("m/44'/0'/0'/0");
648 done();
649 })
650 });
651 });
652
653 // BIP44 extended private key is shown
654 it('Shows the extended private key 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('.extended-priv-key'))
659 .getAttribute("value")
660 .then(function(path) {
661 expect(path).toBe("xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG");
662 done();
663 })
664 });
665 });
666
667 // BIP44 extended public key is shown
668 it('Shows the extended public 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-pub-key'))
673 .getAttribute("value")
674 .then(function(path) {
675 expect(path).toBe("xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM");
676 done();
677 })
678 });
679 });
680
681 // BIP44 account field changes address list
682 it('Changes the address list if bip44 account is changed', function(done) {
683 driver.findElement(By.css('#bip44 .account'))
684 .sendKeys('1');
685 driver.findElement(By.css('.phrase'))
686 .sendKeys('abandon abandon ability');
687 driver.sleep(generateDelay).then(function() {
688 getFirstAddress(function(address) {
689 expect(address).toBe("1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H");
690 done();
691 });
692 });
693 });
694
695 // BIP44 change field changes address list
696 it('Changes the address list if bip44 change is changed', function(done) {
697 driver.findElement(By.css('#bip44 .change'))
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("1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo");
704 done();
705 });
706 });
707 });
708
709 // BIP32 derivation path can be set
710 it('Can use a custom bip32 derivation path', function(done) {
711 driver.findElement(By.css('#bip32-tab a'))
712 .click();
713 driver.findElement(By.css('#bip32 .path'))
714 .clear();
715 driver.findElement(By.css('#bip32 .path'))
716 .sendKeys('m/1');
717 driver.findElement(By.css('.phrase'))
718 .sendKeys('abandon abandon ability');
719 driver.sleep(generateDelay).then(function() {
720 getFirstAddress(function(address) {
721 expect(address).toBe("16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L");
722 done();
723 });
724 });
725 });
726
727 // BIP32 can use hardened derivation paths
728 it('Can use a hardened derivation paths', function(done) {
729 driver.findElement(By.css('#bip32-tab a'))
730 .click();
731 driver.findElement(By.css('#bip32 .path'))
732 .clear();
733 driver.findElement(By.css('#bip32 .path'))
734 .sendKeys("m/0'");
735 driver.findElement(By.css('.phrase'))
736 .sendKeys('abandon abandon ability');
737 driver.sleep(generateDelay).then(function() {
738 getFirstAddress(function(address) {
739 expect(address).toBe("14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4");
740 done();
741 });
742 });
743 });
744
745 // BIP32 extended private key is shown
746 it('Shows the BIP32 extended private key', function(done) {
747 driver.findElement(By.css('#bip32-tab a'))
748 .click();
749 driver.findElement(By.css('.phrase'))
750 .sendKeys('abandon abandon ability');
751 driver.sleep(generateDelay).then(function() {
752 driver.findElement(By.css('.extended-priv-key'))
753 .getAttribute("value")
754 .then(function(privKey) {
755 expect(privKey).toBe("xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe");
756 done();
757 });
758 });
759 });
760
761 // BIP32 extended public key is shown
762 it('Shows the BIP32 extended public key', function(done) {
763 driver.findElement(By.css('#bip32-tab a'))
764 .click();
765 driver.findElement(By.css('.phrase'))
766 .sendKeys('abandon abandon ability');
767 driver.sleep(generateDelay).then(function() {
768 driver.findElement(By.css('.extended-pub-key'))
769 .getAttribute("value")
770 .then(function(pubKey) {
771 expect(pubKey).toBe("xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P");
772 done();
773 });
774 });
775 });
776
777 // Derivation path is shown in table
778 it('Shows the derivation path in the table', function(done) {
779 driver.findElement(By.css('.phrase'))
780 .sendKeys('abandon abandon ability');
781 driver.sleep(generateDelay).then(function() {
782 getFirstPath(function(path) {
783 expect(path).toBe("m/44'/0'/0'/0/0");
784 done();
785 });
786 });
787 });
788
789 // Derivation path for address can be hardened
790 it('Can derive hardened addresses', function(done) {
791 driver.findElement(By.css('#bip32-tab a'))
792 .click();
793 driver.executeScript(function() {
794 $(".hardened-addresses").prop("checked", true);
795 });
796 driver.findElement(By.css('.phrase'))
797 .sendKeys('abandon abandon ability');
798 driver.sleep(generateDelay).then(function() {
799 getFirstAddress(function(address) {
800 expect(address).toBe("18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd");
801 done();
802 });
803 });
804 });
805
806 // Derivation path visibility can be toggled
807 it('Can toggle visibility of the derivation path column', function(done) {
808 driver.findElement(By.css('.phrase'))
809 .sendKeys('abandon abandon ability');
810 driver.sleep(generateDelay).then(function() {
811 driver.findElement(By.css('.index-toggle'))
812 .click();
813 testColumnValuesAreInvisible(done, "index");
814 });
815 });
816
817 // Address is shown
818 it('Shows the address in the table', function(done) {
819 driver.findElement(By.css('.phrase'))
820 .sendKeys('abandon abandon ability');
821 driver.sleep(generateDelay).then(function() {
822 getFirstAddress(function(address) {
823 expect(address).toBe("1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug");
824 done();
825 });
826 });
827 });
828
829 // Addresses are shown in order of derivation path
830 it('Shows the address in order of derivation path', function(done) {
831 driver.findElement(By.css('.phrase'))
832 .sendKeys('abandon abandon ability');
833 driver.sleep(generateDelay).then(function() {
834 testRowsAreInCorrectOrder(done);
835 });
836 });
837
838 // Address visibility can be toggled
839 it('Can toggle visibility of the address column', function(done) {
840 driver.findElement(By.css('.phrase'))
841 .sendKeys('abandon abandon ability');
842 driver.sleep(generateDelay).then(function() {
843 driver.findElement(By.css('.address-toggle'))
844 .click();
845 testColumnValuesAreInvisible(done, "address");
846 });
847 });
848
849 // Public key is shown in table
850 it('Shows the public key in the table', function(done) {
851 driver.findElement(By.css('.phrase'))
852 .sendKeys('abandon abandon ability');
853 driver.sleep(generateDelay).then(function() {
854 driver.findElements(By.css('.pubkey'))
855 .then(function(els) {
856 els[0].getText()
857 .then(function(pubkey) {
858 expect(pubkey).toBe("033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3");
859 done();
860 });
861 });
862 });
863 });
864
865 // Public key visibility can be toggled
866 it('Can toggle visibility of the public key column', function(done) {
867 driver.findElement(By.css('.phrase'))
868 .sendKeys('abandon abandon ability');
869 driver.sleep(generateDelay).then(function() {
870 driver.findElement(By.css('.public-key-toggle'))
871 .click();
872 testColumnValuesAreInvisible(done, "pubkey");
873 });
874 });
875
876 // Private key is shown in table
877 it('Shows the private key in the table', function(done) {
878 driver.findElement(By.css('.phrase'))
879 .sendKeys('abandon abandon ability');
880 driver.sleep(generateDelay).then(function() {
881 driver.findElements(By.css('.privkey'))
882 .then(function(els) {
883 els[0].getText()
884 .then(function(pubkey) {
885 expect(pubkey).toBe("L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE");
886 done();
887 });
888 });
889 });
890 });
891
892 // Private key visibility can be toggled
893 it('Can toggle visibility of the private key column', function(done) {
894 driver.findElement(By.css('.phrase'))
895 .sendKeys('abandon abandon ability');
896 driver.sleep(generateDelay).then(function() {
897 driver.findElement(By.css('.private-key-toggle'))
898 .click();
899 testColumnValuesAreInvisible(done, "privkey");
900 });
901 });
902
903 // More addresses can be generated
904 it('Can generate more rows in the table', function(done) {
905 driver.findElement(By.css('.phrase'))
906 .sendKeys('abandon abandon ability');
907 driver.sleep(generateDelay).then(function() {
908 driver.findElement(By.css('.more'))
909 .click();
910 driver.sleep(generateDelay).then(function() {
911 driver.findElements(By.css('.address'))
912 .then(function(els) {
913 expect(els.length).toBe(40);
914 done();
915 });
916 });
917 });
918 });
919
920 // A custom number of additional addresses can be generated
921 it('Can generate more rows in the table', function(done) {
922 driver.findElement(By.css('.rows-to-add'))
923 .clear();
924 driver.findElement(By.css('.rows-to-add'))
925 .sendKeys('1');
926 driver.findElement(By.css('.phrase'))
927 .sendKeys('abandon abandon ability');
928 driver.sleep(generateDelay).then(function() {
929 driver.findElement(By.css('.more'))
930 .click();
931 driver.sleep(generateDelay).then(function() {
932 driver.findElements(By.css('.address'))
933 .then(function(els) {
934 expect(els.length).toBe(21);
935 done();
936 });
937 });
938 });
939 });
940
941 // Additional addresses are shown in order of derivation path
942 it('Shows additional addresses in order of derivation path', function(done) {
943 driver.findElement(By.css('.phrase'))
944 .sendKeys('abandon abandon ability');
945 driver.sleep(generateDelay).then(function() {
946 driver.findElement(By.css('.more'))
947 .click();
948 driver.sleep(generateDelay).then(function() {
949 testRowsAreInCorrectOrder(done);
950 });
951 });
952 });
953
954 // BIP32 root key can be set by the user
955 it('Allows the user to set the BIP32 root key', function(done) {
956 driver.findElement(By.css('.root-key'))
957 .sendKeys('xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi');
958 driver.sleep(generateDelay).then(function() {
959 getFirstAddress(function(address) {
960 expect(address).toBe("1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug");
961 done();
962 });
963 });
964 });
965
966 // Setting BIP32 root key clears the existing phrase, passphrase and seed
967 // TODO this doesn't work in selenium with chrome
968 it('Confirms the existing phrase should be cleared', function(done) {
969 if (browser == "chrome") {
970 pending("Selenium + Chrome headless bug for alert, see https://stackoverflow.com/q/45242264");
971 }
972 driver.findElement(By.css('.phrase'))
973 .sendKeys('A non-blank but invalid value');
974 driver.findElement(By.css('.root-key'))
975 .sendKeys('xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi');
976 driver.switchTo().alert().accept();
977 driver.findElement(By.css('.phrase'))
978 .getAttribute("value").then(function(value) {
979 expect(value).toBe("");
980 done();
981 });
982 });
983
984 // Clearing of phrase, passphrase and seed can be cancelled by user
985 // TODO this doesn't work in selenium with chrome
986 it('Allows the clearing of the phrase to be cancelled', function(done) {
987 if (browser == "chrome") {
988 pending("Selenium + Chrome headless bug for alert, see https://stackoverflow.com/q/45242264");
989 }
990 driver.findElement(By.css('.phrase'))
991 .sendKeys('abandon abandon ability');
992 driver.sleep(generateDelay).then(function() {
993 driver.findElement(By.css('.root-key'))
994 .clear();
995 driver.findElement(By.css('.root-key'))
996 .sendKeys('x');
997 driver.switchTo().alert().dismiss();
998 driver.findElement(By.css('.phrase'))
999 .getAttribute("value").then(function(value) {
1000 expect(value).toBe("abandon abandon ability");
1001 done();
1002 });
1003 });
1004 });
1005
1006 // Custom BIP32 root key is used when changing the derivation path
1007 it('Can set derivation path for root key instead of phrase', function(done) {
1008 driver.findElement(By.css('#bip44 .account'))
1009 .sendKeys('1');
1010 driver.findElement(By.css('.root-key'))
1011 .sendKeys('xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi');
1012 driver.sleep(generateDelay).then(function() {
1013 getFirstAddress(function(address) {
1014 expect(address).toBe("1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H");
1015 done();
1016 });
1017 });
1018 });
1019
1020 // Incorrect mnemonic shows error
1021 it('Shows an error for incorrect mnemonic', function(done) {
1022 driver.findElement(By.css('.phrase'))
1023 .sendKeys('abandon abandon abandon');
1024 driver.sleep(feedbackDelay).then(function() {
1025 driver.findElement(By.css('.feedback'))
1026 .getText()
1027 .then(function(feedback) {
1028 expect(feedback).toBe("Invalid mnemonic");
1029 done();
1030 });
1031 });
1032 });
1033
1034 // Incorrect word shows suggested replacement
1035 it('Shows word suggestion for incorrect word', function(done) {
1036 driver.findElement(By.css('.phrase'))
1037 .sendKeys('abandon abandon abiliti');
1038 driver.sleep(feedbackDelay).then(function() {
1039 driver.findElement(By.css('.feedback'))
1040 .getText()
1041 .then(function(feedback) {
1042 var msg = "abiliti not in wordlist, did you mean ability?";
1043 expect(feedback).toBe(msg);
1044 done();
1045 });
1046 });
1047 });
1048
1049 // Github pull request 48
1050 // First four letters of word shows that word, not closest
1051 // since first four letters gives unique word in BIP39 wordlist
1052 // eg ille should show illegal, not idle
1053 it('Shows word suggestion based on first four chars', function(done) {
1054 driver.findElement(By.css('.phrase'))
1055 .sendKeys('ille');
1056 driver.sleep(feedbackDelay).then(function() {
1057 driver.findElement(By.css('.feedback'))
1058 .getText()
1059 .then(function(feedback) {
1060 var msg = "ille not in wordlist, did you mean illegal?";
1061 expect(feedback).toBe(msg);
1062 done();
1063 });
1064 });
1065 });
1066
1067 // Incorrect BIP32 root key shows error
1068 it('Shows error for incorrect root key', function(done) {
1069 driver.findElement(By.css('.root-key'))
1070 .sendKeys('xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj');
1071 driver.sleep(feedbackDelay).then(function() {
1072 driver.findElement(By.css('.feedback'))
1073 .getText()
1074 .then(function(feedback) {
1075 var msg = "Invalid root key";
1076 expect(feedback).toBe(msg);
1077 done();
1078 });
1079 });
1080 });
1081
1082 // Derivation path not starting with m shows error
1083 it('Shows error for derivation path not starting with m', function(done) {
1084 driver.findElement(By.css('#bip32-tab a'))
1085 .click();
1086 driver.findElement(By.css('#bip32 .path'))
1087 .clear();
1088 driver.findElement(By.css('#bip32 .path'))
1089 .sendKeys('n/0');
1090 driver.findElement(By.css('.phrase'))
1091 .sendKeys('abandon abandon ability');
1092 driver.sleep(feedbackDelay).then(function() {
1093 driver.findElement(By.css('.feedback'))
1094 .getText()
1095 .then(function(feedback) {
1096 var msg = "First character must be 'm'";
1097 expect(feedback).toBe(msg);
1098 done();
1099 });
1100 });
1101 });
1102
1103 // Derivation path containing invalid characters shows useful error
1104 it('Shows error for derivation path not starting with m', function(done) {
1105 driver.findElement(By.css('#bip32-tab a'))
1106 .click();
1107 driver.findElement(By.css('#bip32 .path'))
1108 .clear();
1109 driver.findElement(By.css('#bip32 .path'))
1110 .sendKeys('m/1/0wrong1/1');
1111 driver.findElement(By.css('.phrase'))
1112 .sendKeys('abandon abandon ability');
1113 driver.sleep(feedbackDelay).then(function() {
1114 driver.findElement(By.css('.feedback'))
1115 .getText()
1116 .then(function(feedback) {
1117 var msg = "Invalid characters 0wrong1 found at depth 2";
1118 expect(feedback).toBe(msg);
1119 done();
1120 });
1121 });
1122 });
1123
1124 // Github Issue 11: Default word length is 15
1125 // https://github.com/iancoleman/bip39/issues/11
1126 it('Sets the default word length to 15', function(done) {
1127 driver.findElement(By.css('.strength'))
1128 .getAttribute("value")
1129 .then(function(strength) {
1130 expect(strength).toBe("15");
1131 done();
1132 });
1133 });
1134
1135 // Github Issue 12: Generate more rows with private keys hidden
1136 // https://github.com/iancoleman/bip39/issues/12
1137 it('Sets the correct hidden column state on new rows', function(done) {
1138 driver.findElement(By.css('.phrase'))
1139 .sendKeys("abandon abandon ability");
1140 driver.sleep(generateDelay).then(function() {
1141 driver.findElement(By.css('.private-key-toggle'))
1142 .click();
1143 driver.findElement(By.css('.more'))
1144 .click();
1145 driver.sleep(generateDelay).then(function() {
1146 driver.findElements(By.css('.privkey'))
1147 .then(function(els) {
1148 expect(els.length).toBe(40);
1149 });
1150 testColumnValuesAreInvisible(done, "privkey");
1151 });
1152 });
1153 });
1154
1155 // Github Issue 19: Mnemonic is not sensitive to whitespace
1156 // https://github.com/iancoleman/bip39/issues/19
1157 it('Ignores excess whitespace in the mnemonic', function(done) {
1158 var doublespace = " ";
1159 var mnemonic = "urge cat" + doublespace + "bid";
1160 driver.findElement(By.css('.phrase'))
1161 .sendKeys(mnemonic);
1162 driver.sleep(generateDelay).then(function() {
1163 driver.findElement(By.css('.root-key'))
1164 .getAttribute("value")
1165 .then(function(seed) {
1166 expect(seed).toBe("xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC");
1167 done();
1168 });
1169 });
1170 });
1171
1172 // Github Issue 23: Part 1: Use correct derivation path when changing tabs
1173 // https://github.com/iancoleman/bip39/issues/23
1174 it('Uses the correct derivation path when changing tabs', function(done) {
1175 // 1) and 2) set the phrase
1176 driver.findElement(By.css('.phrase'))
1177 .sendKeys("abandon abandon ability");
1178 driver.sleep(generateDelay).then(function() {
1179 // 3) select bip32 tab
1180 driver.findElement(By.css('#bip32-tab a'))
1181 .click();
1182 driver.sleep(generateDelay).then(function() {
1183 // 4) switch from bitcoin to litecoin
1184 selectNetwork("LTC - Litecoin");
1185 driver.sleep(generateDelay).then(function() {
1186 // 5) Check address is displayed correctly
1187 getFirstAddress(function(address) {
1188 expect(address).toBe("LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5");
1189 // 5) Check derivation path is displayed correctly
1190 getFirstPath(function(path) {
1191 expect(path).toBe("m/0/0");
1192 done();
1193 });
1194 });
1195 });
1196 });
1197 });
1198 });
1199
1200 // Github Issue 23 Part 2: Coin selection in derivation path
1201 // https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
1202 it('Uses the correct derivation path when changing coins', function(done) {
1203 // set the phrase
1204 driver.findElement(By.css('.phrase'))
1205 .sendKeys("abandon abandon ability");
1206 driver.sleep(generateDelay).then(function() {
1207 // switch from bitcoin to clam
1208 selectNetwork("CLAM - Clams");
1209 driver.sleep(generateDelay).then(function() {
1210 // check derivation path is displayed correctly
1211 getFirstPath(function(path) {
1212 expect(path).toBe("m/44'/23'/0'/0/0");
1213 done();
1214 });
1215 });
1216 });
1217 });
1218
1219 // Github Issue 26: When using a Root key derrived altcoins are incorrect
1220 // https://github.com/iancoleman/bip39/issues/26
1221 it('Uses the correct derivation for altcoins with root keys', function(done) {
1222 // 1) 2) and 3) set the root key
1223 driver.findElement(By.css('.root-key'))
1224 .sendKeys("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi");
1225 driver.sleep(generateDelay).then(function() {
1226 // 4) switch from bitcoin to viacoin
1227 selectNetwork("VIA - Viacoin");
1228 driver.sleep(generateDelay).then(function() {
1229 // 5) ensure the derived address is correct
1230 getFirstAddress(function(address) {
1231 expect(address).toBe("Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT");
1232 done();
1233 });
1234 });
1235 });
1236 });
1237
1238 // Selecting a language with no existing phrase should generate a phrase in
1239 // that language.
1240 it('Generate a random phrase when language is selected and no current phrase', function(done) {
1241 driver.findElement(By.css("a[href='#japanese']"))
1242 .click();
1243 driver.sleep(generateDelay).then(function() {
1244 driver.findElement(By.css(".phrase"))
1245 .getAttribute("value").then(function(phrase) {
1246 expect(phrase.search(/[a-z]/)).toBe(-1);
1247 expect(phrase.length).toBeGreaterThan(0);
1248 done();
1249 });
1250 });
1251 });
1252
1253 // Selecting a language with existing phrase should update the phrase to use
1254 // that language.
1255 it('Updates existing phrases when the language is changed', function(done) {
1256 driver.findElement(By.css(".phrase"))
1257 .sendKeys("abandon abandon ability");
1258 driver.sleep(generateDelay).then(function() {
1259 driver.findElement(By.css("a[href='#italian']"))
1260 .click();
1261 driver.sleep(generateDelay).then(function() {
1262 driver.findElement(By.css(".phrase"))
1263 .getAttribute("value").then(function(phrase) {
1264 // Check only the language changes, not the phrase
1265 expect(phrase).toBe("abaco abaco abbaglio");
1266 getFirstAddress(function(address) {
1267 // Check the address is correct
1268 expect(address).toBe("1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV");
1269 done();
1270 });
1271 });
1272 });
1273 });
1274 });
1275
1276 // Suggested replacement for erroneous word in non-English language
1277 it('Shows word suggestion for incorrect word in non-English language', function(done) {
1278 driver.findElement(By.css('.phrase'))
1279 .sendKeys('abaco abaco zbbaglio');
1280 driver.sleep(feedbackDelay).then(function() {
1281 driver.findElement(By.css('.feedback'))
1282 .getText()
1283 .then(function(feedback) {
1284 var msg = "zbbaglio not in wordlist, did you mean abbaglio?";
1285 expect(feedback).toBe(msg);
1286 done();
1287 });
1288 });
1289 });
1290
1291 // Japanese word does not break across lines.
1292 // Point 2 from
1293 // https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
1294 it('Does not break Japanese words across lines', function(done) {
1295 driver.findElement(By.css('.phrase'))
1296 .getCssValue("word-break")
1297 .then(function(value) {
1298 expect(value).toBe("keep-all");
1299 done();
1300 });
1301 });
1302
1303 // Language can be specified at page load using hash value in url
1304 it('Can set the language from the url hash', function(done) {
1305 driver.get(url + "#japanese").then(function() {
1306 driver.findElement(By.css('.generate')).click();
1307 driver.sleep(generateDelay).then(function() {
1308 driver.findElement(By.css(".phrase"))
1309 .getAttribute("value").then(function(phrase) {
1310 expect(phrase.search(/[a-z]/)).toBe(-1);
1311 expect(phrase.length).toBeGreaterThan(0);
1312 done();
1313 });
1314 });
1315 });
1316 });
1317
1318 // Entropy can be entered by the user
1319 it('Allows entropy to be entered', function(done) {
1320 driver.findElement(By.css('.use-entropy'))
1321 .click();
1322 driver.findElement(By.css('.entropy'))
1323 .sendKeys('00000000 00000000 00000000 00000000');
1324 driver.sleep(generateDelay).then(function() {
1325 driver.findElement(By.css(".phrase"))
1326 .getAttribute("value").then(function(phrase) {
1327 expect(phrase).toBe("abandon abandon ability");
1328 getFirstAddress(function(address) {
1329 expect(address).toBe("1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug");
1330 done();
1331 })
1332 });
1333 });
1334 });
1335
1336 // A warning about entropy is shown to the user, with additional information
1337 it('Shows a warning about using entropy', function(done) {
1338 driver.findElement(By.css('.use-entropy'))
1339 .click();
1340 driver.findElement(By.css('.entropy-container'))
1341 .getText()
1342 .then(function(containerText) {
1343 var warning = "mnemonic may be insecure";
1344 expect(containerText).toContain(warning);
1345 driver.findElement(By.css('#entropy-notes'))
1346 .findElement(By.xpath("parent::*"))
1347 .getText()
1348 .then(function(notesText) {
1349 var detail = "flipping a fair coin, rolling a fair dice, noise measurements etc";
1350 expect(notesText).toContain(detail);
1351 done();
1352 });
1353 });
1354 });
1355
1356 // The types of entropy available are described to the user
1357 it('Shows the types of entropy available', function(done) {
1358 driver.findElement(By.css('.entropy'))
1359 .getAttribute("placeholder")
1360 .then(function(placeholderText) {
1361 var options = [
1362 "binary",
1363 "base 6",
1364 "dice",
1365 "base 10",
1366 "hexadecimal",
1367 "cards",
1368 ];
1369 for (var i=0; i<options.length; i++) {
1370 var option = options[i];
1371 expect(placeholderText).toContain(option);
1372 }
1373 done();
1374 });
1375 });
1376
1377 // The actual entropy used is shown to the user
1378 it('Shows the actual entropy used', function(done) {
1379 driver.findElement(By.css('.use-entropy'))
1380 .click();
1381 driver.findElement(By.css('.entropy'))
1382 .sendKeys('Not A Very Good Entropy Source At All');
1383 driver.sleep(generateDelay).then(function() {
1384 driver.findElement(By.css('.entropy-container'))
1385 .getText()
1386 .then(function(text) {
1387 expect(text).toMatch(/Filtered Entropy\s+AedEceAA/);
1388 done();
1389 });
1390 });
1391 });
1392
1393 // Binary entropy can be entered
1394 it('Allows binary entropy to be entered', function(done) {
1395 testEntropyType(done, "01", "binary");
1396 });
1397
1398 // Base 6 entropy can be entered
1399 it('Allows base 6 entropy to be entered', function(done) {
1400 testEntropyType(done, "012345", "base 6");
1401 });
1402
1403 // Base 6 dice entropy can be entered
1404 it('Allows base 6 dice entropy to be entered', function(done) {
1405 testEntropyType(done, "123456", "base 6 (dice)");
1406 });
1407
1408 // Base 10 entropy can be entered
1409 it('Allows base 10 entropy to be entered', function(done) {
1410 testEntropyType(done, "789", "base 10");
1411 });
1412
1413 // Hexadecimal entropy can be entered
1414 it('Allows hexadecimal entropy to be entered', function(done) {
1415 testEntropyType(done, "abcdef", "hexadecimal");
1416 });
1417
1418 // Dice entropy value is shown as the converted base 6 value
1419 // ie 123456 is converted to 123450
1420 it('Shows dice entropy as base 6', function(done) {
1421 driver.findElement(By.css('.use-entropy'))
1422 .click();
1423 driver.findElement(By.css('.entropy'))
1424 .sendKeys("123456");
1425 driver.sleep(generateDelay).then(function() {
1426 driver.findElement(By.css('.entropy-container'))
1427 .getText()
1428 .then(function(text) {
1429 expect(text).toMatch(/Filtered Entropy\s+123450/);
1430 done();
1431 });
1432 });
1433 });
1434
1435 // The number of bits of entropy accumulated is shown
1436 it("Shows the number of bits of entropy for 20 bits of binary", function(done) {
1437 testEntropyBits(done, "0000 0000 0000 0000 0000", "20");
1438 });
1439 it("Shows the number of bits of entropy for 1 bit of binary", function(done) {
1440 testEntropyBits(done, "0", "1");
1441 });
1442 it("Shows the number of bits of entropy for 4 bits of binary", function(done) {
1443 testEntropyBits(done, "0000", "4");
1444 });
1445 it("Shows the number of bits of entropy for 1 character of base 6 (dice)", function(done) {
1446 // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
1447 testEntropyBits(done, "6", "2");
1448 });
1449 it("Shows the number of bits of entropy for 1 character of base 10 with 3 bits", function(done) {
1450 // 7 in base 10 is 111 in base 2, no leading zeros
1451 testEntropyBits(done, "7", "3");
1452 });
1453 it("Shows the number of bits of entropy for 1 character of base 10 with 4 bis", function(done) {
1454 testEntropyBits(done, "8", "4");
1455 });
1456 it("Shows the number of bits of entropy for 1 character of hex", function(done) {
1457 testEntropyBits(done, "F", "4");
1458 });
1459 it("Shows the number of bits of entropy for 2 characters of base 10", function(done) {
1460 testEntropyBits(done, "29", "6");
1461 });
1462 it("Shows the number of bits of entropy for 2 characters of hex", function(done) {
1463 testEntropyBits(done, "0A", "8");
1464 });
1465 it("Shows the number of bits of entropy for 2 characters of hex with 3 leading zeros", function(done) {
1466 // hex is always multiple of 4 bits of entropy
1467 testEntropyBits(done, "1A", "8");
1468 });
1469 it("Shows the number of bits of entropy for 2 characters of hex with 2 leading zeros", function(done) {
1470 testEntropyBits(done, "2A", "8");
1471 });
1472 it("Shows the number of bits of entropy for 2 characters of hex with 1 leading zero", function(done) {
1473 testEntropyBits(done, "4A", "8");
1474 });
1475 it("Shows the number of bits of entropy for 2 characters of hex with no leading zeros", function(done) {
1476 testEntropyBits(done, "8A", "8");
1477 });
1478 it("Shows the number of bits of entropy for 2 characters of hex starting with F", function(done) {
1479 testEntropyBits(done, "FA", "8");
1480 });
1481 it("Shows the number of bits of entropy for 4 characters of hex with leading zeros", function(done) {
1482 testEntropyBits(done, "000A", "16");
1483 });
1484 it("Shows the number of bits of entropy for 4 characters of base 6", function(done) {
1485 testEntropyBits(done, "5555", "11");
1486 });
1487 it("Shows the number of bits of entropy for 4 characters of base 6 dice", function(done) {
1488 // uses dice, so entropy is actually 0000 in base 6, which is 4 lots of
1489 // 2.58 bits, which is 10.32 bits (rounded down to 10 bits)
1490 testEntropyBits(done, "6666", "10");
1491 });
1492 it("Shows the number of bits of entropy for 4 charactes of base 10", function(done) {
1493 // Uses base 10, which is 4 lots of 3.32 bits, which is 13.3 bits (rounded
1494 // down to 13)
1495 testEntropyBits(done, "2227", "13");
1496 });
1497 it("Shows the number of bits of entropy for 4 characters of hex with 2 leading zeros", function(done) {
1498 testEntropyBits(done, "222F", "16");
1499 });
1500 it("Shows the number of bits of entropy for 4 characters of hex starting with F", function(done) {
1501 testEntropyBits(done, "FFFF", "16");
1502 });
1503 it("Shows the number of bits of entropy for 10 characters of base 10", function(done) {
1504 // 10 events at 3.32 bits per event
1505 testEntropyBits(done, "0000101017", "33");
1506 });
1507 it("Shows the number of bits of entropy for a full deck of cards", function(done) {
1508 // cards are not replaced, so a full deck is not 52^52 entropy which is 296
1509 // bits, it's 52!, which is 225 bits
1510 testEntropyBits(done, "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", "225");
1511 });
1512
1513 it("Shows details about the entered entropy", function(done) {
1514 testEntropyFeedback(done,
1515 {
1516 entropy: "A",
1517 filtered: "A",
1518 type: "hexadecimal",
1519 events: "1",
1520 bits: "4",
1521 words: 0,
1522 strength: "less than a second",
1523 }
1524 );
1525 });
1526 it("Shows details about the entered entropy", function(done) {
1527 testEntropyFeedback(done,
1528 {
1529 entropy: "AAAAAAAA",
1530 filtered: "AAAAAAAA",
1531 type: "hexadecimal",
1532 events: "8",
1533 bits: "32",
1534 words: 3,
1535 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
1536 }
1537 );
1538 });
1539 it("Shows details about the entered entropy", function(done) {
1540 testEntropyFeedback(done,
1541 {
1542 entropy: "AAAAAAAA B",
1543 filtered: "AAAAAAAAB",
1544 type: "hexadecimal",
1545 events: "9",
1546 bits: "36",
1547 words: 3,
1548 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
1549 }
1550 );
1551 });
1552 it("Shows details about the entered entropy", function(done) {
1553 testEntropyFeedback(done,
1554 {
1555 entropy: "AAAAAAAA BBBBBBBB",
1556 filtered: "AAAAAAAABBBBBBBB",
1557 type: "hexadecimal",
1558 events: "16",
1559 bits: "64",
1560 words: 6,
1561 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
1562 }
1563 );
1564 });
1565 it("Shows details about the entered entropy", function(done) {
1566 testEntropyFeedback(done,
1567 {
1568 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
1569 filtered: "AAAAAAAABBBBBBBBCCCCCCCC",
1570 type: "hexadecimal",
1571 events: "24",
1572 bits: "96",
1573 words: 9,
1574 strength: "less than a second",
1575 }
1576 );
1577 });
1578 it("Shows details about the entered entropy", function(done) {
1579 testEntropyFeedback(done,
1580 {
1581 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
1582 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD",
1583 type: "hexadecimal",
1584 events: "32",
1585 bits: "128",
1586 words: 12,
1587 strength: "2 minutes",
1588 }
1589 );
1590 });
1591 it("Shows details about the entered entropy", function(done) {
1592 testEntropyFeedback(done,
1593 {
1594 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA",
1595 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA",
1596 type: "hexadecimal",
1597 events: "32",
1598 bits: "128",
1599 words: 12,
1600 strength: "2 days",
1601 }
1602 );
1603 });
1604 it("Shows details about the entered entropy", function(done) {
1605 testEntropyFeedback(done,
1606 {
1607 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE",
1608 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE",
1609 type: "hexadecimal",
1610 events: "40",
1611 bits: "160",
1612 words: 15,
1613 strength: "3 years",
1614 }
1615 );
1616 });
1617 it("Shows details about the entered entropy", function(done) {
1618 testEntropyFeedback(done,
1619 {
1620 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF",
1621 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF",
1622 type: "hexadecimal",
1623 events: "48",
1624 bits: "192",
1625 words: 18,
1626 strength: "centuries",
1627 }
1628 );
1629 });
1630 it("Shows details about the entered entropy", function(done) {
1631 testEntropyFeedback(done,
1632 {
1633 entropy: "7d",
1634 type: "card",
1635 events: "1",
1636 bits: "4",
1637 words: 0,
1638 strength: "less than a second",
1639 }
1640 );
1641 });
1642 it("Shows details about the entered entropy", function(done) {
1643 testEntropyFeedback(done,
1644 {
1645 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1646 type: "card (full deck)",
1647 events: "52",
1648 bits: "225",
1649 words: 21,
1650 strength: "centuries",
1651 }
1652 );
1653 });
1654 it("Shows details about the entered entropy", function(done) {
1655 testEntropyFeedback(done,
1656 {
1657 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d",
1658 type: "card (full deck, 1 duplicate: 3d)",
1659 events: "53",
1660 bits: "254",
1661 words: 21,
1662 strength: "centuries",
1663 }
1664 );
1665 });
1666 it("Shows details about the entered entropy", function(done) {
1667 testEntropyFeedback(done,
1668 {
1669 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d",
1670 type: "card (2 duplicates: 3d 4d, 1 missing: KS)",
1671 events: "53",
1672 bits: "254",
1673 words: 21,
1674 strength: "centuries",
1675 }
1676 );
1677 });
1678 it("Shows details about the entered entropy", function(done) {
1679 testEntropyFeedback(done,
1680 {
1681 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d",
1682 type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)",
1683 events: "55",
1684 bits: "264",
1685 words: 24,
1686 strength: "centuries",
1687 }
1688 );
1689 });
1690 it("Shows details about the entered entropy", function(done) {
1691 testEntropyFeedback(done,
1692 // Next test was throwing uncaught error in zxcvbn
1693 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
1694 {
1695 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1696 type: "card (full deck, 52 duplicates: ac 2c 3c...)",
1697 events: "104",
1698 bits: "499",
1699 words: 45,
1700 strength: "centuries",
1701 }
1702 );
1703 });
1704 it("Shows details about the entered entropy", function(done) {
1705 testEntropyFeedback(done,
1706 // Case insensitivity to duplicate cards
1707 {
1708 entropy: "asAS",
1709 type: "card (1 duplicate: AS)",
1710 events: "2",
1711 bits: "9",
1712 words: 0,
1713 strength: "less than a second",
1714 }
1715 );
1716 });
1717 it("Shows details about the entered entropy", function(done) {
1718 testEntropyFeedback(done,
1719 {
1720 entropy: "ASas",
1721 type: "card (1 duplicate: as)",
1722 events: "2",
1723 bits: "9",
1724 words: 0,
1725 strength: "less than a second",
1726 }
1727 );
1728 });
1729 it("Shows details about the entered entropy", function(done) {
1730 testEntropyFeedback(done,
1731 // Missing cards are detected
1732 {
1733 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1734 type: "card (1 missing: 9C)",
1735 events: "51",
1736 bits: "221",
1737 words: 18,
1738 strength: "centuries",
1739 }
1740 );
1741 });
1742 it("Shows details about the entered entropy", function(done) {
1743 testEntropyFeedback(done,
1744 {
1745 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1746 type: "card (2 missing: 9C 5D)",
1747 events: "50",
1748 bits: "216",
1749 words: 18,
1750 strength: "centuries",
1751 }
1752 );
1753 });
1754 it("Shows details about the entered entropy", function(done) {
1755 testEntropyFeedback(done,
1756 {
1757 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1758 type: "card (4 missing: 9C 5D QD...)",
1759 events: "48",
1760 bits: "208",
1761 words: 18,
1762 strength: "centuries",
1763 }
1764 );
1765 });
1766 it("Shows details about the entered entropy", function(done) {
1767 testEntropyFeedback(done,
1768 // More than six missing cards does not show message
1769 {
1770 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks",
1771 type: "card",
1772 events: "45",
1773 bits: "195",
1774 words: 18,
1775 strength: "centuries",
1776 }
1777 );
1778 });
1779 it("Shows details about the entered entropy", function(done) {
1780 testEntropyFeedback(done,
1781 // Multiple decks of cards increases bits per event
1782 {
1783 entropy: "3d",
1784 events: "1",
1785 bits: "4",
1786 bitsPerEvent: "4.34",
1787 }
1788 );
1789 });
1790 it("Shows details about the entered entropy", function(done) {
1791 testEntropyFeedback(done,
1792 {
1793 entropy: "3d3d",
1794 events: "2",
1795 bits: "9",
1796 bitsPerEvent: "4.80",
1797 }
1798 );
1799 });
1800 it("Shows details about the entered entropy", function(done) {
1801 testEntropyFeedback(done,
1802 {
1803 entropy: "3d3d3d",
1804 events: "3",
1805 bits: "15",
1806 bitsPerEvent: "5.01",
1807 }
1808 );
1809 });
1810 it("Shows details about the entered entropy", function(done) {
1811 testEntropyFeedback(done,
1812 {
1813 entropy: "3d3d3d3d",
1814 events: "4",
1815 bits: "20",
1816 bitsPerEvent: "5.14",
1817 }
1818 );
1819 });
1820 it("Shows details about the entered entropy", function(done) {
1821 testEntropyFeedback(done,
1822 {
1823 entropy: "3d3d3d3d3d",
1824 events: "5",
1825 bits: "26",
1826 bitsPerEvent: "5.22",
1827 }
1828 );
1829 });
1830 it("Shows details about the entered entropy", function(done) {
1831 testEntropyFeedback(done,
1832 {
1833 entropy: "3d3d3d3d3d3d",
1834 events: "6",
1835 bits: "31",
1836 bitsPerEvent: "5.28",
1837 }
1838 );
1839 });
1840 it("Shows details about the entered entropy", function(done) {
1841 testEntropyFeedback(done,
1842 {
1843 entropy: "3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d",
1844 events: "33",
1845 bits: "184",
1846 bitsPerEvent: "5.59",
1847 strength: 'less than a second - Repeats like "abcabcabc" are only slightly harder to guess than "abc"',
1848 }
1849 );
1850 });
1851
1852 // Entropy is truncated from the left
1853 it('Truncates entropy from the left', function(done) {
1854 // Truncate from left means 0000 is removed from the start
1855 // which gives mnemonic 'avocado zoo zone'
1856 // not 1111 removed from the end
1857 // which gives the mnemonic 'abstract zoo zoo'
1858 var entropy = "00000000 00000000 00000000 00000000";
1859 entropy += "11111111 11111111 11111111 1111"; // Missing last byte
1860 driver.findElement(By.css('.use-entropy'))
1861 .click();
1862 driver.findElement(By.css('.entropy'))
1863 .sendKeys(entropy);
1864 driver.sleep(generateDelay).then(function() {
1865 driver.findElement(By.css(".phrase"))
1866 .getAttribute("value").then(function(phrase) {
1867 expect(phrase).toBe("avocado zoo zone");
1868 done();
1869 });
1870 });
1871 });
1872
1873 // Very large entropy results in very long mnemonics
1874 it('Converts very long entropy to very long mnemonics', function(done) {
1875 var entropy = "";
1876 for (var i=0; i<33; i++) {
1877 entropy += "AAAAAAAA"; // 3 words * 33 iterations = 99 words
1878 }
1879 driver.findElement(By.css('.use-entropy'))
1880 .click();
1881 driver.findElement(By.css('.entropy'))
1882 .sendKeys(entropy);
1883 driver.sleep(generateDelay).then(function() {
1884 driver.findElement(By.css(".phrase"))
1885 .getAttribute("value").then(function(phrase) {
1886 var wordCount = phrase.split(/\s+/g).length;
1887 expect(wordCount).toBe(99);
1888 done();
1889 });
1890 });
1891 });
1892
1893 // Is compatible with bip32jp entropy
1894 // https://bip32jp.github.io/english/index.html
1895 // NOTES:
1896 // Is incompatible with:
1897 // base 20
1898 it('Is compatible with bip32jp.github.io', function(done) {
1899 var entropy = "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543";
1900 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";
1901 driver.findElement(By.css('.use-entropy'))
1902 .click();
1903 driver.findElement(By.css('.entropy'))
1904 .sendKeys(entropy);
1905 driver.sleep(generateDelay).then(function() {
1906 driver.findElement(By.css(".phrase"))
1907 .getAttribute("value").then(function(phrase) {
1908 expect(phrase).toBe(expectedPhrase);
1909 done();
1910 });
1911 });
1912 });
1913
1914 // Blank entropy does not generate mnemonic or addresses
1915 it('Does not generate mnemonic for blank entropy', function(done) {
1916 driver.findElement(By.css('.use-entropy'))
1917 .click();
1918 driver.findElement(By.css('.entropy'))
1919 .clear();
1920 // check there is no mnemonic
1921 driver.sleep(generateDelay).then(function() {
1922 driver.findElement(By.css(".phrase"))
1923 .getAttribute("value").then(function(phrase) {
1924 expect(phrase).toBe("");
1925 // check there is no mnemonic
1926 driver.findElements(By.css(".address"))
1927 .then(function(addresses) {
1928 expect(addresses.length).toBe(0);
1929 // Check the feedback says 'blank entropy'
1930 driver.findElement(By.css(".feedback"))
1931 .getText()
1932 .then(function(feedbackText) {
1933 expect(feedbackText).toBe("Blank entropy");
1934 done();
1935 });
1936 })
1937 });
1938 });
1939 });
1940
1941 // Mnemonic length can be selected even for weak entropy
1942 it('Allows selection of mnemonic length even for weak entropy', function(done) {
1943 driver.findElement(By.css('.use-entropy'))
1944 .click();
1945 driver.executeScript(function() {
1946 $(".mnemonic-length").val("18").trigger("change");
1947 });
1948 driver.findElement(By.css('.entropy'))
1949 .sendKeys("012345");
1950 driver.sleep(generateDelay).then(function() {
1951 driver.findElement(By.css(".phrase"))
1952 .getAttribute("value").then(function(phrase) {
1953 var wordCount = phrase.split(/\s+/g).length;
1954 expect(wordCount).toBe(18);
1955 done();
1956 });
1957 });
1958 });
1959
1960 // Github issue 33
1961 // https://github.com/iancoleman/bip39/issues/33
1962 // Final cards should contribute entropy
1963 it('Uses as much entropy as possible for the mnemonic', function(done) {
1964 driver.findElement(By.css('.use-entropy'))
1965 .click();
1966 driver.findElement(By.css('.entropy'))
1967 .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");
1968 driver.sleep(generateDelay).then(function() {
1969 // Get mnemonic
1970 driver.findElement(By.css(".phrase"))
1971 .getAttribute("value").then(function(originalPhrase) {
1972 // Set the last 12 cards to be AS
1973 driver.findElement(By.css('.entropy'))
1974 .clear();
1975 driver.findElement(By.css('.entropy'))
1976 .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");
1977 driver.sleep(generateDelay).then(function() {
1978 // Get new mnemonic
1979 driver.findElement(By.css(".phrase"))
1980 .getAttribute("value").then(function(newPhrase) {
1981 expect(originalPhrase).not.toEqual(newPhrase);
1982 done();
1983 });
1984 });
1985 });
1986 });
1987 });
1988
1989 // Github issue 35
1990 // https://github.com/iancoleman/bip39/issues/35
1991 // QR Code support
1992 // TODO this doesn't work in selenium with firefox
1993 // see https://stackoverflow.com/q/40360223
1994 it('Shows a qr code on hover for the phrase', function(done) {
1995 if (browser == "firefox") {
1996 pending("Selenium + Firefox bug for mouseMove, see https://stackoverflow.com/q/40360223");
1997 }
1998 // generate a random mnemonic
1999 var generateEl = driver.findElement(By.css('.generate'));
2000 generateEl.click();
2001 // toggle qr to show (hidden by default)
2002 var phraseEl = driver.findElement(By.css(".phrase"));
2003 phraseEl.click();
2004 var rootKeyEl = driver.findElement(By.css(".root-key"));
2005 driver.sleep(generateDelay).then(function() {
2006 // hover over the root key
2007 driver.actions().mouseMove(rootKeyEl).perform().then(function() {
2008 // check the qr code shows
2009 driver.executeScript(function() {
2010 return $(".qr-container").find("canvas").length > 0;
2011 })
2012 .then(function(qrShowing) {
2013 expect(qrShowing).toBe(true);
2014 // hover away from the phrase
2015 driver.actions().mouseMove(generateEl).perform().then(function() {;
2016 // check the qr code hides
2017 driver.executeScript(function() {
2018 return $(".qr-container").find("canvas").length == 0;
2019 })
2020 .then(function(qrHidden) {
2021 expect(qrHidden).toBe(true);
2022 done();
2023 });
2024 });
2025 });
2026 });
2027 });
2028 });
2029
2030 // BIP44 account extendend private key is shown
2031 // github issue 37 - compatibility with electrum
2032 it('Shows the bip44 account extended private key', function(done) {
2033 driver.findElement(By.css(".phrase"))
2034 .sendKeys("abandon abandon ability");
2035 driver.sleep(generateDelay).then(function() {
2036 driver.findElement(By.css("#bip44 .account-xprv"))
2037 .getAttribute("value")
2038 .then(function(xprv) {
2039 expect(xprv).toBe("xprv9yzrnt4zWVJUr1k2VxSPy9ettKz5PpeDMgaVG7UKedhqnw1tDkxP2UyYNhuNSumk2sLE5ctwKZs9vwjsq3e1vo9egCK6CzP87H2cVYXpfwQ");
2040 done();
2041 });
2042 });
2043 });
2044
2045 // BIP44 account extendend public key is shown
2046 // github issue 37 - compatibility with electrum
2047 it('Shows the bip44 account extended public key', function(done) {
2048 driver.findElement(By.css(".phrase"))
2049 .sendKeys("abandon abandon ability");
2050 driver.sleep(generateDelay).then(function() {
2051 driver.findElement(By.css("#bip44 .account-xpub"))
2052 .getAttribute("value")
2053 .then(function(xprv) {
2054 expect(xprv).toBe("xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf");
2055 done();
2056 });
2057 });
2058 });
2059
2060 // github issue 40
2061 // BIP32 root key can be set as an xpub
2062 it('Generates addresses from xpub as bip32 root key', function(done) {
2063 driver.findElement(By.css('#bip32-tab a'))
2064 .click();
2065 // set xpub for account 0 of bip44 for 'abandon abandon ability'
2066 driver.findElement(By.css("#root-key"))
2067 .sendKeys("xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf");
2068 driver.sleep(generateDelay).then(function() {
2069 // check the addresses are generated
2070 getFirstAddress(function(address) {
2071 expect(address).toBe("1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug");
2072 // check the xprv key is not set
2073 driver.findElement(By.css(".extended-priv-key"))
2074 .getAttribute("value")
2075 .then(function(xprv) {
2076 expect(xprv).toBe("NA");
2077 // check the private key is not set
2078 driver.findElements(By.css(".privkey"))
2079 .then(function(els) {
2080 els[0]
2081 .getText()
2082 .then(function(privkey) {
2083 expect(xprv).toBe("NA");
2084 done();
2085 });
2086 });
2087 });
2088 });
2089 });
2090 });
2091
2092 // github issue 40
2093 // xpub for bip32 root key will not work with hardened derivation paths
2094 it('Shows error for hardened derivation paths with xpub root key', function(done) {
2095 // set xpub for account 0 of bip44 for 'abandon abandon ability'
2096 driver.findElement(By.css("#root-key"))
2097 .sendKeys("xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf");
2098 driver.sleep(feedbackDelay).then(function() {
2099 // Check feedback is correct
2100 driver.findElement(By.css('.feedback'))
2101 .getText()
2102 .then(function(feedback) {
2103 var msg = "Hardened derivation path is invalid with xpub key";
2104 expect(feedback).toBe(msg);
2105 // Check no addresses are shown
2106 driver.findElements(By.css('.addresses tr'))
2107 .then(function(rows) {
2108 expect(rows.length).toBe(0);
2109 done();
2110 });
2111 });
2112 });
2113 });
2114
2115 // github issue 39
2116 // no root key shows feedback
2117 it('Shows feedback for no root key', function(done) {
2118 // set xpub for account 0 of bip44 for 'abandon abandon ability'
2119 driver.findElement(By.css('#bip32-tab a'))
2120 .click();
2121 driver.sleep(feedbackDelay).then(function() {
2122 // Check feedback is correct
2123 driver.findElement(By.css('.feedback'))
2124 .getText()
2125 .then(function(feedback) {
2126 expect(feedback).toBe("Invalid root key");
2127 done();
2128 });
2129 });
2130 });
2131
2132 // Github issue 44
2133 // display error switching tabs while addresses are generating
2134 it('Can change details while old addresses are still being generated', function(done) {
2135 // Set to generate 199 more addresses.
2136 // This will take a long time allowing a new set of addresses to be
2137 // generated midway through this lot.
2138 // The newly generated addresses should not include any from the old set.
2139 // Any more than 199 will show an alert which needs to be accepted.
2140 driver.findElement(By.css('.rows-to-add'))
2141 .clear();
2142 driver.findElement(By.css('.rows-to-add'))
2143 .sendKeys('199');
2144 // set the prhase
2145 driver.findElement(By.css('.phrase'))
2146 .sendKeys("abandon abandon ability");
2147 driver.sleep(generateDelay).then(function() {
2148 // generate more addresses
2149 driver.findElement(By.css('.more'))
2150 .click();
2151 // change tabs which should cancel the previous generating
2152 driver.findElement(By.css('#bip32-tab a'))
2153 .click()
2154 driver.sleep(generateDelay).then(function() {
2155 driver.findElements(By.css('.index'))
2156 .then(function(els) {
2157 // check the derivation paths have the right quantity
2158 expect(els.length).toBe(20);
2159 // check the derivation paths are in order
2160 testRowsAreInCorrectOrder(done);
2161 });
2162 });
2163 });
2164 });
2165
2166 // Github issue 49
2167 // padding for binary should give length with multiple of 256
2168 // hashed entropy 1111 is length 252, so requires 4 leading zeros
2169 // prior to issue 49 it would only generate 2 leading zeros, ie missing 2
2170 it('Pads hashed entropy with leading zeros', function(done) {
2171 driver.findElement(By.css('.use-entropy'))
2172 .click();
2173 driver.executeScript(function() {
2174 $(".mnemonic-length").val("15").trigger("change");
2175 });
2176 driver.findElement(By.css('.entropy'))
2177 .sendKeys("1111");
2178 driver.sleep(generateDelay).then(function() {
2179 driver.findElement(By.css('.phrase'))
2180 .getAttribute("value")
2181 .then(function(phrase) {
2182 expect(phrase).toBe("avocado valid quantum cross link predict excuse edit street able flame large galaxy ginger nuclear");
2183 done();
2184 });
2185 });
2186 });
2187
2188 // Github pull request 55
2189 // https://github.com/iancoleman/bip39/pull/55
2190 // Client select
2191 it('Can set the derivation path on bip32 tab for bitcoincore', function(done) {
2192 testClientSelect(done, {
2193 selectValue: "0",
2194 bip32path: "m/0'/0'",
2195 useHardenedAddresses: "true",
2196 });
2197 });
2198 it('Can set the derivation path on bip32 tab for multibit', function(done) {
2199 testClientSelect(done, {
2200 selectValue: "2",
2201 bip32path: "m/0'/0",
2202 useHardenedAddresses: null,
2203 });
2204 });
2205
2206 // github issue 58
2207 // https://github.com/iancoleman/bip39/issues/58
2208 // bip32 derivation is correct, does not drop leading zeros
2209 // see also
2210 // https://medium.com/@alexberegszaszi/why-do-my-bip32-wallets-disagree-6f3254cc5846
2211 it('Retains leading zeros for bip32 derivation', function(done) {
2212 driver.findElement(By.css(".phrase"))
2213 .sendKeys("fruit wave dwarf banana earth journey tattoo true farm silk olive fence");
2214 driver.findElement(By.css(".passphrase"))
2215 .sendKeys("banana");
2216 driver.sleep(generateDelay).then(function() {
2217 getFirstAddress(function(address) {
2218 // Note that bitcore generates an incorrect address
2219 // 13EuKhffWkBE2KUwcbkbELZb1MpzbimJ3Y
2220 // see the medium.com link above for more details
2221 expect(address).toBe("17rxURoF96VhmkcEGCj5LNQkmN9HVhWb7F");
2222 done();
2223 });
2224 });
2225 });
2226
2227 // github issue 60
2228 // Japanese mnemonics generate incorrect bip32 seed
2229 // BIP39 seed is set from phrase
2230 it('Generates correct seed for Japanese mnemonics', function(done) {
2231 driver.findElement(By.css(".phrase"))
2232 .sendKeys("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら");
2233 driver.findElement(By.css(".passphrase"))
2234 .sendKeys("メートルガバヴァぱばぐゞちぢ十人十色");
2235 driver.sleep(generateDelay).then(function() {
2236 driver.findElement(By.css(".seed"))
2237 .getAttribute("value")
2238 .then(function(seed) {
2239 expect(seed).toBe("a262d6fb6122ecf45be09c50492b31f92e9beb7d9a845987a02cefda57a15f9c467a17872029a9e92299b5cbdf306e3a0ee620245cbd508959b6cb7ca637bd55");
2240 done();
2241 });
2242 });
2243 });
2244
2245 // BIP49 official test vectors
2246 // https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki#test-vectors
2247 it('Generates BIP49 addresses matching the official test vectors', function(done) {
2248 driver.findElement(By.css('#bip49-tab a'))
2249 .click();
2250 selectNetwork("BTC - Bitcoin Testnet");
2251 driver.findElement(By.css(".phrase"))
2252 .sendKeys("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about");
2253 driver.sleep(generateDelay).then(function() {
2254 getFirstAddress(function(address) {
2255 expect(address).toBe("2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2");
2256 done();
2257 });
2258 });
2259 });
2260
2261 // BIP49 derivation path is shown
2262 it('Shows the bip49 derivation path', function(done) {
2263 driver.findElement(By.css('#bip49-tab a'))
2264 .click();
2265 driver.findElement(By.css(".phrase"))
2266 .sendKeys("abandon abandon ability");
2267 driver.sleep(generateDelay).then(function() {
2268 driver.findElement(By.css('#bip49 .path'))
2269 .getAttribute("value")
2270 .then(function(path) {
2271 expect(path).toBe("m/49'/0'/0'/0");
2272 done();
2273 });
2274 });
2275 });
2276
2277 // BIP49 extended private key is shown
2278 it('Shows the bip49 extended private key', function(done) {
2279 driver.findElement(By.css('#bip49-tab a'))
2280 .click();
2281 driver.findElement(By.css(".phrase"))
2282 .sendKeys("abandon abandon ability");
2283 driver.sleep(generateDelay).then(function() {
2284 driver.findElement(By.css('.extended-priv-key'))
2285 .getAttribute("value")
2286 .then(function(xprv) {
2287 expect(xprv).toBe("yprvALYB4DYRG6CzzVgzQZwwqjAA2wjBGC3iEd7KYYScpoDdmf75qMRWZWxoFcRXBJjgEXdFqJ9vDRGRLJQsrL22Su5jMbNFeM9vetaGVqy9Qy2");
2288 done();
2289 });
2290 });
2291 });
2292
2293 // BIP49 extended public key is shown
2294 it('Shows the bip49 extended public key', function(done) {
2295 driver.findElement(By.css('#bip49-tab a'))
2296 .click();
2297 driver.findElement(By.css(".phrase"))
2298 .sendKeys("abandon abandon ability");
2299 driver.sleep(generateDelay).then(function() {
2300 driver.findElement(By.css('.extended-pub-key'))
2301 .getAttribute("value")
2302 .then(function(xprv) {
2303 expect(xprv).toBe("ypub6ZXXTj5K6TmJCymTWbUxCs6tayZffemZbr2vLvrEP8kceTSENtjm7KHH6thvAKxVar9fGe8rgsPEX369zURLZ68b4f7Vexz7RuXsjQ69YDt");
2304 done();
2305 });
2306 });
2307 });
2308
2309 // BIP49 account field changes address list
2310 it('Can set the bip49 account field', function(done) {
2311 driver.findElement(By.css('#bip49-tab a'))
2312 .click();
2313 driver.findElement(By.css('#bip49 .account'))
2314 .clear();
2315 driver.findElement(By.css('#bip49 .account'))
2316 .sendKeys("1");
2317 driver.findElement(By.css(".phrase"))
2318 .sendKeys("abandon abandon ability");
2319 driver.sleep(generateDelay).then(function() {
2320 getFirstAddress(function(address) {
2321 expect(address).toBe("381wg1GGN4rP88rNC9v7QWsiww63yLVPsn");
2322 done();
2323 });
2324 });
2325 });
2326
2327 // BIP49 change field changes address list
2328 it('Can set the bip49 change field', function(done) {
2329 driver.findElement(By.css('#bip49-tab a'))
2330 .click();
2331 driver.findElement(By.css('#bip49 .change'))
2332 .clear();
2333 driver.findElement(By.css('#bip49 .change'))
2334 .sendKeys("1");
2335 driver.findElement(By.css(".phrase"))
2336 .sendKeys("abandon abandon ability");
2337 driver.sleep(generateDelay).then(function() {
2338 getFirstAddress(function(address) {
2339 expect(address).toBe("3PEM7MiKed5konBoN66PQhK8r3hjGhy9dT");
2340 done();
2341 });
2342 });
2343 });
2344
2345 // BIP49 account extendend private key is shown
2346 it('Shows the bip49 account extended private key', function(done) {
2347 driver.findElement(By.css('#bip49-tab a'))
2348 .click();
2349 driver.findElement(By.css(".phrase"))
2350 .sendKeys("abandon abandon ability");
2351 driver.sleep(generateDelay).then(function() {
2352 driver.findElement(By.css('#bip49 .account-xprv'))
2353 .getAttribute("value")
2354 .then(function(xprv) {
2355 expect(xprv).toBe("yprvAHtB1M5Wp675aLzFy9TJYK2mSsLkg6mcBRh5DZTR7L4EnYSmYPaL63KFA4ycg1PngW5LfkmejxzosCs17TKZMpRFKc3z5SJar6QAKaFcaZL");
2356 done();
2357 });
2358 });
2359 });
2360
2361 // BIP49 account extendend public key is shown
2362 it('Shows the bip49 account extended public key', function(done) {
2363 driver.findElement(By.css('#bip49-tab a'))
2364 .click();
2365 driver.findElement(By.css(".phrase"))
2366 .sendKeys("abandon abandon ability");
2367 driver.sleep(generateDelay).then(function() {
2368 driver.findElement(By.css('#bip49 .account-xpub'))
2369 .getAttribute("value")
2370 .then(function(xprv) {
2371 expect(xprv).toBe("ypub6WsXQrcQeTfNnq4j5AzJuSyVzuBF5ZVTYecg1ws2ffbDfLmv5vtadqdj1NgR6C6gufMpMfJpHxvb6JEQKvETVNWCRanNedfJtnTchZiJtsL");
2372 done();
2373 });
2374 });
2375 });
2376
2377 // Test selecting coin where bip49 is unavailable (eg CLAM)
2378 it('Shows an error on bip49 tab for coins without bip49', function(done) {
2379 driver.findElement(By.css('#bip49-tab a'))
2380 .click();
2381 driver.findElement(By.css(".phrase"))
2382 .sendKeys("abandon abandon ability");
2383 driver.sleep(generateDelay).then(function() {
2384 selectNetwork("CLAM - Clams");
2385 // bip49 available is hidden
2386 driver.findElement(By.css('#bip49 .available'))
2387 .getAttribute("class")
2388 .then(function(classes) {
2389 expect(classes).toContain("hidden");
2390 // bip49 unavailable is shown
2391 driver.findElement(By.css('#bip49 .unavailable'))
2392 .getAttribute("class")
2393 .then(function(classes) {
2394 expect(classes).not.toContain("hidden");
2395 // check there are no addresses shown
2396 driver.findElements(By.css('.addresses tr'))
2397 .then(function(rows) {
2398 expect(rows.length).toBe(0);
2399 // check the derived private key is blank
2400 driver.findElement(By.css('.extended-priv-key'))
2401 .getAttribute("value")
2402 .then(function(xprv) {
2403 expect(xprv).toBe('');
2404 // check the derived public key is blank
2405 driver.findElement(By.css('.extended-pub-key'))
2406 .getAttribute("value")
2407 .then(function(xpub) {
2408 expect(xpub).toBe('');
2409 done();
2410 });
2411 });
2412 })
2413 });
2414 });
2415 });
2416 });
2417
2418 // github issue 43
2419 // Cleared mnemonic and root key still allows addresses to be generated
2420 // https://github.com/iancoleman/bip39/issues/43
2421 it('Clears old root keys from memory when mnemonic is cleared', function(done) {
2422 // set the phrase
2423 driver.findElement(By.css(".phrase"))
2424 .sendKeys("abandon abandon ability");
2425 driver.sleep(generateDelay).then(function() {
2426 // clear the mnemonic and root key
2427 // using selenium .clear() doesn't seem to trigger the 'input' event
2428 // so clear it using keys instead
2429 driver.findElement(By.css('.phrase'))
2430 .sendKeys(Key.CONTROL,"a");
2431 driver.findElement(By.css('.phrase'))
2432 .sendKeys(Key.DELETE);
2433 driver.findElement(By.css('.root-key'))
2434 .sendKeys(Key.CONTROL,"a");
2435 driver.findElement(By.css('.root-key'))
2436 .sendKeys(Key.DELETE);
2437 driver.sleep(generateDelay).then(function() {
2438 // try to generate more addresses
2439 driver.findElement(By.css('.more'))
2440 .click();
2441 driver.sleep(generateDelay).then(function() {
2442 driver.findElements(By.css(".addresses tr"))
2443 .then(function(els) {
2444 // check there are no addresses shown
2445 expect(els.length).toBe(0);
2446 done();
2447 });
2448 });
2449 });
2450 });
2451 });
2452
2453 // Github issue 95
2454 // error trying to generate addresses from xpub with hardened derivation
2455 it('Shows error for hardened addresses with xpub root key', function(done) {
2456 driver.findElement(By.css('#bip32-tab a'))
2457 .click()
2458 driver.executeScript(function() {
2459 $(".hardened-addresses").prop("checked", true);
2460 });
2461 // set xpub for account 0 of bip44 for 'abandon abandon ability'
2462 driver.findElement(By.css("#root-key"))
2463 .sendKeys("xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf");
2464 driver.sleep(feedbackDelay).then(function() {
2465 // Check feedback is correct
2466 driver.findElement(By.css('.feedback'))
2467 .getText()
2468 .then(function(feedback) {
2469 var msg = "Hardened derivation path is invalid with xpub key";
2470 expect(feedback).toBe(msg);
2471 done();
2472 });
2473 });
2474 });
2475
2476 // Litecoin uses ltub by default, and can optionally be set to xprv
2477 // github issue 96
2478 // https://github.com/iancoleman/bip39/issues/96
2479 // Issue with extended keys on Litecoin
2480 it('Uses ltub by default for litecoin, but can be set to xprv', function(done) {
2481 driver.findElement(By.css('.phrase'))
2482 .sendKeys("abandon abandon ability");
2483 selectNetwork("LTC - Litecoin");
2484 driver.sleep(generateDelay).then(function() {
2485 // check the extended key is generated correctly
2486 driver.findElement(By.css('.root-key'))
2487 .getAttribute("value")
2488 .then(function(rootKey) {
2489 expect(rootKey).toBe("Ltpv71G8qDifUiNesiPqf6h5V6eQ8ic77oxQiYtawiACjBEx3sTXNR2HGDGnHETYxESjqkMLFBkKhWVq67ey1B2MKQXannUqNy1RZVHbmrEjnEU");
2490 // set litecoin to use ltub
2491 driver.executeScript(function() {
2492 $(".litecoin-use-ltub").prop("checked", false);
2493 $(".litecoin-use-ltub").trigger("change");
2494 });
2495 driver.sleep(generateDelay).then(function() {
2496 driver.findElement(By.css('.root-key'))
2497 .getAttribute("value")
2498 .then(function(rootKey) {
2499 expect(rootKey).toBe("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi");
2500 done();
2501 });
2502 })
2503 });
2504 });
2505 });
2506
2507 // BIP32 tab can use P2WPKH Nested In P2SH
2508 // github issue 91 part 2
2509 // https://github.com/iancoleman/bip39/issues/91
2510 // generate new addresses from xpub?
2511 it('Uses xprv by default for litecoin, but can be set to ltpv', function(done) {
2512 // use p2wpkh addresses
2513 driver.executeScript(function() {
2514 $(".p2wpkh-nested-in-p2sh").prop("checked", true);
2515 });
2516 // use bip32 tab
2517 driver.findElement(By.css('#bip32-tab a'))
2518 .click()
2519 // use testnet
2520 selectNetwork("BTC - Bitcoin Testnet");
2521 // Set root xpub to BIP49 official test vector account 0
2522 driver.findElement(By.css('.root-key'))
2523 .sendKeys("tpubDD7tXK8KeQ3YY83yWq755fHY2JW8Ha8Q765tknUM5rSvjPcGWfUppDFMpQ1ScziKfW3ZNtZvAD7M3u7bSs7HofjTD3KP3YxPK7X6hwV8Rk2");
2524 driver.sleep(generateDelay).then(function() {
2525 getFirstAddress(function(address) {
2526 expect(address).toBe("2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2");
2527 done();
2528 });
2529 });
2530 });
2531
2532 // github issue 99
2533 // https://github.com/iancoleman/bip39/issues/99#issuecomment-327094159
2534 // "warn me emphatically when they have detected invalid input" to the entropy field
2535 // A warning is shown when entropy is filtered and discarded
2536 it('Warns when entropy is filtered and discarded', function(done) {
2537 driver.findElement(By.css('.use-entropy'))
2538 .click();
2539 // set entropy to have no filtered content
2540 driver.findElement(By.css('.entropy'))
2541 .sendKeys("00000000 00000000 00000000 00000000");
2542 driver.sleep(generateDelay).then(function() {
2543 // check the filter warning does not show
2544 driver.findElement(By.css('.entropy-container .filter-warning'))
2545 .getAttribute("class")
2546 .then(function(classes) {
2547 expect(classes).toContain("hidden");
2548 // set entropy to have some filtered content
2549 driver.findElement(By.css('.entropy'))
2550 .sendKeys("10000000 zxcvbn 00000000 00000000 00000000");
2551 driver.sleep(entropyFeedbackDelay).then(function() {
2552 // check the filter warning shows
2553 driver.findElement(By.css('.entropy-container .filter-warning'))
2554 .getAttribute("class")
2555 .then(function(classes) {
2556 expect(classes).not.toContain("hidden");
2557 done();
2558 });
2559 });
2560 });
2561 });
2562 });
2563
2564 // Bitcoin Cash address can be set to use bitpay format
2565 it('Can use bitpay format for bitcoin cash addresses', function(done) {
2566 driver.executeScript(function() {
2567 $(".use-bitpay-addresses").prop("checked", true);
2568 });
2569 driver.findElement(By.css('.phrase'))
2570 .sendKeys("abandon abandon ability");
2571 selectNetwork("BCH - Bitcoin Cash");
2572 driver.sleep(generateDelay).then(function() {
2573 getFirstAddress(function(address) {
2574 expect(address).toBe("CZnpA9HPmvhuhLLPWJP8rNDpLUYXy1LXFk");
2575 done();
2576 });
2577 });
2578 });
2579
2580 // End of tests ported from old suit, so no more comments above each test now
2581
2582 it('Can generate more addresses from a custom index', function(done) {
2583 var expectedIndexes = [
2584 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
2585 40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59
2586 ];
2587 driver.findElement(By.css('.phrase'))
2588 .sendKeys("abandon abandon ability");
2589 driver.sleep(generateDelay).then(function() {
2590 // Set start of next lot of rows to be from index 40
2591 // which means indexes 20-39 will not be in the table.
2592 driver.findElement(By.css('.more-rows-start-index'))
2593 .sendKeys("40");
2594 driver.findElement(By.css('.more'))
2595 .click();
2596 driver.sleep(generateDelay).then(function() {
2597 // Check actual indexes in the table match the expected pattern
2598 driver.findElements(By.css(".index"))
2599 .then(function(els) {
2600 expect(els.length).toBe(expectedIndexes.length);
2601 var testRowAtIndex = function(i) {
2602 if (i >= expectedIndexes.length) {
2603 done();
2604 }
2605 else {
2606 els[i].getText()
2607 .then(function(actualPath) {
2608 var noHardened = actualPath.replace(/'/g, "");
2609 var pathBits = noHardened.split("/")
2610 var lastBit = pathBits[pathBits.length-1];
2611 var actualIndex = parseInt(lastBit);
2612 var expectedIndex = expectedIndexes[i];
2613 expect(actualIndex).toBe(expectedIndex);
2614 testRowAtIndex(i+1);
2615 });
2616 }
2617 }
2618 testRowAtIndex(0);
2619 });
2620 });
2621 });
2622 });
2623
2624 });