From 918e86d79a729bba266fd406e824914bccb81393 Mon Sep 17 00:00:00 2001 From: eleith Date: Sun, 30 Sep 2012 18:30:19 -0700 Subject: [PATCH] add browser tests, use webmake for compiling into browser module --- dist/schema-browser.js | 507 +++++++++++++++++++++++++++++++++++++++++ test/browser.html | 30 +++ test/complex.js | 5 +- test/default.js | 5 +- test/error.js | 5 +- test/filters.js | 5 +- test/properties.js | 7 +- test/required.js | 9 +- test/types.js | 7 +- 9 files changed, 562 insertions(+), 18 deletions(-) create mode 100644 dist/schema-browser.js create mode 100644 test/browser.html diff --git a/dist/schema-browser.js b/dist/schema-browser.js new file mode 100644 index 0000000..bccbdfe --- /dev/null +++ b/dist/schema-browser.js @@ -0,0 +1,507 @@ +// This file was generated by modules-webmake (modules for web) project +// see: https://github.com/medikoo/modules-webmake + +window.schema = (function (modules) { + var getModule, getRequire, require; + getModule = (function (wrap) { + return function (scope, tree, path, fullpath) { + var name, dir, exports = {}, module = { exports: exports }, fn, isDir; + path = path.split('/'); + name = path.pop(); + if (!name) { + isDir = true; + name = path.pop(); + } + if ((name === '.') || (name === '..')) { + path.push(name); + name = ''; + } + while ((dir = path.shift())) { + if (dir === '..') { + scope = tree.pop(); + } else if (dir !== '.') { + tree.push(scope); + scope = scope[dir]; + } + } + if (name) { + if (!isDir && scope[name + '.js']) { + name += '.js'; + } + if (typeof scope[name] === 'object') { + tree.push(scope); + scope = scope[name]; + name = 'index.js'; + } + } else { + name = 'index.js'; + } + fn = scope[name]; + if (!fn) { + throw new Error("Could not find module '" + fullpath + "'"); + } + scope[name] = wrap(module); + fn.call(exports, exports, module, getRequire(scope, tree)); + return module.exports; + }; + }(function (cmodule) { + return function (ignore, module) { + module.exports = cmodule.exports; + }; + })); + require = function (scope, tree, fullpath) { + var name, path = fullpath, t = fullpath.charAt(0); + if (t === '/') { + path = path.slice(1); + scope = modules['/']; + tree = []; + } else if (t !== '.') { + name = path.split('/', 1)[0]; + scope = modules[name]; + tree = []; + path = path.slice(name.length + 1) || scope[':mainpath:']; + } + return getModule(scope, tree, path, fullpath); + }; + getRequire = function (scope, tree) { + return function (path) { + return require(scope, [].concat(tree), path); + }; + }; + return getRequire(modules, []); +}) +({ + "schemajs": { + "lib": { + "rules.js": function (exports, module, require) { + var Rules = function(param, rules) + { + this.param = param; + this.rules = rules; + }; + + Rules.prototype.Error = function(message, rule, value) + { + switch(typeof(this.rules.error)) + { + case 'string': + message = this.rules.error; + break; + + case 'object': + + if(this.rules.error[rule]) + { + message = this.rules.error[rule]; + } + else if(this.rules.error['default']) + { + message = this.rules.error['default']; + } + + break; + } + + if(_.isString(value)) + message = message.replace(/%v/, value.toString()); + + if(_.isString(rule)) + message = message.replace(/%r/, rule); + + return message.replace(/%p/, this.param).replace(/%v/, "value").replace(/%r/, "rule"); + }; + + Rules.prototype.apply = function(value) + { + if(_.isEmpty(value) && !_.isUndefined(this.rules['default'])) + { + value = this.rules['default']; + } + + if(this.rules.required) + { + if(this.rules.required && _.isUndefined(value)) + { + throw this.Error("%p is a required parameter", "required", value); + } + } + // if value is not required and is undefined, no more rules need to be run + else if(_.isUndefined(value)) + return value; + + if(this.rules.filters) + { + value = this.filter(value); + } + + if(this.rules.type) + { + if(typeof(this.rules.type) == "string" && typeof(is[this.rules.type]) == "function") + { + if(!is[this.rules.type](value)) + throw this.Error("%p is not a " + this.rules.type, "type", value); + } + else if(typeof(this.rules.type == "function")) + { + if(!this.rules.type(value)) + throw this.Error("%p is not a valid type", "type", value); + } + else + { + throw this.Error("%p is not a valid type", "type", value); + } + } + + if(this.rules.properties) + { + this.check(value); + } + + return value; + }; + + Rules.prototype.filter = function(value) + { + switch(typeof(this.rules.filters)) + { + case 'function': + value = this.rules.filters(value); + break; + + case 'string': + + if(typeof(filters[this.rules.filters]) === 'function') + { + value = filters[this.rules.filters](value); + } + + break; + + case 'object': + + if(_.isArray(this.rules.filters)) + { + this.rules.filters.forEach(function(filter) { value = filters[filter](value); }); + } + + break; + } + + return value; + }; + + Rules.prototype.check = function(value) + { + switch(typeof(this.rules.properties)) + { + case 'function': + + if(!this.rules.properties(value)) + throw this.Error("%p is not valid"); + + break; + + case 'object': + + var properties = _.keys(this.rules.properties); + + for(var i = 0; i < properties.length; i++) + { + var property = properties[i]; + + if(typeof(checks[property]) === "function") + { + var args = this.rules.properties[property]; + + if(!checks[property].apply(null, [value, args])) + throw this.Error("%p failed %r with %v", property); + } + else if(typeof(this.rules.properties[property]) === "function") + { + if(!this.rules.properties[property].apply(null, [value])) + throw this.Error("%p failed on %r with %v", property); + } + } + + break; + } + }; + + var checks = + { + 'max': function(value, length) + { + if(_.isArray(value) || typeof(value) == "string") + { + return value.length <= length; + } + else if(typeof(value) == "number") + { + return value <= length; + } + else + { + return false; + } + }, + + 'min': function(value, length) + { + if(_.isArray(value) || typeof(value) == "string") + { + return value.length >= length; + } + else if(typeof(value) == "number") + { + return value >= length; + } + else + { + return false; + } + }, + + 'regex': function(value, regex) + { + return regex.test(value); + }, + + 'in': function(value, list) + { + return list.indexOf(value) != -1; + } + }; + + var is = + { + 'string+': function(value) + { + return typeof(value) == 'string' && value.length && !(/^\s+$/.test(value)); + }, + + 'string': function(value) + { + return typeof(value) == 'string'; + }, + + 'alphanum': function(value) + { + return (/^[a-zA-Z0-9]+$/i).test(value) && typeof(value) == 'string'; + }, + + 'alpha': function(value) + { + return (/^[a-zA-Z]+$/i).test(value); + }, + + 'object': function(value) + { + return typeof(value) == 'object' && !_.isArray(value); + }, + + 'array': function(value) + { + return _.isArray(value); + }, + + 'date': function(value) + { + // getTime() allows us to check if date is valid + return _.isDate(value) && !isNaN(value.getTime()); + }, + + 'number': function(value) + { + return typeof(value) == 'number' && !isNaN(value); + }, + + 'int': function(value) + { + return typeof(value) == 'number' && value % 1 === 0 && !isNaN(value); + }, + + 'boolean': function(value) + { + return _.isBoolean(value); + }, + + 'float': function(value) + { + return typeof(value) == 'number' && !isNaN(value); + }, + + 'email': function(value) + { + return (/[a-zA-Z0-9!#$%&'*+\/=?\^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'*+\/=?\^_`{|}~\-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?/).test(value); + }, + + 'url': function(value) + { + return (/^(http(s)?:\/\/)?([\w\-]+\.{1})*(([\w\-]*){1}(\.{1}[A-Za-z]{2,4}){1}){1}(\:{1}\d*)?([\?\/|\#]+[\@\~\;\+\'\#\,\.\%\-\/\&\?\=\w\$\s]*)*$/i).test(value); + }, + + 'zipcode': function(value) + { + return (/\d{5}/).test(value); + } + }; + + var filters = + { + 'toInt': function(value) + { + return parseInt(value, 10); + }, + + 'toFloat': function(value) + { + return parseFloat(value, 10); + }, + + 'toString': function(value) + { + return value.toString(); + }, + + 'toDate': function(value) + { + return new Date(value); + }, + + 'toBoolean': function(value) + { + if(value === 1 || value === true || /true|on|yes|1/.test(value)) + return true; + else if(value === 0 || value === false || /false|off|no|0/.test(value)) + return false; + else + return value; + }, + + 'trim': function(value) + { + return _.isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value; + }, + + 'lowercase': function(value) + { + return _.isString(value) ? value.toLowerCase() : value; + }, + + 'uppercase': function(value) + { + return _.isString(value) ? value.toUpperCase() : value; + } + }; + + exports.create = function(param, rules) { return new Rules(param, rules); }; + exports.types = is; + exports.filters = filters; + exports.properties = checks; + }, + "schema.js": function (exports, module, require) { + var rules = require('./rules.js'); + + var Schema = function(schema) + { + this.schema = schema; + }; + + Schema.prototype.validate = function(data) + { + var params = _.keys(this.schema); + var errors = {}; + var values = {}; + var value; + var get = typeof(data) == "function" ? data : function(p) { return data[p]; }; + + for(var i = 0; i < params.length; i++) + { + var schema = this.schema[params[i]]; + + try + { + // if undefined, don't store it. + value = rules.create(params[i], schema).apply(get(params[i])); + + if(!_.isUndefined(value)) + { + values[params[i]] = value; + } + + // does this rule contain embedded schemas + if(typeof(schema.schema) == "object" && !_.isArray(schema.schema) && _.keys(schema.schema).length && !_.isUndefined(values[params[i]])) + { + if(schema.type == "object") + { + _.keys(schema.schema).forEach(function(param) + { + try + { + // if undefined, don't store it + value = rules.create(params[i] + "." + param, schema.schema[param]).apply(get(params[i])[param]); + + if(!_.isUndefined(value)) + { + values[params[i]][param] = value; + } + } + catch(error) + { + if(!errors[params[i]] || typeof(errors[params[i]]) != 'object') + errors[params[i]] = {}; + + errors[params[i]][param] = error; + } + }); + } + else if(schema.type == "array") + { + values[params[i]].forEach(function(value, index) + { + try + { + // if not required and undefined, don't store in values! + values[params[i]][index] = rules.create(params[i] + "[" + index + "]", schema.schema).apply(value); + } + catch(error) + { + if(!_.isArray(errors[params[i]])) + errors[params[i]] = []; + + errors[params[i]][index] = error; + } + }); + } + } + } + catch(error) + { + errors[params[i]] = error; + } + } + + return {data:values, errors:errors, valid:_.keys(errors).length === 0}; + }; + + exports.types = rules.types; + exports.test = function(value, schema) { return (new Schema({input:schema})).validate({input:value}); }; + exports.properties = rules.properties; + exports.filters = rules.filters; + exports.create = function(schema) { return new Schema(schema); }; + exports.middleware = function(schema) + { + return function(req, res, next) + { + req.form = new Schema(schema).validate(req.route.method == "post" ? req.body : req.query); + next(); + }; + }; + } + }, + "schema.js": function (exports, module, require) { + module.exports = require("./lib/schema.js"); + } + } +}) +("schemajs/schema"); diff --git a/test/browser.html b/test/browser.html new file mode 100644 index 0000000..1684f13 --- /dev/null +++ b/test/browser.html @@ -0,0 +1,30 @@ + + + + schemajs browser tests + + + + + + + + + + + + + + + + + + + + +
+ + + diff --git a/test/complex.js b/test/complex.js index 87fa2e2..31bb028 100644 --- a/test/complex.js +++ b/test/complex.js @@ -1,7 +1,8 @@ describe("complex schemas", function() { - var schemajs = require('../schema'); - var expect = require('chai').expect; + /*jshint expr:true*/ + var schemajs = (typeof window === 'undefined') ? require('../schema') : window.schema; + var expect = (typeof window === 'undefined') ? require('chai').expect : window.chai.expect; it("arrays", function() { diff --git a/test/default.js b/test/default.js index f7476ad..e28cbab 100644 --- a/test/default.js +++ b/test/default.js @@ -1,7 +1,8 @@ describe("default schemas", function() { - var schemajs = require('../schema'); - var expect = require('chai').expect; + /*jshint expr:true*/ + var schemajs = (typeof window === 'undefined') ? require('../schema') : window.schema; + var expect = (typeof window === 'undefined') ? require('chai').expect : window.chai.expect; it("default values", function() { diff --git a/test/error.js b/test/error.js index 85222bf..22e995c 100644 --- a/test/error.js +++ b/test/error.js @@ -1,7 +1,8 @@ describe("schema errors", function() { - var schemajs = require('../schema'); - var expect = require('chai').expect; + /*jshint expr:true*/ + var schemajs = (typeof window === 'undefined') ? require('../schema') : window.schema; + var expect = (typeof window === 'undefined') ? require('chai').expect : window.chai.expect; it("basic error", function() { diff --git a/test/filters.js b/test/filters.js index ee9f1dc..2f1e66a 100644 --- a/test/filters.js +++ b/test/filters.js @@ -1,7 +1,8 @@ describe("filter schemas", function() { - var schemajs = require('../schema'); - var expect = require('chai').expect; + /*jshint expr:true*/ + var schemajs = (typeof window === 'undefined') ? require('../schema') : window.schema; + var expect = (typeof window === 'undefined') ? require('chai').expect : window.chai.expect; it("toInt filter", function() { diff --git a/test/properties.js b/test/properties.js index 4586ddd..e7aef28 100644 --- a/test/properties.js +++ b/test/properties.js @@ -1,8 +1,9 @@ describe("filter schemas", function() { - var schemajs = require('../schema'); - var expect = require('chai').expect; - + /*jshint expr:true*/ + var schemajs = (typeof window === 'undefined') ? require('../schema') : window.schema; + var expect = (typeof window === 'undefined') ? require('chai').expect : window.chai.expect; + it("minimum string length property", function() { var schema = schemajs.create( diff --git a/test/required.js b/test/required.js index ae34971..f1e339a 100644 --- a/test/required.js +++ b/test/required.js @@ -1,14 +1,15 @@ describe("required schemas", function() { - var schemajs = require('../schema'); - var expect = require('chai').expect; - + /*jshint expr:true*/ + var schemajs = (typeof window === 'undefined') ? require('../schema') : window.schema; + var expect = (typeof window === 'undefined') ? require('chai').expect : window.chai.expect; + it("required", function() { var schema = schemajs.create( { input: {type:'string', required:true}, - output: {type:'string'}, + output: {type:'string'} }); var input1 = schema.validate({input: 'username'}); var input2 = schema.validate({}); diff --git a/test/types.js b/test/types.js index f58f88b..3da9283 100644 --- a/test/types.js +++ b/test/types.js @@ -1,8 +1,9 @@ describe("schema types", function() { - var schemajs = require('../schema'); - var expect = require('chai').expect; - + /*jshint expr:true*/ + var schemajs = (typeof window === 'undefined') ? require('../schema') : window.schema; + var expect = (typeof window === 'undefined') ? require('chai').expect : window.chai.expect; + it("string", function() { var schema = schemajs.create(