Merge lp:~jjacobs/methanal/verified-password-input into lp:methanal

Proposed by Jonathan Jacobs
Status: Superseded
Proposed branch: lp:~jjacobs/methanal/verified-password-input
Merge into: lp:methanal
Diff against target: 305 lines
4 files modified
methanal/js/Methanal/Tests/TestView.js (+128/-3)
methanal/js/Methanal/View.js (+84/-0)
methanal/themes/methanal-base/methanal-verified-password-input.html (+15/-0)
methanal/view.py (+32/-0)
To merge this branch: bzr merge lp:~jjacobs/methanal/verified-password-input
Reviewer Review Type Date Requested Status
Forrest Aldridge (community) Needs Fixing
Methanal maintainers Pending
Review via email: mp+13134@code.launchpad.net

This proposal has been superseded by a proposal from 2009-10-09.

To post a comment you must log in.
Revision history for this message
Forrest Aldridge (forrest-aldridge) wrote :

 1. The docstring in TestView.js on line 834 is an incomplete sentence and is slightly unclear.
 2. This is a matter of preference really, but I think it would be clearer if the variable c in View.js in lines 1828-1831 were renamed to 'criterion'

review: Needs Fixing
111. By Jonathan Jacobs

Rename variable for clarity.

112. By Jonathan Jacobs

Finish incomplete docstring.

113. By Jonathan Jacobs

Don't bother saving and restoring input node values.

114. By Jonathan Jacobs

