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