UNPKG

ractive

Version:

Next-generation DOM manipulation

1,384 lines (1,100 loc) 33.7 kB
define([ 'ractive' ], function ( Ractive ) { 'use strict'; return function () { var fixture, Foo, fooAdaptor; // some set-up fixture = document.getElementById( 'qunit-fixture' ); Foo = function ( content ) { this.content = content; }; fooAdaptor = { filter: function ( object ) { return object instanceof Foo; }, wrap: function ( ractive, foo, keypath, prefix ) { return { get: function () { return foo.content; }, teardown: function () { } }; } }; module( 'Miscellaneous', { setup: function(){ Ractive.adaptors.foo = fooAdaptor; }, teardown: function(){ delete Ractive.adaptors.foo; } } ); test( 'Subclass instance data extends prototype data', function ( t ) { var Subclass, instance; Subclass = Ractive.extend({ template: '{{foo}} {{bar}}', data: { foo: 1 } }); instance = new Subclass({ el: fixture, data: { bar: 2 } }); t.htmlEqual( fixture.innerHTML, '1 2' ); t.deepEqual( instance.get(), { foo: 1, bar: 2 }); }); test( 'Subclasses of subclasses inherit data, partials and transitions', function ( t ) { var Subclass, SubSubclass, wiggled, shimmied, instance; Subclass = Ractive.extend({ template: '<div intro="wiggle">{{>foo}}{{>bar}}{{>baz}}</div><div intro="shimmy">{{foo}}{{bar}}{{baz}}</div>', data: { foo: 1 }, partials: { foo: 'fooPartial' }, transitions: { wiggle: function ( t ) { wiggled = true; } } }); SubSubclass = Subclass.extend({ data: { bar: 2 }, partials: { bar: 'barPartial' }, transitions: { shimmy: function ( t ) { shimmied = true; } } }); instance = new SubSubclass({ el: fixture, data: { baz: 3 }, partials: { baz: 'bazPartial' } }); t.htmlEqual( fixture.innerHTML, '<div>fooPartialbarPartialbazPartial</div><div>123</div>' ); t.ok( wiggled ); t.ok( shimmied ); }); test( 'Multiple identical evaluators merge', function ( t ) { var ractive; ractive = new Ractive({ el: fixture, template: '{{( a+b )}} {{( a+b )}} {{( a+b )}}', data: { a: 1, b: 2 } }); t.htmlEqual( fixture.innerHTML, '3 3 3' ); t.equal( ractive.viewmodel.deps[ 'computed' ].a.length, 1 ); t.equal( ractive.viewmodel.deps[ 'computed' ].b.length, 1 ); }); test( 'Boolean attributes work as expected', function ( t ) { var ractive; ractive = new Ractive({ el: fixture, template: '<input id="one" type="checkbox" checked="{{falsy}}"><input id="two" type="checkbox" checked="{{truthy}}">', data: { truthy: true, falsy: false } }); t.equal( ractive.nodes.one.checked, false ); t.equal( ractive.nodes.two.checked, true ); }); test( 'Instances can be created without an element', function ( t ) { var ractive; ractive = new Ractive({ template: '<ul>{{#items:i}}<li>{{i}}: {{.}}</li>{{/items}}</ul>', data: { items: [ 'a', 'b', 'c' ] } }); t.ok( ractive ); }); test( 'Instances without an element can render HTML', function ( t ) { var ractive; ractive = new Ractive({ template: '<ul>{{#items:i}}<li>{{i}}: {{.}}</li>{{/items}}</ul>', data: { items: [ 'a', 'b', 'c' ] } }); t.htmlEqual( ractive.toHTML(), '<ul><li>0: a</li><li>1: b</li><li>2: c</li></ul>' ); }); test( 'Triples work with toHTML', function ( t ) { var ractive; ractive = new Ractive({ template: '{{{ triple }}}', data: { triple: '<p>test</p>' } }); t.htmlEqual( ractive.toHTML(), '<p>test</p>' ); }); test( 'Passing in alternative delimiters', function ( t ) { var ractive = new Ractive({ el: fixture, template: '/~ greeting ~/, /~recipient~/! /~~ triple ~~/', data: { greeting: 'Hello', recipient: 'world', triple: '<p>here is some HTML</p>' }, delimiters: [ '/~', '~/' ], tripleDelimiters: [ '/~~', '~~/' ] }); t.htmlEqual( fixture.innerHTML, 'Hello, world! <p>here is some HTML</p>' ); }); test( 'Using alternative delimiters in template', function ( t ) { var ractive = new Ractive({ el: fixture, template: '{{=/~ ~/=}} {{{=/~~ ~~/=}}} /~ greeting ~/, /~recipient~/! /~~ triple ~~/', data: { greeting: 'Hello', recipient: 'world', triple: '<p>here is some HTML</p>' } }); t.htmlEqual( fixture.innerHTML, 'Hello, world! <p>here is some HTML</p>' ); }); test( '.unshift() works with proxy event handlers, without index references', function ( t ) { var ractive = new Ractive({ el: fixture, template: '{{#items}}<button on-click="bla">Level1: {{ title }}</button>{{/items}}', data: { items: [{ title: 'Title1' }] } }); ractive.get('items').unshift({title: 'Title0'}); t.htmlEqual( fixture.innerHTML, '<button>Level1: Title0</button><button>Level1: Title1</button>' ); }); test( 'Updating values with properties corresponding to unresolved references works', function ( t ) { var ractive, user; user = {}; ractive = new Ractive({ el: fixture, template: '{{#user}}{{name}}{{/user}}', data: { user: user } }); t.equal( fixture.innerHTML, '' ); user.name = 'Jim'; ractive.update( 'user' ); t.equal( fixture.innerHTML, 'Jim' ); }); test( 'Setting nested properties with a keypath correctly updates value of intermediate keypaths', function ( t ) { var ractive = new Ractive({ el: fixture, template: '{{#foo}}{{#bar}}{{baz}}{{/bar}}{{/foo}}' }); ractive.set( 'foo.bar.baz', 'success' ); t.htmlEqual( fixture.innerHTML, 'success' ); }); test( 'Functions are called with the ractive instance as context', function ( t ) { expect( 1 ); var ractive = new Ractive({ el: fixture, template: '{{ foo() }}' }); ractive.set( 'foo', function () { t.equal( this, ractive ); }); }); test( 'Methods are called with their object as context', function ( t ) { expect( 1 ); var foo, run, ractive = new Ractive({ el: fixture, template: '{{ foo.bar() }}' }); foo = { bar: function () { // TODO why is this running twice? if ( !run ) { t.equal( this, foo ); } run = true; } }; ractive.set( 'foo', foo ); }); test( 'Delimiters can be reset globally', function ( t ) { var oldDelimiters, oldTripledDelimiters, ractive; oldDelimiters = Ractive.defaults.delimiters; oldTripledDelimiters = Ractive.defaults.tripleDelimiters; Ractive.defaults.delimiters = [ '/~', '~/' ]; Ractive.defaults.tripleDelimiters = [ '/~~', '~~/' ]; ractive = new Ractive({ el: fixture, template: '/~foo~/ /~~bar~~/', data: { foo: 'text', bar: '<strong>html</strong>' } }); t.htmlEqual( fixture.innerHTML, 'text <strong>html</strong>' ); Ractive.defaults.delimiters = oldDelimiters; Ractive.defaults.tripleDelimiters = oldTripledDelimiters; }); test( 'Teardown works without throwing an error (#205)', function ( t ) { var ractive = new Ractive({ el: fixture, template: 'a {{generic}} template', data: { generic: 'bog standard' } }); expect( 1 ); try { ractive.teardown(); t.ok( 1 ); } catch ( err ) { t.ok( 0 ); } }); test( 'Bindings without explicit keypaths can survive a splice operation', function ( t ) { var items, ractive; items = new Array( 3 ); ractive = new Ractive({ el: fixture, template: '<ul>{{#items}}<li><input value="{{foo}}"></li>{{/items}}</ul>', data: { items: items } }); expect( 1 ); items.splice( 1, 1 ); try { items.splice( 1, 1 ); t.ok( 1 ); } catch ( err ) { t.ok( 0 ); } }); test( 'Keypath resolutions that trigger teardowns don\'t cause the universe to implode', function ( t ) { var ractive = new Ractive({ el: fixture, template: '{{^foo}}not foo{{/foo}}{{#foo}}<widget items="{{items}}"/>{{/foo}}', data: { items: [ 1, 2 ] }, components: { widget: Ractive.extend({ template: 'widget' }) } }); expect( 1 ); try { ractive.set( 'foo', true ); t.ok( 1 ); } catch ( err ) { t.ok( 0 ); } }); test( 'Inverted sections aren\'t broken by unshift operations', function ( t ) { var ractive = new Ractive({ el: fixture, template: '{{^items}}no items{{/items}}{{#items}}{{.}}{{/items}}', data: { items: [] } }); t.htmlEqual( fixture.innerHTML, 'no items' ); ractive.get( 'items' ).unshift( 'foo' ); t.htmlEqual( fixture.innerHTML, 'foo' ); }); test( 'Splice operations that try to remove more items than there are from an array are handled', function ( t ) { var ractive = new Ractive({ el: fixture, template: '{{#items}}{{.}}{{/items}}', data: { items: [ 'a', 'b', 'c' ] } }); t.htmlEqual( fixture.innerHTML, 'abc' ); ractive.get( 'items' ).splice( 2, 2 ); t.htmlEqual( fixture.innerHTML, 'ab' ); }); test( 'Partial templates will be drawn from script tags if not already registered', function ( t ) { var partialScr, ractive; partialScr = document.createElement( 'script' ); partialScr.id = 'thePartial'; partialScr.type = 'text/ractive'; partialScr.text = '{{one}}{{two}}{{three}}'; document.getElementsByTagName('body')[0].appendChild( partialScr ); ractive = new Ractive({ el: fixture, template: '{{>thePartial}}', data: { one: 1, two: 2, three: 3 } }); t.htmlEqual( fixture.innerHTML, '123' ); }); // ARGH these tests don't work in phantomJS /*test( 'ractive.detach() removes an instance from the DOM and returns a document fragment', function ( t ) { var ractive, p, docFrag; ractive = new Ractive({ el: fixture, template: '<p>{{foo}}</p>', data: { foo: 'whee!' } }); p = ractive.find( 'p' ); docFrag = ractive.detach(); t.ok( docFrag instanceof DocumentFragment ); t.ok( docFrag.contains( p ) ); }); test( 'ractive.detach() works with a previously unrendered ractive', function ( t ) { var ractive, p, docFrag; ractive = new Ractive({ template: '<p>{{foo}}</p>', data: { foo: 'whee!' } }); p = ractive.find( 'p' ); docFrag = ractive.detach(); t.ok( docFrag instanceof DocumentFragment ); t.ok( docFrag.contains( p ) ); });*/ test( 'ractive.insert() moves an instance to a different location', function ( t ) { var ractive, p, one, two, three; one = document.createElement( 'div' ); two = document.createElement( 'div' ); three = document.createElement( 'div' ); three.innerHTML = '<p>before</p><p class="after">after</p>'; ractive = new Ractive({ el: fixture, template: '<p>{{foo}}</p>', data: { foo: 'whee!' } }); p = ractive.find( 'p' ); ractive.insert( one ); t.ok( one.contains( p ) ); ractive.insert( two ); t.ok( !one.contains( p ) ); t.ok( two.contains( p ) ); ractive.insert( three, three.querySelector( '.after' ) ); t.ok( three.contains( p ) ); t.htmlEqual( three.innerHTML, '<p>before</p><p>whee!</p><p class="after">after</p>' ); }); test( 'ractive.insert() throws an error if instance is not rendered (#712)', function ( t ) { var ractive, p, one, two, three; one = document.createElement( 'div' ); two = document.createElement( 'div' ); three = document.createElement( 'div' ); three.innerHTML = '<p>before</p><p class="after">after</p>'; ractive = new Ractive({ template: '<p>{{foo}}</p>', data: { foo: 'whee!' } }); try { ractive.insert( one ); t.ok( false ); } catch ( err ) { t.ok( true ); } ractive.render( two ); p = ractive.find( 'p' ); t.ok( !one.contains( p ) ); t.ok( two.contains( p ) ); ractive.insert( three, three.querySelector( '.after' ) ); t.ok( three.contains( p ) ); t.htmlEqual( three.innerHTML, '<p>before</p><p>whee!</p><p class="after">after</p>' ); }); test( 'Regression test for #271', function ( t ) { var ractive, items; items = [{}]; ractive = new Ractive({ el: fixture, template: '{{#items}}<p>foo</p>{{# items.length > 1 }}<p>bar</p>{{/}}{{/items}}', data: { items: items } }); t.htmlEqual( fixture.innerHTML, '<p>foo</p>' ); try { items.push({}); t.htmlEqual( fixture.innerHTML, '<p>foo</p><p>bar</p><p>foo</p><p>bar</p>' ); items.push({}); t.htmlEqual( fixture.innerHTML, '<p>foo</p><p>bar</p><p>foo</p><p>bar</p><p>foo</p><p>bar</p>' ); items.splice( 1, 1 ); t.htmlEqual( fixture.innerHTML, '<p>foo</p><p>bar</p><p>foo</p><p>bar</p>' ); items.splice( 1, 1 ); t.htmlEqual( fixture.innerHTML, '<p>foo</p>' ); } catch ( err ) { t.ok( false ); } }); test( 'Regression test for #297', function ( t ) { var ractive, items; items = [ 'one', 'two', 'three' ]; ractive = new Ractive({ el: fixture, template: '{{#items}}{{>item}}{{/items}}', data: { items: items }, partials: { item: '<p>{{.}}</p>' } }); t.htmlEqual( fixture.innerHTML, '<p>one</p><p>two</p><p>three</p>' ); items.splice( 1, 1 ); t.htmlEqual( fixture.innerHTML, '<p>one</p><p>three</p>' ); }); test( 'Regression test for #316', function ( t ) { var ractive, a, b; a = []; b = []; ractive = new Ractive({ el: fixture, template: '{{ a.length ? "foo" : b.length ? "bar" : "baz" }}', data: { a: a, b: b } }); t.htmlEqual( fixture.innerHTML, 'baz' ); b.push( 1 ); t.htmlEqual( fixture.innerHTML, 'bar' ); a.push( 1 ); t.htmlEqual( fixture.innerHTML, 'foo' ); }); test( 'Regression test for #321', function ( t ) { var ractive, buttons, expected; ractive = new Ractive({ el: fixture, template: '<button on-click=\'test:{{ ["just a string"] }}\'>test 1</button><button on-click=\'test:{{ {bar: 3} }}\'>test 2</button>' }); ractive.on( 'test', function ( event, arg ) { t.deepEqual( arg, expected ); }); expect( 2 ); buttons = ractive.findAll( 'button' ); expected = ['just a string']; simulant.fire( buttons[0], 'click' ); expected = { bar: 3 }; simulant.fire( buttons[1], 'click' ); }); test( 'Evaluators that have a value of undefined behave correctly', function ( t ) { var ractive = new Ractive({ el: fixture, template: '{{ list[index] }}', data: { index: 0, list: [ 'foo' ] } }); t.htmlEqual( fixture.innerHTML, 'foo' ); ractive.set( 'index', 1 ); t.htmlEqual( fixture.innerHTML, '' ); }); test( 'Components inherit adaptors from their parent', function ( t ) { var ractive; Ractive.components.widget = Ractive.extend({ template: '<p>{{wrappedThing}}</p>' }); ractive = new Ractive({ el: fixture, template: '<widget wrappedThing="{{thing}}"/>', adapt: [ 'foo' ], data: { thing: new Foo( 'whee!' ) } }); t.htmlEqual( fixture.innerHTML, '<p>whee!</p>' ); }); test( 'Components made with Ractive.extend() can include adaptors', function ( t ) { var Widget, ractive; Widget = Ractive.extend({ adapt: [ 'foo' ] }); ractive = new Widget({ el: fixture, template: '<p>{{thing}}</p>', data: { thing: new Foo( 'whee!' ) } }); t.deepEqual( ractive.adapt, [ Ractive.adaptors.foo ] ); t.htmlEqual( fixture.innerHTML, '<p>whee!</p>' ); }); test( 'Two-way binding can be set up against expressions that resolve to regular keypaths', function ( t ) { var ractive, input; ractive = new Ractive({ el: fixture, template: '{{#items:i}}<label><input value="{{ proxies[i].name }}"> name: {{ proxies[i].name }}</label>{{/items}}', data: { items: [{}], proxies: [] } }); input = ractive.find( 'input' ); input.value = 'foo'; ractive.updateModel(); t.deepEqual( ractive.get( 'proxies' ), [{name: 'foo' }] ); t.htmlEqual( fixture.innerHTML, '<label><input> name: foo</label>' ); }); test( 'Instances of subclasses with non-POJO default models have the correct prototype', function ( t ) { var Model, Subclass, instance; Model = function ( data ) { var key; for ( key in data ) { if ( data.hasOwnProperty( key ) ) { this[ key ] = data[ key ]; } } }; Model.prototype.test = function () { t.ok( true ); }; Subclass = Ractive.extend({ data: function () { return new Model({ foo: 'bar' }) } }); instance = new Subclass({ el: fixture, template: '{{foo}}{{bar}}', data: { bar: 'baz' } }); t.ok( instance.data instanceof Model ); }); test( 'Regression test for #798', function ( t ) { var ClassA, ClassB, ractive; ClassB = function () {}; ClassA = function () {}; ClassA.prototype.resources = new ClassB(); ractive = new Ractive({ el: fixture, template: '<widget attr="{{item.resources}}"/>', data: { item: new ClassA() }, components: { widget: Ractive.extend({}) } }); t.ok( ractive.findComponent('widget').data.attr instanceof ClassB ); }); asyncTest( 'Subclass instance complete() handlers can call _super', function ( t ) { var Subclass, instance; expect( 1 ); Subclass = Ractive.extend({ complete: function () { return 42; } }); instance = new Subclass({ el: fixture, complete: function () { t.equal( this._super(), 42 ); start(); } }); }); test( 'ractive.insert() with triples doesn\'t invoke Yoda (#391)', function ( t ) { var ractive = new Ractive({ el: document.createElement( 'div' ), template: '{{{value}}}', data: { 'value': ' you are <i>very puzzled now</i>' } }); ractive.insert( fixture ); t.htmlEqual( fixture.innerHTML, ' you are <i>very puzzled now</i>' ); }); test( '<input value="{{foo}}"> where foo === null should not render a value (#390)', function ( t ) { var ractive = new Ractive({ el: fixture, template: '<input value="{{foo}}">', data: { foo: null } }); t.equal( ractive.find( 'input' ).value, '' ); }); // only run these tests if magic mode is supported try { var obj = {}, _foo; Object.defineProperty( obj, 'foo', { get: function () { return _foo; }, set: function ( value ) { _foo = value; } }); test( 'Array mutators work when `magic` is `true` (#376)', function ( t ) { var ractive, items; items = [ { name: 'one' }, { name: 'two' }, { name: 'three' } ]; ractive = new Ractive({ el: fixture, template: '{{#items}}{{name}}{{/items}}', magic: true, data: { items: items } }); ractive.data.items.push({ name: 'four' }); t.htmlEqual( fixture.innerHTML, 'onetwothreefour' ); }); test( 'Implicit iterators work in magic mode', function ( t ) { var ractive, items; items = [ { name: 'one' }, { name: 'two' }, { name: 'three' } ]; ractive = new Ractive({ el: fixture, template: '{{#.}}{{name}}{{/.}}', magic: true, data: items }); t.htmlEqual( fixture.innerHTML, 'onetwothree' ); ractive.data[2].name = 'threefourfive'; t.htmlEqual( fixture.innerHTML, 'onetwothreefourfive' ); }); obj.foo = 'bar'; } catch ( err ) { // do nothing } test( 'Foo.extend(Bar), where both Foo and Bar are Ractive instances, returns on object that inherits from Foo and Bar', function ( t ) { var Human, Spider, Spiderman, spiderman; Human = Ractive.extend({ template: '<p>type: {{type}}</p>', talk: function () { return 'hello'; } }); Spider = Ractive.extend({ // registries data: { type: 'arachnid' }, // defaults lazy: true, // methods climb: function () { return 'climbing'; }, talk: function () { return this._super() + ' my name is Peter Parker'; } }); Spiderman = Human.extend( Spider ); spiderman = new Spiderman({ el: fixture }); t.htmlEqual( fixture.innerHTML, '<p>type: arachnid</p>' ); t.ok( spiderman.lazy ); t.equal( spiderman.climb(), 'climbing' ); t.equal( spiderman.talk(), 'hello my name is Peter Parker' ); }); test( 'Regression test for #460', function ( t ) { var items, ractive, baz; items = [ { desc: 'foo' }, { desc: 'bar' }, { desc: 'baz' } ] ractive = new Ractive({ el: fixture, template: '{{#items}}<p>{{desc}}:{{missing[data]}}</p>{{/items}}', data: { items: items } }); baz = items.pop(); t.htmlEqual( fixture.innerHTML, '<p>foo:</p><p>bar:</p>' ); items.push( baz ); t.htmlEqual( fixture.innerHTML, '<p>foo:</p><p>bar:</p><p>baz:</p>' ); }); test( 'Regression test for #457', function ( t ) { var ractive = new Ractive({ el: fixture, template: '{{#step.current == step.current}}<p>{{foo}}</p>{{/step.current == step.current}}' }); ractive.set({ "foo": "bar", "step": { "current": 2 } }); t.ok( true ); }); if ( Ractive.svg ) { test( 'Triples work inside SVG elements', function ( t ) { var text, ractive = new Ractive({ el: document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ), template: '{{{code}}}', data: { code: '<text>works</text>' } }); text = ractive.find( 'text' ); t.ok( !!text ); t.equal( text.namespaceURI, 'http://www.w3.org/2000/svg' ); }); } test( 'Custom delimiters apply to partials (#601)', function ( t ) { var ractive = new Ractive({ el: fixture, template: '([#items:i])([>foo])([/items])', partials: { foo: '([a])' }, data: { items: [{a:0},{a:1}] }, delimiters: [ '([', '])' ], tripleDelimiters: [ '([[', ']])' ] }); t.htmlEqual( fixture.innerHTML, '01') }); test( 'Rendering to an element, if `append` is false, causes any existing instances to be torn down', function ( t ) { var ractive1, ractive2; expect( 2 ); ractive1 = new Ractive({ el: fixture, template: 'foo' }); ractive1.on( 'teardown', function () { t.ok( true ); }); ractive2 = new Ractive({ el: fixture, template: 'bar' }); t.htmlEqual( fixture.innerHTML, 'bar' ); }); if ( Ractive.svg ) { test( 'foreignObject elements and their children default to html namespace (#713)', function ( t ) { var ractive = new Ractive({ el: fixture, template: '<svg><foreignObject><p>foo</p></foreignObject></svg>' }); t.equal( ractive.find( 'foreignObject' ).namespaceURI, 'http://www.w3.org/1999/xhtml' ); t.equal( ractive.find( 'p' ).namespaceURI, 'http://www.w3.org/1999/xhtml' ); }); } // This test fails since #816, because evaluators are treated as computed properties. // Kept here in case we come up with a smart way to have the best of both worlds /*test( 'Evaluators are not called if their expressions no longer exist (#716)', function ( t ) { var ractive, doubled = 0, tripled = 0; ractive = new Ractive({ el: fixture, template: '<p>{{double(foo)}}</p>{{#bar}}<p>{{triple(foo)}}</p>{{/bar}}', data: { foo: 3, double: function ( foo ) { doubled += 1; return foo * 2; }, triple: function ( foo ) { tripled += 1; return foo * 3; } } }); t.equal( doubled, 1 ); t.equal( tripled, 0 ); ractive.set({ foo: 4, bar: true }); t.equal( doubled, 2 ); t.equal( tripled, 1 ); ractive.set({ foo: 5, bar: false }); t.equal( doubled, 3 ); t.equal( tripled, 1 ); });*/ test( 'Regression test for #695 (unrendering non-rendered items)', function ( t ) { var ractive = new Ractive({ el: fixture, template: '{{# { items: nested.items } }}{{#insert}}{{#items}}<div decorator="foo"></div>{{/items}}{{/insert}}{{/}}', decorators: { foo: function () { return { teardown: function () {} }; } } }); ractive.set({ nested: { items: [0,1,2] }, insert: false }); ractive.set({ nested: { items: [0,1] }, insert: true }); t.ok( true ); }); asyncTest( 'A Promise will be rejected if its callback throws (#759)', function ( t ) { var p = new Ractive.Promise( function () { throw 'ruh-roh'; }); p.then( null, function ( err ) { t.equal( err, 'ruh-roh' ); QUnit.start(); }); }); test( 'Keypaths in ractive.set() can contain wildcards (#784)', function ( t ) { var ractive = new Ractive({ data: { array: [ { active: true }, { active: false }, { active: true } ], object: { foo: 1, bar: 2, baz: 3 } } }); ractive.set( 'array.*.active', false ); t.deepEqual( ractive.get( 'array' ), [{ active: false }, { active: false }, { active: false }]); ractive.set( 'object.*', 42 ); t.deepEqual( ractive.get( 'object' ), { foo: 42, bar: 42, baz: 42 }); }); test( 'Wildcard keypaths do not affect array length', function ( t ) { var ractive = new Ractive({ el: fixture, template: '{{array.length}}', data: { array: [ 1, 2, 3 ] } }); ractive.set( 'array.*', 10 ); t.deepEqual( ractive.get( 'array.length' ), 3 ); }); test( 'Regression test for #801', function ( t ) { var ractive = new Ractive({ el: document.createElement( 'div' ), template: '<div>{{#(foo !== "bar")}}not bar{{#(foo !== "baz")}}not baz{{/()}}{{/()}}</div>', data: { foo: 'baz' } }); ractive.set( 'foo', 'bar' ); t.ok( true ); }); test( 'Regression test for #832', function ( t ) { var ractive = new Ractive({ el: document.createElement( 'div' ), template: '{{#if obj[foo].length}}{{#each obj[foo]}}{{this}}{{/each}}{{/if}}', data: { obj: { a: ['x'] }, foo: 'a' } }); ractive.set( 'foo', 'b' ); t.ok( true ); }); test( 'Regression test for #857', function ( t ) { var ractive = new Ractive({ el: document.createElement( 'div' ), template: '<textarea value="{{foo}}"></textarea>', data: { foo: 'works' } }); t.equal( ractive.find( 'textarea' ).value, 'works' ); }); test( 'Radio input can have name/checked attributes without two-way binding (#783)', function ( t ) { expect( 0 ); var ractive = new Ractive({ el: fixture, template: '<input type="radio" name="a" value="a" checked>' }); }); asyncTest( 'Complete handlers are called for lazily-rendered instances (#749)', function ( t ) { expect( 1 ); var ractive = new Ractive({ template: '<p>foo</p>', complete: function () { t.ok( true ); ractive.teardown(); QUnit.start(); } }); ractive.render( fixture ); }); test( 'Doctype declarations are handled, and the tag name is uppercased (#877)', function ( t ) { var ractive = new Ractive({ el: fixture, template: '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>{{title}}</title></head><body>{{hello}} World!</body></html>', data: { title: 'hi', hello: 'Hello' } }); t.equal( ractive.toHTML(), '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>hi</title></head><body>Hello World!</body></html>' ); }); test( 'Resolvers are torn down (#884)', function ( t ) { var ractive = new Ractive({ el: fixture, template: ` {{#if foo}} {{# steps[currentStep] }} argh {{/}} <!-- comment --> {{/if}}`, data: { currentStep: 0, steps: [{}, {}] } }); expect( 0 ); ractive.set( 'foo', true ); ractive.set( 'foo', false ); ractive.set( 'currentStep', 1 ); }); test( 'Regression test for #844', function ( t ) { var ractive = new Ractive({ el: fixture, template: ` {{# steps[currentStep] }} {{#if bool}} some text <!-- this is needed! --> {{/if}} {{/}} {{#if currentStep < steps.length - 1 }} <a on-click='toggleStep'>toggle step: 1</a> {{else}} <a on-click='toggleStep'>toggle step: 0</a> {{/if}}`, data: { currentStep: 0, stepsQuantity: 2, steps: [{}, {}], bool: true } }); ractive.set( 'currentStep', null ); ractive.set( 'currentStep', 1 ); t.htmlEqual( fixture.innerHTML, 'some text <a>toggle step: 0</a>' ); ractive.set( 'currentStep', 0 ); t.htmlEqual( fixture.innerHTML, 'some text <a>toggle step: 1</a>' ); }); test( 'Mustaches that re-resolve to undefined behave correctly (#908)', function ( t ) { var ractive = new Ractive({ el: fixture, template: ` {{# steps[currentStep] }} <p>{{currentStep}}: {{name}}</p> {{/}}`, data: { currentStep: 0, steps: [ { name: 'zero' }, { name: 'one' } ] } }); ractive.set( 'currentStep', null ); ractive.set( 'currentStep', 1 ); t.htmlEqual( fixture.innerHTML, '<p>1: one</p>' ); }); test( 'Content renders to correct place when subsequent sections have no nodes (#910)', function ( t ) { var ractive = new Ractive({ el: fixture, template: '{{ >partial}} <!-- foo -->', partials: { partial: ` {{# steps[currentStep] }} before {{/}} {{#if currentStep !== 1 }} after {{else}} after {{/if}}` }, data: { currentStep: 0, steps: [ true, true ] } }); ractive.set( 'currentStep', null ); ractive.set( 'currentStep', 1 ); t.htmlEqual( fixture.innerHTML, 'before after' ); }); test( 'Dependants can register more than once without error (#838)', function ( t ) { var ractive = new Ractive({ el: fixture, template: '{{#if foo}}<p>{{fn(foo)}}</p>{{/if}}', data: { fn: function ( foo ) { foo.bar; this.get( 'foo' ); this.get( 'bar' ); } } }); expect( 0 ); ractive.set( 'foo', {} ); ractive.set( 'foo', null ); }); test( 'Ractive.extend() with parsed template (#939)', function ( t ) { var parsed, Widget, ractive; parsed = Ractive.parse( '<p>{{foo}}</p>' ); Widget = Ractive.extend({ template: parsed }); ractive = new Widget({ data: { foo: 'bar' }}); t.equal( ractive.toHTML(), '<p>bar</p>' ); }); // These tests run fine in the browser but not in PhantomJS. WTF I don't even. // Anyway I can't be bothered to figure it out right now so I'm just commenting // these out so it will build /*test( 'Components with two-way bindings set parent values on initialisation', function ( t ) { var Dropdown, ractive; Dropdown = Ractive.extend({ template: '<select value="{{value}}">{{#options}}<option value="{{this}}">{{ this[ display ] }}</option>{{/options}}</select>' }); ractive = new Ractive({ el: fixture, template: '<h2>Select an option:</h2><dropdown options="{{numbers}}" value="{{number}}" display="word"/><p>Selected: {{number.digit}}</p>', data: { numbers: [ { word: 'one', digit: 1 }, { word: 'two', digit: 2 }, { word: 'three', digit: 3 }, { word: 'four', digit: 4 } ] }, components: { dropdown: Dropdown } }); t.deepEqual( ractive.get( 'number' ), { word: 'one', digit: 1 }); }); { name: 'Tearing down expression mustaches and recreating them does\'t throw errors', test: function () { var ractive; ractive = new Ractive({ el: fixture, template: '{{#condition}}{{( a+b )}} {{( a+b )}} {{( a+b )}}{{/condition}}', data: { a: 1, b: 2, condition: true } }); equal( fixture.innerHTML, '3 3 3' ); ractive.set( 'condition', false ); equal( fixture.innerHTML, '' ); ractive.set( 'condition', true ); equal( fixture.innerHTML, '3 3 3' ); } }, { name: 'Updating an expression section doesn\'t throw errors', test: function () { var ractive, array; array = [{ foo: 1 }, { foo: 2 }, { foo: 3 }, { foo: 4 }, { foo: 5 }]; ractive = new Ractive({ el: fixture, template: '{{#( array.slice( 0, 3 ) )}}{{foo}}{{/()}}', data: { array: array } }); equal( fixture.innerHTML, '123' ); array.push({ foo: 6 }); equal( fixture.innerHTML, '123' ); array.unshift({ foo: 0 }); equal( fixture.innerHTML, '012' ); ractive.set( 'array', [] ); equal( array._ractive, undefined ); equal( fixture.innerHTML, '' ); ractive.set( 'array', array ); ok( array._ractive ); equal( fixture.innerHTML, '012' ); } }, { name: 'Updating a list section with child list expressions doesn\'t throw errors', test: function () { var ractive, array; array = [ { foo: [ 1, 2, 3, 4, 5 ] }, { foo: [ 2, 3, 4, 5, 6 ] }, { foo: [ 3, 4, 5, 6, 7 ] }, { foo: [ 4, 5, 6, 7, 8 ] }, { foo: [ 5, 6, 7, 8, 9 ] } ]; ractive = new Ractive({ el: fixture, template: '{{#array}}<p>{{#( foo.slice( 0, 3 ) )}}{{.}}{{/()}}</p>{{/array}}', data: { array: array } }); equal( fixture.innerHTML, '<p>123</p><p>234</p><p>345</p><p>456</p><p>567</p>' ); array.push({ foo: [ 6, 7, 8, 9, 10 ] }); equal( fixture.innerHTML, '<p>123</p><p>234</p><p>345</p><p>456</p><p>567</p><p>678</p>' ); array.unshift({ foo: [ 0, 1, 2, 3, 4 ] }); equal( fixture.innerHTML, '<p>012</p><p>123</p><p>234</p><p>345</p><p>456</p><p>567</p><p>678</p>' ); ractive.set( 'array', [] ); equal( array._ractive, undefined ); equal( fixture.innerHTML, '' ); ractive.set( 'array', array ); ok( array._ractive ); equal( fixture.innerHTML, '<p>012</p><p>123</p><p>234</p><p>345</p><p>456</p><p>567</p><p>678</p>' ); } }*/ }; });