openui5-password
Version:
An OpenUI5 Control which checks password strength and validates it against predefined rules
595 lines (542 loc) • 22.3 kB
JavaScript
sap.ui.require([
'jquery.sap.global',
'sap/ui/core/ValueState',
'sap/m/List',
'sap/m/ResponsivePopover',
'sap/m/StandardListItem',
'openui5/password/Password'
], function(jQuery, ValueState, List, ResponsivePopover, StandardListItem, Password) {
'use strict';
function createPasswordHelper() {
const password = new Password();
password.placeAt(domRefId);
sap.ui.getCore().applyChanges();
return password;
}
const domRefId = 'qunit-fixture';
const {test} = QUnit;
const sandbox = (sinon.createSandbox) ? sinon.createSandbox() : sinon.sandbox.create();
const resourceBundle = sap.ui.getCore().getLibraryResourceBundle('openui5.password');
QUnit.module('Password', function() {
QUnit.module('Password basics', () => {
test('Should instantiate the control with defaults', (assert) => {
const password = new Password();
assert.strictEqual(password.getValue(), '');
assert.strictEqual(password.getRequireNumbers(), true);
assert.strictEqual(password.getRequireLetters(), true);
assert.strictEqual(password.getRequireSymbols(), true);
assert.strictEqual(password.getRequireLowercase(), true);
assert.strictEqual(password.getRequireUppercase(), true);
assert.strictEqual(password.getMinLength(), 0);
assert.strictEqual(password.getMaxLength(), 0);
assert.strictEqual(password._popover, undefined);
});
});
QUnit.module('Password properties', () => {
test('Should get/set properties', (assert) => {
const password = createPasswordHelper();
password.setWidth();
assert.strictEqual(password.getWidth(), '100%');
password.setWidth('42%');
assert.strictEqual(password.getWidth(), '42%');
password.destroy();
const password2 = new Password();
password2.setEnabled(false);
password2.placeAt(domRefId);
sap.ui.getCore().applyChanges();
assert.strictEqual(jQuery('#' + password2.getId() + '-inner').prop('readonly'), true);
password2.destroy();
});
});
QUnit.module('_calculateScore()', () => {
test('Should load library responsible for the calculation', (assert) => {
assert.strictEqual(zxcvbn instanceof Function, true);
});
test('Should calculate password strength and return score', (assert) => {
const password = new Password();
assert.strictEqual(password._calculateScore(), 0);
assert.strictEqual(password._calculateScore('123456'), 0);
assert.strictEqual(password._calculateScore('abcd12345'), 1);
assert.strictEqual(password._calculateScore('@Nac123!'), 2);
assert.strictEqual(password._calculateScore('@Nac123!P2'), 3);
assert.strictEqual(password._calculateScore('@Nac123!P2$#'), 4);
});
});
QUnit.module('_setStatus()', () => {
test('Should set status for password input element', (assert) => {
const password = new Password();
password._setStatus(0);
assert.strictEqual(password.getValueState(), ValueState.Error);
assert.strictEqual(password.getValueStateText(), resourceBundle.getText('PASSWORD_IS_VERY_WEAK'));
});
});
QUnit.module('_getStatus()', () => {
test('Should return default status for a not expected score', (assert) => {
const password = new Password();
const score0 = password._getStatus();
const score1 = password._getStatus(5);
assert.strictEqual(score0.state, ValueState.None);
assert.strictEqual(score0.text, '');
assert.strictEqual(score1.state, ValueState.None);
assert.strictEqual(score1.text, '');
});
test('Should return error status for a given score', (assert) => {
const password = new Password();
const score0 = password._getStatus(0);
const score1 = password._getStatus(1);
assert.strictEqual(score0.state, ValueState.Error);
assert.strictEqual(score0.text, resourceBundle.getText('PASSWORD_IS_VERY_WEAK'));
assert.strictEqual(score1.state, ValueState.Error);
assert.strictEqual(score1.text, resourceBundle.getText('PASSWORD_IS_WEAK'));
});
test('Should return warning status for a given score', (assert) => {
const password = new Password();
const score2 = password._getStatus(2);
assert.strictEqual(score2.state, ValueState.Warning);
assert.strictEqual(score2.text, resourceBundle.getText('PASSWORD_IS_NOT_STRONG_ENOUGH'));
});
test('Should return success status for a given score', (assert) => {
const password = new Password();
const score3 = password._getStatus(3);
const score4 = password._getStatus(4);
assert.strictEqual(score3.state, ValueState.Success);
assert.strictEqual(score3.text, resourceBundle.getText('PASSWORD_IS_STRONG'));
assert.strictEqual(score4.state, ValueState.Success);
assert.strictEqual(score4.text, resourceBundle.getText('PASSWORD_IS_VERY_STRONG'));
});
});
QUnit.module('_getPasswordErrors()', () => {
test('Should fail because it requires a number', (assert) => {
const password = new Password({
requireNumbers: true,
requireLetters: false,
requireSymbols: false,
requireLowercase: false,
requireUppercase: false
});
const expectedInfo = '[0-9]';
let value = 'abcde';
let errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 1);
errors.forEach((err) => {
assert.strictEqual(err.getInfo(), expectedInfo);
assert.strictEqual(err.getInfoState(), ValueState.Error);
});
value = 'abcde!@#$';
errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 1);
errors.forEach((err) => {
assert.strictEqual(err.getInfo(), expectedInfo);
assert.strictEqual(err.getInfoState(), ValueState.Error);
});
});
test('Should fail because it requires a letter', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: true,
requireSymbols: false,
requireLowercase: false,
requireUppercase: false
});
const expectedInfo = '[a-z , A-Z]';
let value = '123456';
let errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 1);
errors.forEach((err) => {
assert.strictEqual(err.getInfo(), expectedInfo);
assert.strictEqual(err.getInfoState(), ValueState.Error);
});
value = '1321 !@#$';
errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 1);
errors.forEach((err) => {
assert.strictEqual(err.getInfo(), expectedInfo);
assert.strictEqual(err.getInfoState(), ValueState.Error);
});
});
test('Should fail because it requires a lowercase letter', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: true,
requireSymbols: false,
requireLowercase: true,
requireUppercase: false
});
const value = 'ABCDE';
const errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 1);
errors.forEach((err) => {
assert.strictEqual(err.getInfo(), '[a-z]');
assert.strictEqual(err.getInfoState(), ValueState.Error);
});
});
test('Should fail because it requires an uppercase letter', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: true,
requireSymbols: false,
requireLowercase: false,
requireUppercase: true
});
const value = 'abcde';
const errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 1);
errors.forEach((err) => {
assert.strictEqual(err.getInfo(), '[A-Z]');
assert.strictEqual(err.getInfoState(), ValueState.Error);
});
});
test('Should fail because it requires a special symbol', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: false,
requireSymbols: true,
requireLowercase: false,
requireUppercase: false
});
const expectedInfo = '[!, @, #, $, %, &...]';
let value = 'abcde';
let errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 1);
errors.forEach((err) => {
assert.strictEqual(err.getInfo(), expectedInfo);
assert.strictEqual(err.getInfoState(), ValueState.Error);
});
value = '123456';
errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 1);
errors.forEach((err) => {
assert.strictEqual(err.getInfo(), expectedInfo);
assert.strictEqual(err.getInfoState(), ValueState.Error);
});
value = 'ABce123';
errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 1);
errors.forEach((err) => {
assert.strictEqual(err.getInfo(), expectedInfo);
assert.strictEqual(err.getInfoState(), ValueState.Error);
});
});
test('Should fail because it limits the minimum length', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: false,
requireSymbols: false,
requireLowercase: false,
requireUppercase: false,
minLength: 10
});
const expectedInfo = password.getMinLength() + ' characters';
const value = 'abcde1234';
const errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 1);
errors.forEach((err) => {
assert.strictEqual(err.getInfo(), expectedInfo);
assert.strictEqual(err.getInfoState(), ValueState.Error);
});
});
test('Should fail because it limits the maximum length', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: false,
requireSymbols: false,
requireLowercase: false,
requireUppercase: false,
maxLength: 10
});
const expectedInfo = password.getMaxLength() + ' characters';
const value = 'abcde123456';
const errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 1);
errors.forEach((err) => {
assert.strictEqual(err.getInfo(), expectedInfo);
assert.strictEqual(err.getInfoState(), ValueState.Error);
});
});
test('Should pass because it requires a number', (assert) => {
const password = new Password({
requireNumbers: true,
requireLetters: false,
requireSymbols: false,
requireLowercase: false,
requireUppercase: false
});
let value = 'abcde1';
let errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
value = '123456';
errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
value = '$@123adsfa456a';
errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
});
test('Should pass because it requires a letter', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: true,
requireSymbols: false,
requireLowercase: false,
requireUppercase: false
});
let value = 'abcde';
let errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
value = 'ABCDE';
errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
value = 'abc123DEF!@#';
errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
});
test('Should pass because it requires a lowercase letter', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: true,
requireSymbols: false,
requireLowercase: true,
requireUppercase: false
});
const value = 'abcde';
const errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
});
test('Should pass because it requires an uppercase letter', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: true,
requireSymbols: false,
requireLowercase: false,
requireUppercase: true
});
const value = 'ABCDE';
const errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
});
test('Should pass because it requires a symbol', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: false,
requireSymbols: true,
requireLowercase: false,
requireUppercase: false
});
let value = '!@#$%';
let errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
value = '123$456';
errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
value = 'asdf#%ttyty';
errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
value = '$@123adsfa4DD%&56a';
errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
});
test('Should pass because it limits the minimum length', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: false,
requireSymbols: false,
requireLowercase: false,
requireUppercase: false,
minLength: 10
});
let value = 'abcde12345';
let errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
value = 'abcde12345$';
errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
});
test('Should pass because it limits the maximum length', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: false,
requireSymbols: false,
requireLowercase: false,
requireUppercase: false,
maxLength: 10
});
let value = 'abcde1234';
let errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
value = 'abcde12345';
errors = password._getPasswordErrors(value);
assert.strictEqual(errors.length, 0);
});
});
QUnit.module('_createPopover()', () => {
test('Should instantiate a popover', (assert) => {
const password = new Password();
password._createPopover();
assert.strictEqual(password._popover instanceof ResponsivePopover, true);
assert.strictEqual(password._popover.getId(), password.getId() + '-popover');
assert.strictEqual(password._popover.getContent()[0] instanceof List, true);
});
});
QUnit.module('_getPopover()', () => {
test('Should return a popover instance', (assert) => {
const password = new Password();
const oPopover = password._getPopover();
assert.strictEqual(oPopover instanceof ResponsivePopover, true);
assert.strictEqual(oPopover.getId(), password.getId() + '-popover');
assert.strictEqual(password._popover instanceof ResponsivePopover, oPopover instanceof ResponsivePopover);
assert.strictEqual(password._popover.getId(), oPopover.getId());
assert.strictEqual(password._popover.getContent()[0].getItems().length, 0);
for (let i = 0; i < 5; i++) {
assert.strictEqual(oPopover.getId(), password._getPopover().getId());
}
});
test('Should return a popover instance with errors', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: true,
requireSymbols: false,
requireLowercase: true,
requireUppercase: false
});
const value = '12345';
const errors = password._getPasswordErrors(value);
const popover = password._getPopover(errors);
assert.strictEqual(popover.getContent()[0].getItems().length, 2);
});
});
QUnit.module('_addPasswordErrorsToPopover()', () => {
test('Should add messages to a list in a popover', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: true,
requireSymbols: false,
requireLowercase: true,
requireUppercase: false
});
const value = '12345';
const errors = password._getPasswordErrors(value);
const oPopover = password._getPopover();
assert.strictEqual(oPopover.getContent()[0].getItems().length, 0);
for (let i = 0; i < 5; i++) {
password._addPasswordErrorsToPopover(errors);
assert.strictEqual(oPopover.getContent()[0].getItems().length, 2);
assert.strictEqual(password._popover.getContent()[0].getItems()[0] instanceof StandardListItem, true);
}
});
});
QUnit.module('_showPasswordErrors()', () => {
test('Should open a popover', (assert) => {
const password = new Password();
password.placeAt(domRefId);
sap.ui.getCore().applyChanges();
password._showPasswordErrors();
assert.notStrictEqual(password._getPopover(), undefined);
assert.strictEqual(password._getPopover().isOpen(), true);
assert.strictEqual(password.getValueState(), ValueState.Error);
password.destroy();
});
test('Should not open a popover', (assert) => {
const password = new Password({
requireNumbers: false,
requireLetters: false,
requireSymbols: false,
requireLowercase: false,
requireUppercase: false
});
password.placeAt(domRefId);
sap.ui.getCore().applyChanges();
password._showPasswordErrors();
assert.notStrictEqual(password._getPopover(), undefined);
assert.strictEqual(password._getPopover().isOpen(), false);
assert.strictEqual(password.getValueState(), ValueState.Success);
password.destroy();
});
});
QUnit.module('exit()', () => {
test('Should destroy instances when exit', (assert) => {
const password = createPasswordHelper();
password._getPopover();
password.exit();
assert.strictEqual(password._popover, null);
password.destroy();
});
});
QUnit.module('oninput()', {
afterEach: () => {
sandbox.restore();
}
}, () => {
test('Should fire oninput event', (assert) => {
const spy = sandbox.spy(Password.prototype, 'oninput');
const password = createPasswordHelper();
password.setValue('abcd12345');
jQuery('#' + password.getId()).trigger('input');
assert.strictEqual(password.getScore(), 1);
assert.strictEqual(spy.callCount, 1);
password.destroy();
});
test('Should fire oninput event marked as invalid', (assert) => {
const spy = sandbox.spy(Password.prototype, 'oninput');
const fakeEvent = {
isMarked: () => 'invalid'
};
const password = createPasswordHelper();
password.setValue('abcd12345');
password.oninput(fakeEvent);
assert.strictEqual(password.getScore(), 0);
assert.strictEqual(spy.callCount, 1);
password.destroy();
});
});
QUnit.module('onfocusin()', {
afterEach: () => {
sandbox.restore();
}
}, () => {
test('Should fire onfocusin event', (assert) => {
const spy = sandbox.spy(Password.prototype, 'onfocusin');
const password = createPasswordHelper();
const passwordDomRef = jQuery('#' + password.getId());
passwordDomRef.trigger('focusin');
assert.strictEqual(passwordDomRef.hasClass('sapMInputFocused'), true);
assert.strictEqual(password._getPopover().isOpen(), false);
passwordDomRef.trigger('focusout');
passwordDomRef.trigger('focusin');
assert.strictEqual(password._getPopover().isOpen(), false);
assert.strictEqual(spy.callCount, 2);
password.destroy();
});
});
QUnit.module('onfocusout()', {
afterEach: () => {
sandbox.restore();
}
}, () => {
test('Should fire onfocusout event', (assert) => {
const spy = sandbox.spy(Password.prototype, 'onfocusout');
const password = createPasswordHelper();
const passwordDomRef = jQuery('#' + password.getId());
password.focus();
passwordDomRef.trigger('focusout');
assert.strictEqual(passwordDomRef.hasClass('sapMInputFocused'), false);
assert.strictEqual(spy.callCount, 1);
password.destroy();
});
});
QUnit.module('onsapfocusleave()', {
afterEach: () => {
sandbox.restore();
}
}, () => {
test('Should fire onsapfocusleave event', (assert) => {
const spy = sandbox.spy(Password.prototype, 'onsapfocusleave');
const password = createPasswordHelper();
password.onfocusin();
password.onsapfocusleave();
assert.notStrictEqual(password._getPopover(), undefined);
assert.strictEqual(password._getPopover().isOpen(), true);
assert.strictEqual(spy.callCount, 1);
password.destroy();
});
});
});
});