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