gecko-dev/toolkit/components/passwordmgr/test/test_basic_form_autocomplet...

822 строки
24 KiB
HTML

<!DOCTYPE HTML>
<html>
<head>
<title>Test for Login Manager</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="pwmgr_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
Login Manager test: multiple login autocomplete
<p id="display"></p>
<!-- we presumably can't hide the content for this test. -->
<div id="content">
<!-- form1 tests multiple matching logins -->
<form id="form1" action="http://autocomplete:8888/formtest.js" onsubmit="return false;">
<input type="text" name="uname">
<input type="password" name="pword">
<button type="submit">Submit</button>
</form>
<!-- other forms test single logins, with autocomplete=off set -->
<form id="form2" action="http://autocomplete2" onsubmit="return false;">
<input type="text" name="uname">
<input type="password" name="pword" autocomplete="off">
<button type="submit">Submit</button>
</form>
<form id="form3" action="http://autocomplete2" onsubmit="return false;">
<input type="text" name="uname" autocomplete="off">
<input type="password" name="pword">
<button type="submit">Submit</button>
</form>
<form id="form4" action="http://autocomplete2" onsubmit="return false;" autocomplete="off">
<input type="text" name="uname">
<input type="password" name="pword">
<button type="submit">Submit</button>
</form>
<form id="form5" action="http://autocomplete2" onsubmit="return false;">
<input type="text" name="uname" autocomplete="off">
<input type="password" name="pword" autocomplete="off">
<button type="submit">Submit</button>
</form>
<!-- control -->
<form id="form6" action="http://autocomplete2" onsubmit="return false;">
<input type="text" name="uname">
<input type="password" name="pword">
<button type="submit">Submit</button>
</form>
<!-- This form will be manipulated to insert a different username field. -->
<form id="form7" action="http://autocomplete3" onsubmit="return false;">
<input type="text" name="uname">
<input type="password" name="pword">
<button type="submit">Submit</button>
</form>
<!-- test for no autofill after onblur with blank username -->
<form id="form8" action="http://autocomplete4" onsubmit="return false;">
<input type="text" name="uname">
<input type="password" name="pword">
<button type="submit">Submit</button>
</form>
<!-- test autocomplete dropdown -->
<form id="form9" action="http://autocomplete5" onsubmit="return false;">
<input type="text" name="uname">
<input type="password" name="pword">
<button type="submit">Submit</button>
</form>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Login Manager: multiple login autocomplete. **/
commonInit();
var uname = $_(1, "uname");
var pword = $_(1, "pword");
const shiftModifier = Components.interfaces.nsIDOMEvent.SHIFT_MASK;
// Get the pwmgr service
var pwmgr = SpecialPowers.wrap(Components).classes["@mozilla.org/login-manager;1"]
.getService(Components.interfaces.nsILoginManager);
ok(pwmgr != null, "nsLoginManager service");
// Create some logins just for this form, since we'll be deleting them.
var nsLoginInfo =
SpecialPowers.wrap(Components).Constructor("@mozilla.org/login-manager/loginInfo;1",
Components.interfaces.nsILoginInfo, "init");
ok(nsLoginInfo != null, "nsLoginInfo constructor");
// login0 has no username, so should be filtered out from the autocomplete list.
var login0 = new nsLoginInfo(
"http://mochi.test:8888", "http://autocomplete:8888", null,
"", "user0pass", "", "pword");
var login1 = new nsLoginInfo(
"http://mochi.test:8888", "http://autocomplete:8888", null,
"tempuser1", "temppass1", "uname", "pword");
var login2 = new nsLoginInfo(
"http://mochi.test:8888", "http://autocomplete:8888", null,
"testuser2", "testpass2", "uname", "pword");
var login3 = new nsLoginInfo(
"http://mochi.test:8888", "http://autocomplete:8888", null,
"testuser3", "testpass3", "uname", "pword");
var login4 = new nsLoginInfo(
"http://mochi.test:8888", "http://autocomplete:8888", null,
"zzzuser4", "zzzpass4", "uname", "pword");
// login 5 only used in the single-user forms
var login5 = new nsLoginInfo(
"http://mochi.test:8888", "http://autocomplete2", null,
"singleuser5", "singlepass5", "uname", "pword");
var login6A = new nsLoginInfo(
"http://mochi.test:8888", "http://autocomplete3", null,
"form7user1", "form7pass1", "uname", "pword");
var login6B = new nsLoginInfo(
"http://mochi.test:8888", "http://autocomplete3", null,
"form7user2", "form7pass2", "uname", "pword");
var login7 = new nsLoginInfo(
"http://mochi.test:8888", "http://autocomplete4", null,
"form8user", "form8pass", "uname", "pword");
var login8A = new nsLoginInfo(
"http://mochi.test:8888", "http://autocomplete5", null,
"form9userAB", "form9pass", "uname", "pword");
var login8B = new nsLoginInfo(
"http://mochi.test:8888", "http://autocomplete5", null,
"form9userAAB", "form9pass", "uname", "pword");
// login8C is added later
var login8C = new nsLoginInfo(
"http://mochi.test:8888", "http://autocomplete5", null,
"form9userAABz", "form9pass", "uname", "pword");
// try/catch in case someone runs the tests manually, twice.
try {
pwmgr.addLogin(login0);
pwmgr.addLogin(login1);
pwmgr.addLogin(login2);
pwmgr.addLogin(login3);
pwmgr.addLogin(login4);
pwmgr.addLogin(login5);
pwmgr.addLogin(login6A);
pwmgr.addLogin(login6B);
pwmgr.addLogin(login7);
pwmgr.addLogin(login8A);
pwmgr.addLogin(login8B);
} catch (e) {
ok(false, "addLogin threw: " + e);
}
// Restore the form to the default state.
function restoreForm() {
uname.value = "";
pword.value = "";
uname.focus();
}
// Check for expected username/password in form.
function checkACForm(expectedUsername, expectedPassword) {
var formID = uname.parentNode.id;
is(uname.value, expectedUsername, "Checking " + formID + " username");
is(pword.value, expectedPassword, "Checking " + formID + " password");
}
function sendFakeAutocompleteEvent(element) {
var acEvent = document.createEvent("HTMLEvents");
acEvent.initEvent("DOMAutoComplete", true, false);
element.dispatchEvent(acEvent);
}
function hitEventLoop(func, times) {
if (times > 0) {
setTimeout(hitEventLoop, 0, func, times - 1);
} else {
setTimeout(func, 0);
}
}
var gNextTestWillOpenPopup = true;
var gLastTest = 704;
function addPopupListener(eventName, func, capture) {
autocompletePopup.addEventListener(eventName, func, capture);
}
function removePopupListener(eventName, func, capture) {
autocompletePopup.removeEventListener(eventName, func, capture);
}
/*
* Main section of test...
*
* This is a bit hacky, because the events are either being sent or
* processes asynchronously, so we need to interrupt our flow with lots of
* setTimeout() calls. The case statements are executed in order, one per
* timeout.
*/
function runTest(testNum) {
ok(true, "Starting test #" + testNum);
if (gNextTestWillOpenPopup) {
addPopupListener("popupshown", function() {
removePopupListener("popupshown", arguments.callee, false);
if (testNum != gLastTest) {
window.setTimeout(runTest, 0, testNum + 1);
}
}, false);
} else {
var unexpectedPopup = function() {
removePopupListener("popupshown", arguments.callee, false);
ok(false, "Test " + testNum + " should not show a popup");
};
addPopupListener("popupshown", unexpectedPopup, false);
if (testNum == gLastTest) {
removePopupListener("popupshown", unexpectedPopup, false);
} else {
hitEventLoop(function() {
removePopupListener("popupshown", unexpectedPopup, false);
runTest(testNum + 1);
}, 100);
}
}
switch(testNum) {
case 1:
// Make sure initial form is empty.
checkACForm("", "");
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 2:
// Check first entry
doKey("down");
checkACForm("", ""); // value shouldn't update
doKey("return"); // not "enter"!
checkACForm("tempuser1", "temppass1");
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 3:
// Check second entry
doKey("down");
doKey("down");
doKey("return"); // not "enter"!
checkACForm("testuser2", "testpass2");
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 4:
// Check third entry
doKey("down");
doKey("down");
doKey("down");
doKey("return");
checkACForm("testuser3", "testpass3");
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 5:
// Check fourth entry
doKey("down");
doKey("down");
doKey("down");
doKey("down");
doKey("return");
checkACForm("zzzuser4", "zzzpass4");
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 6:
// Check first entry (wraparound)
doKey("down");
doKey("down");
doKey("down");
doKey("down");
doKey("down"); // deselects
doKey("down");
doKey("return");
checkACForm("tempuser1", "temppass1");
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 7:
// Check the last entry via arrow-up
doKey("up");
doKey("return");
checkACForm("zzzuser4", "zzzpass4");
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 8:
// Check the last entry via arrow-up
doKey("down"); // select first entry
doKey("up"); // selects nothing!
doKey("up"); // select last entry
doKey("return");
checkACForm("zzzuser4", "zzzpass4");
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 9:
// Check the last entry via arrow-up (wraparound)
doKey("down");
doKey("up"); // deselects
doKey("up"); // last entry
doKey("up");
doKey("up");
doKey("up"); // first entry
doKey("up"); // deselects
doKey("up"); // last entry
doKey("return");
checkACForm("zzzuser4", "zzzpass4");
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 10:
// Set first entry w/o triggering autocomplete
doKey("down");
doKey("right");
checkACForm("tempuser1", ""); // empty password
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 11:
// Set first entry w/o triggering autocomplete
doKey("down");
doKey("left");
checkACForm("tempuser1", ""); // empty password
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 12:
// Check first entry (page up)
doKey("down");
doKey("down");
doKey("page_up");
doKey("return");
checkACForm("tempuser1", "temppass1");
// Trigger autocomplete popup
restoreForm();
doKey("down");
gNextTestWillOpenPopup = false;
break;
case 13:
// Check last entry (page down)
doKey("down");
doKey("page_down");
doKey("return");
checkACForm("zzzuser4", "zzzpass4");
restoreForm();
gNextTestWillOpenPopup = false;
break;
/* The previous comments said that test 14 was special and needed to not
* have enablePrivilege, or else dispatchEvent will send a trusted event
* (we're testing to see if we ignore untrusted events, so don't want
* that).
* We should file a bug to look into this.
*/
case 14:
/*
// Send a fake (untrusted) event.
checkACForm("", "");
uname.value = "zzzuser4";
sendFakeAutocompleteEvent(uname);
checkACForm("zzzuser4", "");
*/
gNextTestWillOpenPopup = true;
break;
case 15:
//checkACForm("zzzuser4", "");
// Trigger autocomplete popup
restoreForm();
doKey("down");
testNum = 49;
break;
// XXX tried sending character "t" before/during dropdown to test
// filtering, but had no luck. Seemed like the character was getting lost.
// Setting uname.value didn't seem to work either. This works with a human
// driver, so I'm not sure what's up.
case 50:
// Delete the first entry (of 4), "tempuser1"
doKey("down");
var numLogins;
numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
is(numLogins, 5, "Correct number of logins before deleting one");
// On OS X, shift-backspace and shift-delete work, just delete does not.
// On Win/Linux, shift-backspace does not work, delete and shift-delete do.
doKey("delete", shiftModifier);
checkACForm("", "");
numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
is(numLogins, 4, "Correct number of logins after deleting one");
doKey("return");
checkACForm("testuser2", "testpass2");
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 51:
// Check the new first entry (of 3)
doKey("down");
doKey("return");
checkACForm("testuser2", "testpass2");
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 52:
// Delete the second entry (of 3), "testuser3"
doKey("down");
doKey("down");
doKey("delete", shiftModifier);
checkACForm("", "");
numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
is(numLogins, 3, "Correct number of logins after deleting one");
doKey("return");
checkACForm("zzzuser4", "zzzpass4");
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 53:
// Check the new second entry (of 2)
doKey("down");
doKey("return");
checkACForm("testuser2", "testpass2");
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 54:
// Delete the last entry (of 2), "zzzuser4"
doKey("down");
doKey("down");
doKey("delete", shiftModifier);
checkACForm("", "");
numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
is(numLogins, 2, "Correct number of logins after deleting one");
doKey("return");
checkACForm("testuser2", "testpass2");
// Trigger autocomplete popup
restoreForm();
doKey("down");
break;
case 55:
// Check the new second entry (of 2)
doKey("down");
doKey("return");
checkACForm("testuser2", "testpass2");
// Trigger autocomplete popup
restoreForm();
doKey("down");
gNextTestWillOpenPopup = false;
break;
case 56:
// Delete the only remaining entry, "testuser2"
doKey("down");
doKey("delete", shiftModifier);
//doKey("return");
checkACForm("", "");
numLogins = pwmgr.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
is(numLogins, 1, "Correct number of logins after deleting one");
pwmgr.removeLogin(login0); // remove the login that's not shown in the list.
testNum = 99;
gNextTestWillOpenPopup = true;
break;
/* Tests for single-user forms with autocomplete=off */
case 100:
// Turn our attention to form2
uname = $_(2, "uname");
pword = $_(2, "pword");
checkACForm("", "");
// Trigger autocomplete popup
restoreForm();
doKey("down");
gNextTestWillOpenPopup = false;
break;
case 101:
// Check first entry
doKey("down");
checkACForm("", ""); // value shouldn't update
doKey("return"); // not "enter"!
checkACForm("singleuser5", "singlepass5");
restoreForm(); // clear field, so reloading test doesn't fail
gNextTestWillOpenPopup = true;
break;
case 102:
// Turn our attention to form3
uname = $_(3, "uname");
pword = $_(3, "pword");
checkACForm("", "");
// Trigger autocomplete popup
restoreForm();
doKey("down");
gNextTestWillOpenPopup = false;
break;
case 103:
// Check first entry
doKey("down");
checkACForm("", ""); // value shouldn't update
doKey("return"); // not "enter"!
checkACForm("singleuser5", "singlepass5");
gNextTestWillOpenPopup = true;
break;
case 104:
// Turn our attention to form4
uname = $_(4, "uname");
pword = $_(4, "pword");
checkACForm("", "");
// Trigger autocomplete popup
restoreForm();
doKey("down");
gNextTestWillOpenPopup = false;
break;
case 105:
// Check first entry
doKey("down");
checkACForm("", ""); // value shouldn't update
doKey("return"); // not "enter"!
checkACForm("singleuser5", "singlepass5");
gNextTestWillOpenPopup = true;
break;
case 106:
// Turn our attention to form5
uname = $_(5, "uname");
pword = $_(5, "pword");
checkACForm("", "");
// Trigger autocomplete popup
restoreForm();
doKey("down");
gNextTestWillOpenPopup = false;
break;
case 107:
// Check first entry
doKey("down");
checkACForm("", ""); // value shouldn't update
doKey("return"); // not "enter"!
checkACForm("singleuser5", "singlepass5");
gNextTestWillOpenPopup = false;
break;
case 108:
// Turn our attention to form6
// (this is a control, w/o autocomplete=off, to ensure the login
// that was being suppressed would have been filled in otherwise)
uname = $_(6, "uname");
pword = $_(6, "pword");
checkACForm("singleuser5", "singlepass5");
gNextTestWillOpenPopup = false;
break;
case 109:
// Test that the password field remains filled in after changing
// the username.
uname.focus();
doKey("right");
sendChar("X");
// Trigger the 'blur' event on uname
pword.focus();
checkACForm("sXingleuser5", "singlepass5");
pwmgr.removeLogin(login5);
testNum = 499;
gNextTestWillOpenPopup = true;
break;
case 500:
// Turn our attention to form7
uname = $_(7, "uname");
pword = $_(7, "pword");
checkACForm("", "");
// Insert a new username field into the form. We'll then make sure
// that invoking the autocomplete doesn't try to fill the form.
var newField = document.createElement("input");
newField.setAttribute("type", "text");
newField.setAttribute("name", "uname2");
pword.parentNode.insertBefore(newField, pword);
is($_(7, "uname2").value, "", "Verifying empty uname2");;
// Delete login6B. It was created just to prevent filling in a login
// automatically, removing it makes it more likely that we'll catch a
// future regression with form filling here.
pwmgr.removeLogin(login6B);
// Trigger autocomplete popup
restoreForm();
doKey("down");
gNextTestWillOpenPopup = false;
break;
case 501:
// Check first entry
doKey("down");
checkACForm("", ""); // value shouldn't update
doKey("return"); // not "enter"!
// The form changes, so we expect the old username field to get the
// selected autocomplete value, but neither the new username field nor
// the password field should have any values filled in.
checkACForm("form7user1", "");
is($_(7, "uname2").value, "", "Verifying empty uname2");;
restoreForm(); // clear field, so reloading test doesn't fail
pwmgr.removeLogin(login6A);
testNum = 599;
gNextTestWillOpenPopup = false;
break;
case 600:
// Turn our attention to form8
uname = $_(8, "uname");
pword = $_(8, "pword");
checkACForm("form8user", "form8pass");
restoreForm();
gNextTestWillOpenPopup = false;
break;
case 601:
checkACForm("", "");
// Focus the previous form to trigger a blur.
$_(7, "uname").focus();
gNextTestWillOpenPopup = false;
break;
case 602:
checkACForm("", "");
restoreForm();
gNextTestWillOpenPopup = false;
break;
case 603:
checkACForm("", "");
pwmgr.removeLogin(login7);
testNum = 699;
gNextTestWillOpenPopup = true;
break;
case 700:
// Turn our attention to form9 to test the dropdown - bug 497541
uname = $_(9, "uname");
pword = $_(9, "pword");
uname.focus();
sendString("form9userAB");
gNextTestWillOpenPopup = true;
break;
case 701:
checkACForm("form9userAB", "");
uname.focus();
doKey("left");
sendChar("A");
gNextTestWillOpenPopup = false;
break;
case 702:
// check dropdown is updated after inserting "A"
checkACForm("form9userAAB", "");
checkMenuEntries(["form9userAAB"]);
doKey("down");
doKey("return");
checkACForm("form9userAAB", "form9pass");
gNextTestWillOpenPopup = false;
break;
case 703:
pwmgr.addLogin(login8C);
uname.focus();
sendChar("z");
gNextTestWillOpenPopup = false;
break;
case 704:
// check that empty results are cached - bug 496466
checkMenuEntries([]);
SimpleTest.finish();
return;
default:
ok(false, "Unexpected invocation of test #" + testNum);
SimpleTest.finish();
return;
}
}
function checkMenuEntries(expectedValues) {
var actualValues = getMenuEntries();
is(actualValues.length, expectedValues.length, "Checking length of expected menu");
for (var i = 0; i < expectedValues.length; i++)
is(actualValues[i], expectedValues[i], "Checking menu entry #"+i);
}
var autocompletePopup;
function getMenuEntries() {
var entries = [];
// Could perhaps pull values directly from the controller, but it seems
// more reliable to test the values that are actually in the tree?
var column = autocompletePopup.tree.columns[0];
var numRows = autocompletePopup.tree.view.rowCount;
for (var i = 0; i < numRows; i++) {
entries.push(autocompletePopup.tree.view.getValueAt(i, column));
}
return entries;
}
function startTest() {
var Ci = Components.interfaces;
chromeWin = SpecialPowers.wrap(window)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow)
.QueryInterface(Ci.nsIDOMChromeWindow);
// shouldn't reach into browser internals like this and
// shouldn't assume ID is consistent across products
autocompletePopup = chromeWin.document.getElementById("PopupAutoComplete");
ok(autocompletePopup, "Got autocomplete popup");
runTest(1);
}
window.onload = startTest;
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>