diff --git a/resources/static/dialog/js/modules/pick_email.js b/resources/static/dialog/js/modules/pick_email.js index ecd77cbcb94c2160710fe787645a6488e1e777e7..973630c942e712b6116ac39dd750dbe31be6d5af 100644 --- a/resources/static/dialog/js/modules/pick_email.js +++ b/resources/static/dialog/js/modules/pick_email.js @@ -1,5 +1,5 @@ /*jshint browser:true, jquery: true, forin: true, laxbreak:true */ -/*global _: true, BrowserID: true, PageController: true */ +/*global _: true, BrowserID: true, PageController: true, alert: true, gettext: true*/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -12,25 +12,37 @@ BrowserID.Modules.PickEmail = (function() { storage = bid.Storage, helpers = bid.Helpers, dialogHelpers = helpers.Dialog, + tooltip = bid.Tooltip, dom = bid.DOM, sc; function pickEmailState(event) { - var self=this; - if (!dom.getElements("input[type=radio]:checked").length) { - // If none are already checked, select the first one. - dom.setAttr('input[type=radio]:eq(0)', 'checked', true); + /*jshint validthis: true*/ + var self=this, + // focus the first radio button by default. + focusSelector = "input[type=radio]:eq(0)"; + + // unless a radio button is checked, then focus it. + if (dom.getElements("input[type=radio]:checked").length) { + focusSelector = "input[type=radio]:checked"; } - // focus whichever is checked. - dom.focus("input[type=radio]:checked"); + dom.focus(focusSelector); + self.submit = signIn; } function addEmail() { + /*jshint validthis: true*/ this.publish("add_email"); } function checkEmail(email) { + /*jshint validthis: true*/ + if (!email) { + tooltip.showTooltip("#must_choose_email"); + return; + } + var identity = user.getStoredEmailKeypair(email); if (!identity) { alert(gettext("The selected email is invalid or has been deleted.")); @@ -43,6 +55,7 @@ BrowserID.Modules.PickEmail = (function() { } function signIn() { + /*jshint validthis: true*/ var self=this, email = dom.getInner("input[type=radio]:checked"); @@ -77,6 +90,7 @@ BrowserID.Modules.PickEmail = (function() { } function notMe() { + /*jshint validthis: true*/ this.publish("notme"); } diff --git a/resources/static/dialog/views/pick_email.ejs b/resources/static/dialog/views/pick_email.ejs index 244d801fa400c6b33ffceba5c354a34e595a66d9..b41b6370fa168a475af093af92f20f98d7dc8181 100644 --- a/resources/static/dialog/views/pick_email.ejs +++ b/resources/static/dialog/views/pick_email.ejs @@ -9,12 +9,12 @@ </p> <div id="selectEmail" class="form_section"> - <ul class="inputs"> + <ul class="inputs" id="emailList"> <% _.each(identities, function(item, index) { var emailAddress = item.address, id = "email_" + index; %> <li> <label for="<%= id %>" class="<% if (emailAddress === siteEmail) { %> preselected<% } %> selectable" title="<%= emailAddress %>" > <input type="radio" name="email" id="<%= id %>" value="<%= emailAddress %>" - <% if (emailAddress === siteEmail) { %> checked="checked" <% } %> + <% if (emailAddress === siteEmail || identities.length === 1) { %> checked="checked" <% } %> /> <%= emailAddress %> </label> @@ -22,6 +22,10 @@ <% }); %> </ul> + <div id="must_choose_email" class="tooltip" for="emailList"> + <%= gettext('Please choose an email address.') %> + </div> + <a id="useNewEmail" class="emphasize" href="#"><%= gettext('Add another email address') %></a> <a id="thisIsNotMe" class="emphasize" href="#"><%= gettext('This is not me') %></a> diff --git a/resources/static/test/cases/dialog/js/modules/pick_email.js b/resources/static/test/cases/dialog/js/modules/pick_email.js index d422732cd0d741b9f3c2da158c3c41cbabea11db..8269c1d51c76005371b19bc6f32505cbfec1a72a 100644 --- a/resources/static/test/cases/dialog/js/modules/pick_email.js +++ b/resources/static/test/cases/dialog/js/modules/pick_email.js @@ -11,6 +11,9 @@ storage = bid.Storage, testHelpers = bid.TestHelpers, testOrigin = testHelpers.testOrigin, + testElementFocused = testHelpers.testElementFocused, + testElementChecked = testHelpers.testElementChecked, + testElementNotChecked = testHelpers.testElementNotChecked, register = bid.TestHelpers.register; module("dialog/js/modules/pick_email", { @@ -37,7 +40,7 @@ controller.start({}); } - test("multiple emails - print emails in alphabetical order", function() { + test("multiple emails with no email assocated with site - print emails in alphabetical order, select none", function() { storage.addEmail("third@testuser.com", {}); storage.addEmail("second@testuser.com", {}); storage.addEmail("first@testuser.com", {}); @@ -48,9 +51,13 @@ equal(inputs.eq(0).val(), "first@testuser.com", "correct email for the first element"); equal(inputs.eq(1).val(), "second@testuser.com", "correct email for the second element"); equal(inputs.eq(2).val(), "third@testuser.com", "correct email for the third element"); + + equal($("input[type=radio]:checked").length, 0, "if there is no email associated with the site, but there are multiple addresses, do not select an address"); + equal($("label.preselected").length, 0, "no item preselected"); + testElementFocused("input[type=radio]:eq(0)", "if there is no email associated with the site, but there are multiple addresses, focus the first email address"); }); - test("pickemail controller with email associated with site - check correct email", function() { + test("email associated with site - check correct email", function() { storage.addEmail("testuser@testuser.com", {}); storage.addEmail("testuser2@testuser.com", {}); storage.site.set(testOrigin, "email", "testuser2@testuser.com"); @@ -58,19 +65,21 @@ createController(); var radioButton = $("input[type=radio]").eq(0); - ok(radioButton.is(":checked"), "the email address we specified is checked"); + testElementChecked(radioButton, "the email address we specified is checked"); + testElementFocused(radioButton, "checked element is focused"); var label = $("label[for=" + radioButton.attr("id") + "]"); ok(label.hasClass("preselected"), "the label has the preselected class"); }); - test("pickemail controller without email associated with site checks first radio button", function() { + test("single email, no email associated with site - check first radio button", function() { storage.addEmail("testuser@testuser.com", {}); createController(); var radioButton = $("input[type=radio]").eq(0); - equal(radioButton.is(":checked"), true, "The email address is not checked"); + testElementChecked(radioButton, "The lone email address is not checked"); + testElementFocused(radioButton, "the lone email address is still focused for keyboard navigation"); var label = radioButton.parent(); equal(label.hasClass("preselected"), false, "the label has no class"); @@ -82,14 +91,20 @@ createController(); - $("input[type=radio]").eq(0).trigger("click"); - + // this should only be triggered once. testHelpers.register checks this + // for us. var assertion; - register("email_chosen", function(msg, info) { ok(info.email, "email_chosen message triggered with email"); start(); }); + + // trying to sign in without an email selected shows a tooltip. + controller.signIn(); + testHelpers.testTooltipVisible(); + + // trying to sign in with an email selected operates as expected. + $("input[type=radio]").eq(0).trigger("click"); controller.signIn(); }); @@ -109,15 +124,15 @@ createController(); - equal($("#email_1").is(":checked"), false, "radio button is not selected before click."); + testElementNotChecked("#mail_1", "radio button is not selected before click."); // selects testuser@testuser.com $(".inputs label:eq(1)").trigger("click"); - equal($("#email_1").is(":checked"), true, "radio button is correctly selected"); + testElementChecked("#email_1", "radio button is correctly selected after click"); // selects testuser2@testuser.com $(".inputs label:eq(0)").trigger("click"); - equal($("#email_0").is(":checked"), true, "radio button is correctly selected"); + testElementChecked("#email_0", "radio button is correctly selected after click"); }); test("click on an email label that contains a + - select corresponding radio button", function() { @@ -126,15 +141,15 @@ createController(); - equal($("#email_1").is(":checked"), false, "radio button is not selected before click."); + testElementNotChecked("#email_1", "radio button is not selected before click."); // selects testuser+test1@testuser.com $(".inputs label:eq(1)").trigger("click"); - equal($("#email_1").is(":checked"), true, "radio button is correctly selected"); + testElementChecked("#email_1", "radio button is correctly selected after click"); // selects testuser+test0@testuser.com $(".inputs label:eq(0)").trigger("click"); - equal($("#email_0").is(":checked"), true, "radio button is correctly selected"); + testElementChecked("#email_0", "radio button is correctly selected after click"); }); asyncTest("click on not me button - trigger notme message", function() { diff --git a/resources/static/test/testHelpers/helpers.js b/resources/static/test/testHelpers/helpers.js index c9f9329442d808d31b1e158ccb23c079519d1bd1..21416158407c31224d7590210ea219dc38585f89 100644 --- a/resources/static/test/testHelpers/helpers.js +++ b/resources/static/test/testHelpers/helpers.js @@ -1,5 +1,5 @@ -/*jshint browsers: true laxbreak: true, expr: true */ -/*global BrowserID: true, ok: true, equal: true, start: true */ +/*jshint browser: true laxbreak: true, expr: true */ +/*global BrowserID: true, ok: true, equal: true, start: true, deepEqual: true, notEqual: true */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -287,6 +287,39 @@ BrowserID.TestHelpers = (function() { testRPTosPPNotShown: function(msg) { TestHelpers.testNotHasClass("body", "rptospp", msg || "RP TOS/PP not shown"); + }, + + testElementChecked: function(selector, msg) { + equal($(selector).is(":checked"), true, msg || selector + " is checked"); + }, + + testElementNotChecked: function(selector, msg) { + equal($(selector).is(":checked"), false, msg || selector + " is not checked"); + }, + + testElementFocused: function(selector, msg) { + var focusedEl = $(":focus"); + + if (focusedEl.is(selector)) { + ok(true, msg || selector + " is focused"); + } + else { + // In some environments such as PhantomJS, input elements cannot be + // checked for focus. Make a temporary input element which we can + // check to see if it is possible to focus. If it is possible, this is + // a failure. If it is not possible, print a message and continue. + // Remove the element when complete. + var input = $("<input type='text' />").appendTo("body").focus(); + if (input.is(":focus")) { + ok(false, msg || selector + " is focused"); + // refocus the original input element. + if (focusedEl.length) $(focusedEl).focus(); + } + else { + window.console && console.log("currently unable to focus elements, focus check skipped - try focusing the unit test page"); + } + input.remove(); + } } };