// Usage:
// $ phantomjs tests.js
var page = require('webpage').create();
var url = 'src/index.html';
var testMaxTime = 5000;
page.onResourceError = function(e) {
console.log("Error loading " + e.url);
phantom.exit();
}
function fail() {
console.log("Failed");
phantom.exit();
}
function waitForGenerate(fn, maxTime) {
if (!maxTime) {
maxTime = testMaxTime;
}
var start = new Date().getTime();
var prevAddressCount = -1;
var wait = function keepWaiting() {
var now = new Date().getTime();
var hasTimedOut = now - start > maxTime;
var addressCount = page.evaluate(function() {
return $(".address").length;
});
var hasFinished = addressCount > 0 && addressCount == prevAddressCount;
prevAddressCount = addressCount;
if (hasFinished) {
fn();
}
else if (hasTimedOut) {
console.log("Test timed out");
fn();
}
else {
setTimeout(keepWaiting, 100);
}
}
wait();
}
function waitForFeedback(fn, maxTime) {
if (!maxTime) {
maxTime = testMaxTime;
}
var start = new Date().getTime();
var wait = function keepWaiting() {
var now = new Date().getTime();
var hasTimedOut = now - start > maxTime;
if (hasTimedOut) {
console.log("Test timed out");
fn();
return;
}
var feedback = page.evaluate(function() {
var feedback = $(".feedback");
if (feedback.css("display") == "none") {
return "";
}
return feedback.text();
});
var hasFinished = feedback.length > 0 && feedback != "Calculating...";
if (hasFinished) {
fn();
}
else {
setTimeout(keepWaiting, 100);
}
}
wait();
}
function next() {
if (tests.length > 0) {
var testsStr = tests.length == 1 ? "test" : "tests";
console.log(tests.length + " " + testsStr + " remaining");
tests.shift()();
}
else {
console.log("Finished with 0 failures");
phantom.exit();
}
}
/**
* Randomize array element order in-place.
* Using Durstenfeld shuffle algorithm.
* See http://stackoverflow.com/a/12646864
*/
function shuffle(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
tests = [
// Page loads with status of 'success'
function() {
page.open(url, function(status) {
if (status != "success") {
console.log("Page did not load with status 'success'");
fail();
}
next();
});
},
// Page has text
function() {
page.open(url, function(status) {
var content = page.evaluate(function() {
return document.body.textContent.trim();
});
if (!content) {
console.log("Page does not have text");
fail();
}
next();
});
},
// Entering mnemonic generates addresses
function() {
page.open(url, function(status) {
// set the phrase
page.evaluate(function() {
$(".phrase").val("abandon abandon ability").trigger("input");
});
// get the address
waitForGenerate(function() {
var addressCount = page.evaluate(function() {
return $(".address").length;
});
if (addressCount != 20) {
console.log("Mnemonic did not generate addresses");
console.log("Expected: " + expected);
console.log("Got: " + actual);
fail();
}
next();
});
});
},
// Random button generates random mnemonic
function() {
page.open(url, function(status) {
// check initial phrase is empty
var phrase = page.evaluate(function() {
return $(".phrase").text();
});
if (phrase != "") {
console.log("Initial phrase is not blank");
fail();
}
// press the 'generate' button
page.evaluate(function() {
$(".generate").click();
});
// get the new phrase
waitForGenerate(function() {
var phrase = page.evaluate(function() {
return $(".phrase").val();
});
if (phrase.length <= 0) {
console.log("Phrase not generated by pressing button");
fail();
}
next();
});
});
},
// Mnemonic length can be customized
function() {
page.open(url, function(status) {
// set the length to 6
var expectedLength = "6";
page.evaluate(function() {
$(".strength option[selected]").removeAttr("selected");
$(".strength option[value=6]").prop("selected", true);
});
// press the 'generate' button
page.evaluate(function() {
$(".generate").click();
});
// check the new phrase is six words long
waitForGenerate(function() {
var actualLength = page.evaluate(function() {
var words = $(".phrase").val().split(" ");
return words.length;
});
if (actualLength != expectedLength) {
console.log("Phrase not generated with correct length");
console.log("Expected: " + expectedLength);
console.log("Actual: " + actualLength);
fail();
}
next();
});
});
},
// Passphrase can be set
function() {
page.open(url, function(status) {
// set the phrase and passphrase
var expected = "15pJzUWPGzR7avffV9nY5by4PSgSKG9rba";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".passphrase").val("secure_passphrase").trigger("input");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Passphrase results in wrong address");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Network can be set to bitcoin testnet
function() {
page.open(url, function(status) {
// set the phrase and coin
var expected = "mucaU5iiDaJDb69BHLeDv8JFfGiyg2nJKi";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
$(".network option[selected]").removeAttr("selected");
$(".network option[value=1]").prop("selected", true);
$(".network").trigger("change");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Bitcoin testnet address is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Network can be set to litecoin
function() {
page.open(url, function(status) {
// set the phrase and coin
var expected = "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
$(".network option[selected]").removeAttr("selected");
$(".network option[value=2]").prop("selected", true);
$(".network").trigger("change");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Litecoin address is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Network can be set to dogecoin
function() {
page.open(url, function(status) {
// set the phrase and coin
var expected = "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
$(".network option[selected]").removeAttr("selected");
$(".network option[value=3]").prop("selected", true);
$(".network").trigger("change");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Dogecoin address is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Network can be set to shadowcash
function() {
page.open(url, function(status) {
// set the phrase and coin
var expected = "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
$(".network option[selected]").removeAttr("selected");
$(".network option[value=4]").prop("selected", true);
$(".network").trigger("change");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Shadowcash address is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Network can be set to shadowcash testnet
function() {
page.open(url, function(status) {
// set the phrase and coin
var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
$(".network option[selected]").removeAttr("selected");
$(".network option[value=5]").prop("selected", true);
$(".network").trigger("change");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Shadowcash testnet address is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Network can be set to viacoin
function() {
page.open(url, function(status) {
// set the phrase and coin
var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
$(".network option[selected]").removeAttr("selected");
$(".network option[value=6]").prop("selected", true);
$(".network").trigger("change");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Viacoin address is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Network can be set to viacoin testnet
function() {
page.open(url, function(status) {
// set the phrase and coin
var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
$(".network option[selected]").removeAttr("selected");
$(".network option[value=7]").prop("selected", true);
$(".network").trigger("change");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Viacoin testnet address is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Network can be set to jumbucks
function() {
page.open(url, function(status) {
// set the phrase and coin
var expected = "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
$(".network option[selected]").removeAttr("selected");
$(".network option[value=8]").prop("selected", true);
$(".network").trigger("change");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Jumbucks address is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Network can be set to clam
function() {
page.open(url, function(status) {
// set the phrase and coin
var expected = "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
$(".network option[selected]").removeAttr("selected");
$(".network option[value=9]").prop("selected", true);
$(".network").trigger("change");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("CLAM address is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Network can be set to dash
function() {
page.open(url, function(status) {
// set the phrase and coin
var expected = "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
$(".network option[selected]").removeAttr("selected");
$(".network option[value=10]").prop("selected", true);
$(".network").trigger("change");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("DASH address is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Network can be set to namecoin
function() {
page.open(url, function(status) {
// set the phrase and coin
var expected = "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
$(".network option[selected]").removeAttr("selected");
$(".network option[value=11]").prop("selected", true);
$(".network").trigger("change");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Namecoin address is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Network can be set to peercoin
function() {
page.open(url, function(status) {
// set the phrase and coin
var expected = "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
$(".network option[selected]").removeAttr("selected");
$(".network option[value=12]").prop("selected", true);
$(".network").trigger("change");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Peercoin address is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// BIP39 seed is set from phrase
function() {
page.open(url, function(status) {
// set the phrase
var expected = "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".seed").val();
});
if (actual != expected) {
console.log("BIP39 seed is incorrectly generated from mnemonic");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// BIP32 root key is set from phrase
function() {
page.open(url, function(status) {
// set the phrase
var expected = "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".root-key").val();
});
if (actual != expected) {
console.log("Root key is incorrectly generated from mnemonic");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Tabs show correct addresses when changed
function() {
page.open(url, function(status) {
// set the phrase
var expected = "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
// change tabs
waitForGenerate(function() {
page.evaluate(function() {
$("#bip32-tab a").click();
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Clicking tab generates incorrect address");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
});
},
// BIP44 derivation path is shown
function() {
page.open(url, function(status) {
// set the phrase
var expected = "m/44'/0'/0'/0";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
// check the derivation path of the first address
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $("#bip44 .path").val();
});
if (actual != expected) {
console.log("BIP44 derivation path is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// BIP44 extended private key is shown
function() {
page.open(url, function(status) {
// set the phrase
var expected = "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
// check the BIP44 extended private key
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".extended-priv-key").val();
});
if (actual != expected) {
console.log("BIP44 extended private key is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// BIP44 extended public key is shown
function() {
page.open(url, function(status) {
// set the phrase
var expected = "xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
// check the BIP44 extended public key
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".extended-pub-key").val();
});
if (actual != expected) {
console.log("BIP44 extended public key is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// BIP44 purpose field changes address list
function() {
page.open(url, function(status) {
// set the phrase
var expected = "1JbDzRJ2cDT8aat2xwKd6Pb2zzavow5MhF";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
waitForGenerate(function() {
// change the bip44 purpose field to 45
page.evaluate(function() {
$("#bip44 .purpose").val("45");
$("#bip44 .purpose").trigger("input");
});
waitForGenerate(function() {
// check the address for the new derivation path
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("BIP44 purpose field generates incorrect address");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
});
},
// BIP44 coin field changes address list
function() {
page.open(url, function(status) {
// set the phrase
var expected = "1F6dB2djQYrxoyfZZmfr6D5voH8GkJTghk";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
waitForGenerate(function() {
// change the bip44 purpose field to 45
page.evaluate(function() {
$("#bip44 .coin").val("1");
$("#bip44 .coin").trigger("input");
});
waitForGenerate(function() {
// check the address for the new derivation path
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("BIP44 coin field generates incorrect address");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
});
},
// BIP44 account field changes address list
function() {
page.open(url, function(status) {
// set the phrase
var expected = "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
waitForGenerate(function() {
// change the bip44 purpose field to 45
page.evaluate(function() {
$("#bip44 .account").val("1");
$("#bip44 .account").trigger("input");
});
waitForGenerate(function() {
// check the address for the new derivation path
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("BIP44 account field generates incorrect address");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
});
},
// BIP44 change field changes address list
function() {
page.open(url, function(status) {
// set the phrase
var expected = "1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
waitForGenerate(function() {
// change the bip44 purpose field to 45
page.evaluate(function() {
$("#bip44 .change").val("1");
$("#bip44 .change").trigger("input");
});
waitForGenerate(function() {
// check the address for the new derivation path
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("BIP44 change field generates incorrect address");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
});
},
// BIP32 derivation path can be set
function() {
page.open(url, function(status) {
// set the phrase
var expected = "16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
// change tabs
waitForGenerate(function() {
page.evaluate(function() {
$("#bip32-tab a").click();
});
// set the derivation path to m/1
waitForGenerate(function() {
page.evaluate(function() {
$("#bip32 .path").val("m/1");
$("#bip32 .path").trigger("input");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Custom BIP32 path generates incorrect address");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
});
});
},
// BIP32 can use hardened derivation paths
function() {
page.open(url, function(status) {
// set the phrase
var expected = "14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
// change tabs
waitForGenerate(function() {
page.evaluate(function() {
$("#bip32-tab a").click();
});
// set the derivation path to m/0'
waitForGenerate(function() {
page.evaluate(function() {
$("#bip32 .path").val("m/0'");
$("#bip32 .path").trigger("input");
});
// check the address is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Hardened BIP32 path generates incorrect address");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
});
});
},
// BIP32 extended private key is shown
function() {
page.open(url, function(status) {
// set the phrase
var expected = "xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
// change tabs
waitForGenerate(function() {
page.evaluate(function() {
$("#bip32-tab a").click();
});
// check the extended private key is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".extended-priv-key").val();
});
if (actual != expected) {
console.log("BIP32 extended private key is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
});
},
// BIP32 extended public key is shown
function() {
page.open(url, function(status) {
// set the phrase
var expected = "xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
// change tabs
waitForGenerate(function() {
page.evaluate(function() {
$("#bip32-tab a").click();
});
// check the extended public key is generated correctly
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".extended-pub-key").val();
});
if (actual != expected) {
console.log("BIP32 extended public key is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
});
},
// Derivation path is shown in table
function() {
page.open(url, function(status) {
// set the phrase
var expected = "m/44'/0'/0'/0/0";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
// check for derivation path in table
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".index:first").text();
});
if (actual != expected) {
console.log("Derivation path shown incorrectly in table");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Derivation path for address can be hardened
function() {
page.open(url, function(status) {
// set the phrase
var expected = "18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd";
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
// change tabs
waitForGenerate(function() {
page.evaluate(function() {
$("#bip32-tab a").click();
});
waitForGenerate(function() {
// select the hardened addresses option
page.evaluate(function() {
$(".hardened-addresses").prop("checked", true);
$(".hardened-addresses").trigger("change");
});
waitForGenerate(function() {
// check the generated address is hardened
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Hardened address is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
});
});
},
// Derivation path visibility can be toggled
function() {
page.open(url, function(status) {
// set the phrase
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
waitForGenerate(function() {
// toggle path visibility
page.evaluate(function() {
$(".index-toggle").click();
});
// check the path is not visible
var isInvisible = page.evaluate(function() {
return $(".index:first span").hasClass("invisible");
});
if (!isInvisible) {
console.log("Toggled derivation path is visible");
fail();
}
next();
});
});
},
// Address is shown
function() {
page.open(url, function(status) {
var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
// set the phrase
page.evaluate(function() {
$(".phrase").val("abandon abandon ability").trigger("input");
});
// get the address
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Address is not shown");
console.log("Expected: " + expected);
console.log("Got: " + actual);
fail();
}
next();
});
});
},
// Addresses are shown in order of derivation path
function() {
page.open(url, function(status) {
// set the phrase
page.evaluate(function() {
$(".phrase").val("abandon abandon ability").trigger("input");
});
// get the derivation paths
waitForGenerate(function() {
var paths = page.evaluate(function() {
return $(".index").map(function(i, e) {
return $(e).text();
});
});
if (paths.length != 20) {
console.log("Total paths is less than expected: " + paths.length);
fail();
}
for (var i=0; i<paths.length; i++) {
var expected = "m/44'/0'/0'/0/" + i;
var actual = paths[i];
if (actual != expected) {
console.log("Path " + i + " is incorrect");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
}
next();
});
});
},
// Address visibility can be toggled
function() {
page.open(url, function(status) {
// set the phrase
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
waitForGenerate(function() {
// toggle address visibility
page.evaluate(function() {
$(".address-toggle").click();
});
// check the address is not visible
var isInvisible = page.evaluate(function() {
return $(".address:first span").hasClass("invisible");
});
if (!isInvisible) {
console.log("Toggled address is visible");
fail();
}
next();
});
});
},
// Private key is shown
function() {
page.open(url, function(status) {
var expected = "L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE";
// set the phrase
page.evaluate(function() {
$(".phrase").val("abandon abandon ability").trigger("input");
});
// get the address
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".privkey:first").text();
});
if (actual != expected) {
console.log("Private key is not shown");
console.log("Expected: " + expected);
console.log("Got: " + actual);
fail();
}
next();
});
});
},
// Private key visibility can be toggled
function() {
page.open(url, function(status) {
// set the phrase
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
waitForGenerate(function() {
// toggle private key visibility
page.evaluate(function() {
$(".private-key-toggle").click();
});
// check the private key is not visible
var isInvisible = page.evaluate(function() {
return $(".privkey:first span").hasClass("invisible");
});
if (!isInvisible) {
console.log("Toggled private key is visible");
fail();
}
next();
});
});
},
// More addresses can be generated
function() {
page.open(url, function(status) {
// set the phrase
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
waitForGenerate(function() {
// generate more addresses
page.evaluate(function() {
$(".more").click();
});
waitForGenerate(function() {
// check there are more addresses
var addressCount = page.evaluate(function() {
return $(".address").length;
});
if (addressCount != 40) {
console.log("More addresses cannot be generated");
fail();
}
next();
});
});
});
},
// A custom number of additional addresses can be generated
function() {
page.open(url, function(status) {
// set the phrase
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
waitForGenerate(function() {
// get the current number of addresses
var oldAddressCount = page.evaluate(function() {
return $(".address").length;
});
// set a custom number of additional addresses
page.evaluate(function() {
$(".rows-to-add").val(1);
});
// generate more addresses
page.evaluate(function() {
$(".more").click();
});
waitForGenerate(function() {
// check there are the correct number of addresses
var newAddressCount = page.evaluate(function() {
return $(".address").length;
});
if (newAddressCount - oldAddressCount != 1) {
console.log("Number of additional addresses cannot be customized");
console.log(newAddressCount)
console.log(oldAddressCount)
fail();
}
next();
});
});
});
},
// Additional addresses are shown in order of derivation path
function() {
page.open(url, function(status) {
// set the phrase
page.evaluate(function() {
$(".phrase").val("abandon abandon ability").trigger("input");
});
waitForGenerate(function() {
// generate more addresses
page.evaluate(function() {
$(".more").click();
});
// get the derivation paths
waitForGenerate(function() {
var paths = page.evaluate(function() {
return $(".index").map(function(i, e) {
return $(e).text();
});
});
if (paths.length != 40) {
console.log("Total additional paths is less than expected: " + paths.length);
fail();
}
for (var i=0; i<paths.length; i++) {
var expected = "m/44'/0'/0'/0/" + i;
var actual = paths[i];
if (actual != expected) {
console.log("Path " + i + " is not in correct order");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
}
next();
});
});
});
},
// BIP32 root key can be set by the user
function() {
page.open(url, function(status) {
var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
// set the root key
page.evaluate(function() {
$(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
});
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Setting BIP32 root key results in wrong address");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Setting BIP32 root key clears the existing phrase, passphrase and seed
function() {
page.open(url, function(status) {
var expected = "";
// set a mnemonic
page.evaluate(function() {
$(".phrase").val("A non-blank but invalid value");
});
// Accept any confirm dialogs
page.onConfirm = function() {
return true;
};
// set the root key
page.evaluate(function() {
$(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
});
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".phrase").val();
});
if (actual != expected) {
console.log("Phrase not cleared when setting BIP32 root key");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Clearing of phrase, passphrase and seed can be cancelled by user
function() {
page.open(url, function(status) {
var expected = "abandon abandon ability";
// set a mnemonic
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
});
// Cancel any confirm dialogs
page.onConfirm = function() {
return false;
};
// set the root key
page.evaluate(function() {
$(".root-key").val("xprv9s21ZrQH143K3d3vzEDD3KpSKmxsZ3y7CqhAL1tinwtP6wqK4TKEKjpBuo6P2hUhB6ZENo7TTSRytiP857hBZVpBdk8PooFuRspE1eywwNZ").trigger("input");
});
var actual = page.evaluate(function() {
return $(".phrase").val();
});
if (actual != expected) {
console.log("Phrase not retained when cancelling changes to BIP32 root key");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
},
// Custom BIP32 root key is used when changing the derivation path
function() {
page.open(url, function(status) {
var expected = "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
// set the root key
page.evaluate(function() {
$(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
});
waitForGenerate(function() {
// change the derivation path
page.evaluate(function() {
$("#account").val("1").trigger("input");
});
// check the bip32 root key is used for derivation, not the blank phrase
waitForGenerate(function() {
var actual = page.evaluate(function() {
return $(".address:first").text();
});
if (actual != expected) {
console.log("Changing the derivation path does not use BIP32 root key");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
});
},
// Incorrect mnemonic shows error
function() {
page.open(url, function(status) {
// set the root key
page.evaluate(function() {
$(".phrase").val("abandon abandon abandon").trigger("input");
});
waitForFeedback(function() {
// check there is an error shown
var feedback = page.evaluate(function() {
return $(".feedback").text();
});
if (feedback.length <= 0) {
console.log("Invalid mnemonic does not show error");
fail();
}
next();
});
});
},
// Incorrect word shows suggested replacement
function() {
page.open(url, function(status) {
// set the root key
page.evaluate(function() {
$(".phrase").val("abandon abandon abiliti").trigger("input");
});
// check there is a suggestion shown
waitForFeedback(function() {
var feedback = page.evaluate(function() {
return $(".feedback").text();
});
if (feedback.indexOf("did you mean ability?") < 0) {
console.log("Incorrect word does not show suggested replacement");
console.log("Error: " + error);
fail();
}
next();
});
});
},
// Incorrect BIP32 root key shows error
function() {
page.open(url, function(status) {
// set the root key
page.evaluate(function() {
$(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj").trigger("input");
});
// check there is an error shown
waitForFeedback(function() {
var feedback = page.evaluate(function() {
return $(".feedback").text();
});
if (feedback != "Invalid root key") {
console.log("Invalid root key does not show error");
console.log("Error: " + error);
fail();
}
next();
});
});
},
// Derivation path not starting with m shows error
function() {
page.open(url, function(status) {
// set the mnemonic phrase
page.evaluate(function() {
$(".phrase").val("abandon abandon ability").trigger("input");
});
waitForGenerate(function() {
// select the bip32 tab so custom derivation path can be set
page.evaluate(function() {
$("#bip32-tab a").click();
});
waitForGenerate(function() {
// set the incorrect derivation path
page.evaluate(function() {
$("#bip32 .path").val("n/0").trigger("input");
});
waitForFeedback(function() {
var feedback = page.evaluate(function() {
return $(".feedback").text();
});
if (feedback != "First character must be 'm'") {
console.log("Derivation path not starting with m should show error");
console.log("Error: " + error);
fail();
}
next();
});
});
});
});
},
// Derivation path containing invalid characters shows useful error
function() {
page.open(url, function(status) {
// set the mnemonic phrase
page.evaluate(function() {
$(".phrase").val("abandon abandon ability").trigger("input");
});
waitForGenerate(function() {
// select the bip32 tab so custom derivation path can be set
page.evaluate(function() {
$("#bip32-tab a").click();
});
waitForGenerate(function() {
// set the incorrect derivation path
page.evaluate(function() {
$("#bip32 .path").val("m/1/0wrong1/1").trigger("input");
});
waitForFeedback(function() {
var feedback = page.evaluate(function() {
return $(".feedback").text();
});
if (feedback != "Invalid characters 0wrong1 found at depth 2") {
console.log("Derivation path with invalid characters should show error");
console.log("Error: " + error);
fail();
}
next();
});
});
});
});
},
// Github Issue 11: Default word length is 15
// https://github.com/dcpos/bip39/issues/11
function() {
page.open(url, function(status) {
// get the word length
var defaultLength = page.evaluate(function() {
return $(".strength").val();
});
if (defaultLength != 15) {
console.log("Default word length is not 15");
fail();
}
next();
});
},
// Github Issue 12: Generate more rows with private keys hidden
// https://github.com/dcpos/bip39/issues/12
function() {
page.open(url, function(status) {
// set the phrase
page.evaluate(function() {
$(".phrase").val("abandon abandon ability");
$(".phrase").trigger("input");
});
waitForGenerate(function() {
// toggle private keys hidden, then generate more addresses
page.evaluate(function() {
$(".private-key-toggle").click();
$(".more").click();
});
waitForGenerate(function() {
// check more have been generated
var expected = 40;
var numPrivKeys = page.evaluate(function() {
return $(".privkey").length;
});
if (numPrivKeys != expected) {
console.log("Wrong number of addresses when clicking 'more' with hidden privkeys");
console.log("Expected: " + expected);
console.log("Actual: " + numPrivKeys);
fail();
}
// check no private keys are shown
var numHiddenPrivKeys = page.evaluate(function() {
return $(".privkey span[class=invisible]").length;
});
if (numHiddenPrivKeys != expected) {
console.log("Generating more does not retain hidden state of privkeys");
console.log("Expected: " + expected);
console.log("Actual: " + numHiddenPrivKeys);
fail();
}
next();
});
});
});
},
// Github Issue 19: Mnemonic is not sensitive to whitespace
// https://github.com/dcpos/bip39/issues/19
function() {
page.open(url, function(status) {
// set the phrase
var expected = "xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC";
page.evaluate(function() {
var doubleSpace = " ";
$(".phrase").val("urge cat" + doubleSpace + "bid");
$(".phrase").trigger("input");
});
waitForGenerate(function() {
// Check the bip32 root key is correct
var actual = page.evaluate(function() {
return $(".root-key").val();
});
if (actual != expected) {
console.log("Mnemonic is sensitive to whitespace");
console.log("Expected: " + expected);
console.log("Actual: " + actual);
fail();
}
next();
});
});
},
// Github Issue 23: Use correct derivation path when changing tabs
// https://github.com/dcpos/bip39/issues/23
// Github Issue 26: When using a Root key derrived altcoins are incorrect
// https://github.com/dcpos/bip39/issues/26
];
console.log("Running tests...");
tests = shuffle(tests);
next();