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