UNPKG

@transmutable/bink

Version:

A sleek and reactive framework for web pages

420 lines (379 loc) 15.1 kB
import Test from '../src/test/Test.js' import Runner from '../src/test/Runner.js' import SynchronousFetchMap from '../src/test/SynchronousFetchMap.js' import TestResultsRenderer from '../src/test/TestResultsRenderer.js' import { dom, Router, Component, DataModel, DataObject, EventHandler, DataCollection } from '../src/index.js' const tests = [] tests.push( new Test('Events test', (test) => { const model = new DataModel() const receivedEvents = [] model.addListener(EventHandler.ALL_EVENTS, (eventName, target, ...params) => { receivedEvents.push({ eventName: eventName, target: target, params: params }) }) model.addListener('changed:foo', (eventName, target, ...params) => { receivedEvents.push({ eventName: eventName, target: target, params: params }) }) model.addListener('changed:not_foo', (eventName, target, ...params) => { receivedEvents.push({ eventName: eventName, target: target, params: params }) }) model.set('foo', 'bar') test.assertEqual(receivedEvents.length, 3) test.assertEqual(receivedEvents[receivedEvents.length - 2].eventName, 'changed:foo') test.assertEqual(receivedEvents[receivedEvents.length - 2].target, model) test.assertEqual(receivedEvents[receivedEvents.length - 2].params[0], 'foo') const listener = (eventName, target, ...params) => { receivedEvents.push({ eventName: eventName, target: target, params: params }) } model.addListener('changed:baz', listener) receivedEvents.length = 0 model.set('baz', 23) test.assertEqual(receivedEvents.length, 3) model.removeListener('changed:bogus', listener) // Wrong eventName so should not be removed receivedEvents.length = 0 model.set('baz', 100) test.assertEqual(receivedEvents.length, 3) // Should still be a listener model.removeListener('changed:baz', listener) receivedEvents.length = 0 model.set('baz', 43) test.assertEqual(receivedEvents.length, 2) // Should only match the EventListener.ALL_EVENTS listener from above model.cleanup() receivedEvents.length = 0 model.trigger('changed:foo', model, 'foo') test.assertEqual(receivedEvents.length, 0) const model2 = new DataModel({ foo: 'bar' }) receivedEvents.length = 0 model2.addListener('changed:foo', listener, true) // listen just once model2.set('foo', 'baz') test.assertEqual(receivedEvents.length, 1) model2.set('foo', 'blitz') test.assertEqual(receivedEvents.length, 1) // should have been removed as a listener }) ) tests.push( new Test('DataModel', (test) => { class FlowersCollection extends DataCollection {} const model = new DataModel(null, { fieldDataObjects: { flowers: FlowersCollection }, }) const receivedEvents = [] model.addListener(EventHandler.ALL_EVENTS, (eventName, target, ...params) => { receivedEvents.push({ eventName: eventName, target: target, params: params }) }) test.assertNull(model.get('bogus')) test.assertEqual(model.get('bogus', 'moon'), 'moon') model.set('moon', 'unit') test.assertEqual(model.get('moon'), 'unit') test.assertEqual(model.get('moon', 'goon'), 'unit') test.assertEqual(receivedEvents.length, 2) test.assertEqual(receivedEvents[0].eventName, 'changed:moon') test.assertEqual(receivedEvents[1].eventName, 'changed') model.setBatch({ dink: 'donk', pink: 'punk', }) test.assertEqual(model.get('dink'), 'donk') test.assertEqual(model.get('pink'), 'punk') test.assertEqual(receivedEvents.length, 5) test.assertEqual(receivedEvents[2].eventName, 'changed:dink') test.assertEqual(receivedEvents[3].eventName, 'changed:pink') test.assertEqual(receivedEvents[4].eventName, 'changed') model.set('dink', 'donk') test.assertEqual(receivedEvents.length, 5) // Set to same value, should trigger no events model.set('flowers', [{ petals: 5 }, { petals: 6 }]) test.assertInstanceOf(model.get('flowers'), FlowersCollection) let flowerCount = 0 for (const flower of model.get('flowers')) { flowerCount++ } test.assertEqual(flowerCount, 2) model.set('subModel', new DataModel({ mesmer: 'scout' })) test.assertInstanceOf(model.get('subModel'), DataModel) test.assertEqual(model.get('subModel').get('mesmer'), 'scout') model.set('subModel', { cheese: 'tall' }) test.assertInstanceOf(model.get('subModel'), DataModel) test.assertEqual(model.get('subModel').get('cheese'), 'tall') test.assertEqual(model.get('subModel').get('mesmer'), null) receivedEvents.length = 0 model.increment('pageCount') // Creates the field since it doesn't exist test.assertEqual(model.get('pageCount'), 1) test.assertEqual(receivedEvents.length, 2) test.assertEqual(receivedEvents[0].eventName, 'changed:pageCount') receivedEvents.length = 0 model.increment('pageCount', -2) test.assertEqual(model.get('pageCount'), -1) test.assertEqual(receivedEvents.length, 2) test.assertEqual(receivedEvents[0].eventName, 'changed:pageCount') model.increment('pageCount', 10) test.assertEqual(model.get('pageCount'), 9) class ChildModel extends DataModel { get countPlusFour() { return this.get('count') + 4 } } const parentModel = new DataModel({ id: 5, child: { count: 20 } }, { fieldDataObjects: { child: ChildModel } }) test.assertInstanceOf(parentModel.get('child'), ChildModel) // child should be mapped to DataModel by fieldDataObjects test.assertEqual(parentModel.get('child').countPlusFour, 24) }) ) tests.push( new Test('DataCollection', (test) => { const col1 = new DataCollection() const receivedEvents = [] col1.addListener(EventHandler.ALL_EVENTS, (eventName, target, ...params) => { receivedEvents.push({ eventName: eventName, target: target, params: params }) }) test.assertEqual(col1.length, 0) const model1 = new DataModel({ foo: 'bar' }) col1.add(model1) test.assertEqual(receivedEvents.length, 1) test.assertEqual(receivedEvents[0].eventName, DataCollection.AddedEvent) test.assertEqual(col1.length, 1) test.assertEqual(col1.at(0).get('foo'), 'bar') receivedEvents.length = 0 col1.remove(model1) test.assertEqual(receivedEvents.length, 1) test.assertEqual(receivedEvents[0].eventName, DataCollection.RemovedEvent) test.assertEqual(col1.length, 0) col1.remove(model1) test.assertEqual(receivedEvents.length, 1) test.assertEqual(col1.length, 0) receivedEvents.length = 0 col1.addBatch([ new DataModel({ id: 1 }), new DataModel({ id: 2 }), new DataModel({ id: 3 }), new DataModel({ id: 4 }), ]) test.assertEqual(receivedEvents.length, 4) for (const event in receivedEvents) { test.assertEqual(event.eventName, DataCollection.RemovedEvent) } receivedEvents.length = 0 col1.reset([{ id: 10 }, { id: 11 }]) test.assertEqual(receivedEvents[0].eventName, DataCollection.RemovedEvent) test.assertEqual(receivedEvents[1].eventName, DataCollection.RemovedEvent) test.assertEqual(receivedEvents[2].eventName, DataCollection.RemovedEvent) test.assertEqual(receivedEvents[3].eventName, DataCollection.RemovedEvent) test.assertEqual(receivedEvents[4].eventName, DataCollection.AddedEvent) test.assertEqual(receivedEvents[5].eventName, DataCollection.AddedEvent) test.assertEqual(receivedEvents[6].eventName, DataCollection.ResetEvent) }) ) tests.push( new Test('DataCollection sorting', (test) => { const col1 = new DataCollection([ { id: 4, foo: 'a' }, { id: 1, foo: 'b' }, { id: 2, foo: 'c' }, { id: 3, foo: 'd' }, ]) col1.sort() // Sorts by id test.assertEqual(col1.at(0).get('id'), 1) test.assertEqual(col1.at(1).get('id'), 2) test.assertEqual(col1.at(2).get('id'), 3) test.assertEqual(col1.at(3).get('id'), 4) col1.sortByAttribute('foo') test.assertEqual(col1.at(0).get('foo'), 'a') test.assertEqual(col1.at(1).get('foo'), 'b') test.assertEqual(col1.at(2).get('foo'), 'c') test.assertEqual(col1.at(3).get('foo'), 'd') col1.keepSortedByField('foo') test.assertEqual(col1.at(0).get('foo'), 'a') test.assertEqual(col1.at(1).get('foo'), 'b') test.assertEqual(col1.at(2).get('foo'), 'c') test.assertEqual(col1.at(3).get('foo'), 'd') col1.at(0).set('foo', 'z') test.assertEqual(col1.at(0).get('foo'), 'b') test.assertEqual(col1.at(1).get('foo'), 'c') test.assertEqual(col1.at(2).get('foo'), 'd') test.assertEqual(col1.at(3).get('foo'), 'z') col1.reset([ { id: 1, foo: 'b' }, { id: 2, foo: 'c' }, { id: 3, foo: 'd' }, { id: 4, foo: 'a' }, ]) test.assertEqual(col1.at(0).get('foo'), 'a') test.assertEqual(col1.at(1).get('foo'), 'b') test.assertEqual(col1.at(2).get('foo'), 'c') test.assertEqual(col1.at(3).get('foo'), 'd') }) ) tests.push( new Test( 'SynchronousFetch', (test) => { SynchronousFetchMap.set('foo', { foo: 'bar' }) let thenCount = 0 fetch('foo') .then((r) => r.json()) .then((data) => { thenCount += 1 test.assertEqual(data.status, 200) test.assertEqual(data.foo, 'bar') }) .catch(() => { throw new Error('Should not have caught on this promise.') }) fetch('bogus') .then((r) => r.json()) .then((data) => { thenCount += 1 test.assertEqual(data.status, 404) }) .catch(() => { throw new Error('Should not have caught on this promise.') }) test.assertEqual(thenCount, 2) // Make sure we reached the then functions }, (test) => { test.originalFetch = window.fetch window.fetch = SynchronousFetchMap.synchronousFetch SynchronousFetchMap.clear() }, (test) => { window.fetch = test.originalFetch } ) ) tests.push( new Test('DOM manipulation', (test) => { const el1 = dom.div() // Add and remove classes test.assertType(el1.removeClass, 'function') test.assertType(el1.addClass, 'function') test.assertEqual(el1.addClass('foo').nodeType, 1) test.assertEqual(el1.getAttribute('class'), 'foo') el1.addClass('bar') test.assertEqual(el1.getAttribute('class'), 'foo bar') test.assertEqual(el1.removeClass('foo').nodeType, 1) test.assertEqual(el1.getAttribute('class'), 'bar') el1.removeClass('bar') test.assertEqual(el1.getAttribute('class'), null) // Removing the last class removes the attribute el1.addClass('blinz') el1.addClass('batz') test.assertEqual(el1.getAttribute('class'), 'blinz batz') el1.removeClass('batz') test.assertEqual(el1.getAttribute('class'), 'blinz') const el2 = dom.span().appendTo(el1) test.assertEqual(el1.children.length, 1) test.assertEqual(el1.children[0], el2) const el3 = dom.div({ foo: 'bar' }, 'Howdy', dom.span('Moo')) test.assertEqual(el3.childNodes[0].text, 'Howdy') test.assertEqual(el3.getAttribute('foo'), 'bar') test.assertEqual(el3.children[0].innerText, 'Moo') }) ) tests.push( new Test('DOM sorting', (test) => { const el1 = dom.div() test.assertEqual(el1.sort(), el1) // sort is in-place and returns the element for chaining el1.appendChild(dom.div({ id: 3 })) el1.appendChild(dom.div({ id: 5 })) el1.appendChild(dom.div({ id: 1 })) el1.appendChild(dom.div({ id: 2 })) el1.appendChild(dom.div({ id: 5 })) // Note, there are two fives el1.appendChild(dom.div({ id: 4 })) el1.sortByAttribute('id') test.assertEqual(el1.children.length, 6) for (let i = 0; i < el1.children.length - 1; i++) { test.assertEqual(el1.children.item(i).getAttribute('id'), i + 1) } test.assertEqual(el1.children.item(el1.children.length - 1).getAttribute('id'), 5) }) ) tests.push( new Test('Component', (test) => { let component1 = new Component(undefined, { dom: document.createElement('bogus'), }) test.assertEqual(component1.dom.tagName.toLowerCase(), 'bogus') const fooSpan = dom.span('Mooo', { foo: 'bar' }, dom.p({ blatz: 'biz' }, 'flowers')).appendTo(component1.dom) component1.dom.appendChild(fooSpan) test.assertEqual(fooSpan.getAttribute('foo'), 'bar') test.assertNotEqual(fooSpan.innerHTML.indexOf('blatz="biz"')) component1 = dom.div() component1.append('Foo') test.assertEqual(component1.innerHTML, 'Foo') component1.append({ bling: 'ring' }) test.assertEqual(component1.getAttribute('bling'), 'ring') class C1 extends Component { constructor(dataObject, options) { super(dataObject, options) this.fooDOM = dom.div().appendTo(this.dom) this.bindText('foo', this.fooDOM) this.burfDOM = dom.div().appendTo(this.dom) this.bindText('burf', this.burfDOM, (value) => { return (value ? value : 'nothing') + ' and more!' }) this.bindAttribute('blart', this.dom, 'bluez') } } const model1 = new DataModel({ foo: 'bar', blart: 'binz' }) const c2 = new C1(model1, { bibim: 'bap' }) test.assertEqual(c2.fooDOM.innerHTML, 'bar') test.assertEqual(c2.burfDOM.innerHTML, 'nothing and more!') test.assertEqual(c2.dom.getAttribute('bluez'), 'binz') model1.set('foo', 'biz') test.assertEqual(c2.fooDOM.innerHTML, 'biz') model1.set('foo', null) test.assertEqual(c2.fooDOM.innerHTML, '') model1.set('foo', 23) test.assertEqual(c2.fooDOM.innerHTML, '23') model1.set('burf', 'sugar') test.assertEqual(c2.burfDOM.innerHTML, 'sugar and more!') model1.set('blart', 'floop') test.assertEqual(c2.dom.getAttribute('bluez'), 'floop') c2.cleanup() }) ) tests.push( new Test( 'Routing', (test) => { const router = new Router() const receivedEvents = [] router.addListener(EventHandler.ALL_EVENTS, (eventName, target, ...params) => { receivedEvents.push({ eventName: eventName, target: target, params: params }) }) router.addRoute(/^$/, 'splash', { foo: 'bar' }, 'tinkle') router.addRoute(/^tos$/, 'terms-of-service') router.addRoute(/^blog\/([0-9]+)$/, 'blog', { hello: 'nurse' }) router.addRoute(/^blog\/([0-9]+)\/post\/([0-9a-zA-Z]+)$/, 'post') test.assertEqual(receivedEvents.length, 4) test.assertTrue(receivedEvents.some((ev) => ev.eventName !== Router.RouteAddedEvent) === false) receivedEvents.length = 0 router.start() test.assertEqual(receivedEvents.length, 2) test.assertEqual(receivedEvents[0].eventName, 'splash') test.assertEqual(receivedEvents[0].params[0].foo, 'bar') test.assertEqual(receivedEvents[1].eventName, Router.StartedRoutingEvent) receivedEvents.length = 0 router._handleNewPath('bogus') test.assertEqual(receivedEvents.length, 1) test.assertEqual(receivedEvents[0].eventName, Router.UnknownRouteEvent) test.assertEqual(receivedEvents[0].params.length, 0) receivedEvents.length = 0 router._handleNewPath('blog/23') test.assertEqual(receivedEvents.length, 1) test.assertEqual(receivedEvents[0].eventName, 'blog') test.assertEqual(receivedEvents[0].params[0], '23') test.assertEqual(receivedEvents[0].params[1].hello, 'nurse') receivedEvents.length = 0 router._handleNewPath('blog/23/post/alphaZ') test.assertEqual(receivedEvents.length, 1) test.assertEqual(receivedEvents[0].eventName, 'post') test.assertEqual(receivedEvents[0].params[0], '23') test.assertEqual(receivedEvents[0].params[1], 'alphaZ') router.cleanup() }, () => { document.location.hash = '' }, () => { document.location.hash = '' } ) ) export { tests, Runner, TestResultsRenderer as Renderer }