700cfddc by Ean Schuessler

Merge branch 'BF-2656' of /home/git/repositories/brainfood/rivets-error-binder

2 parents 5a5b030e a1ae100f
{
"directory": "src/lib"
}
.*.swp
.tmp/
bin/coverage/
dist/
node_modules/
src/lib/
.grunt/
_SpecRunner.html
// Generated on 2014-02-06 using generator-webapp 0.4.7
/* global module */
// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'
module.exports = function (grunt) {
/* global require */
'use strict';
var jasmineRequirejsTemplateOptions = function(withInstanbul) {
/* global requirejs */
var callback;
if (withInstanbul) {
callback = function() {
var oldLoad = requirejs.load;
requirejs.load = function (context, moduleName, url) {
//console.log('context=' + JSON.stringify(arguments), 'moduleName=' + moduleName, 'url=' + url);
var parts = url.split('/');
for (var i = 0; i < parts.length; ) {
var part = parts[i];
if (part === '.') {
parts.splice(i, 1);
} else if (part === '') {
parts.splice(i, 1);
} else if (part === '..') {
if (i > 0) {
i--;
parts.splice(i, 2);
} else {
parts.splice(i, 1);
}
} else {
i++;
}
}
url = parts.join('/');
if (url.indexOf('src/scripts/') === 0) {
url = './.grunt/grunt-contrib-jasmine/' + url;
}
if (url.indexOf('test/specs/') === 0) {
url = './.grunt/grunt-contrib-jasmine/' + url;
}
//console.log('url=' + url);
return oldLoad.apply(this, [context, moduleName, url]);
};
};
}
return {
requireConfigFile: '<%= yeoman.src %>/scripts/config.js',
requireConfig: {
baseUrl: '<%= yeoman.src %>/scripts',
callback: callback
}
};
};
var jasmineInstanbulTemplateOptions = function(nestedTemplate, nestedOptions) {
return {
coverage: 'bin/coverage/coverage.json',
report: 'bin/coverage',
replace: false,
template: require(nestedTemplate),
templateOptions: nestedOptions
};
};
// Load grunt tasks automatically
require('load-grunt-tasks')(grunt);
// Time how long tasks take. Can help when optimizing build times
require('time-grunt')(grunt);
// Define the configuration for all the tasks
grunt.initConfig({
bower: {
target: {
options: {
exclude: [
'requirejs',
],
transitive: true,
},
rjsConfig: '<%= yeoman.src %>/scripts/config.js'
}
},
// Project settings
yeoman: {
// Configurable paths
app: 'app',
dist: 'dist',
src: 'src',
},
// Watches files for changes and runs tasks based on the changed files
watch: {
js: {
files: ['<%= yeoman.src %>/scripts/{,*/}*.js'],
tasks: ['jshint'],
},
jstest: {
files: ['test/spec/{,*/}*.js'],
tasks: ['test:watch']
},
gruntfile: {
files: ['Gruntfile.js']
},
styles: {
files: ['<%= yeoman.src %>/styles/{,*/}*.css'],
tasks: ['newer:copy:styles', 'autoprefixer']
}
},
// The actual grunt server settings
connect: {
options: {
port: 9000,
// Change this to '0.0.0.0' to access the server from outside
hostname: 'localhost'
},
app: {
options: {
open: false,
base: [
'.tmp',
'<%= yeoman.src %>'
]
}
},
test: {
options: {
port: 9001,
base: [
'.tmp',
'test',
'<%= yeoman.src %>'
]
}
},
dist: {
options: {
open: false,
base: '<%= yeoman.dist %>',
}
}
},
// Empties folders to start fresh
clean: {
dist: {
files: [{
dot: true,
src: [
'.tmp',
'<%= yeoman.dist %>/*',
'!<%= yeoman.dist %>/.git*'
]
}]
},
server: '.tmp'
},
// Make sure code styles are up to par and there are no obvious mistakes
jshint: {
options: {
browser: true,
esnext: true,
bitwise: true,
camelcase: true,
curly: true,
eqeqeq: true,
immed: true,
indent: 4,
latedef: true,
newcap: true,
noarg: true,
quotmark: 'single',
undef: true,
unused: true,
strict: true,
trailing: true,
smarttabs: true,
jquery: true,
reporter: require('jshint-stylish')
},
all: [
'Gruntfile.js',
],
scripts: {
options: {
globals: {
define: false,
}
},
files: {
src: [
'<%= yeoman.src %>/scripts/**/*.js',
'!<%= yeoman.src %>/scripts/vendor/*',
]
}
},
specs: {
options: {
globals: {
afterEach: false,
beforeEach: false,
define: false,
describe: false,
expect: false,
it: false,
jasmine: false,
}
},
files: {
src: [
'test/specs/**/*.spec.js'
]
}
}
},
jasmine: {
all: {
src: [
'<%= yeoman.src %>/scripts/{,**/}*.js',
'test/specs/**/*.spec.js',
],
options: {
template: require('grunt-template-jasmine-istanbul'),
templateOptions: jasmineInstanbulTemplateOptions('grunt-template-jasmine-requirejs', jasmineRequirejsTemplateOptions(true))
}
}
},
// Mocha testing framework configuration options
mocha: {
all: {
options: {
run: true,
urls: ['http://<%= connect.test.options.hostname %>:<%= connect.test.options.port %>/index.html']
}
}
},
// Add vendor prefixed styles
autoprefixer: {
options: {
browsers: ['last 1 version']
},
dist: {
files: [{
expand: true,
cwd: '.tmp/styles/',
src: '{,*/}*.css',
dest: '.tmp/styles/'
}]
}
},
// Automatically inject Bower components into the HTML file
'bower-install': {
app: {
html: '<%= yeoman.src %>/index.html',
ignorePath: '<%= yeoman.src %>/'
}
},
// Renames files for browser caching purposes
rev: {
dist: {
files: {
src: [
'<%= yeoman.dist %>/scripts/*/**/*.js',
'<%= yeoman.dist %>/scripts/!(config)*.js',
'<%= yeoman.dist %>/styles/{,*/}*.css',
'<%= yeoman.dist %>/images/{,*/}*.{gif,jpeg,jpg,png,webp}',
'<%= yeoman.dist %>/styles/fonts/{,*/}*.*'
]
}
},
requireconfig: {
files: {
src: [
'<%= yeoman.dist %>/scripts/config.js'
]
}
}
},
requirejs: {
dist: {
options: {
done: function(done) {
var requireModules = grunt.config('requireModules') || {};
var lines = [
'require.bundles = (function(bundles) {',
];
for (var key in requireModules) {
var keyS = JSON.stringify(key);
var value = requireModules[key];
var included = [];
for (var i = 0; i < value.included.length; i++) {
var file = value.included[i];
if (file.match(/\.js$/)) {
included.push(file.substring(0, file.length - 3));
}
}
lines.push('bundles[' + keyS + '] = ' + JSON.stringify(included) + ';');
}
lines.push('return bundles;');
lines.push('})(require.bundles || {});');
grunt.file.write('.tmp/scripts/bundles.js', lines.join('\n'));
done();
},
baseUrl: '<%= yeoman.src %>/scripts',
mainConfigFile: '<%= yeoman.src %>/scripts/config.js',
wrapShim: true,
dir: '<%= yeoman.dist %>/scripts',
optimize: 'none',
removeCombined: true,
onModuleBundleComplete: function(data) {
if (data.name.slice(0, 'bundles/'.length) === 'bundles/') {
var requireModules = grunt.config('requireModules') || {};
requireModules[data.name] = data;
grunt.config('requireModules', requireModules);
}
},
}
},
},
// Reads HTML for usemin blocks to enable smart builds that automatically
// concat, minify and revision files. Creates configurations in memory so
// additional tasks can operate on them
useminPrepare: {
options: {
dest: '<%= yeoman.dist %>'
},
html: '<%= yeoman.src %>/index.html'
},
// Performs rewrites based on rev and the useminPrepare configuration
usemin: {
options: {
assetsDirs: ['<%= yeoman.dist %>']
},
html: ['<%= yeoman.dist %>/{,*/}*.html'],
css: ['<%= yeoman.dist %>/styles/{,*/}*.css']
},
// The following *-min tasks produce minified files in the dist folder
imagemin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.src %>/images',
src: '{,*/}*.{gif,jpeg,jpg,png}',
dest: '<%= yeoman.dist %>/images'
}]
}
},
svgmin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.src %>/images',
src: '{,*/}*.svg',
dest: '<%= yeoman.dist %>/images'
}]
}
},
htmlmin: {
dist: {
options: {
collapseBooleanAttributes: true,
collapseWhitespace: true,
removeAttributeQuotes: true,
removeCommentsFromCDATA: true,
removeEmptyAttributes: true,
removeOptionalTags: true,
removeRedundantAttributes: true,
useShortDoctype: true
},
files: [{
expand: true,
cwd: '<%= yeoman.dist %>',
src: '{,*/}*.html',
dest: '<%= yeoman.dist %>'
}]
}
},
// By default, your `index.html`'s <!-- Usemin block --> will take care of
// minification. These next options are pre-configured if you do not wish
// to use the Usemin blocks.
// cssmin: {
// dist: {
// files: {
// '<%= yeoman.dist %>/styles/main.css': [
// '.tmp/styles/{,*/}*.css',
// '<%= yeoman.src %>/styles/{,*/}*.css'
// ]
// }
// }
// },
// uglify: {
// dist: {
// files: {
// '<%= yeoman.dist %>/scripts/scripts.js': [
// '<%= yeoman.dist %>/scripts/scripts.js'
// ]
// }
// }
// },
// concat: {
// dist: {}
// },
concat: {
requireconfig: {
}
},
uglify: {
dist: {
},
requireconfig: {
files: {
'<%= yeoman.dist %>/scripts/config.js': [
'<%= yeoman.dist %>/scripts/config.js',
'.tmp/scripts/config.js',
],
}
}
},
// Copies remaining files to places other tasks can use
copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: '<%= yeoman.src %>',
dest: '<%= yeoman.dist %>',
src: [
'*.{ico,png,txt}',
'.htaccess',
'images/{,*/}*.webp',
'{,*/}*.html',
'styles/fonts/{,*/}*.*'
]
}]
},
styles: {
expand: true,
dot: true,
cwd: '<%= yeoman.src %>/styles',
dest: '.tmp/styles/',
src: '{,*/}*.css'
}
},
// Run some tasks in parallel to speed up build process
concurrent: {
server: [
'copy:styles'
],
test: [
'copy:styles'
],
dist: [
'copy:styles',
'imagemin',
'svgmin'
]
}
});
grunt.loadNpmTasks('grunt-bower-requirejs');
grunt.registerTask('revconfig', function () {
var prefix = grunt.template.process('<%= yeoman.dist %>/scripts/');
var pattern = prefix + '**/*.{js,html}';
var files = grunt.file.expand(pattern);
var lines = [];
grunt.util._.each(files, function(file) {
file = file.substring(prefix.length);
var res = file.match(/^(.*\/)?([0-9a-f]+)\.([^\/]+)\.([^\.]+)$/);
if (!res) {
return;
}
//grunt.log.oklns(JSON.stringify(res));
var dir = res[1] || '';
//var hash = res[2];
var base = res[3];
var ext = res[4];
var id;
if (ext === 'js') {
id = dir + base;
file = file.substring(0, file.length - ext.length - 1);
} else if (ext === 'html') {
id = 'text!' + dir + base + '.' + ext;
}
grunt.log.oklns('map: ' + id + ' -> ' + file);
lines.push('require.paths[' + JSON.stringify(id) + ']=' + JSON.stringify(file) + ';\n');
});
grunt.file.write('.tmp/scripts/config.js', lines.join(''));
});
grunt.registerTask('serve', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'connect:dist:keepalive']);
}
grunt.task.run([
'clean:server',
'concurrent:server',
'autoprefixer',
'connect:app',
'watch'
]);
});
grunt.registerTask('server', function () {
grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
grunt.task.run(['serve']);
});
grunt.registerTask('test', function(target) {
if (target !== 'watch') {
grunt.task.run([
'clean:server',
'concurrent:test',
'autoprefixer',
]);
}
grunt.task.run([
'connect:test',
'mocha'
]);
});
grunt.registerTask('build', [
'clean:dist',
'useminPrepare',
'requirejs',
'concurrent:dist',
'autoprefixer',
'concat',
// 'cssmin',
'uglify:dist',
'copy:dist',
// 'rev:dist',
'revconfig',
'uglify:requireconfig',
// 'rev:requireconfig',
'usemin',
'htmlmin'
]);
grunt.registerTask('dist', [
'bower',
'newer:jshint',
// 'test',
'build'
]);
grunt.registerTask('default', []);
};
{
"name": "rivets-error-binder",
"version": "0.0.0",
"authors": [
"Adam Heath <doogie@brainfood.com>"
],
"main": [
"src/scripts/rivets-error-binder.js"
],
"private": true,
"ignore": [
"**/.*",
"node_modules",
"src/lib",
"test"
],
"dependencies": {
"backbone-seen": "git@gitlab.brainfood.com:brainfood/backbone-seen.git",
"backbone": "~1.1.0",
"backbone-validation": "0.9.1",
"jquery": "~1.10.2",
"requirejs": "~2.1.10",
"rivets": "~0.6.6",
"underscore": "~1.6.0"
},
"devDependencies": {
"rivets-backbone-adapter": "~1.1.1"
}
}
{
"name": "rivets-error-binder",
"version": "0.0.0",
"main": [
"src/scripts/rivets-error-binder.js"
],
"dependencies": {
"rivets": "~0.6.6",
"requirejs": "~2.1.10"
},
"devDependencies": {
"bower-requirejs": "~0.9.2",
"grunt": "~0.4.1",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-uglify": "~0.2.0",
"grunt-contrib-jshint": "~0.7.0",
"grunt-contrib-cssmin": "~0.7.0",
"grunt-contrib-connect": "~0.5.0",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-htmlmin": "~0.1.3",
"grunt-bower-install": "~0.7.0",
"grunt-contrib-imagemin": "~0.2.0",
"grunt-contrib-watch": "~0.5.2",
"grunt-rev": "~0.1.0",
"grunt-autoprefixer": "~0.5.0",
"grunt-usemin": "~0.1.10",
"grunt-mocha": "~0.4.0",
"grunt-newer": "~0.6.0",
"grunt-svgmin": "~0.2.0",
"grunt-concurrent": "~0.4.0",
"load-grunt-tasks": "~0.2.0",
"time-grunt": "~0.2.0",
"jshint-stylish": "~0.1.3",
"grunt-contrib-requirejs": "~0.4.0",
"grunt-bower-requirejs": "~0.8.4",
"grunt-template-jasmine-istanbul": "~0.2.6",
"grunt-template-jasmine-requirejs": "~0.1.10",
"grunt-contrib-jasmine": "~0.5.3"
},
"engines": {
"node": ">=0.8.0"
}
}
define(['rivets', 'bootstrap'], function(rivets) {
var rivetsBinderCall = function(binding, binderName, methodName, args) {
var binder = rivets.binders[binderName];
if (binder instanceof Function) {
if (methodName == 'routine') {
binder.apply(binding, args);
};
} else if (binder) {
if (binder[methodName]) {
binder[methodName].apply(binding, args);
}
}
}
var diveIntoObject = function(obj, keypath, callback) {
if (!keypath) {
return callback(obj, null);
}
//return callback(obj, keypath);
var keyparts = keypath.replace(/^:/, '').split(/\:/);
//console.log('diveIntoObject(keyparts):', obj, keyparts);
while (keyparts.length > 1) {
var part = keyparts.shift();
if (part.length == 0) {
continue;
}
//console.log('diveIntoObject:', obj, part);
obj = doObjectRead(obj, part);
}
//console.log('callback:', obj, keyparts[0]);
return callback(obj, keyparts.shift());
};
var doObjectRead = function(obj, id) {
if (obj === null) return obj;
if (!id) return obj;
//console.log('doObjectRead:', obj, id, obj instanceof Backbone.Model, obj instanceof Backbone.Collection);
if (obj instanceof Backbone.Model) {
return obj.get(id);
} else if (obj instanceof Backbone.Collection) {
return obj.at(id);
} else if (obj != null) {
return obj[id];
}
};
rivets.binders['error-*'] = {
bind: function(el) {
var self = this;
var holder = this.validationHolder = {
//marker: el.parentNode.insertBefore(document.createComment(" rivets: " + this.type + " "), el),
focus: function() {
$(holder.container).removeClass('focused');
},
blur: function() {
if (holder.lastObj) holder.lastObj.seen(holder.lastId, true);
$(holder.container).addClass('focused');
if (holder.lastObj) holder.lastObj.validate();
},
validated: function(isValid, model, errors) {
var errorList = errors[holder.lastId];
if (errorList && holder.lastObj.seen(holder.lastId)) {
$(el).tooltip({title: errorList, trigger: 'focus'});
$(el).tooltip('show');
$(el).parent().addClass('has-error');
} else {
$(el).tooltip('destroy');
$(el).parent().removeClass('has-error');
}
}
};
$(el).on('focus', holder.focus).on('blur', holder.blur);
rivetsBinderCall(this, this.args[0], 'bind', arguments);
},
unbind: function(el) {
var holder = this.validationHolder;
$(this.validationHolder.marker).after(el).remove();
$(el).off('focus', holder.focus).off('blur', holder.blur);
diveIntoObject(this.model, this.keypath, function(obj, id) {
obj.off('validated', holder.validated);
});
delete this.validationHolder;
rivetsBinderCall(this, this.args[0], 'unbind', arguments);
},
routine: function(el, value) {
var holder = this.validationHolder;
if (holder.lastObj) {
holder.lastObj.off('validated', holder.validated);
}
diveIntoObject(this.observer.target, this.observer.key.path, function(obj, id) {
holder.lastObj = obj;
holder.lastId = id;
obj.on('validated', holder.validated);
});
rivetsBinderCall(this, this.args[0], 'routine', arguments);
}
};
});
define(['backbone'], function(Backbone) {
'use strict';
return Backbone;
});
/* global require:true */
var require;
require = (function() {
'use strict';
var require = {
baseUrl: 'scripts',
config: {
'rivets-error-binder': {}
},
shim: {
bootstrap: {
deps: [
'jquery'
]
},
rivets: {
deps: [
'jquery'
]
}
},
paths: {
'backbone-validation': '../lib/backbone-validation/dist/backbone-validation-amd',
backbone: '../lib/backbone/backbone',
underscore: '../lib/underscore/underscore',
rivets: '../lib/rivets/dist/rivets',
bootstrap: '../lib/bootstrap/dist/js/bootstrap',
jquery: '../lib/jquery/dist/jquery',
'rivets-backbone-adapter': '../lib/rivets-backbone-adapter/rivets-backbone',
'backbone-seen': '../lib/backbone-seen/src/scripts/backbone-seen'
}
};
return require;
})();
/* global require */
require(
[],
function() {
'use strict';
}
);
define([
'module',
'rivets',
], function(
module,
rivets
) {
'use strict';
var rivetsBinderCall = function(binding, binderName, methodName, args) {
var binder = rivets.binders[binderName];
binder[methodName].apply(binding, args);
};
var render = function() {
var renderImpl = module.config().render;
if (renderImpl) {
return renderImpl.apply(this, arguments);
}
};
rivets.binders['error-*'] = {
bind: function(el) {
var holder = this.validationHolder = {
//marker: el.parentNode.insertBefore(document.createComment(" rivets: " + this.type + " "), el),
focus: function() {
render(el, 'focus', false);
},
blur: function() {
if (holder.observer && holder.observer.target) {
holder.observer.target.seen(holder.observer.key.path, true);
}
render(el, 'blur', false);
if (holder.observer && holder.observer.target) {
holder.observer.target.validate();
}
},
validated: function(isValid, model, errors) {
var errorList = errors[holder.observer.key.path];
if (errorList && holder.observer.target && holder.observer.target.seen(holder.observer.key.path)) {
render(el, 'validated', errorList);
} else {
render(el, 'validated', false);
}
}
};
$(el).on('focus', holder.focus).on('blur', holder.blur);
rivetsBinderCall(this, this.args[0], 'bind', arguments);
},
unbind: function(el) {
var holder = this.validationHolder;
$(this.validationHolder.marker).after(el).remove();
$(el).off('focus', holder.focus).off('blur', holder.blur);
if (holder.observer.target) {
holder.observer.target.off('validated', holder.validated);
}
delete this.validationHolder;
rivetsBinderCall(this, this.args[0], 'unbind', arguments);
},
routine: function() {
var holder = this.validationHolder;
if (holder.observer) {
holder.observer.target.off('validated', holder.validated);
}
holder.observer = this.observer;
if (this.observer.target) {
this.observer.target.on('validated', holder.validated);
}
rivetsBinderCall(this, this.args[0], 'routine', arguments);
}
};
});
define(function(require) {
'use strict';
var $ = require('jquery');
window.jQuery = $;
var RivetsErrorBinder = require('rivets-error-binder');
var _ = require('underscore');
var Backbone = require('backbone');
var rivets = require('rivets');
require('backbone-validation');
var BackboneSeen = require('backbone-seen');
require('rivets-backbone-adapter');
_.extend(Backbone.Model.prototype, Backbone.Validation.mixin);
//rivets.config.rootInterface = ':';
describe('RivetsErrorBinder', function() {
it('exists', function() {
expect(RivetsErrorBinder).toBeUndefined();
});
});
describe('RivetsErrorBinder', function() {
var Model, Collection;
var scope;
var test;
var view;
var render;
beforeEach(function() {
render = function(el, cmd, errorList) {
render.lastCmd = cmd;
render.lastErrorList = errorList;
switch (cmd) {
case 'focus':
render.counts.focus++;
break;
case 'blur':
render.counts.blur++;
break;
case 'validated':
if (errorList) {
render.counts.validatedError++;
} else {
render.counts.validatedClean++;
}
break;
}
};
render.counts = {focus: 0, blur: 0, validatedError: 0, validatedClean: 0};
jasmine.Clock.useMock();
Model = BackboneSeen.mixin(Backbone.Model.extend());
Collection = Backbone.Collection.extend({model: Model});
scope = new Model({
test: test = new Model({
constant: 'CONSTANT'
})
});
test.validation = {
constant: {required: true}
};
});
afterEach(function() {
view.unbind();
});
it('existing-no-render', function() {
var $node = $($.parseHTML('<div><form><input id="_1" rv-error-value="test:constant" /></form></div>'));
$(document).find('body').append($node[0]);
var $1 = $node.find('#_1');
expect($1.length).toEqual(1);
expect($1.val()).toEqual('');
expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0});
view = rivets.bind($node, scope.attributes);
expect(view).toBeTruthy();
expect($1.val()).toEqual('CONSTANT');
//expect($node.html()).toBe('');
test.set('constant', 'one');
expect($1.val()).toEqual('one');
test.set('constant', '');
expect($1.val()).toEqual('');
test.set('constant', 'CONSTANT');
expect($1.val()).toEqual('CONSTANT');
expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0});
$1.focus().val('one').change().blur();
jasmine.Clock.tick(1);
expect(test.get('constant')).toEqual('one');
expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0});
$1.focus().val('').change().blur();
jasmine.Clock.tick(1);
expect(test.get('constant')).toEqual('');
expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0});
jasmine.Clock.tick(1);
expect(render.lastErrorList).toBeUndefined();
});
it('existing-with-render', function() {
/* global requirejs */
requirejs.config({config: {'rivets-error-binder': { render: render } } });
var $node = $($.parseHTML('<div><form><input id="_1" rv-error-value="test:constant" /></form></div>'));
$(document).find('body').append($node[0]);
var $1 = $node.find('#_1');
expect($1.length).toEqual(1);
expect($1.val()).toEqual('');
expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0});
view = rivets.bind($node, scope.attributes);
expect(view).toBeTruthy();
expect($1.val()).toEqual('CONSTANT');
//expect($node.html()).toBe('');
test.set('constant', 'one');
expect($1.val()).toEqual('one');
test.set('constant', '');
expect($1.val()).toEqual('');
test.set('constant', 'CONSTANT');
expect($1.val()).toEqual('CONSTANT');
expect(render.counts).toEqual({focus : 0, blur : 0, validatedError : 0, validatedClean : 0});
$1.focus().val('one').change().blur();
jasmine.Clock.tick(1);
expect(test.get('constant')).toEqual('one');
expect(render.counts).toEqual({focus : 1, blur : 1, validatedError : 0, validatedClean : 1});
$1.focus().val('').change().blur();
jasmine.Clock.tick(1);
expect(test.get('constant')).toEqual('');
expect(render.counts).toEqual({focus : 2, blur : 2, validatedError : 1, validatedClean : 1});
jasmine.Clock.tick(1);
expect(render.lastErrorList).toEqual(jasmine.any(String));
});
it('missing', function() {
var $node = $($.parseHTML('<div><form><input id="_1" rv-error-value="test:missing:child" /></form></div>'));
$(document).find('body').append($node[0]);
var $1 = $node.find('#_1');
expect($1.length).toEqual(1);
expect($1.val()).toEqual('');
view = rivets.bind($node, scope.attributes);
expect(view).toBeTruthy();
expect($1.val()).toEqual('');
$1.focus().val('one').change().blur();
jasmine.Clock.tick(1);
});
});
});