Throw a real exception.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'methanal/js/Methanal/Tests/TestView.js'
--- methanal/js/Methanal/Tests/TestView.js 2009-10-04 11:18:09 +0000
+++ methanal/js/Methanal/Tests/TestView.js 2009-10-09 17:35:23 +0000
@@ -168,11 +168,13 @@
168 * Set the input node's value to C{value} and passes the result of168 * Set the input node's value to C{value} and passes the result of
169 * C{control.getValue} to C{control.baseValidator}.169 * C{control.getValue} to C{control.baseValidator}.
170 */170 */
171 function assertValidInput(self, control, value) {171 function assertValidInput(self, control, value, msg) {
172 var oldValue = control.inputNode.value;172 var oldValue = control.inputNode.value;
173 control.inputNode.value = value;173 control.inputNode.value = value;
174 var msg = (Methanal.Util.repr(value) + ' is NOT valid input for ' +174 if (msg === undefined) {
175 Methanal.Util.repr(control));175 msg = (Methanal.Util.repr(value) + ' is NOT valid input for ' +
176 Methanal.Util.repr(control));
177 }
176 self.assertIdentical(178 self.assertIdentical(
177 control.baseValidator(control.getValue()), undefined, msg);179 control.baseValidator(control.getValue()), undefined, msg);
178 control.inputNode.value = oldValue;180 control.inputNode.value = oldValue;
@@ -754,6 +756,129 @@
754756
755757
756/**758/**
759 * Tests for L{Methanal.View.VerifiedPasswordInput}.
760 */
761Methanal.Tests.TestView.BaseTestTextInput.subclass(Methanal.Tests.TestView, 'TestVerifiedPasswordInput').methods(
762 function setUp(self) {
763 self.controlType = Methanal.View.VerifiedPasswordInput;
764 },
765
766
767 /**
768 * Perform some setup tasks for password validation asserting.
769 */
770 function _assertPassword(self, fn, control, password, confirmPassword) {
771 if (confirmPassword === undefined) {
772 confirmPassword = password;
773 }
774 var oldPasswordValue = control._confirmPasswordNode.value;
775 control._confirmPasswordNode.value = confirmPassword;
776 fn();
777 control._confirmPasswordNode.value = oldPasswordValue;
778 },
779
780
781 /**
782 * Assert that C{password}, and optionally C{confirmPassword}, are a good
783 * input.
784 */
785 function assertGoodPassword(self, control, password, confirmPassword) {
786 self._assertPassword(function () {
787 self.assertValidInput(
788 control, password,
789 Methanal.Util.repr(password) + ' is NOT a good password');
790 }, control, password, confirmPassword);
791 },
792
793
794 /**
795 * Assert that C{password}, and optionally C{confirmPassword}, are a bad
796 * input.
797 */
798 function assertBadPassword(self, control, password, confirmPassword) {
799 self._assertPassword(function () {
800 self.assertInvalidInput(
801 control, password,
802 Methanal.Util.repr(password) + ' IS a good password');
803 }, control, password, confirmPassword);
804 },
805
806
807 function createControl(self, args) {
808 var control = Methanal.Tests.TestView.TestVerifiedPasswordInput.upcall(
809 self, 'createControl', args);
810 Methanal.Tests.TestView.makeWidgetChildNode(
811 control, 'input', 'confirmPassword');
812 return control;
813 },
814
815
816 /**
817 * Validation will fail under the following conditions:
818 * 1. The input and confirmPasswordNode node values don't match.
819 * 2. If either of the above node values have no length (are blank).
820 */
821 function test_inputValidation(self) {
822 self.testControl({value: null},
823 function (control) {
824 // Test condition 1
825 self.assertBadPassword(control, 'match', 'no match');
826 self.assertGoodPassword(control, 'match', 'match');
827 // Test condition 2
828 self.assertBadPassword(control, '', '');
829 });
830 },
831
832
833 /**
834 * Changing the password strength criteria results in different
835 */
836 function test_strengthCriteria(self) {
837 // Override the default criteria of 5 or more characters.
838 self.testControl({value: null, minPasswordLength: 3},
839 function (control) {
840 self.assertBadPassword(control, '12');
841 self.assertGoodPassword(control, '123');
842
843 control.setStrengthCriteria(['ALPHA']);
844 self.assertGoodPassword(control, 'Abc');
845 self.assertBadPassword(control, '123');
846
847 control.setStrengthCriteria(['NUMERIC']);
848 self.assertBadPassword(control, 'Abc');
849 self.assertGoodPassword(control, '123');
850
851 control.setStrengthCriteria(['ALPHA', 'NUMERIC']);
852 self.assertBadPassword(control, 'Abc');
853 self.assertBadPassword(control, '123');
854 self.assertGoodPassword(control, 'Abc123');
855
856 control.setStrengthCriteria(['MIXEDCASE']);
857 self.assertGoodPassword(control, 'Abc');
858 self.assertGoodPassword(control, 'abC');
859 self.assertBadPassword(control, 'abc');
860 self.assertBadPassword(control, '123');
861
862 control.setStrengthCriteria(['SYMBOLS']);
863 self.assertGoodPassword(control, '!@#_');
864 self.assertBadPassword(control, ' ');
865 self.assertBadPassword(control, 'abc');
866
867 control.setStrengthCriteria([]);
868 self.assertGoodPassword(control, '!@#_');
869 self.assertGoodPassword(control, 'abc');
870 self.assertGoodPassword(control, '123');
871
872 self.assertThrows(Error,
873 function () {
874 control.setStrengthCriteria(['DANGERWILLROBINSON']);
875 });
876 });
877 });
878
879
880
881/**
757 * Tests for L{Methanal.View.InputContainer}.882 * Tests for L{Methanal.View.InputContainer}.
758 */883 */
759Methanal.Tests.TestView.BaseTestTextInput.subclass(Methanal.Tests.TestView, 'TestFormGroup').methods(884Methanal.Tests.TestView.BaseTestTextInput.subclass(Methanal.Tests.TestView, 'TestFormGroup').methods(
760885
=== modified file 'methanal/js/Methanal/View.js'
--- methanal/js/Methanal/View.js 2009-10-05 00:15:43 +0000
+++ methanal/js/Methanal/View.js 2009-10-09 17:35:23 +0000
@@ -1793,3 +1793,87 @@
1793 return 'Percentage values must be between 0% and 100%'1793 return 'Percentage values must be between 0% and 100%'
1794 }1794 }
1795 });1795 });
1796
1797
1798
1799/**
1800 * Password input with a verification field and strength checking.
1801 */
1802Methanal.View.TextInput.subclass(Methanal.View, 'VerifiedPasswordInput').methods(
1803 function __init__(self, node, args) {
1804 Methanal.View.VerifiedPasswordInput.upcall(
1805 self, '__init__', node, args);
1806 self._minPasswordLength = args.minPasswordLength || 5;
1807 self.setStrengthCriteria(args.strengthCriteria || []);
1808 },
1809
1810
1811 function nodeInserted(self) {
1812 self._confirmPasswordNode = self.nodeById('confirmPassword');
1813 Methanal.View.VerifiedPasswordInput.upcall(self, 'nodeInserted');
1814 },
1815
1816
1817 /**
1818 * Set the password strength criteria.
1819 *
1820 * @type criteria: C{Array} of C{String}
1821 * @param criteria: An array of names, matching those found in
1822 * L{Methanal.View.VerifiedPasswordInput.STRENGTH_CRITERIA}, indicating
1823 * the password strength criteria
1824 */
1825 function setStrengthCriteria(self, criteria) {
1826 var fns = Methanal.View.VerifiedPasswordInput.STRENGTH_CRITERIA;
1827 for (var i = 0; i < criteria.length; ++i) {
1828 var c = criteria[i];
1829 if (fns[c] === undefined) {
1830 c = Methanal.Util.repr(c);
1831 throw new Error('Unknown strength criterion: ' + c);
1832 }
1833 }
1834 self._strengthCriteria = criteria;
1835 },
1836
1837
1838 /**
1839 * Override this method to change the definition of a 'strong' password.
1840 */
1841 function passwordIsStrong(self, password) {
1842 if (password.length < self._minPasswordLength) {
1843 return false;
1844 }
1845
1846 var fns = Methanal.View.VerifiedPasswordInput.STRENGTH_CRITERIA;
1847 for (var i = 0; i < self._strengthCriteria.length; ++i) {
1848 var fn = fns[self._strengthCriteria[i]];
1849 if (!fn(password)) {
1850 return false;
1851 }
1852 }
1853 return true;
1854 },
1855
1856
1857 /**
1858 * This default validator ensures that the password is strong and that
1859 * the password given in both fields have length > 0 and match exactly.
1860 */
1861 function baseValidator(self, value) {
1862 if (value !== self._confirmPasswordNode.value || value === null ||
1863 self._confirmPasswordNode.value === null) {
1864 return 'Passwords do not match.';
1865 }
1866
1867 if (!self.passwordIsStrong(value)) {
1868 return 'Password is too weak.';
1869 }
1870 });
1871
1872
1873
1874Methanal.View.VerifiedPasswordInput.STRENGTH_CRITERIA = {
1875 'ALPHA': function (value) { return /[a-zA-Z]/.test(value); },
1876 'NUMERIC': function (value) { return /[0-9]/.test(value); },
1877 'MIXEDCASE': function (value) {
1878 return /[a-z]/.test(value) && /[A-Z]/.test(value); },
1879 'SYMBOLS': function (value) { return /[^A-Za-z0-9\s]/.test(value); }};
17961880
=== added file 'methanal/themes/methanal-base/methanal-verified-password-input.html'
--- methanal/themes/methanal-base/methanal-verified-password-input.html 1970-01-01 00:00:00 +0000
+++ methanal/themes/methanal-base/methanal-verified-password-input.html 2009-10-09 17:35:23 +0000
@@ -0,0 +1,15 @@
1<div xmlns:nevow="http://nevow.com/ns/nevow/0.1" xmlns:athena="http://divmod.org/ns/athena/0.7" nevow:render="liveElement">
2 <input class="methanal-input" type="password">
3 <nevow:attr name="value" nevow:render="value" />
4 <athena:handler event="onchange" handler="onChange" />
5 <athena:handler event="onkeyup" handler="onKeyUp" />
6 <athena:handler event="onblur" handler="onBlur" />
7 <athena:handler event="onfocus" handler="onFocus" />
8 </input>
9 <input class="methanal-input" type="password" id="confirmPassword">
10 <nevow:attr name="value" nevow:render="value" />
11 <athena:handler event="onchange" handler="onChange" />
12 </input>
13 <span style="display: none;" class="friendly-representation" id="displayValue" />
14 <span class="methanal-error" id="error" />
15</div>
016
=== modified file 'methanal/view.py'
--- methanal/view.py 2009-10-04 10:17:26 +0000
+++ methanal/view.py 2009-10-09 17:35:23 +0000
@@ -511,6 +511,38 @@
511511
512512
513513
514class VerifiedPasswordInput(TextInput):
515 """
516 Password input with verification and strength checking.
517
518 @type minPasswordLength: C{int}
519 @ivar minPasswordLength: Minimum acceptable password length, or C{None}
520 to use the default client-side value
521
522 @type strengthCriteria: C{list} of C{unicode}
523 @ivar strengthCriteria: A list of criteria names for password strength
524 testing, or C{None} for no additional strength criteria. See
525 L{Methanal.View.VerifiedPasswordInput.STRENGTH_CRITERIA} in the
526 Javascript source for possible values
527 """
528 fragmentName = 'methanal-verified-password-input'
529 jsClass = u'Methanal.View.VerifiedPasswordInput'
530
531
532 def __init__(self, minPasswordLength=None, strengthCriteria=None, **kw):
533 super(VerifiedPasswordInput, self).__init__(**kw)
534 self.minPasswordLength = minPasswordLength
535 if strengthCriteria is None:
536 strengthCriteria = []
537 self.strengthCriteria = strengthCriteria
538
539
540 def getArgs(self):
541 return {u'minPasswordLength': self.minPasswordLength,
542 u'strengthCriteria': self.strengthCriteria}
543
544
545
514class ChoiceInput(FormInput):546class ChoiceInput(FormInput):
515 """547 """
516 Abstract input with multiple options.548 Abstract input with multiple options.

Subscribers

People subscribed via source and target branches