2 // cd /path/to/repo/tests
3 // jasmine spec/tests.js
9 // see https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode#Automated_testing_with_headless_mode
11 // USER SPECIFIED OPTIONS
12 var browser
= process
.env
.BROWSER
; //"firefox"; // or "chrome"
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");
21 console
.log("Using browser: " + browser
);
26 var webdriver
= require('selenium-webdriver');
27 var By
= webdriver
.By
;
28 var Key
= webdriver
.Key
;
29 var until
= webdriver
.until
;
33 var generateDelay
= 1500;
34 var feedbackDelay
= 500;
35 var entropyFeedbackDelay
= 500;
37 // url uses file:// scheme
38 var path
= require('path')
39 var parentDir
= path
.resolve(process
.cwd(), '..', 'src', 'index.html');
40 var url
= "file://" + parentDir
;
41 if (browser
== "firefox") {
42 // TODO loading local html in firefox is broken
43 console
.log("Loading local html in firefox is broken, see https://stackoverflow.com/q/46367054");
44 console
.log("You must run a server in this case, ie do this:");
45 console
.log("$ cd /path/to/bip39/src");
46 console
.log("$ python -m http.server");
47 url
= "http://localhost:8000";
50 // Variables dependent on specific browser selection
52 if (browser
== "firefox") {
53 var firefox
= require('selenium-webdriver/firefox');
54 var binary
= new firefox
.Binary(firefox
.Channel
.NIGHTLY
);
55 binary
.addArguments("-headless");
56 newDriver = function() {
57 return new webdriver
.Builder()
58 .forBrowser('firefox')
59 .setFirefoxOptions(new firefox
.Options().setBinary(binary
))
63 else if (browser
== "chrome") {
64 var chrome
= require('selenium-webdriver/chrome');
65 newDriver = function() {
66 return new webdriver
.Builder()
68 .setChromeOptions(new chrome
.Options().addArguments("headless"))
75 function testNetwork(done
, params
) {
76 var phrase
= params
.phrase
|| 'abandon abandon ability';
77 driver
.findElement(By
.css('.phrase'))
79 selectNetwork(params
.selectText
);
80 driver
.sleep(generateDelay
).then(function() {
81 getFirstAddress(function(address
) {
82 expect(address
).toBe(params
.firstAddress
);
88 function getFirstRowValue(handler
, selector
) {
89 driver
.findElements(By
.css(selector
))
96 function getFirstAddress(handler
) {
97 getFirstRowValue(handler
, ".address");
100 function getFirstPath(handler
) {
101 getFirstRowValue(handler
, ".index");
104 function testColumnValuesAreInvisible(done
, columnClassName
) {
105 var selector
= "." + columnClassName
+ " span";
106 driver
.findElements(By
.css(selector
))
107 .then(function(els
) {
108 els
[0].getAttribute("class")
109 .then(function(classes
) {
110 expect(classes
).toContain("invisible");
116 function testRowsAreInCorrectOrder(done
) {
117 driver
.findElements(By
.css('.index'))
118 .then(function(els
) {
119 var testRowAtIndex = function(i
) {
120 if (i
>= els
.length
) {
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);
139 function selectNetwork(name) {
140 driver.executeScript(function() {
141 var selectText = arguments[0];
142 $(".network option[selected]").removeAttr("selected");
143 $(".network option").filter(function(i,e) {
144 return $(e).html() == selectText;
145 }).prop("selected", true);
146 $(".network").trigger("change");
150 function testEntropyType(done, entropyText, entropyTypeUnsafe) {
151 // entropy type is compiled into regexp so needs escaping
152 // see https://stackoverflow.com/a/2593661
153 var entropyType = (entropyTypeUnsafe+'').replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&");
154 driver.findElement(By.css('.use-entropy
'))
156 driver.findElement(By.css('.entropy
'))
157 .sendKeys(entropyText);
158 driver.sleep(generateDelay).then(function() {
159 driver.findElement(By.css('.entropy
-container
'))
161 .then(function(text) {
162 var re = new RegExp("Entropy Type\\s+" + entropyType);
163 expect(text).toMatch(re);
169 function testEntropyBits(done, entropyText, entropyBits) {
170 driver.findElement(By.css('.use-entropy
'))
172 driver.findElement(By.css('.entropy
'))
173 .sendKeys(entropyText);
174 driver.sleep(generateDelay).then(function() {
175 driver.findElement(By.css('.entropy
-container
'))
177 .then(function(text) {
178 var re = new RegExp("Total Bits\\s+" + entropyBits);
179 expect(text).toMatch(re);
185 function testEntropyFeedback(done, entropyDetail) {
186 // entropy type is compiled into regexp so needs escaping
187 // see https://stackoverflow.com/a/2593661
188 if ("type" in entropyDetail) {
189 entropyDetail.type = (entropyDetail.type+'').replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&");
191 driver.findElement(By.css('.use-entropy
'))
193 driver.findElement(By.css('.entropy
'))
194 .sendKeys(entropyDetail.entropy);
195 driver.sleep(entropyFeedbackDelay).then(function() {
196 driver.findElement(By.css('.entropy
-container
'))
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);
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);
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);
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);
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);
237 if ("words" in entropyDetail) {
238 var actualWords = phrase.split(/\s+/)
239 .filter(function(w) { return w.length > 0 })
241 expect(actualWords).toBe(entropyDetail.words);
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);
256 function testClientSelect(done, params) {
257 // set mnemonic and select bip32 tab
258 driver.findElement(By.css('#bip32
-tab a
'))
260 driver.findElement(By.css('.phrase
'))
261 .sendKeys("abandon abandon ability");
262 driver.sleep(generateDelay).then(function() {
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");
294 describe('BIP39 Tool Tests
', function() {
296 beforeEach(function(done) {
297 driver = newDriver();
298 driver.get(url).then(done);
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);
308 // Page initially loads with blank phrase
309 it('Should load the page
', function(done) {
310 driver.findElement(By.css('.phrase
'))
311 .getAttribute('value
').then(function(value) {
312 expect(value).toBe('');
318 it('Should have text on the page
', function(done) {
319 driver.findElement(By.css('body
'))
321 .then(function(text) {
322 var textToFind = "You can enter an existing BIP39 mnemonic";
323 expect(text).toContain(textToFind);
328 // Entering mnemonic generates addresses
329 it('Should have a list
of addresses
', function(done) {
330 driver.findElement(By.css('.phrase
'))
331 .sendKeys('abandon abandon ability
');
332 driver.sleep(generateDelay).then(function() {
333 driver.findElements(By.css('.address
'))
334 .then(function(els) {
335 expect(els.length).toBe(20);
341 // Generate button generates random mnemonic
342 it('Should be able to generate a random mnemonic
', function(done) {
343 // initial phrase is blank
344 driver.findElement(By.css('.phrase
'))
345 .getAttribute("value")
346 .then(function(phrase) {
347 expect(phrase.length).toBe(0);
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);
362 // Mnemonic length can be customized
363 it('Should allow custom length mnemonics
', function(done) {
365 driver.executeScript(function() {
366 $(".strength option[selected]").removeAttr("selected");
367 $(".strength option[value=6]").prop("selected", true);
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);
381 // Passphrase can be set
382 it('Allows a passphrase to be
set', function(done) {
383 driver.findElement(By.css('.phrase
'))
384 .sendKeys('abandon abandon ability
');
385 driver.findElement(By.css('.passphrase
'))
386 .sendKeys('secure_passphrase
');
387 driver.sleep(generateDelay).then(function() {
388 getFirstAddress(function(address) {
389 expect(address).toBe("15pJzUWPGzR7avffV9nY5by4PSgSKG9rba");
395 // Network can be set to networks other than bitcoin
396 it('Allows selection
of bitcoin testnet
', function(done) {
398 selectText: "BTC - Bitcoin Testnet",
399 firstAddress: "mucaU5iiDaJDb69BHLeDv8JFfGiyg2nJKi",
401 testNetwork(done, params);
403 it('Allows selection
of litecoin
', function(done) {
405 selectText: "LTC - Litecoin",
406 firstAddress: "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn",
408 testNetwork(done, params);
410 it('Allows selection
of ripple
', function(done) {
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",
416 testNetwork(done, params);
418 it('Allows selection
of dogecoin
', function(done) {
420 selectText: "DOGE - Dogecoin",
421 firstAddress: "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA",
423 testNetwork(done, params);
425 it('Allows selection
of shadowcash
', function(done) {
427 selectText: "SDC - ShadowCash",
428 firstAddress: "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG",
430 testNetwork(done, params);
432 it('Allows selection
of shadowcash testnet
', function(done) {
434 selectText: "SDC - ShadowCash Testnet",
435 firstAddress: "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe",
437 testNetwork(done, params);
439 it('Allows selection
of viacoin
', function(done) {
441 selectText: "VIA - Viacoin",
442 firstAddress: "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT",
444 testNetwork(done, params);
446 it('Allows selection
of viacoin testnet
', function(done) {
448 selectText: "VIA - Viacoin Testnet",
449 firstAddress: "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe",
451 testNetwork(done, params);
453 it('Allows selection
of jumbucks
', function(done) {
455 selectText: "JBS - Jumbucks",
456 firstAddress: "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew",
458 testNetwork(done, params);
460 it('Allows selection
of clam
', function(done) {
462 selectText: "CLAM - Clams",
463 firstAddress: "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y",
465 testNetwork(done, params);
467 it('Allows selection
of crown
', function(done) {
469 selectText: "CRW - Crown",
470 firstAddress: "18pWSwSUAQdiwMHUfFZB1fM2xue9X1FqE5",
472 testNetwork(done, params);
474 it('Allows selection
of dash
', function(done) {
476 selectText: "DASH - Dash",
477 firstAddress: "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT",
479 testNetwork(done, params);
481 it('Allows selection
of dash testnet
', function(done) {
483 selectText: "DASH - Dash Testnet",
484 firstAddress: "yaR52EN4oojdJfBgzWJTymC4uuCLPT29Gw",
486 testNetwork(done, params);
488 it('Allows selection
of game
', function(done) {
490 selectText: "GAME - GameCredits",
491 firstAddress: "GSMY9bAp36cMR4zyT4uGVS7GFjpdXbao5Q",
493 testNetwork(done, params);
495 it('Allows selection
of komodo
', function(done) {
497 selectText: "KMD - Komodo",
498 firstAddress: "RMPPzJwAjPVZZAwJvXivHJGGjdCx6WBD2t",
500 testNetwork(done, params);
502 it('Allows selection
of namecoin
', function(done) {
504 selectText: "NMC - Namecoin",
505 firstAddress: "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2",
507 testNetwork(done, params);
509 it('Allows selection
of onixcoin
', function(done) {
511 selectText: "ONX - Onixcoin",
512 firstAddress: "XGwMqddeKjT3ddgX73QokjVbCL3aK6Yxfk",
514 testNetwork(done, params);
516 it('Allows selection
of peercoin
', function(done) {
518 selectText: "PPC - Peercoin",
519 firstAddress: "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm",
521 testNetwork(done, params);
523 it('Allows selection
of ethereum
', function(done) {
525 selectText: "ETH - Ethereum",
526 firstAddress: "0xe5815d5902Ad612d49283DEdEc02100Bd44C2772",
528 testNetwork(done, params);
529 // TODO test private key and public key
531 it('Allows selection
of slimcoin
', function(done) {
533 selectText: "SLM - Slimcoin",
534 firstAddress: "SNzPi1CafHFm3WWjRo43aMgiaEEj3ogjww",
536 testNetwork(done, params);
538 it('Allows selection
of slimcoin testnet
', function(done) {
540 selectText: "SLM - Slimcoin Testnet",
541 firstAddress: "n3nMgWufTek5QQAr6uwMhg5xbzj8xqc4Dq",
543 testNetwork(done, params);
545 it('Allows selection
of bitcoin cash
', function(done) {
547 selectText: "BCH - Bitcoin Cash",
548 firstAddress: "1JKvb6wKtsjNoCRxpZ4DGrbniML7z5U16A",
550 testNetwork(done, params);
552 it('Allows selection
of myriadcoin
', function(done) {
554 selectText: "XMY - Myriadcoin",
555 firstAddress: "MJEswvRR46wh9BoiVj9DzKYMBkCramhoBV",
557 testNetwork(done, params);
559 it('Allows selection
of pivx
', function(done) {
561 selectText: "PIVX - PIVX",
562 firstAddress: "DBxgT7faCuno7jmtKuu6KWCiwqsVPqh1tS",
564 testNetwork(done, params);
566 it('Allows selection
of pivx testnet
', function(done) {
568 selectText: "PIVX - PIVX Testnet",
569 firstAddress: "yB5U384n6dGkVE3by5y9VdvHHPwPg68fQj",
571 testNetwork(done, params);
573 it('Allows selection
of maza
', function(done) {
575 selectText: "MAZA - Maza",
576 firstAddress: "MGW4Bmi2NEm4PxSjgeFwhP9vg18JHoRnfw",
578 testNetwork(done, params);
580 it('Allows selection
of fujicoin
', function(done) {
582 selectText: "FJC - Fujicoin",
583 firstAddress: "FgiaLpG7C99DyR4WnPxXedRVHXSfKzUDhF",
585 testNetwork(done, params);
587 it('Allows selection
of nubits
', function(done) {
589 selectText: "USNBT - NuBits",
590 firstAddress: "BLxkabXuZSJSdesLD7KxZdqovd4YwyBTU6",
592 testNetwork(done, params);
594 it('Allows selection
of bitcoin gold
', function(done) {
596 selectText: "BTG - Bitcoin Gold",
597 firstAddress: "GWYxuwSqANWGV3WT7Gpr6HE91euYXBqtwQ",
599 testNetwork(done, params);
601 it('Allows selection
of monacoin
', function(done) {
603 selectText: "MONA - Monacoin",
604 firstAddress: "MKMiMr7MyjDKjJbCBzgF6u4ByqTS4NkRB1",
606 testNetwork(done, params);
609 // BIP39 seed is set from phrase
610 it('Sets the bip39 seed
from the prhase
', function(done) {
611 driver.findElement(By.css('.phrase
'))
612 .sendKeys('abandon abandon ability
');
613 driver.sleep(generateDelay).then(function() {
614 driver.findElement(By.css('.seed
'))
615 .getAttribute("value")
616 .then(function(seed) {
617 expect(seed).toBe("20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3");
623 // BIP32 root key is set from phrase
624 it('Sets the bip39 root key
from the prhase
', function(done) {
625 driver.findElement(By.css('.phrase
'))
626 .sendKeys('abandon abandon ability
');
627 driver.sleep(generateDelay).then(function() {
628 driver.findElement(By.css('.root
-key
'))
629 .getAttribute("value")
630 .then(function(seed) {
631 expect(seed).toBe("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi");
637 // Tabs show correct addresses when changed
638 it('Shows the correct address when tab is changed
', function(done) {
639 driver.findElement(By.css('.phrase
'))
640 .sendKeys('abandon abandon ability
');
641 driver.sleep(generateDelay).then(function() {
642 driver.findElement(By.css('#bip32
-tab a
'))
644 driver.sleep(generateDelay).then(function() {
645 getFirstAddress(function(address) {
646 expect(address).toBe("17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz");
653 // BIP44 derivation path is shown
654 it('Shows the derivation path
for bip44 tab
', function(done) {
655 driver.findElement(By.css('.phrase
'))
656 .sendKeys('abandon abandon ability
');
657 driver.sleep(generateDelay).then(function() {
658 driver.findElement(By.css('#bip44
.path
'))
659 .getAttribute("value")
660 .then(function(path) {
661 expect(path).toBe("m/44'/0'/0'/0");
667 // BIP44 extended private key is shown
668 it('Shows the extended private key for bip44 tab', function(done) {
669 driver.findElement(By.css('.phrase'))
670 .sendKeys('abandon abandon ability');
671 driver.sleep(generateDelay).then(function() {
672 driver.findElement(By.css('.extended-priv-key'))
673 .getAttribute("value
")
674 .then(function(path) {
675 expect(path).toBe("xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG
");
681 // BIP44 extended public key is shown
682 it('Shows the extended public key for bip44 tab', function(done) {
683 driver.findElement(By.css('.phrase'))
684 .sendKeys('abandon abandon ability');
685 driver.sleep(generateDelay).then(function() {
686 driver.findElement(By.css('.extended-pub-key'))
687 .getAttribute("value
")
688 .then(function(path) {
689 expect(path).toBe("xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM
");
695 // BIP44 account field changes address list
696 it('Changes the address list if bip44 account is changed', function(done) {
697 driver.findElement(By.css('#bip44 .account'))
699 driver.findElement(By.css('.phrase'))
700 .sendKeys('abandon abandon ability');
701 driver.sleep(generateDelay).then(function() {
702 getFirstAddress(function(address) {
703 expect(address).toBe("1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H
");
709 // BIP44 change field changes address list
710 it('Changes the address list if bip44 change is changed', function(done) {
711 driver.findElement(By.css('#bip44 .change'))
713 driver.findElement(By.css('.phrase'))
714 .sendKeys('abandon abandon ability');
715 driver.sleep(generateDelay).then(function() {
716 getFirstAddress(function(address) {
717 expect(address).toBe("1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo
");
723 // BIP32 derivation path can be set
724 it('Can use a custom bip32 derivation path', function(done) {
725 driver.findElement(By.css('#bip32-tab a'))
727 driver.findElement(By.css('#bip32 .path'))
729 driver.findElement(By.css('#bip32 .path'))
731 driver.findElement(By.css('.phrase'))
732 .sendKeys('abandon abandon ability');
733 driver.sleep(generateDelay).then(function() {
734 getFirstAddress(function(address) {
735 expect(address).toBe("16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L
");
741 // BIP32 can use hardened derivation paths
742 it('Can use a hardened derivation paths', function(done) {
743 driver.findElement(By.css('#bip32-tab a'))
745 driver.findElement(By.css('#bip32 .path'))
747 driver.findElement(By.css('#bip32 .path'))
749 driver.findElement(By.css('.phrase
'))
750 .sendKeys('abandon abandon ability
');
751 driver.sleep(generateDelay).then(function() {
752 getFirstAddress(function(address) {
753 expect(address).toBe("14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4");
759 // BIP32 extended private key is shown
760 it('Shows the BIP32 extended
private key
', function(done) {
761 driver.findElement(By.css('#bip32
-tab a
'))
763 driver.findElement(By.css('.phrase
'))
764 .sendKeys('abandon abandon ability
');
765 driver.sleep(generateDelay).then(function() {
766 driver.findElement(By.css('.extended
-priv
-key
'))
767 .getAttribute("value")
768 .then(function(privKey) {
769 expect(privKey).toBe("xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe");
775 // BIP32 extended public key is shown
776 it('Shows the BIP32 extended
public key
', function(done) {
777 driver.findElement(By.css('#bip32
-tab a
'))
779 driver.findElement(By.css('.phrase
'))
780 .sendKeys('abandon abandon ability
');
781 driver.sleep(generateDelay).then(function() {
782 driver.findElement(By.css('.extended
-pub
-key
'))
783 .getAttribute("value")
784 .then(function(pubKey) {
785 expect(pubKey).toBe("xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P");
791 // Derivation path is shown in table
792 it('Shows the derivation path
in the table
', function(done) {
793 driver.findElement(By.css('.phrase
'))
794 .sendKeys('abandon abandon ability
');
795 driver.sleep(generateDelay).then(function() {
796 getFirstPath(function(path) {
797 expect(path).toBe("m/44'/0'/0'/0/0");
803 // Derivation path for address can be hardened
804 it('Can derive hardened addresses', function(done) {
805 driver.findElement(By.css('#bip32-tab a'))
807 driver.executeScript(function() {
808 $(".hardened
-addresses
").prop("checked
", true);
810 driver.findElement(By.css('.phrase'))
811 .sendKeys('abandon abandon ability');
812 driver.sleep(generateDelay).then(function() {
813 getFirstAddress(function(address) {
814 expect(address).toBe("18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd
");
820 // Derivation path visibility can be toggled
821 it('Can toggle visibility of the derivation path column', function(done) {
822 driver.findElement(By.css('.phrase'))
823 .sendKeys('abandon abandon ability');
824 driver.sleep(generateDelay).then(function() {
825 driver.findElement(By.css('.index-toggle'))
827 testColumnValuesAreInvisible(done, "index
");
832 it('Shows the address in the table', function(done) {
833 driver.findElement(By.css('.phrase'))
834 .sendKeys('abandon abandon ability');
835 driver.sleep(generateDelay).then(function() {
836 getFirstAddress(function(address) {
837 expect(address).toBe("1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug
");
843 // Addresses are shown in order of derivation path
844 it('Shows the address in order of derivation path', function(done) {
845 driver.findElement(By.css('.phrase'))
846 .sendKeys('abandon abandon ability');
847 driver.sleep(generateDelay).then(function() {
848 testRowsAreInCorrectOrder(done);
852 // Address visibility can be toggled
853 it('Can toggle visibility of the address column', function(done) {
854 driver.findElement(By.css('.phrase'))
855 .sendKeys('abandon abandon ability');
856 driver.sleep(generateDelay).then(function() {
857 driver.findElement(By.css('.address-toggle'))
859 testColumnValuesAreInvisible(done, "address
");
863 // Public key is shown in table
864 it('Shows the public key in the table', function(done) {
865 driver.findElement(By.css('.phrase'))
866 .sendKeys('abandon abandon ability');
867 driver.sleep(generateDelay).then(function() {
868 driver.findElements(By.css('.pubkey'))
869 .then(function(els) {
871 .then(function(pubkey) {
872 expect(pubkey).toBe("033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3
");
879 // Public key visibility can be toggled
880 it('Can toggle visibility of the public key column', function(done) {
881 driver.findElement(By.css('.phrase'))
882 .sendKeys('abandon abandon ability');
883 driver.sleep(generateDelay).then(function() {
884 driver.findElement(By.css('.public-key-toggle'))
886 testColumnValuesAreInvisible(done, "pubkey
");
890 // Private key is shown in table
891 it('Shows the private key in the table', function(done) {
892 driver.findElement(By.css('.phrase'))
893 .sendKeys('abandon abandon ability');
894 driver.sleep(generateDelay).then(function() {
895 driver.findElements(By.css('.privkey'))
896 .then(function(els) {
898 .then(function(pubkey) {
899 expect(pubkey).toBe("L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE
");
906 // Private key visibility can be toggled
907 it('Can toggle visibility of the private key column', function(done) {
908 driver.findElement(By.css('.phrase'))
909 .sendKeys('abandon abandon ability');
910 driver.sleep(generateDelay).then(function() {
911 driver.findElement(By.css('.private-key-toggle'))
913 testColumnValuesAreInvisible(done, "privkey
");
917 // More addresses can be generated
918 it('Can generate more rows in the table', function(done) {
919 driver.findElement(By.css('.phrase'))
920 .sendKeys('abandon abandon ability');
921 driver.sleep(generateDelay).then(function() {
922 driver.findElement(By.css('.more'))
924 driver.sleep(generateDelay).then(function() {
925 driver.findElements(By.css('.address'))
926 .then(function(els) {
927 expect(els.length).toBe(40);
934 // A custom number of additional addresses can be generated
935 it('Can generate more rows in the table', function(done) {
936 driver.findElement(By.css('.rows-to-add'))
938 driver.findElement(By.css('.rows-to-add'))
940 driver.findElement(By.css('.phrase'))
941 .sendKeys('abandon abandon ability');
942 driver.sleep(generateDelay).then(function() {
943 driver.findElement(By.css('.more'))
945 driver.sleep(generateDelay).then(function() {
946 driver.findElements(By.css('.address'))
947 .then(function(els) {
948 expect(els.length).toBe(21);
955 // Additional addresses are shown in order of derivation path
956 it('Shows additional addresses in order of derivation path', function(done) {
957 driver.findElement(By.css('.phrase'))
958 .sendKeys('abandon abandon ability');
959 driver.sleep(generateDelay).then(function() {
960 driver.findElement(By.css('.more'))
962 driver.sleep(generateDelay).then(function() {
963 testRowsAreInCorrectOrder(done);
968 // BIP32 root key can be set by the user
969 it('Allows the user to set the BIP32 root key', function(done) {
970 driver.findElement(By.css('.root-key'))
971 .sendKeys('xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi');
972 driver.sleep(generateDelay).then(function() {
973 getFirstAddress(function(address) {
974 expect(address).toBe("1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug
");
980 // Setting BIP32 root key clears the existing phrase, passphrase and seed
981 // TODO this doesn't work in selenium with chrome
982 it('Confirms the existing phrase should be cleared', function(done) {
983 if (browser == "chrome
") {
984 pending("Selenium
+ Chrome headless bug
for alert
, see
https://stackoverflow.com/q/45242264");
986 driver
.findElement(By
.css('.phrase'))
987 .sendKeys('A non-blank but invalid value');
988 driver
.findElement(By
.css('.root-key'))
989 .sendKeys('xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi');
990 driver
.switchTo().alert().accept();
991 driver
.findElement(By
.css('.phrase'))
992 .getAttribute("value").then(function(value
) {
993 expect(value
).toBe("");
998 // Clearing of phrase, passphrase and seed can be cancelled by user
999 // TODO this doesn't work in selenium with chrome
1000 it('Allows the clearing of the phrase to be cancelled', function(done
) {
1001 if (browser
== "chrome") {
1002 pending("Selenium + Chrome headless bug for alert, see https://stackoverflow.com/q/45242264");
1004 driver
.findElement(By
.css('.phrase'))
1005 .sendKeys('abandon abandon ability');
1006 driver
.sleep(generateDelay
).then(function() {
1007 driver
.findElement(By
.css('.root-key'))
1009 driver
.findElement(By
.css('.root-key'))
1011 driver
.switchTo().alert().dismiss();
1012 driver
.findElement(By
.css('.phrase'))
1013 .getAttribute("value").then(function(value
) {
1014 expect(value
).toBe("abandon abandon ability");
1020 // Custom BIP32 root key is used when changing the derivation path
1021 it('Can set derivation path for root key instead of phrase', function(done
) {
1022 driver
.findElement(By
.css('#bip44 .account'))
1024 driver
.findElement(By
.css('.root-key'))
1025 .sendKeys('xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi');
1026 driver
.sleep(generateDelay
).then(function() {
1027 getFirstAddress(function(address
) {
1028 expect(address
).toBe("1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H");
1034 // Incorrect mnemonic shows error
1035 it('Shows an error for incorrect mnemonic', function(done
) {
1036 driver
.findElement(By
.css('.phrase'))
1037 .sendKeys('abandon abandon abandon');
1038 driver
.sleep(feedbackDelay
).then(function() {
1039 driver
.findElement(By
.css('.feedback'))
1041 .then(function(feedback
) {
1042 expect(feedback
).toBe("Invalid mnemonic");
1048 // Incorrect word shows suggested replacement
1049 it('Shows word suggestion for incorrect word', function(done
) {
1050 driver
.findElement(By
.css('.phrase'))
1051 .sendKeys('abandon abandon abiliti');
1052 driver
.sleep(feedbackDelay
).then(function() {
1053 driver
.findElement(By
.css('.feedback'))
1055 .then(function(feedback
) {
1056 var msg
= "abiliti not in wordlist, did you mean ability?";
1057 expect(feedback
).toBe(msg
);
1063 // Github pull request 48
1064 // First four letters of word shows that word, not closest
1065 // since first four letters gives unique word in BIP39 wordlist
1066 // eg ille should show illegal, not idle
1067 it('Shows word suggestion based on first four chars', function(done
) {
1068 driver
.findElement(By
.css('.phrase'))
1070 driver
.sleep(feedbackDelay
).then(function() {
1071 driver
.findElement(By
.css('.feedback'))
1073 .then(function(feedback
) {
1074 var msg
= "ille not in wordlist, did you mean illegal?";
1075 expect(feedback
).toBe(msg
);
1081 // Incorrect BIP32 root key shows error
1082 it('Shows error for incorrect root key', function(done
) {
1083 driver
.findElement(By
.css('.root-key'))
1084 .sendKeys('xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj');
1085 driver
.sleep(feedbackDelay
).then(function() {
1086 driver
.findElement(By
.css('.feedback'))
1088 .then(function(feedback
) {
1089 var msg
= "Invalid root key";
1090 expect(feedback
).toBe(msg
);
1096 // Derivation path not starting with m shows error
1097 it('Shows error for derivation path not starting with m', function(done
) {
1098 driver
.findElement(By
.css('#bip32-tab a'))
1100 driver
.findElement(By
.css('#bip32 .path'))
1102 driver
.findElement(By
.css('#bip32 .path'))
1104 driver
.findElement(By
.css('.phrase'))
1105 .sendKeys('abandon abandon ability');
1106 driver
.sleep(feedbackDelay
).then(function() {
1107 driver
.findElement(By
.css('.feedback'))
1109 .then(function(feedback
) {
1110 var msg
= "First character must be 'm'";
1111 expect(feedback
).toBe(msg
);
1117 // Derivation path containing invalid characters shows useful error
1118 it('Shows error for derivation path not starting with m', function(done
) {
1119 driver
.findElement(By
.css('#bip32-tab a'))
1121 driver
.findElement(By
.css('#bip32 .path'))
1123 driver
.findElement(By
.css('#bip32 .path'))
1124 .sendKeys('m/1/0wrong1/1');
1125 driver
.findElement(By
.css('.phrase'))
1126 .sendKeys('abandon abandon ability');
1127 driver
.sleep(feedbackDelay
).then(function() {
1128 driver
.findElement(By
.css('.feedback'))
1130 .then(function(feedback
) {
1131 var msg
= "Invalid characters 0wrong1 found at depth 2";
1132 expect(feedback
).toBe(msg
);
1138 // Github Issue 11: Default word length is 15
1139 // https://github.com/iancoleman/bip39/issues/11
1140 it('Sets the default word length to 15', function(done
) {
1141 driver
.findElement(By
.css('.strength'))
1142 .getAttribute("value")
1143 .then(function(strength
) {
1144 expect(strength
).toBe("15");
1149 // Github Issue 12: Generate more rows with private keys hidden
1150 // https://github.com/iancoleman/bip39/issues/12
1151 it('Sets the correct hidden column state on new rows', function(done
) {
1152 driver
.findElement(By
.css('.phrase'))
1153 .sendKeys("abandon abandon ability");
1154 driver
.sleep(generateDelay
).then(function() {
1155 driver
.findElement(By
.css('.private-key-toggle'))
1157 driver
.findElement(By
.css('.more'))
1159 driver
.sleep(generateDelay
).then(function() {
1160 driver
.findElements(By
.css('.privkey'))
1161 .then(function(els
) {
1162 expect(els
.length
).toBe(40);
1164 testColumnValuesAreInvisible(done
, "privkey");
1169 // Github Issue 19: Mnemonic is not sensitive to whitespace
1170 // https://github.com/iancoleman/bip39/issues/19
1171 it('Ignores excess whitespace in the mnemonic', function(done
) {
1172 var doublespace
= " ";
1173 var mnemonic
= "urge cat" + doublespace
+ "bid";
1174 driver
.findElement(By
.css('.phrase'))
1175 .sendKeys(mnemonic
);
1176 driver
.sleep(generateDelay
).then(function() {
1177 driver
.findElement(By
.css('.root-key'))
1178 .getAttribute("value")
1179 .then(function(seed
) {
1180 expect(seed
).toBe("xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC");
1186 // Github Issue 23: Part 1: Use correct derivation path when changing tabs
1187 // https://github.com/iancoleman/bip39/issues/23
1188 it('Uses the correct derivation path when changing tabs', function(done
) {
1189 // 1) and 2) set the phrase
1190 driver
.findElement(By
.css('.phrase'))
1191 .sendKeys("abandon abandon ability");
1192 driver
.sleep(generateDelay
).then(function() {
1193 // 3) select bip32 tab
1194 driver
.findElement(By
.css('#bip32-tab a'))
1196 driver
.sleep(generateDelay
).then(function() {
1197 // 4) switch from bitcoin to litecoin
1198 selectNetwork("LTC - Litecoin");
1199 driver
.sleep(generateDelay
).then(function() {
1200 // 5) Check address is displayed correctly
1201 getFirstAddress(function(address
) {
1202 expect(address
).toBe("LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5");
1203 // 5) Check derivation path is displayed correctly
1204 getFirstPath(function(path
) {
1205 expect(path
).toBe("m/0/0");
1214 // Github Issue 23 Part 2: Coin selection in derivation path
1215 // https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
1216 it('Uses the correct derivation path when changing coins', function(done
) {
1218 driver
.findElement(By
.css('.phrase'))
1219 .sendKeys("abandon abandon ability");
1220 driver
.sleep(generateDelay
).then(function() {
1221 // switch from bitcoin to clam
1222 selectNetwork("CLAM - Clams");
1223 driver
.sleep(generateDelay
).then(function() {
1224 // check derivation path is displayed correctly
1225 getFirstPath(function(path
) {
1226 expect(path
).toBe("m/44'/23'/0'/0/0");
1233 // Github Issue 26: When using a Root key derrived altcoins are incorrect
1234 // https://github.com/iancoleman/bip39/issues/26
1235 it('Uses the correct derivation for altcoins with root keys', function(done
) {
1236 // 1) 2) and 3) set the root key
1237 driver
.findElement(By
.css('.root-key'))
1238 .sendKeys("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi");
1239 driver
.sleep(generateDelay
).then(function() {
1240 // 4) switch from bitcoin to viacoin
1241 selectNetwork("VIA - Viacoin");
1242 driver
.sleep(generateDelay
).then(function() {
1243 // 5) ensure the derived address is correct
1244 getFirstAddress(function(address
) {
1245 expect(address
).toBe("Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT");
1252 // Selecting a language with no existing phrase should generate a phrase in
1254 it('Generate a random phrase when language is selected and no current phrase', function(done
) {
1255 driver
.findElement(By
.css("a[href='#japanese']"))
1257 driver
.sleep(generateDelay
).then(function() {
1258 driver
.findElement(By
.css(".phrase"))
1259 .getAttribute("value").then(function(phrase
) {
1260 expect(phrase
.search(/[a-z]/)).toBe(-1);
1261 expect(phrase
.length
).toBeGreaterThan(0);
1267 // Selecting a language with existing phrase should update the phrase to use
1269 it('Updates existing phrases when the language is changed', function(done
) {
1270 driver
.findElement(By
.css(".phrase"))
1271 .sendKeys("abandon abandon ability");
1272 driver
.sleep(generateDelay
).then(function() {
1273 driver
.findElement(By
.css("a[href='#italian']"))
1275 driver
.sleep(generateDelay
).then(function() {
1276 driver
.findElement(By
.css(".phrase"))
1277 .getAttribute("value").then(function(phrase
) {
1278 // Check only the language changes, not the phrase
1279 expect(phrase
).toBe("abaco abaco abbaglio");
1280 getFirstAddress(function(address
) {
1281 // Check the address is correct
1282 expect(address
).toBe("1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV");
1290 // Suggested replacement for erroneous word in non-English language
1291 it('Shows word suggestion for incorrect word in non-English language', function(done
) {
1292 driver
.findElement(By
.css('.phrase'))
1293 .sendKeys('abaco abaco zbbaglio');
1294 driver
.sleep(feedbackDelay
).then(function() {
1295 driver
.findElement(By
.css('.feedback'))
1297 .then(function(feedback
) {
1298 var msg
= "zbbaglio not in wordlist, did you mean abbaglio?";
1299 expect(feedback
).toBe(msg
);
1305 // Japanese word does not break across lines.
1307 // https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
1308 it('Does not break Japanese words across lines', function(done
) {
1309 driver
.findElement(By
.css('.phrase'))
1310 .getCssValue("word-break")
1311 .then(function(value
) {
1312 expect(value
).toBe("keep-all");
1317 // Language can be specified at page load using hash value in url
1318 it('Can set the language from the url hash', function(done
) {
1319 driver
.get(url
+ "#japanese").then(function() {
1320 driver
.findElement(By
.css('.generate')).click();
1321 driver
.sleep(generateDelay
).then(function() {
1322 driver
.findElement(By
.css(".phrase"))
1323 .getAttribute("value").then(function(phrase
) {
1324 expect(phrase
.search(/[a-z]/)).toBe(-1);
1325 expect(phrase
.length
).toBeGreaterThan(0);
1332 // Entropy can be entered by the user
1333 it('Allows entropy to be entered', function(done
) {
1334 driver
.findElement(By
.css('.use-entropy'))
1336 driver
.findElement(By
.css('.entropy'))
1337 .sendKeys('00000000 00000000 00000000 00000000');
1338 driver
.sleep(generateDelay
).then(function() {
1339 driver
.findElement(By
.css(".phrase"))
1340 .getAttribute("value").then(function(phrase
) {
1341 expect(phrase
).toBe("abandon abandon ability");
1342 getFirstAddress(function(address
) {
1343 expect(address
).toBe("1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug");
1350 // A warning about entropy is shown to the user, with additional information
1351 it('Shows a warning about using entropy', function(done
) {
1352 driver
.findElement(By
.css('.use-entropy'))
1354 driver
.findElement(By
.css('.entropy-container'))
1356 .then(function(containerText
) {
1357 var warning
= "mnemonic may be insecure";
1358 expect(containerText
).toContain(warning
);
1359 driver
.findElement(By
.css('#entropy-notes'))
1360 .findElement(By
.xpath("parent::*"))
1362 .then(function(notesText
) {
1363 var detail
= "flipping a fair coin, rolling a fair dice, noise measurements etc";
1364 expect(notesText
).toContain(detail
);
1370 // The types of entropy available are described to the user
1371 it('Shows the types of entropy available', function(done
) {
1372 driver
.findElement(By
.css('.entropy'))
1373 .getAttribute("placeholder")
1374 .then(function(placeholderText
) {
1383 for (var i
=0; i
<options
.length
; i
++) {
1384 var option
= options
[i
];
1385 expect(placeholderText
).toContain(option
);
1391 // The actual entropy used is shown to the user
1392 it('Shows the actual entropy used', function(done
) {
1393 driver
.findElement(By
.css('.use-entropy'))
1395 driver
.findElement(By
.css('.entropy'))
1396 .sendKeys('Not A Very Good Entropy Source At All');
1397 driver
.sleep(generateDelay
).then(function() {
1398 driver
.findElement(By
.css('.entropy-container'))
1400 .then(function(text
) {
1401 expect(text
).toMatch(/Filtered Entropy
\s
+AedEceAA
/);
1407 // Binary entropy can be entered
1408 it('Allows binary entropy to be entered', function(done
) {
1409 testEntropyType(done
, "01", "binary");
1412 // Base 6 entropy can be entered
1413 it('Allows base 6 entropy to be entered', function(done
) {
1414 testEntropyType(done
, "012345", "base 6");
1417 // Base 6 dice entropy can be entered
1418 it('Allows base 6 dice entropy to be entered', function(done
) {
1419 testEntropyType(done
, "123456", "base 6 (dice)");
1422 // Base 10 entropy can be entered
1423 it('Allows base 10 entropy to be entered', function(done
) {
1424 testEntropyType(done
, "789", "base 10");
1427 // Hexadecimal entropy can be entered
1428 it('Allows hexadecimal entropy to be entered', function(done
) {
1429 testEntropyType(done
, "abcdef", "hexadecimal");
1432 // Dice entropy value is shown as the converted base 6 value
1433 // ie 123456 is converted to 123450
1434 it('Shows dice entropy as base 6', function(done
) {
1435 driver
.findElement(By
.css('.use-entropy'))
1437 driver
.findElement(By
.css('.entropy'))
1438 .sendKeys("123456");
1439 driver
.sleep(generateDelay
).then(function() {
1440 driver
.findElement(By
.css('.entropy-container'))
1442 .then(function(text
) {
1443 expect(text
).toMatch(/Filtered Entropy
\s
+123450/);
1449 // The number of bits of entropy accumulated is shown
1450 it("Shows the number of bits of entropy for 20 bits of binary", function(done
) {
1451 testEntropyBits(done
, "0000 0000 0000 0000 0000", "20");
1453 it("Shows the number of bits of entropy for 1 bit of binary", function(done
) {
1454 testEntropyBits(done
, "0", "1");
1456 it("Shows the number of bits of entropy for 4 bits of binary", function(done
) {
1457 testEntropyBits(done
, "0000", "4");
1459 it("Shows the number of bits of entropy for 1 character of base 6 (dice)", function(done
) {
1460 // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
1461 testEntropyBits(done
, "6", "2");
1463 it("Shows the number of bits of entropy for 1 character of base 10 with 3 bits", function(done
) {
1464 // 7 in base 10 is 111 in base 2, no leading zeros
1465 testEntropyBits(done
, "7", "3");
1467 it("Shows the number of bits of entropy for 1 character of base 10 with 4 bis", function(done
) {
1468 testEntropyBits(done
, "8", "4");
1470 it("Shows the number of bits of entropy for 1 character of hex", function(done
) {
1471 testEntropyBits(done
, "F", "4");
1473 it("Shows the number of bits of entropy for 2 characters of base 10", function(done
) {
1474 testEntropyBits(done
, "29", "6");
1476 it("Shows the number of bits of entropy for 2 characters of hex", function(done
) {
1477 testEntropyBits(done
, "0A", "8");
1479 it("Shows the number of bits of entropy for 2 characters of hex with 3 leading zeros", function(done
) {
1480 // hex is always multiple of 4 bits of entropy
1481 testEntropyBits(done
, "1A", "8");
1483 it("Shows the number of bits of entropy for 2 characters of hex with 2 leading zeros", function(done
) {
1484 testEntropyBits(done
, "2A", "8");
1486 it("Shows the number of bits of entropy for 2 characters of hex with 1 leading zero", function(done
) {
1487 testEntropyBits(done
, "4A", "8");
1489 it("Shows the number of bits of entropy for 2 characters of hex with no leading zeros", function(done
) {
1490 testEntropyBits(done
, "8A", "8");
1492 it("Shows the number of bits of entropy for 2 characters of hex starting with F", function(done
) {
1493 testEntropyBits(done
, "FA", "8");
1495 it("Shows the number of bits of entropy for 4 characters of hex with leading zeros", function(done
) {
1496 testEntropyBits(done
, "000A", "16");
1498 it("Shows the number of bits of entropy for 4 characters of base 6", function(done
) {
1499 testEntropyBits(done
, "5555", "11");
1501 it("Shows the number of bits of entropy for 4 characters of base 6 dice", function(done
) {
1502 // uses dice, so entropy is actually 0000 in base 6, which is 4 lots of
1503 // 2.58 bits, which is 10.32 bits (rounded down to 10 bits)
1504 testEntropyBits(done
, "6666", "10");
1506 it("Shows the number of bits of entropy for 4 charactes of base 10", function(done
) {
1507 // Uses base 10, which is 4 lots of 3.32 bits, which is 13.3 bits (rounded
1509 testEntropyBits(done
, "2227", "13");
1511 it("Shows the number of bits of entropy for 4 characters of hex with 2 leading zeros", function(done
) {
1512 testEntropyBits(done
, "222F", "16");
1514 it("Shows the number of bits of entropy for 4 characters of hex starting with F", function(done
) {
1515 testEntropyBits(done
, "FFFF", "16");
1517 it("Shows the number of bits of entropy for 10 characters of base 10", function(done
) {
1518 // 10 events at 3.32 bits per event
1519 testEntropyBits(done
, "0000101017", "33");
1521 it("Shows the number of bits of entropy for a full deck of cards", function(done
) {
1522 // cards are not replaced, so a full deck is not 52^52 entropy which is 296
1523 // bits, it's 52!, which is 225 bits
1524 testEntropyBits(done
, "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", "225");
1527 it("Shows details about the entered entropy", function(done
) {
1528 testEntropyFeedback(done
,
1532 type: "hexadecimal",
1536 strength: "less than a second",
1540 it("Shows details about the entered entropy", function(done
) {
1541 testEntropyFeedback(done
,
1543 entropy: "AAAAAAAA",
1544 filtered: "AAAAAAAA",
1545 type: "hexadecimal",
1549 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
1553 it("Shows details about the entered entropy", function(done
) {
1554 testEntropyFeedback(done
,
1556 entropy: "AAAAAAAA B",
1557 filtered: "AAAAAAAAB",
1558 type: "hexadecimal",
1562 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
1566 it("Shows details about the entered entropy", function(done
) {
1567 testEntropyFeedback(done
,
1569 entropy: "AAAAAAAA BBBBBBBB",
1570 filtered: "AAAAAAAABBBBBBBB",
1571 type: "hexadecimal",
1575 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
1579 it("Shows details about the entered entropy", function(done
) {
1580 testEntropyFeedback(done
,
1582 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
1583 filtered: "AAAAAAAABBBBBBBBCCCCCCCC",
1584 type: "hexadecimal",
1588 strength: "less than a second",
1592 it("Shows details about the entered entropy", function(done
) {
1593 testEntropyFeedback(done
,
1595 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
1596 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD",
1597 type: "hexadecimal",
1601 strength: "2 minutes",
1605 it("Shows details about the entered entropy", function(done
) {
1606 testEntropyFeedback(done
,
1608 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA",
1609 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA",
1610 type: "hexadecimal",
1618 it("Shows details about the entered entropy", function(done
) {
1619 testEntropyFeedback(done
,
1621 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE",
1622 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE",
1623 type: "hexadecimal",
1627 strength: "3 years",
1631 it("Shows details about the entered entropy", function(done
) {
1632 testEntropyFeedback(done
,
1634 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF",
1635 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF",
1636 type: "hexadecimal",
1640 strength: "centuries",
1644 it("Shows details about the entered entropy", function(done
) {
1645 testEntropyFeedback(done
,
1652 strength: "less than a second",
1656 it("Shows details about the entered entropy", function(done
) {
1657 testEntropyFeedback(done
,
1659 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1660 type: "card (full deck)",
1664 strength: "centuries",
1668 it("Shows details about the entered entropy", function(done
) {
1669 testEntropyFeedback(done
,
1671 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d",
1672 type: "card (full deck, 1 duplicate: 3d)",
1676 strength: "centuries",
1680 it("Shows details about the entered entropy", function(done
) {
1681 testEntropyFeedback(done
,
1683 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d",
1684 type: "card (2 duplicates: 3d 4d, 1 missing: KS)",
1688 strength: "centuries",
1692 it("Shows details about the entered entropy", function(done
) {
1693 testEntropyFeedback(done
,
1695 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d",
1696 type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)",
1700 strength: "centuries",
1704 it("Shows details about the entered entropy", function(done
) {
1705 testEntropyFeedback(done
,
1706 // Next test was throwing uncaught error in zxcvbn
1707 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
1709 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1710 type: "card (full deck, 52 duplicates: ac 2c 3c...)",
1714 strength: "centuries",
1718 it("Shows details about the entered entropy", function(done
) {
1719 testEntropyFeedback(done
,
1720 // Case insensitivity to duplicate cards
1723 type: "card (1 duplicate: AS)",
1727 strength: "less than a second",
1731 it("Shows details about the entered entropy", function(done
) {
1732 testEntropyFeedback(done
,
1735 type: "card (1 duplicate: as)",
1739 strength: "less than a second",
1743 it("Shows details about the entered entropy", function(done
) {
1744 testEntropyFeedback(done
,
1745 // Missing cards are detected
1747 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1748 type: "card (1 missing: 9C)",
1752 strength: "centuries",
1756 it("Shows details about the entered entropy", function(done
) {
1757 testEntropyFeedback(done
,
1759 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1760 type: "card (2 missing: 9C 5D)",
1764 strength: "centuries",
1768 it("Shows details about the entered entropy", function(done
) {
1769 testEntropyFeedback(done
,
1771 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
1772 type: "card (4 missing: 9C 5D QD...)",
1776 strength: "centuries",
1780 it("Shows details about the entered entropy", function(done
) {
1781 testEntropyFeedback(done
,
1782 // More than six missing cards does not show message
1784 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks",
1789 strength: "centuries",
1793 it("Shows details about the entered entropy", function(done
) {
1794 testEntropyFeedback(done
,
1795 // Multiple decks of cards increases bits per event
1800 bitsPerEvent: "4.34",
1804 it("Shows details about the entered entropy", function(done
) {
1805 testEntropyFeedback(done
,
1810 bitsPerEvent: "4.80",
1814 it("Shows details about the entered entropy", function(done
) {
1815 testEntropyFeedback(done
,
1820 bitsPerEvent: "5.01",
1824 it("Shows details about the entered entropy", function(done
) {
1825 testEntropyFeedback(done
,
1827 entropy: "3d3d3d3d",
1830 bitsPerEvent: "5.14",
1834 it("Shows details about the entered entropy", function(done
) {
1835 testEntropyFeedback(done
,
1837 entropy: "3d3d3d3d3d",
1840 bitsPerEvent: "5.22",
1844 it("Shows details about the entered entropy", function(done
) {
1845 testEntropyFeedback(done
,
1847 entropy: "3d3d3d3d3d3d",
1850 bitsPerEvent: "5.28",
1854 it("Shows details about the entered entropy", function(done
) {
1855 testEntropyFeedback(done
,
1857 entropy: "3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d",
1860 bitsPerEvent: "5.59",
1861 strength: 'less than a second - Repeats like "abcabcabc" are only slightly harder to guess than "abc"',
1866 // Entropy is truncated from the left
1867 it('Truncates entropy from the left', function(done
) {
1868 // Truncate from left means 0000 is removed from the start
1869 // which gives mnemonic 'avocado zoo zone'
1870 // not 1111 removed from the end
1871 // which gives the mnemonic 'abstract zoo zoo'
1872 var entropy
= "00000000 00000000 00000000 00000000";
1873 entropy
+= "11111111 11111111 11111111 1111"; // Missing last byte
1874 driver
.findElement(By
.css('.use-entropy'))
1876 driver
.findElement(By
.css('.entropy'))
1878 driver
.sleep(generateDelay
).then(function() {
1879 driver
.findElement(By
.css(".phrase"))
1880 .getAttribute("value").then(function(phrase
) {
1881 expect(phrase
).toBe("avocado zoo zone");
1887 // Very large entropy results in very long mnemonics
1888 it('Converts very long entropy to very long mnemonics', function(done
) {
1890 for (var i
=0; i
<33; i
++) {
1891 entropy
+= "AAAAAAAA"; // 3 words * 33 iterations = 99 words
1893 driver
.findElement(By
.css('.use-entropy'))
1895 driver
.findElement(By
.css('.entropy'))
1897 driver
.sleep(generateDelay
).then(function() {
1898 driver
.findElement(By
.css(".phrase"))
1899 .getAttribute("value").then(function(phrase
) {
1900 var wordCount
= phrase
.split(/\s+/g).length
;
1901 expect(wordCount
).toBe(99);
1907 // Is compatible with bip32jp entropy
1908 // https://bip32jp.github.io/english/index.html
1910 // Is incompatible with:
1912 it('Is compatible with bip32jp.github.io', function(done
) {
1913 var entropy
= "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543";
1914 var expectedPhrase
= "train then jungle barely whip fiber purpose puppy eagle cloud clump hospital robot brave balcony utility detect estate old green desk skill multiply virus";
1915 driver
.findElement(By
.css('.use-entropy'))
1917 driver
.findElement(By
.css('.entropy'))
1919 driver
.sleep(generateDelay
).then(function() {
1920 driver
.findElement(By
.css(".phrase"))
1921 .getAttribute("value").then(function(phrase
) {
1922 expect(phrase
).toBe(expectedPhrase
);
1928 // Blank entropy does not generate mnemonic or addresses
1929 it('Does not generate mnemonic for blank entropy', function(done
) {
1930 driver
.findElement(By
.css('.use-entropy'))
1932 driver
.findElement(By
.css('.entropy'))
1934 // check there is no mnemonic
1935 driver
.sleep(generateDelay
).then(function() {
1936 driver
.findElement(By
.css(".phrase"))
1937 .getAttribute("value").then(function(phrase
) {
1938 expect(phrase
).toBe("");
1939 // check there is no mnemonic
1940 driver
.findElements(By
.css(".address"))
1941 .then(function(addresses
) {
1942 expect(addresses
.length
).toBe(0);
1943 // Check the feedback says 'blank entropy'
1944 driver
.findElement(By
.css(".feedback"))
1946 .then(function(feedbackText
) {
1947 expect(feedbackText
).toBe("Blank entropy");
1955 // Mnemonic length can be selected even for weak entropy
1956 it('Allows selection of mnemonic length even for weak entropy', function(done
) {
1957 driver
.findElement(By
.css('.use-entropy'))
1959 driver
.executeScript(function() {
1960 $(".mnemonic-length").val("18").trigger("change");
1962 driver
.findElement(By
.css('.entropy'))
1963 .sendKeys("012345");
1964 driver
.sleep(generateDelay
).then(function() {
1965 driver
.findElement(By
.css(".phrase"))
1966 .getAttribute("value").then(function(phrase
) {
1967 var wordCount
= phrase
.split(/\s+/g).length
;
1968 expect(wordCount
).toBe(18);
1975 // https://github.com/iancoleman/bip39/issues/33
1976 // Final cards should contribute entropy
1977 it('Uses as much entropy as possible for the mnemonic', function(done
) {
1978 driver
.findElement(By
.css('.use-entropy'))
1980 driver
.findElement(By
.css('.entropy'))
1981 .sendKeys("7S 9H 9S QH 8C KS AS 7D 7C QD 4S 4D TC 2D 5S JS 3D 8S 8H 4C 3C AC 3S QC 9C JC 7H AD TD JD 6D KH 5C QS 2S 6S 6H JH KD 9D-6C TS TH 4H KC 5H 2H AH 2C 8D 3H 5D");
1982 driver
.sleep(generateDelay
).then(function() {
1984 driver
.findElement(By
.css(".phrase"))
1985 .getAttribute("value").then(function(originalPhrase
) {
1986 // Set the last 12 cards to be AS
1987 driver
.findElement(By
.css('.entropy'))
1989 driver
.findElement(By
.css('.entropy'))
1990 .sendKeys("7S 9H 9S QH 8C KS AS 7D 7C QD 4S 4D TC 2D 5S JS 3D 8S 8H 4C 3C AC 3S QC 9C JC 7H AD TD JD 6D KH 5C QS 2S 6S 6H JH KD 9D-AS AS AS AS AS AS AS AS AS AS AS AS");
1991 driver
.sleep(generateDelay
).then(function() {
1993 driver
.findElement(By
.css(".phrase"))
1994 .getAttribute("value").then(function(newPhrase
) {
1995 expect(originalPhrase
).not
.toEqual(newPhrase
);
2004 // https://github.com/iancoleman/bip39/issues/35
2006 // TODO this doesn't work in selenium with firefox
2007 // see https://stackoverflow.com/q/40360223
2008 it('Shows a qr code on hover for the phrase', function(done
) {
2009 if (browser
== "firefox") {
2010 pending("Selenium + Firefox bug for mouseMove, see https://stackoverflow.com/q/40360223");
2012 // generate a random mnemonic
2013 var generateEl
= driver
.findElement(By
.css('.generate'));
2015 // toggle qr to show (hidden by default)
2016 var phraseEl
= driver
.findElement(By
.css(".phrase"));
2018 var rootKeyEl
= driver
.findElement(By
.css(".root-key"));
2019 driver
.sleep(generateDelay
).then(function() {
2020 // hover over the root key
2021 driver
.actions().mouseMove(rootKeyEl
).perform().then(function() {
2022 // check the qr code shows
2023 driver
.executeScript(function() {
2024 return $(".qr-container").find("canvas").length
> 0;
2026 .then(function(qrShowing
) {
2027 expect(qrShowing
).toBe(true);
2028 // hover away from the phrase
2029 driver
.actions().mouseMove(generateEl
).perform().then(function() {;
2030 // check the qr code hides
2031 driver
.executeScript(function() {
2032 return $(".qr-container").find("canvas").length
== 0;
2034 .then(function(qrHidden
) {
2035 expect(qrHidden
).toBe(true);
2044 // BIP44 account extendend private key is shown
2045 // github issue 37 - compatibility with electrum
2046 it('Shows the bip44 account extended private key', function(done
) {
2047 driver
.findElement(By
.css(".phrase"))
2048 .sendKeys("abandon abandon ability");
2049 driver
.sleep(generateDelay
).then(function() {
2050 driver
.findElement(By
.css("#bip44 .account-xprv"))
2051 .getAttribute("value")
2052 .then(function(xprv
) {
2053 expect(xprv
).toBe("xprv9yzrnt4zWVJUr1k2VxSPy9ettKz5PpeDMgaVG7UKedhqnw1tDkxP2UyYNhuNSumk2sLE5ctwKZs9vwjsq3e1vo9egCK6CzP87H2cVYXpfwQ");
2059 // BIP44 account extendend public key is shown
2060 // github issue 37 - compatibility with electrum
2061 it('Shows the bip44 account extended public key', function(done
) {
2062 driver
.findElement(By
.css(".phrase"))
2063 .sendKeys("abandon abandon ability");
2064 driver
.sleep(generateDelay
).then(function() {
2065 driver
.findElement(By
.css("#bip44 .account-xpub"))
2066 .getAttribute("value")
2067 .then(function(xprv
) {
2068 expect(xprv
).toBe("xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf");
2075 // BIP32 root key can be set as an xpub
2076 it('Generates addresses from xpub as bip32 root key', function(done
) {
2077 driver
.findElement(By
.css('#bip32-tab a'))
2079 // set xpub for account 0 of bip44 for 'abandon abandon ability'
2080 driver
.findElement(By
.css("#root-key"))
2081 .sendKeys("xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf");
2082 driver
.sleep(generateDelay
).then(function() {
2083 // check the addresses are generated
2084 getFirstAddress(function(address
) {
2085 expect(address
).toBe("1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug");
2086 // check the xprv key is not set
2087 driver
.findElement(By
.css(".extended-priv-key"))
2088 .getAttribute("value")
2089 .then(function(xprv
) {
2090 expect(xprv
).toBe("NA");
2091 // check the private key is not set
2092 driver
.findElements(By
.css(".privkey"))
2093 .then(function(els
) {
2096 .then(function(privkey
) {
2097 expect(xprv
).toBe("NA");
2107 // xpub for bip32 root key will not work with hardened derivation paths
2108 it('Shows error for hardened derivation paths with xpub root key', function(done
) {
2109 // set xpub for account 0 of bip44 for 'abandon abandon ability'
2110 driver
.findElement(By
.css("#root-key"))
2111 .sendKeys("xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf");
2112 driver
.sleep(feedbackDelay
).then(function() {
2113 // Check feedback is correct
2114 driver
.findElement(By
.css('.feedback'))
2116 .then(function(feedback
) {
2117 var msg
= "Hardened derivation path is invalid with xpub key";
2118 expect(feedback
).toBe(msg
);
2119 // Check no addresses are shown
2120 driver
.findElements(By
.css('.addresses tr'))
2121 .then(function(rows
) {
2122 expect(rows
.length
).toBe(0);
2130 // no root key shows feedback
2131 it('Shows feedback for no root key', function(done
) {
2132 // set xpub for account 0 of bip44 for 'abandon abandon ability'
2133 driver
.findElement(By
.css('#bip32-tab a'))
2135 driver
.sleep(feedbackDelay
).then(function() {
2136 // Check feedback is correct
2137 driver
.findElement(By
.css('.feedback'))
2139 .then(function(feedback
) {
2140 expect(feedback
).toBe("Invalid root key");
2147 // display error switching tabs while addresses are generating
2148 it('Can change details while old addresses are still being generated', function(done
) {
2149 // Set to generate 199 more addresses.
2150 // This will take a long time allowing a new set of addresses to be
2151 // generated midway through this lot.
2152 // The newly generated addresses should not include any from the old set.
2153 // Any more than 199 will show an alert which needs to be accepted.
2154 driver
.findElement(By
.css('.rows-to-add'))
2156 driver
.findElement(By
.css('.rows-to-add'))
2159 driver
.findElement(By
.css('.phrase'))
2160 .sendKeys("abandon abandon ability");
2161 driver
.sleep(generateDelay
).then(function() {
2162 // generate more addresses
2163 driver
.findElement(By
.css('.more'))
2165 // change tabs which should cancel the previous generating
2166 driver
.findElement(By
.css('#bip32-tab a'))
2168 driver
.sleep(generateDelay
).then(function() {
2169 driver
.findElements(By
.css('.index'))
2170 .then(function(els
) {
2171 // check the derivation paths have the right quantity
2172 expect(els
.length
).toBe(20);
2173 // check the derivation paths are in order
2174 testRowsAreInCorrectOrder(done
);
2181 // padding for binary should give length with multiple of 256
2182 // hashed entropy 1111 is length 252, so requires 4 leading zeros
2183 // prior to issue 49 it would only generate 2 leading zeros, ie missing 2
2184 it('Pads hashed entropy with leading zeros', function(done
) {
2185 driver
.findElement(By
.css('.use-entropy'))
2187 driver
.executeScript(function() {
2188 $(".mnemonic-length").val("15").trigger("change");
2190 driver
.findElement(By
.css('.entropy'))
2192 driver
.sleep(generateDelay
).then(function() {
2193 driver
.findElement(By
.css('.phrase'))
2194 .getAttribute("value")
2195 .then(function(phrase
) {
2196 expect(phrase
).toBe("avocado valid quantum cross link predict excuse edit street able flame large galaxy ginger nuclear");
2202 // Github pull request 55
2203 // https://github.com/iancoleman/bip39/pull/55
2205 it('Can set the derivation path on bip32 tab for bitcoincore', function(done
) {
2206 testClientSelect(done
, {
2208 bip32path: "m/0'/0'",
2209 useHardenedAddresses: "true",
2212 it('Can set the derivation path on bip32 tab for multibit', function(done
) {
2213 testClientSelect(done
, {
2215 bip32path: "m/0'/0",
2216 useHardenedAddresses: null,
2221 // https://github.com/iancoleman/bip39/issues/58
2222 // bip32 derivation is correct, does not drop leading zeros
2224 // https://medium.com/@alexberegszaszi/why-do-my-bip32-wallets-disagree-6f3254cc5846
2225 it('Retains leading zeros for bip32 derivation', function(done
) {
2226 driver
.findElement(By
.css(".phrase"))
2227 .sendKeys("fruit wave dwarf banana earth journey tattoo true farm silk olive fence");
2228 driver
.findElement(By
.css(".passphrase"))
2229 .sendKeys("banana");
2230 driver
.sleep(generateDelay
).then(function() {
2231 getFirstAddress(function(address
) {
2232 // Note that bitcore generates an incorrect address
2233 // 13EuKhffWkBE2KUwcbkbELZb1MpzbimJ3Y
2234 // see the medium.com link above for more details
2235 expect(address
).toBe("17rxURoF96VhmkcEGCj5LNQkmN9HVhWb7F");
2242 // Japanese mnemonics generate incorrect bip32 seed
2243 // BIP39 seed is set from phrase
2244 it('Generates correct seed for Japanese mnemonics', function(done
) {
2245 driver
.findElement(By
.css(".phrase"))
2246 .sendKeys("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら");
2247 driver
.findElement(By
.css(".passphrase"))
2248 .sendKeys("メートルガバヴァぱばぐゞちぢ十人十色");
2249 driver
.sleep(generateDelay
).then(function() {
2250 driver
.findElement(By
.css(".seed"))
2251 .getAttribute("value")
2252 .then(function(seed
) {
2253 expect(seed
).toBe("a262d6fb6122ecf45be09c50492b31f92e9beb7d9a845987a02cefda57a15f9c467a17872029a9e92299b5cbdf306e3a0ee620245cbd508959b6cb7ca637bd55");
2259 // BIP49 official test vectors
2260 // https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki#test-vectors
2261 it('Generates BIP49 addresses matching the official test vectors', function(done
) {
2262 driver
.findElement(By
.css('#bip49-tab a'))
2264 selectNetwork("BTC - Bitcoin Testnet");
2265 driver
.findElement(By
.css(".phrase"))
2266 .sendKeys("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about");
2267 driver
.sleep(generateDelay
).then(function() {
2268 getFirstAddress(function(address
) {
2269 expect(address
).toBe("2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2");
2275 // BIP49 derivation path is shown
2276 it('Shows the bip49 derivation path', function(done
) {
2277 driver
.findElement(By
.css('#bip49-tab a'))
2279 driver
.findElement(By
.css(".phrase"))
2280 .sendKeys("abandon abandon ability");
2281 driver
.sleep(generateDelay
).then(function() {
2282 driver
.findElement(By
.css('#bip49 .path'))
2283 .getAttribute("value")
2284 .then(function(path
) {
2285 expect(path
).toBe("m/49'/0'/0'/0");
2291 // BIP49 extended private key is shown
2292 it('Shows the bip49 extended private key', function(done
) {
2293 driver
.findElement(By
.css('#bip49-tab a'))
2295 driver
.findElement(By
.css(".phrase"))
2296 .sendKeys("abandon abandon ability");
2297 driver
.sleep(generateDelay
).then(function() {
2298 driver
.findElement(By
.css('.extended-priv-key'))
2299 .getAttribute("value")
2300 .then(function(xprv
) {
2301 expect(xprv
).toBe("yprvALYB4DYRG6CzzVgzQZwwqjAA2wjBGC3iEd7KYYScpoDdmf75qMRWZWxoFcRXBJjgEXdFqJ9vDRGRLJQsrL22Su5jMbNFeM9vetaGVqy9Qy2");
2307 // BIP49 extended public key is shown
2308 it('Shows the bip49 extended public key', function(done
) {
2309 driver
.findElement(By
.css('#bip49-tab a'))
2311 driver
.findElement(By
.css(".phrase"))
2312 .sendKeys("abandon abandon ability");
2313 driver
.sleep(generateDelay
).then(function() {
2314 driver
.findElement(By
.css('.extended-pub-key'))
2315 .getAttribute("value")
2316 .then(function(xprv
) {
2317 expect(xprv
).toBe("ypub6ZXXTj5K6TmJCymTWbUxCs6tayZffemZbr2vLvrEP8kceTSENtjm7KHH6thvAKxVar9fGe8rgsPEX369zURLZ68b4f7Vexz7RuXsjQ69YDt");
2323 // BIP49 account field changes address list
2324 it('Can set the bip49 account field', function(done
) {
2325 driver
.findElement(By
.css('#bip49-tab a'))
2327 driver
.findElement(By
.css('#bip49 .account'))
2329 driver
.findElement(By
.css('#bip49 .account'))
2331 driver
.findElement(By
.css(".phrase"))
2332 .sendKeys("abandon abandon ability");
2333 driver
.sleep(generateDelay
).then(function() {
2334 getFirstAddress(function(address
) {
2335 expect(address
).toBe("381wg1GGN4rP88rNC9v7QWsiww63yLVPsn");
2341 // BIP49 change field changes address list
2342 it('Can set the bip49 change field', function(done
) {
2343 driver
.findElement(By
.css('#bip49-tab a'))
2345 driver
.findElement(By
.css('#bip49 .change'))
2347 driver
.findElement(By
.css('#bip49 .change'))
2349 driver
.findElement(By
.css(".phrase"))
2350 .sendKeys("abandon abandon ability");
2351 driver
.sleep(generateDelay
).then(function() {
2352 getFirstAddress(function(address
) {
2353 expect(address
).toBe("3PEM7MiKed5konBoN66PQhK8r3hjGhy9dT");
2359 // BIP49 account extendend private key is shown
2360 it('Shows the bip49 account extended private key', function(done
) {
2361 driver
.findElement(By
.css('#bip49-tab a'))
2363 driver
.findElement(By
.css(".phrase"))
2364 .sendKeys("abandon abandon ability");
2365 driver
.sleep(generateDelay
).then(function() {
2366 driver
.findElement(By
.css('#bip49 .account-xprv'))
2367 .getAttribute("value")
2368 .then(function(xprv
) {
2369 expect(xprv
).toBe("yprvAHtB1M5Wp675aLzFy9TJYK2mSsLkg6mcBRh5DZTR7L4EnYSmYPaL63KFA4ycg1PngW5LfkmejxzosCs17TKZMpRFKc3z5SJar6QAKaFcaZL");
2375 // BIP49 account extendend public key is shown
2376 it('Shows the bip49 account extended public key', function(done
) {
2377 driver
.findElement(By
.css('#bip49-tab a'))
2379 driver
.findElement(By
.css(".phrase"))
2380 .sendKeys("abandon abandon ability");
2381 driver
.sleep(generateDelay
).then(function() {
2382 driver
.findElement(By
.css('#bip49 .account-xpub'))
2383 .getAttribute("value")
2384 .then(function(xprv
) {
2385 expect(xprv
).toBe("ypub6WsXQrcQeTfNnq4j5AzJuSyVzuBF5ZVTYecg1ws2ffbDfLmv5vtadqdj1NgR6C6gufMpMfJpHxvb6JEQKvETVNWCRanNedfJtnTchZiJtsL");
2391 // Test selecting coin where bip49 is unavailable (eg CLAM)
2392 it('Shows an error on bip49 tab for coins without bip49', function(done
) {
2393 driver
.findElement(By
.css('#bip49-tab a'))
2395 driver
.findElement(By
.css(".phrase"))
2396 .sendKeys("abandon abandon ability");
2397 driver
.sleep(generateDelay
).then(function() {
2398 selectNetwork("CLAM - Clams");
2399 // bip49 available is hidden
2400 driver
.findElement(By
.css('#bip49 .available'))
2401 .getAttribute("class")
2402 .then(function(classes
) {
2403 expect(classes
).toContain("hidden");
2404 // bip49 unavailable is shown
2405 driver
.findElement(By
.css('#bip49 .unavailable'))
2406 .getAttribute("class")
2407 .then(function(classes
) {
2408 expect(classes
).not
.toContain("hidden");
2409 // check there are no addresses shown
2410 driver
.findElements(By
.css('.addresses tr'))
2411 .then(function(rows
) {
2412 expect(rows
.length
).toBe(0);
2413 // check the derived private key is blank
2414 driver
.findElement(By
.css('.extended-priv-key'))
2415 .getAttribute("value")
2416 .then(function(xprv
) {
2417 expect(xprv
).toBe('');
2418 // check the derived public key is blank
2419 driver
.findElement(By
.css('.extended-pub-key'))
2420 .getAttribute("value")
2421 .then(function(xpub
) {
2422 expect(xpub
).toBe('');
2433 // Cleared mnemonic and root key still allows addresses to be generated
2434 // https://github.com/iancoleman/bip39/issues/43
2435 it('Clears old root keys from memory when mnemonic is cleared', function(done
) {
2437 driver
.findElement(By
.css(".phrase"))
2438 .sendKeys("abandon abandon ability");
2439 driver
.sleep(generateDelay
).then(function() {
2440 // clear the mnemonic and root key
2441 // using selenium .clear() doesn't seem to trigger the 'input' event
2442 // so clear it using keys instead
2443 driver
.findElement(By
.css('.phrase'))
2444 .sendKeys(Key
.CONTROL
,"a");
2445 driver
.findElement(By
.css('.phrase'))
2446 .sendKeys(Key
.DELETE
);
2447 driver
.findElement(By
.css('.root-key'))
2448 .sendKeys(Key
.CONTROL
,"a");
2449 driver
.findElement(By
.css('.root-key'))
2450 .sendKeys(Key
.DELETE
);
2451 driver
.sleep(generateDelay
).then(function() {
2452 // try to generate more addresses
2453 driver
.findElement(By
.css('.more'))
2455 driver
.sleep(generateDelay
).then(function() {
2456 driver
.findElements(By
.css(".addresses tr"))
2457 .then(function(els
) {
2458 // check there are no addresses shown
2459 expect(els
.length
).toBe(0);
2468 // error trying to generate addresses from xpub with hardened derivation
2469 it('Shows error for hardened addresses with xpub root key', function(done
) {
2470 driver
.findElement(By
.css('#bip32-tab a'))
2472 driver
.executeScript(function() {
2473 $(".hardened-addresses").prop("checked", true);
2475 // set xpub for account 0 of bip44 for 'abandon abandon ability'
2476 driver
.findElement(By
.css("#root-key"))
2477 .sendKeys("xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf");
2478 driver
.sleep(feedbackDelay
).then(function() {
2479 // Check feedback is correct
2480 driver
.findElement(By
.css('.feedback'))
2482 .then(function(feedback
) {
2483 var msg
= "Hardened derivation path is invalid with xpub key";
2484 expect(feedback
).toBe(msg
);
2490 // Litecoin uses ltub by default, and can optionally be set to xprv
2492 // https://github.com/iancoleman/bip39/issues/96
2493 // Issue with extended keys on Litecoin
2494 it('Uses ltub by default for litecoin, but can be set to xprv', function(done
) {
2495 driver
.findElement(By
.css('.phrase'))
2496 .sendKeys("abandon abandon ability");
2497 selectNetwork("LTC - Litecoin");
2498 driver
.sleep(generateDelay
).then(function() {
2499 // check the extended key is generated correctly
2500 driver
.findElement(By
.css('.root-key'))
2501 .getAttribute("value")
2502 .then(function(rootKey
) {
2503 expect(rootKey
).toBe("Ltpv71G8qDifUiNesiPqf6h5V6eQ8ic77oxQiYtawiACjBEx3sTXNR2HGDGnHETYxESjqkMLFBkKhWVq67ey1B2MKQXannUqNy1RZVHbmrEjnEU");
2504 // set litecoin to use ltub
2505 driver
.executeScript(function() {
2506 $(".litecoin-use-ltub").prop("checked", false);
2507 $(".litecoin-use-ltub").trigger("change");
2509 driver
.sleep(generateDelay
).then(function() {
2510 driver
.findElement(By
.css('.root-key'))
2511 .getAttribute("value")
2512 .then(function(rootKey
) {
2513 expect(rootKey
).toBe("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi");
2522 // https://github.com/iancoleman/bip39/issues/99#issuecomment-327094159
2523 // "warn me emphatically when they have detected invalid input" to the entropy field
2524 // A warning is shown when entropy is filtered and discarded
2525 it('Warns when entropy is filtered and discarded', function(done
) {
2526 driver
.findElement(By
.css('.use-entropy'))
2528 // set entropy to have no filtered content
2529 driver
.findElement(By
.css('.entropy'))
2530 .sendKeys("00000000 00000000 00000000 00000000");
2531 driver
.sleep(generateDelay
).then(function() {
2532 // check the filter warning does not show
2533 driver
.findElement(By
.css('.entropy-container .filter-warning'))
2534 .getAttribute("class")
2535 .then(function(classes
) {
2536 expect(classes
).toContain("hidden");
2537 // set entropy to have some filtered content
2538 driver
.findElement(By
.css('.entropy'))
2539 .sendKeys("10000000 zxcvbn 00000000 00000000 00000000");
2540 driver
.sleep(entropyFeedbackDelay
).then(function() {
2541 // check the filter warning shows
2542 driver
.findElement(By
.css('.entropy-container .filter-warning'))
2543 .getAttribute("class")
2544 .then(function(classes
) {
2545 expect(classes
).not
.toContain("hidden");
2553 // Bitcoin Cash address can be set to use bitpay format
2554 it('Can use bitpay format for bitcoin cash addresses', function(done
) {
2555 driver
.executeScript(function() {
2556 $(".use-bitpay-addresses").prop("checked", true);
2558 driver
.findElement(By
.css('.phrase'))
2559 .sendKeys("abandon abandon ability");
2560 selectNetwork("BCH - Bitcoin Cash");
2561 driver
.sleep(generateDelay
).then(function() {
2562 getFirstAddress(function(address
) {
2563 expect(address
).toBe("CZnpA9HPmvhuhLLPWJP8rNDpLUYXy1LXFk");
2569 // End of tests ported from old suit, so no more comments above each test now
2571 it('Can generate more addresses from a custom index', function(done
) {
2572 var expectedIndexes
= [
2573 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
2574 40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59
2576 driver
.findElement(By
.css('.phrase'))
2577 .sendKeys("abandon abandon ability");
2578 driver
.sleep(generateDelay
).then(function() {
2579 // Set start of next lot of rows to be from index 40
2580 // which means indexes 20-39 will not be in the table.
2581 driver
.findElement(By
.css('.more-rows-start-index'))
2583 driver
.findElement(By
.css('.more'))
2585 driver
.sleep(generateDelay
).then(function() {
2586 // Check actual indexes in the table match the expected pattern
2587 driver
.findElements(By
.css(".index"))
2588 .then(function(els
) {
2589 expect(els
.length
).toBe(expectedIndexes
.length
);
2590 var testRowAtIndex = function(i
) {
2591 if (i
>= expectedIndexes
.length
) {
2596 .then(function(actualPath
) {
2597 var noHardened
= actualPath
.replace(/'/g, "");
2598 var pathBits = noHardened.split("/")
2599 var lastBit = pathBits[pathBits.length-1];
2600 var actualIndex = parseInt(lastBit);
2601 var expectedIndex = expectedIndexes[i];
2602 expect(actualIndex).toBe(expectedIndex);
2603 testRowAtIndex(i+1);
2613 it('Can generate BIP141 addresses
with P2WPKH
-in-P2SH semanitcs
', function(done) {
2614 // Sourced from BIP49 official test specs
2615 driver.findElement(By.css('#bip141
-tab a
'))
2617 driver.findElement(By.css('.bip141
-path
'))
2619 driver.findElement(By.css('.bip141
-path
'))
2620 .sendKeys("m/49'/1'/0'/0");
2621 selectNetwork("BTC
- Bitcoin Testnet
");
2622 driver.findElement(By.css(".phrase
"))
2623 .sendKeys("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
");
2624 driver.sleep(generateDelay).then(function() {
2625 getFirstAddress(function(address) {
2626 expect(address).toBe("2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2
");
2632 it('Can generate BIP141 addresses with P2WPKH semanitcs', function(done) {
2633 // This result tested against bitcoinjs-lib test spec for segwit address
2634 // using the first private key of this mnemonic and default path m/0
2635 // https://github.com/bitcoinjs/bitcoinjs-lib/blob/9c8503cab0c6c30a95127042703bc18e8d28c76d/test/integration/addresses.js#L50
2636 // so whilst not directly comparable, substituting the private key produces
2637 // identical results between this tool and the bitcoinjs-lib test.
2638 // Private key generated is:
2639 // L3L8Nu9whawPBNLGtFqDhKut9DKKfG3CQoysupT7BimqVCZsLFNP
2640 driver.findElement(By.css('#bip141-tab a'))
2643 driver.executeScript(function() {
2644 $(".bip141
-semantics option
[selected
]").removeAttr("selected
");
2645 $(".bip141
-semantics option
").filter(function(i,e) {
2646 return $(e).html() == "P2WPKH
";
2647 }).prop("selected
", true);
2648 $(".bip141
-semantics
").trigger("change
");
2650 driver.findElement(By.css(".phrase
"))
2651 .sendKeys("abandon abandon ability
");
2652 driver.sleep(generateDelay).then(function() {
2653 getFirstAddress(function(address) {
2654 expect(address).toBe("bc1qfwu6a5a3evygrk8zvdxxvz4547lmpyx5vsfxe9
");
2660 it('Shows the entropy used by the PRNG when clicking generate', function(done) {
2661 driver.findElement(By.css('.generate')).click();
2662 driver.sleep(generateDelay).then(function() {
2663 driver.findElement(By.css('.entropy'))
2664 .getAttribute("value
")
2665 .then(function(entropy) {
2666 expect(entropy).not.toBe("");
2672 it('Shows the index of each word in the mnemonic', function(done) {
2673 driver.findElement(By.css('.phrase'))
2674 .sendKeys("abandon abandon ability
");
2675 driver.sleep(generateDelay).then(function() {
2676 driver.findElement(By.css('.use-entropy'))
2678 driver.findElement(By.css('.word-indexes'))
2680 .then(function(indexes) {
2681 expect(indexes).toBe("0, 0, 1");
2687 it('Shows the derivation path for bip84 tab', function(done) {
2688 driver.findElement(By.css('#bip84-tab a'))
2690 driver.findElement(By.css('.phrase'))
2691 .sendKeys('abandon abandon ability');
2692 driver.sleep(generateDelay).then(function() {
2693 driver.findElement(By.css('#bip84 .path'))
2694 .getAttribute("value
")
2695 .then(function(path) {
2696 expect(path).toBe("m
/84'/0'/0'/0");
2702 it('Shows the extended private key for bip84 tab', function(done) {
2703 driver.findElement(By.css('#bip84-tab a'))
2705 driver.findElement(By.css('.phrase'))
2706 .sendKeys('abandon abandon ability');
2707 driver.sleep(generateDelay).then(function() {
2708 driver.findElement(By.css('.extended-priv-key'))
2709 .getAttribute("value
")
2710 .then(function(path) {
2711 expect(path).toBe("zprvAev3RKrZ3QVKiUFCfdeMRen1BPDJgdNt1XpxiDy8acSs4kkAGTCvq7HeRYRNNpo8EtEjCFQBWavJwtCUR29y4TUCH4X5RXMcyq48uN8y9BP
");
2717 it('Shows the extended public key for bip84 tab', function(done) {
2718 driver.findElement(By.css('#bip84-tab a'))
2720 driver.findElement(By.css('.phrase'))
2721 .sendKeys('abandon abandon ability');
2722 driver.sleep(generateDelay).then(function() {
2723 driver.findElement(By.css('.extended-pub-key'))
2724 .getAttribute("value
")
2725 .then(function(path) {
2726 expect(path).toBe("zpub6suPpqPSsn3cvxKfmfBMnnijjR3o666jNkkZWcNk8wyqwZ5JozXBNuc8Gs7DB3uLwTDvGVTspVEAUQcEjKF3pZHgywVbubdTqbXTUg7usyx
");
2732 it('Changes the address list if bip84 account is changed', function(done) {
2733 driver.findElement(By.css('#bip84-tab a'))
2735 driver.findElement(By.css('#bip84 .account'))
2737 driver.findElement(By.css('.phrase'))
2738 .sendKeys('abandon abandon ability');
2739 driver.sleep(generateDelay).then(function() {
2740 getFirstAddress(function(address) {
2741 expect(address).toBe("bc1qp7vv669t2fy965jdzvqwrraana89ctd5ewc662
");
2747 it('Changes the address list if bip84 change is changed', function(done) {
2748 driver.findElement(By.css('#bip84-tab a'))
2750 driver.findElement(By.css('#bip84 .change'))
2752 driver.findElement(By.css('.phrase'))
2753 .sendKeys('abandon abandon ability');
2754 driver.sleep(generateDelay).then(function() {
2755 getFirstAddress(function(address) {
2756 expect(address).toBe("bc1qr39vj6rh06ff05m53uxq8uazehwhccswylhrs2
");
2762 it('Passes the official BIP84 test spec for rootpriv', function(done) {
2763 driver.findElement(By.css('#bip84-tab a'))
2765 driver.findElement(By.css('.phrase'))
2766 .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about');
2767 driver.sleep(generateDelay).then(function() {
2768 driver.findElement(By.css(".root
-key
"))
2769 .getAttribute("value
")
2770 .then(function(rootKey) {
2771 expect(rootKey).toBe("zprvAWgYBBk7JR8Gjrh4UJQ2uJdG1r3WNRRfURiABBE3RvMXYSrRJL62XuezvGdPvG6GFBZduosCc1YP5wixPox7zhZLfiUm8aunE96BBa4Kei5
");
2777 it('Passes the official BIP84 test spec for account 0 xprv', function(done) {
2778 driver.findElement(By.css('#bip84-tab a'))
2780 driver.findElement(By.css('.phrase'))
2781 .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about');
2782 driver.sleep(generateDelay).then(function() {
2783 driver.findElement(By.css("#bip84
.account
-xprv
"))
2784 .getAttribute("value
")
2785 .then(function(rootKey) {
2786 expect(rootKey).toBe("zprvAdG4iTXWBoARxkkzNpNh8r6Qag3irQB8PzEMkAFeTRXxHpbF9z4QgEvBRmfvqWvGp42t42nvgGpNgYSJA9iefm1yYNZKEm7z6qUWCroSQnE
");
2792 it('Passes the official BIP84 test spec for account 0 xpub', function(done) {
2793 driver.findElement(By.css('#bip84-tab a'))
2795 driver.findElement(By.css('.phrase'))
2796 .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about');
2797 driver.sleep(generateDelay).then(function() {
2798 driver.findElement(By.css("#bip84
.account
-xpub
"))
2799 .getAttribute("value
")
2800 .then(function(rootKey) {
2801 expect(rootKey).toBe("zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs
");
2807 it('Passes the official BIP84 test spec for account 0 first address', function(done) {
2808 driver.findElement(By.css('#bip84-tab a'))
2810 driver.findElement(By.css('.phrase'))
2811 .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about');
2812 driver.sleep(generateDelay).then(function() {
2813 getFirstAddress(function(address) {
2814 expect(address).toBe("bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu
");
2820 it('Passes the official BIP84 test spec for account 0 first change address', function(done) {
2821 driver.findElement(By.css('#bip84-tab a'))
2823 driver.findElement(By.css('.phrase'))
2824 .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about');
2825 driver.findElement(By.css('#bip84 .change'))
2827 driver.sleep(generateDelay).then(function() {
2828 getFirstAddress(function(address) {
2829 expect(address).toBe("bc1q8c6fshw2dlwun7ekn9qwf37cu2rn755upcp6el
");
2835 it('Can display the table as csv', function(done) {
2836 var headings = "path
,address
,public key
,private key
";
2837 var row1 = "m
/44'/0'/0'/0/0,1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug
,033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3
,L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE
";
2838 var row20 = "m
/44'/0'/0'/0/19,1KhBy28XLAciXnnRvm71PvQJaETyrxGV55
,02b4b3e396434d8cdd20c03ac4aaa07387784d5d867b75987f516f5705ee68cb3a
,L4GrDrjReMsCAu5DkLXn79jSb95qR7Zfx7eshybCQZ1qL32MXJab
";
2839 driver.findElement(By.css('.phrase'))
2840 .sendKeys('abandon abandon ability');
2841 driver.sleep(generateDelay).then(function() {
2842 driver.findElement(By.css('.csv'))
2843 .getAttribute("value
")
2844 .then(function(csv) {
2845 expect(csv).toContain(headings);
2846 expect(csv).toContain(row1);
2847 expect(csv).toContain(row20);
2853 it('LeftPads ethereum keys that are less than 32 bytes', function(done) {
2854 // see https://github.com/iancoleman/bip39/issues/155
2855 selectNetwork("ETH
- Ethereum
");
2856 driver.findElement(By.css('#bip32-tab a'))
2858 driver.findElement(By.css('#bip32-path'))
2860 driver.findElement(By.css('#bip32-path'))
2861 .sendKeys("m
/44'/60'/0'");
2862 driver.findElement(By.css('.phrase'))
2863 .sendKeys('scout sort custom elite radar rare vivid thing trophy gesture cover snake change narrow kite list nation sustain buffalo erode open balance system young');
2864 driver.sleep(generateDelay).then(function() {
2865 driver.findElement(By.css(".address
"))
2867 .then(function(address) {
2868 expect(address).toBe("0x8943E785B4a5714FC87a3aFAad1eB1FeB602B118");
2871 //getFirstAddress(function(address) {
2872 // expect(address).toBe("0x8943E785B4a5714FC87a3aFAad1eB1FeB602B118");