diff --git a/.travis.yml b/.travis.yml index aae912b..c1df0a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ before_install: - curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash - && sudo apt-get install -y nodejs before_script: - - cd lib/assets && npm install && npm run bundle && cd - + - cd lib/assets && npm install && npm run bundle -- -p && cd - - cp config/database_example.yml config/database.yml - cp config/secrets_example.yml config/secrets.yml - cp config/branding_example.yml config/branding.yml diff --git a/app/views/layouts/_es5_scripts.html.erb b/app/views/layouts/_es5_scripts.html.erb index 8bfc6b8..3d4c9b6 100644 --- a/app/views/layouts/_es5_scripts.html.erb +++ b/app/views/layouts/_es5_scripts.html.erb @@ -14,13 +14,13 @@ - <%= javascript_include_tag 'utils/define.js' %> - <%= javascript_include_tag 'utils/debounce.js' %> - <%= javascript_include_tag 'utils/tinymce.js' %> - <%= javascript_include_tag 'utils/validate.js' %> - <%= javascript_include_tag 'utils/ariatiseForm.js' %> - <%= javascript_include_tag 'utils/filteriseTable.js' %> - <%= javascript_include_tag 'utils/collateTable.js' %> + <%= javascript_include_tag 'utils_es5/define.js' %> + <%= javascript_include_tag 'utils_es5/debounce.js' %> + <%= javascript_include_tag 'utils_es5/tinymce.js' %> + <%= javascript_include_tag 'utils_es5/validate.js' %> + <%= javascript_include_tag 'utils_es5/ariatiseForm.js' %> + <%= javascript_include_tag 'utils_es5/filteriseTable.js' %> + <%= javascript_include_tag 'utils_es5/collateTable.js' %> diff --git a/lib/assets/.eslintrc.json b/lib/assets/.eslintrc.json index 6f67564..6ebd320 100644 --- a/lib/assets/.eslintrc.json +++ b/lib/assets/.eslintrc.json @@ -1,3 +1,6 @@ { - "extends": "airbnb-base" + "extends": "airbnb-base", + "env": { + "jasmine": true + } } \ No newline at end of file diff --git a/lib/assets/README.md b/lib/assets/README.md index 820a2dc..7156ebf 100644 --- a/lib/assets/README.md +++ b/lib/assets/README.md @@ -1,10 +1,31 @@ -## Bundle for development +## Set up +We use WebPack to pre-compile all our assets. Before executing any bundling, please make sure that all the dependencies are installed first by typing: + ``` - npm run bundle +npm install +``` +within lib/assets + +## Assets Bundling + +We have set up two environments, one for development which watching for changes at any .js or .css file to pre-compile on the fly and another for production. For a development environment, i.e. if the developer is making changes, please type: + +``` +npm run bundle ``` -## Bundle for production: +and for production, please type: + ``` - npm run bundle -- -p +npm run bundle -- -p ``` -Remember this will generated output files with a new hash associated in order to prevent browser to use a previous cached version. You will need to stop and start the rails server. \ No newline at end of file + +Note, the above commands have to run within lib/assets directory. + +## Testing + +We use jasmine to write unit tests together with karma for testing in real browser our functionality. Please type the following command to execute every test for JavaScript modules. + +``` +npm test +``` diff --git a/lib/assets/javascripts/constants.js b/lib/assets/javascripts/constants.js new file mode 100644 index 0000000..407b385 --- /dev/null +++ b/lib/assets/javascripts/constants.js @@ -0,0 +1,2 @@ +export const PASSWORD_MIN_LENGTH = 8; +export const PASSWORD_MAX_LENGTH = 128; diff --git a/lib/assets/javascripts/utils/ariatiseForm.js b/lib/assets/javascripts/utils/ariatiseForm.js deleted file mode 100644 index 6762df8..0000000 --- a/lib/assets/javascripts/utils/ariatiseForm.js +++ /dev/null @@ -1,122 +0,0 @@ -/* - dmproadmap.utils.ariatiseForm augmentates a HTML form by: - - Associating help text with form controls - - Adding validation state to each form group - - Adding specific attributes for user with assistive technologies. - - For example the following form : -
- will be augmentated as follows: - - and any time the buttton is clicked the validation according to each type (e.g. text, email) will be triggered. An invalid result for a form-control will: - 1. Add has-error class to its form-group parent and aria-invalid="true" to the form-control - 2. Show its help-block following sibling - 3. Prevent form to be submitted -*/ -(function(ctx){ - var requiredFields=(function(selector){ - return $(selector).find('.form-control').filter('[aria-required="true"]'); - }); - var blockHelp=(function (id,type){ - var msg='Please fill out this field with a valid '+type+'.'; //TODO internationalisation - return ''; - }); - var ariaDescribedBy=(function(value){ - return { 'aria-describedby': value }; - }); - var ariaInvalid=(function(value){ - return { 'aria-invalid': value }; - }); - var validationStates={ - hasWarning: 'has-warning', - hasError: 'has-error', - hasSuccess: 'has-success' - }; - var getTypeForSubmittableElement=(function(el){ - // Reference from https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Form-associated_content - if($(el).is('input')){ - return $(el).attr('type'); // available types at https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form__types - } - else if($(el).is('select')){ - return 'select'; - } - else if($(el).is('textarea')){ - return 'textarea'; - } - else - return 'unknown'; - }); - var isValid=(function(type, value){ - // TODO add more validation for each new type coming along by: - // 1. defining a function at dmproadmap.utils.validate - // 2. adding the case in the switch below - switch(type){ - case 'text': - case 'textarea': - return dmproadmap.utils.validate.text(value); - case 'email': - return dmproadmap.utils.validate.email(value); - default: - return false; - } - }); - var valid=(function(el){ - $(el).parent().removeClass(validationStates.hasError); - $(el).attr(ariaInvalid(false)); - $(el).next().hide(); - }); - var invalid=(function(el){ - $(el).parent().addClass(validationStates.hasError); - $(el).attr(ariaInvalid(true)); - $(el).next().show(); - }); - ctx.init=ctx.init || (function(options){ - if($ && options && options.selector){ - requiredFields(options.selector).each(function(i,el){ - $(el).attr(ariaDescribedBy('help'+i)); - $(el).after(blockHelp('help'+i, getTypeForSubmittableElement(el))); - }); - $(options.selector+' [type="submit"]').click(function(e){ - requiredFields(options.selector).each(function(i,el){ - if(isValid(getTypeForSubmittableElement(el),$(el).val())){ - valid(el); - } - else{ - e.preventDefault(); - invalid(el); - } - }); - }); - } - }); -})(define('dmproadmap.utils.ariatiseForm')); \ No newline at end of file diff --git a/lib/assets/javascripts/utils/collateTable.js b/lib/assets/javascripts/utils/collateTable.js deleted file mode 100644 index a410c12..0000000 --- a/lib/assets/javascripts/utils/collateTable.js +++ /dev/null @@ -1,21 +0,0 @@ -/* - tablesorter is an external library located in vendor/tablesorter. - - it does not provide us with icons though, so we add our own below along - with logic to change them to up/down arrows when the user sorts the column -*/ - -(function(ctx){ - ctx.init = ctx.init || (function(options){ - if($ && options && options.selector){ - /* Bind the table to the external tablesorter JS (see vendor/tablesorter) */ - $(options.selector).tablesorter({ - theme: 'bootstrap_3', - headerTemplate: '{content} {icon}', - cssIconAsc: 'fa fa-sort-asc', - cssIconDesc: 'fa fa-sort-desc', - cssIconNone: 'fa fa-sort' - }); - } - }); -})(define('dmproadmap.utils.collateTable')); \ No newline at end of file diff --git a/lib/assets/javascripts/utils/debounce.js b/lib/assets/javascripts/utils/debounce.js index aa00c85..8013d68 100644 --- a/lib/assets/javascripts/utils/debounce.js +++ b/lib/assets/javascripts/utils/debounce.js @@ -1,33 +1,24 @@ -(function(ctx){ - /* - Delays invoking of the function passed until after wait milliseconds have elapsed since - the last time the debounced function was invoked. - @param {function} func - the function to execute later on - @param {number} wait - the number of milliseconds to wait until func is executed - @returns The debounced function. It comes with a cancel method to cancel delayed func invocation - */ - ctx.debounce = ctx.debounce || (function(func, wait){ - var timeoutID = null; - function cancel() { - if(timeoutID !== null){ - clearTimeout(timeoutID); - return true; - } - return false; - } - return (function() { - var debounced = function() { - var ctx = this; - var args = arguments; - var later = function() { - timeoutID = null; - func.apply(ctx, args); - } - clearTimeout(timeoutID); - timeoutID = setTimeout(later, wait || 1000); - } - debounced.cancel = cancel; - return debounced; - })(); - }); -})(define('dmproadmap.utils')); \ No newline at end of file +import { isFunction, isNumber } from './isType'; + +export default function debounce(func, wait) { + if (isFunction(func) && (wait || isNumber(wait))) { + let timeoutID = null; + const closureDebounce = (...args) => { + const delayed = () => { + timeoutID = null; + func.apply(this, args); + }; + clearTimeout(timeoutID); + timeoutID = setTimeout(delayed, wait || 1000); + }; + closureDebounce.cancel = () => { + if (timeoutID) { + clearTimeout(timeoutID); + return true; + } + return false; + }; + return closureDebounce; + } + return null; +} diff --git a/lib/assets/javascripts/utils/define.js b/lib/assets/javascripts/utils/define.js deleted file mode 100644 index b1e44ed..0000000 --- a/lib/assets/javascripts/utils/define.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - define is an utility to avoid namespace collisions. This function assumes that the root is dmproadmap. - The local names are visited and constructed if they do not exist to return the context - object of the last local name specified in the String. - The string has to be separated by '.' and currently there are no restrictions for local names - (e.g. empty strings) are allowed. - @param value String value representing a fully namespace - @return The last local name context from the hierarchy of objects - - Usage: define('dmproadmap.a.b.c.d') will return the context of d by creating the following hierarchy if - does not exist: - dmproadmap: { - a: { - b: { - c: { - d: {} - } - } - } - } -*/ -var define=function(value){ - var root='dmproadmap'; - if(Object.prototype.toString.call(value) === '[object String]'){ - var arrayNs=value.split('.'); - var restNs; - if(arrayNs[0] === root){ - var restNs=arrayNs.slice(1); - return restNs.reduce(function(ns, value){ - if(!ns[value]) return ns[value]={}; - return ns[value]; - }, window[root] = window[root] || {}); - } - } -}; \ No newline at end of file diff --git a/lib/assets/javascripts/utils/filteriseTable.js b/lib/assets/javascripts/utils/filteriseTable.js deleted file mode 100644 index 25e2aff..0000000 --- a/lib/assets/javascripts/utils/filteriseTable.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - filteriseTable adds filter capabilities to an HTML table - table rows are shown/hidden as the user enters text into the filter input field - all rows are made visible when the user clicks the 'clear' icon -*/ -(function(ctx){ - - var filter = (function(el){ - var query = $(el).val(), - regex = new RegExp(query, 'i'); - - $.each($(el).closest("table").find("tbody tr"), function(idx, tr){ - if(regex.test($(tr).text())){ - $(tr).show(); - }else{ - $(tr).hide(); - } - }); - }); - - var clear = (function(el){ - $(el).val(''); - $(el).closest("table").find("tbody tr").show(); - }); - - ctx.init = ctx.init || (function(options){ - if($ && options && options.selector){ - var id = $(this).attr("id"); - - /* initialize a debounced listener for the filter box */ - var debounced = dmproadmap.utils.debounce(filter); - - /* Bind the clear function to the clear icon's click event */ - $(options.selector).keyup(function(){ - debounced(this); - }); - - $(options.selector).siblings("#clear_filter").click(function(e){ - e.preventDefault(); - clear(this); - debounced.cancel(); - }); - } - }); - -})(define('dmproadmap.utils.filteriseTable')); \ No newline at end of file diff --git a/lib/assets/javascripts/utils/isType.js b/lib/assets/javascripts/utils/isType.js new file mode 100644 index 0000000..f8fd966 --- /dev/null +++ b/lib/assets/javascripts/utils/isType.js @@ -0,0 +1,68 @@ +const toString = Object.prototype.toString; +/* + Checks whether or the value passed is type Array. + @param value to check + @return true or false +*/ +export const isArray = Array.isArray; +/* + Checks whether or the value passed is type boolean. + Note the use of new is discouraged, e.g. new Boolean(true) and might return false in some cases + @param value to check + @return true or false +*/ +export const isBoolean = value => typeof value === 'boolean'; +/* + Checks whether or the value passed is type Date. + @param value to check + @return true or false +*/ +export const isDate = value => toString.call(value) === '[object Date]'; +/* + Checks whether or the value passed is type function. + Note the use of new is discouraged, e.g. new Function(...) and might return false in some cases + @param value to check + @return true or false +*/ +export const isFunction = value => typeof value === 'function'; +/* + Checks whether or the value passed is type number. + Note the use of new is discouraged, e.g. new Number(1) and might return false in some cases. + This method will return true for NaN and Infinity too. + @param value to check + @return true or false +*/ +export const isNumber = value => typeof value === 'number'; +/* + Checks whether or the value passed is type null. + @param value to check + @return true or false +*/ +export const isNull = value => value === null; +/* + Checks whether or the value passed is type object. + This will return true for any kind of object (Array, Date, RegExp ...) so consider + using more accurate method defined here. + @param value to check + @return true or false +*/ +export const isObject = value => value !== null && typeof value === 'object'; +/* + Checks whether or the value passed is type RegExp + @param value to check + @return true or false +*/ +export const isRegExp = value => toString.call(value) === '[object RegExp]'; +/* + Checks whether or the value passed is type string. + Note the use of new is discouraged, e.g. new String('aaa') and might return false in some cases + @param value to check + @return true or false +*/ +export const isString = value => typeof value === 'string'; +/* + Checks whether or the value passed is type undefined. + @param value to check + @return true or false +*/ +export const isUndefined = value => value === undefined; diff --git a/lib/assets/javascripts/utils/isTypeSpec.js b/lib/assets/javascripts/utils/isTypeSpec.js new file mode 100644 index 0000000..f85ef1a --- /dev/null +++ b/lib/assets/javascripts/utils/isTypeSpec.js @@ -0,0 +1,146 @@ +import { + isArray, + isBoolean, + isDate, + isFunction, + isNumber, + isNull, + isObject, + isRegExp, + isString, + isUndefined } from './isType'; + +describe('isArray test suite', () => { + it('expect true for []', () => expect(isArray([])).toBe(true)); + it('expect false for Boolean', () => expect(isArray(true)).toBe(false)); + it('expect false for Date', () => expect(isArray(new Date())).toBe(false)); + it('expect false for Function', () => expect(isArray(() => {})).toBe(false)); + it('expect false for Number', () => expect(isArray(1)).toBe(false)); + it('expect false for Null', () => expect(isArray(null)).toBe(false)); + it('expect false for Object', () => expect(isArray({})).toBe(false)); + it('expect false for RegExp', () => expect(isArray(/foo/)).toBe(false)); + it('expect false for String', () => expect(isArray('Hello World!')).toBe(false)); + it('expect false for Undefined', () => expect(isArray(undefined)).toBe(false)); + it('expect false for zero args', () => expect(isArray()).toBe(false)); +}); + +describe('isBoolean test suite', () => { + it('expect true for true', () => expect(isBoolean(true)).toBe(true)); + it('expect true for false', () => expect(isBoolean(true)).toBe(true)); + it('expect false for []', () => expect(isBoolean([])).toBe(false)); + it('expect false for Date', () => expect(isBoolean(new Date())).toBe(false)); + it('expect false for Function', () => expect(isBoolean(() => {})).toBe(false)); + it('expect false for Number', () => expect(isBoolean(1)).toBe(false)); + it('expect false for Null', () => expect(isBoolean(null)).toBe(false)); + it('expect false for Object', () => expect(isBoolean({})).toBe(false)); + it('expect false for RegExp', () => expect(isBoolean(/foo/)).toBe(false)); + it('expect false for String', () => expect(isBoolean('Hello World!')).toBe(false)); + it('expect false for Undefined', () => expect(isBoolean(undefined)).toBe(false)); + it('expect false for zero args', () => expect(isBoolean()).toBe(false)); +}); + +describe('isDate test suite', () => { + it('expect true for Date', () => expect(isDate(new Date())).toBe(true)); + it('expect fase for []', () => expect(isDate([])).toBe(false)); + it('expect fase for Boolean', () => expect(isDate(true)).toBe(false)); + it('expect fase for Function', () => expect(isDate(() => {})).toBe(false)); + it('expect fase for Number', () => expect(isDate(1)).toBe(false)); + it('expect fase for Null', () => expect(isDate(null)).toBe(false)); + it('expect fase for Object', () => expect(isDate({})).toBe(false)); + it('expect fase for RegExp', () => expect(isDate(/foo/)).toBe(false)); + it('expect fase for String', () => expect(isDate('Hello World!')).toBe(false)); + it('expect fase for zero args', () => expect(isDate(undefined)).toBe(false)); +}); + +describe('isFunction test suite', () => { + it('expect true for Function', () => expect(isFunction(() => {})).toBe(true)); + it('expect false for []', () => expect(isFunction([])).toBe(false)); + it('expect false for Boolean', () => expect(isFunction(true)).toBe(false)); + it('expect false for Date', () => expect(isFunction(new Date())).toBe(false)); + it('expect false for Number', () => expect(isFunction(1)).toBe(false)); + it('expect false for Null', () => expect(isFunction(null)).toBe(false)); + it('expect false for Object', () => expect(isFunction({})).toBe(false)); + it('expect false for RegExp', () => expect(isFunction(/foo/)).toBe(false)); + it('expect false for String', () => expect(isFunction('Hello World!')).toBe(false)); + it('expect false for undefined', () => expect(isFunction(undefined)).toBe(false)); + it('expect false for zero args', () => expect(isFunction()).toBe(false)); +}); + +describe('isNumber test suite', () => { + it('expect true for 1', () => expect(isNumber(1)).toBe(true)); + it('expect true for NaN', () => expect(isNumber(NaN)).toBe(true)); + it('expect true for Infinity', () => expect(isNumber(Infinity)).toBe(true)); + it('expect false for []', () => expect(isNumber([])).toBe(false)); + it('expect false for Boolean', () => expect(isNumber(true)).toBe(false)); + it('expect false for Date', () => expect(isNumber(new Date())).toBe(false)); + it('expect false for Null', () => expect(isNumber(null)).toBe(false)); + it('expect false for Object', () => expect(isNumber({})).toBe(false)); + it('expect false for RegExp', () => expect(isNumber(/foo/)).toBe(false)); + it('expect false for String', () => expect(isNumber('Hello World!')).toBe(false)); + it('expect false for undefined', () => expect(isNumber(undefined)).toBe(false)); + it('expect false for zero args', () => expect(isNumber()).toBe(false)); +}); + +describe('isNull test suite', () => { + it('expect true for Null', () => expect(isNull(null)).toBe(true)); + it('expect false for []', () => expect(isNull([])).toBe(false)); + it('expect false for Boolean', () => expect(isNull(true)).toBe(false)); + it('expect false for Date', () => expect(isNull(new Date())).toBe(false)); + it('expect false for Number', () => expect(isNull(1)).toBe(false)); + it('expect false for Object', () => expect(isNull(Object.create(null))).toBe(false)); + it('expect false for RegExp', () => expect(isNull(/foo/)).toBe(false)); + it('expect false for String', () => expect(isNull('null')).toBe(false)); + it('expect false for undefined', () => expect(isNull(undefined)).toBe(false)); + it('expect false for zero args', () => expect(isNull()).toBe(false)); +}); + +describe('isObject test suite', () => { + it('expect true for {}', () => expect(isObject({})).toBe(true)); + it('expect true for []', () => expect(isObject([])).toBe(true)); + it('expect true for Date', () => expect(isObject(new Date())).toBe(true)); + it('expect true for RegExp', () => expect(isObject(/foo/)).toBe(true)); + it('expect false for Number', () => expect(isObject(1)).toBe(false)); + it('expect false for Null', () => expect(isObject(null)).toBe(false)); + it('expect false for String', () => expect(isObject('Hello World!')).toBe(false)); + it('expect false for undefined', () => expect(isObject(undefined)).toBe(false)); + it('expect false for zero args', () => expect(isObject()).toBe(false)); +}); + +describe('isRegExp test suite', () => { + it('expect true for RegExp', () => expect(isRegExp(/foo/)).toBe(true)); + it('expect true for RegExp', () => expect(isRegExp(new RegExp('foo'))).toBe(true)); + it('expect false for []', () => expect(isRegExp([])).toBe(false)); + it('expect false for Boolean', () => expect(isRegExp(true)).toBe(false)); + it('expect false for Date', () => expect(isRegExp(new Date())).toBe(false)); + it('expect false for Number', () => expect(isRegExp(1)).toBe(false)); + it('expect false for Null', () => expect(isRegExp(null)).toBe(false)); + it('expect false for String', () => expect(isRegExp('Hello World!')).toBe(false)); + it('expect false for undefined', () => expect(isRegExp(undefined)).toBe(false)); + it('expect false for zero args', () => expect(isRegExp()).toBe(false)); +}); + +describe('isString test suite', () => { + it('expect true for String', () => expect(isString('Hello World!')).toBe(true)); + it('expect false for []', () => expect(isString([])).toBe(false)); + it('expect false for Boolean', () => expect(isString(true)).toBe(false)); + it('expect false for Date', () => expect(isString(new Date())).toBe(false)); + it('expect false for Function', () => expect(isString(() => {})).toBe(false)); + it('expect false for Number', () => expect(isString(1)).toBe(false)); + it('expect false for Null', () => expect(isString(null)).toBe(false)); + it('expect false for RegExp', () => expect(isString(/foo/)).toBe(false)); + it('expect false for Undefined', () => expect(isString(undefined)).toBe(false)); + it('expect false for zero args', () => expect(isString()).toBe(false)); +}); + +describe('isUndefined test suite', () => { + it('expect true for Undefined', () => expect(isUndefined(undefined)).toBe(true)); + it('expect false for zero args', () => expect(isUndefined()).toBe(true)); + it('expect false for []', () => expect(isUndefined([])).toBe(false)); + it('expect false for Boolean', () => expect(isUndefined(true)).toBe(false)); + it('expect false for Date', () => expect(isUndefined(new Date())).toBe(false)); + it('expect false for Function', () => expect(isUndefined(() => {})).toBe(false)); + it('expect false for Number', () => expect(isUndefined(1)).toBe(false)); + it('expect false for Null', () => expect(isUndefined(null)).toBe(false)); + it('expect false for RegExp', () => expect(isUndefined(/foo/)).toBe(false)); + it('expect false for String', () => expect(isUndefined('Hello World!')).toBe(false)); +}); diff --git a/lib/assets/javascripts/utils/isValidInputType.js b/lib/assets/javascripts/utils/isValidInputType.js new file mode 100644 index 0000000..6073730 --- /dev/null +++ b/lib/assets/javascripts/utils/isValidInputType.js @@ -0,0 +1,56 @@ +import { isString, isNumber } from './isType'; +import { + PASSWORD_MIN_LENGTH, + PASSWORD_MAX_LENGTH } from '../constants'; + +/* + Validates whether or not the value passed matches to a valid email + @param value String to search for a match + @return true or false +*/ +export const isValidEmail = (value) => { + if (isString(value)) { + return /[^@\s]+@(?:[-a-z0-9]+\.)+[a-z]{2,}$/.test(value); + } + return false; +}; + +/* + Validates whether or not the value passed is a valid number. + @param value Number to validate +*/ +export const isValidNumber = (value) => { + if (isString(value)) { // Only if is string value we try to convert to Number + // since Number([]), Number(new Date()), Number(null) are converted to zero + return !isNaN(Number(value)); + } + return isNumber(value); +}; + +/* + Validates whether or not the value passed falls between the min and max length + string specified for a password. + @param value String to verify its length + @return true or false +*/ +export const isValidPassword = (value) => { + if (isString(value)) { + const trimmed = value.trim(); + return trimmed.length >= PASSWORD_MIN_LENGTH && + trimmed.length <= PASSWORD_MAX_LENGTH; + } + return false; +}; + +/* + Validates whether or not the value passed is a non-empty String type. + @param value String to verify its length + @return true or false +*/ +export const isValidText = (value) => { + if (isString(value)) { + return value.trim().length > 0; + } + return false; +}; + diff --git a/lib/assets/javascripts/utils/isValidInputTypeSpec.js b/lib/assets/javascripts/utils/isValidInputTypeSpec.js new file mode 100644 index 0000000..6eaf31f --- /dev/null +++ b/lib/assets/javascripts/utils/isValidInputTypeSpec.js @@ -0,0 +1,43 @@ +import { + isValidEmail, + isValidNumber, + isValidPassword, + isValidText, +} from './isValidInputType'; + +describe('isValidEmail test suite', () => { + it('expect true for someone@somewhere.com', () => expect(isValidEmail('someone@somewhere.com')).toBe(true)); + it('expect true for s@somewhere.ac.uk', () => expect(isValidEmail('s@somewhere.ac.uk')).toBe(true)); + it('expect true for someone@somewhere.gov.ac.uk', () => expect(isValidEmail('someone@somewhere.gov.ac.uk')).toBe(true)); + it('expect false for @somewhere.com', () => expect(isValidEmail('@somewhere.com')).toBe(false)); + it('expect false for s@somewhere.ac.u', () => expect(isValidEmail('s@somewhere.ac.u')).toBe(false)); + it('expect false for someone@somewhere.gov.ac.u', () => expect(isValidEmail('someone@somewhere.gov.ac.u')).toBe(false)); +}); + +describe('isValidNumber test suite', () => { + it('expect true for 1', () => expect(isValidNumber(1)).toBe(true)); + it('expect true for \'1\'', () => expect(isValidNumber('1')).toBe(true)); + it('expect true for Infinity', () => expect(isValidNumber(Infinity)).toBe(true)); + it('expect false for Array', () => expect(isValidNumber([])).toBe(false)); + it('expect false for Boolean', () => expect(isValidNumber(true)).toBe(false)); + it('expect false for Date', () => expect(isValidNumber(new Date())).toBe(false)); + it('expect false for Function', () => expect(isValidNumber(() => {})).toBe(false)); + it('expect false for Object', () => expect(isValidNumber({})).toBe(false)); + it('expect false for RegExp', () => expect(isValidNumber(/foo/)).toBe(false)); + it('expect false for String', () => expect(isValidNumber('Hello World!')).toBe(false)); + it('expect false for Null', () => expect(isValidNumber(null)).toBe(false)); + it('expect false for Array', () => expect(isValidNumber(undefined)).toBe(false)); + it('expect false for zero args', () => expect(isValidNumber()).toBe(false)); +}); + +describe('isValidPassword test suite', () => { + it('expect true for hjkl7890', () => expect(isValidPassword('hjkl7890')).toBe(true)); + it('expect false for hjkl', () => expect(isValidPassword('hjkl')).toBe(false)); + it('expect false for \' abcd \'', () => expect(isValidPassword(' abcd ')).toBe(false)); + it('expect false for non-string', () => expect(isValidPassword(null)).toBe(false)); +}); + +describe('isValid test suite', () => { + it('expect true for h', () => expect(isValidText('h')).toBe(true)); + it('expect false for \' \'', () => expect(isValidText(' ')).toBe(false)); +}); diff --git a/lib/assets/javascripts/utils/tinymce.js b/lib/assets/javascripts/utils/tinymce.js deleted file mode 100644 index 657a352..0000000 --- a/lib/assets/javascripts/utils/tinymce.js +++ /dev/null @@ -1,101 +0,0 @@ -(function(ctx){ - ctx.tinymce = ctx.tinymce || {}; - var tinymce = ctx.tinymce; - /* - Factory to create tinyMCE object that overrides defaults with the given object options passed - @param options An object with known properties for tinyMCE - @return A tinyMCE object or throws an error if jQuery is not present when this function is invoked - */ - tinymce.factory = tinymce.factory || (function(options){ - if($){ - return $.extend(true, { - selector: 'textarea.tinymce', - statusbar: false, - menubar: false, - toolbar: 'bold italic | bullist numlist | link | table', - plugins: 'table autoresize link paste advlist', - advlist_bullet_styles: 'circle,disc,square', //only disc bullets display on htmltoword - target_list: false, - autoresize_min_height: 130, - autoresize_bottom_margin: 10, - extended_valid_elements: 'iframe[tooltip] , a[href|target=_blank]', - extended_valid_elements: 'a[href|target=_blank]', - paste_auto_cleanup_on_paste : true, - paste_remove_styles: true, - paste_retain_style_properties: 'none', - paste_convert_middot_lists: true, - paste_remove_styles_if_webkit: true, - paste_remove_spans: true, - paste_strip_class_attributes: "all", - table_default_attributes: { - border: 1 - } - }, options); - } - else - throw Error('$ is undefined'); - }); - /* - Initialises a tinymce editor given the object passed. If the object is - undefined, a default object generated by dmproadmap.utils.tinymce.factory will be used - @param obj An object with known properties for tinyMCE - */ - tinymce.init = tinymce.init || (function(options){ - if(window.tinymce){ - //TODO, there is a bug on Firefox when init is executed again after partially refreshing page (e.g. https://github.com/tinymce/tinymce/issues/3763) - window.tinymce.init(tinymce.factory(options)); - } - }); - /* - Finds any tinyMCE editor whose target element/textarea has className passed - @param className A string representing the class name of the tinyMCE editor target element/textarea to look for - @return An Array of tinymce.Editor objects - */ - tinymce.findEditorsByClassName = tinymce.findEditorsByClassName || (function(className){ - if($ && window.tinymce && className){ - return window.tinymce.editors.reduce(function(acc,e){ - if($(e.getElement()).hasClass(className)) - return acc.concat([e]); - return acc; - },[]); - } - return []; - }); - /* - Finds a tinyMCE editor whose target element/textarea has id passed - @param id A string representing the id of the tinyMCE editor target element/textarea to look for - @return tinymce.Editor object or undefined if not found - */ - tinymce.findEditorById = tinymce.findEditorById || (function(id){ - if($ && window.tinymce && id){ - // Usage of Array.prototype.find below is desired, however IE does not support it. - for(var i=0, l=window.tinymce.editors.length;i