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