From 50976b2d09d35c6d762c8e71da3e3131f275098c Mon Sep 17 00:00:00 2001 From: antelle Date: Sun, 27 Oct 2019 10:35:36 +0100 Subject: [PATCH] collection tests --- app/scripts/framework/collection.js | 8 +- test/src/framework/collection.js | 373 ++++++++++++++++++++++++++++ test/test.webpack.config.js | 4 + 3 files changed, 382 insertions(+), 3 deletions(-) create mode 100644 test/src/framework/collection.js diff --git a/app/scripts/framework/collection.js b/app/scripts/framework/collection.js index bd067a13..a8028b12 100644 --- a/app/scripts/framework/collection.js +++ b/app/scripts/framework/collection.js @@ -15,7 +15,7 @@ function emitSet(target, value, prevValue) { emitter.emit('add', value, target); updates.added.push(value); } - emitter.emit('change', updates, this); + emitter.emit('change', updates, target); } } @@ -25,7 +25,7 @@ function emitRemoved(target, removed) { for (const item of removed) { emitter.emit('remove', item, target); } - emitter.emit('change', { added: [], removed }, this); + emitter.emit('change', { added: [], removed }, target); } } @@ -127,6 +127,7 @@ class Collection { this[SymbolEvents].emit('remove', item, this); this[SymbolEvents].emit('change', { added: [], removed: [item] }, this); } + return item; } shift() { @@ -137,6 +138,7 @@ class Collection { this[SymbolEvents].emit('remove', item, this); this[SymbolEvents].emit('change', { added: [], removed: [item] }, this); } + return item; } unshift(...items) { @@ -197,7 +199,7 @@ class Collection { } sort() { - this[SymbolArray].sort(this.comparator); + return this[SymbolArray].sort(this.comparator); } fill() { diff --git a/test/src/framework/collection.js b/test/src/framework/collection.js new file mode 100644 index 00000000..6304c5b2 --- /dev/null +++ b/test/src/framework/collection.js @@ -0,0 +1,373 @@ +import { expect } from 'chai'; +import { Model } from 'framework/model'; +import { Collection } from 'framework/collection'; + +describe('Collection', () => { + class TestModel extends Model { + constructor(id) { + super(); + this.id = id; + } + } + TestModel.defineModelProperties({ id: 'bar' }); + + class TestCollection extends Collection { + static model = TestModel; + } + + it('should create a collection without models', () => { + const collection = new TestCollection(); + + expect(collection).to.be.ok; + expect(collection.length).to.eql(0); + }); + + it('should create a collection with models', () => { + const collection = new TestCollection([new TestModel('1'), new TestModel('2')]); + + expect(collection).to.be.ok; + expect(collection.length).to.eql(2); + expect(collection[0]).to.be.ok; + expect(collection[0].id).to.eql('1'); + expect(collection[1]).to.be.ok; + expect(collection[1].id).to.eql('2'); + expect(collection[2]).to.be.undefined; + }); + + it('should check types when adding new items', () => { + const collection = new TestCollection(); + + expect(() => { + collection.push({ id: 'bar' }); + }).to.throw(); + }); + + it('should serialize a collection to JSON', () => { + const collection = new TestCollection([new TestModel('1'), new TestModel('2')]); + + expect(JSON.stringify(collection)).to.eql('[{"id":"1"},{"id":"2"}]'); + }); + + it('should add models with push', () => { + const collection = new TestCollection(); + + const callsChange = []; + const callsAdd = []; + + collection.on('change', (...args) => callsChange.push(args)); + collection.on('add', (...args) => callsAdd.push(args)); + + expect(JSON.stringify(collection)).to.eql('[]'); + + const model1 = new TestModel(1); + collection.push(model1); + expect(callsChange).to.eql([[{ added: [model1], removed: [] }, collection]]); + expect(callsAdd).to.eql([[model1, collection]]); + expect(collection.length).to.eql(1); + expect(JSON.stringify(collection)).to.eql('[{"id":1}]'); + callsChange.length = 0; + callsAdd.length = 0; + + const modelA = new TestModel('a'); + const modelB = new TestModel('b'); + collection.push(modelA, modelB); + expect(callsChange).to.eql([[{ added: [modelA, modelB], removed: [] }, collection]]); + expect(callsAdd).to.eql([[modelA, collection], [modelB, collection]]); + expect(collection.length).to.eql(3); + expect(JSON.stringify(collection)).to.eql('[{"id":1},{"id":"a"},{"id":"b"}]'); + }); + + it('should add models with unshift', () => { + const collection = new TestCollection([new TestModel(1)]); + + const callsChange = []; + const callsAdd = []; + + collection.on('change', (...args) => callsChange.push(args)); + collection.on('add', (...args) => callsAdd.push(args)); + + expect(JSON.stringify(collection)).to.eql('[{"id":1}]'); + + const model2 = new TestModel(2); + collection.unshift(model2); + expect(callsChange).to.eql([[{ added: [model2], removed: [] }, collection]]); + expect(callsAdd).to.eql([[model2, collection]]); + expect(collection.length).to.eql(2); + expect(JSON.stringify(collection)).to.eql('[{"id":2},{"id":1}]'); + callsChange.length = 0; + callsAdd.length = 0; + + const modelA = new TestModel('a'); + const modelB = new TestModel('b'); + collection.unshift(modelA, modelB); + expect(callsChange).to.eql([[{ added: [modelA, modelB], removed: [] }, collection]]); + expect(callsAdd).to.eql([[modelA, collection], [modelB, collection]]); + expect(collection.length).to.eql(4); + expect(JSON.stringify(collection)).to.eql('[{"id":"a"},{"id":"b"},{"id":2},{"id":1}]'); + }); + + it('should remove models with pop', () => { + const model1 = new TestModel(1); + const model2 = new TestModel(2); + const collection = new TestCollection([model1, model2]); + + const callsChange = []; + const callsRemove = []; + + collection.on('change', (...args) => callsChange.push(args)); + collection.on('remove', (...args) => callsRemove.push(args)); + + expect(JSON.stringify(collection)).to.eql('[{"id":1},{"id":2}]'); + + const popped1 = collection.pop(); + expect(popped1).to.eql(model2); + expect(callsChange).to.eql([[{ added: [], removed: [model2] }, collection]]); + expect(callsRemove).to.eql([[model2, collection]]); + expect(collection.length).to.eql(1); + expect(JSON.stringify(collection)).to.eql('[{"id":1}]'); + callsChange.length = 0; + callsRemove.length = 0; + + const popped2 = collection.pop(); + expect(popped2).to.eql(model1); + expect(callsChange).to.eql([[{ added: [], removed: [model1] }, collection]]); + expect(callsRemove).to.eql([[model1, collection]]); + expect(JSON.stringify(collection)).to.eql('[]'); + callsChange.length = 0; + callsRemove.length = 0; + + const popped3 = collection.pop(); + expect(popped3).to.be.undefined; + expect(callsChange).to.eql([]); + expect(callsRemove).to.eql([]); + expect(JSON.stringify(collection)).to.eql('[]'); + }); + + it('should remove models with shift', () => { + const model1 = new TestModel(1); + const model2 = new TestModel(2); + const collection = new TestCollection([model1, model2]); + + const callsChange = []; + const callsRemove = []; + + collection.on('change', (...args) => callsChange.push(args)); + collection.on('remove', (...args) => callsRemove.push(args)); + + expect(JSON.stringify(collection)).to.eql('[{"id":1},{"id":2}]'); + + const shifted1 = collection.shift(); + expect(shifted1).to.eql(model1); + expect(callsChange).to.eql([[{ added: [], removed: [model1] }, collection]]); + expect(callsRemove).to.eql([[model1, collection]]); + expect(collection.length).to.eql(1); + expect(JSON.stringify(collection)).to.eql('[{"id":2}]'); + callsChange.length = 0; + callsRemove.length = 0; + + const shifted2 = collection.shift(); + expect(shifted2).to.eql(model2); + expect(callsChange).to.eql([[{ added: [], removed: [model2] }, collection]]); + expect(callsRemove).to.eql([[model2, collection]]); + expect(collection.length).to.eql(0); + expect(JSON.stringify(collection)).to.eql('[]'); + callsChange.length = 0; + callsRemove.length = 0; + + const shifted3 = collection.shift(); + expect(shifted3).to.be.undefined; + expect(callsChange).to.eql([]); + expect(callsRemove).to.eql([]); + expect(collection.length).to.eql(0); + expect(JSON.stringify(collection)).to.eql('[]'); + }); + + it('should remove models by setting length', () => { + const model1 = new TestModel(1); + const model2 = new TestModel(2); + const model3 = new TestModel(3); + const collection = new TestCollection([model1, model2, model3]); + + const callsChange = []; + const callsRemove = []; + + collection.on('change', (...args) => callsChange.push(args)); + collection.on('remove', (...args) => callsRemove.push(args)); + + expect(JSON.stringify(collection)).to.eql('[{"id":1},{"id":2},{"id":3}]'); + + collection.length = 1; + expect(callsChange).to.eql([[{ added: [], removed: [model2, model3] }, collection]]); + expect(callsRemove).to.eql([[model2, collection], [model3, collection]]); + expect(collection.length).to.eql(1); + expect(JSON.stringify(collection)).to.eql('[{"id":1}]'); + callsChange.length = 0; + callsRemove.length = 0; + + collection.length = 1; + expect(callsChange).to.eql([]); + expect(callsRemove).to.eql([]); + expect(collection.length).to.eql(1); + expect(JSON.stringify(collection)).to.eql('[{"id":1}]'); + + collection.length = 0; + expect(callsChange).to.eql([[{ added: [], removed: [model1] }, collection]]); + expect(callsRemove).to.eql([[model1, collection]]); + expect(collection.length).to.eql(0); + expect(JSON.stringify(collection)).to.eql('[]'); + }); + + it('should set custom properties', () => { + const collection = new TestCollection(); + + const calls = []; + + collection.on('change', (...args) => calls.push(args)); + collection.on('add', (...args) => calls.push(args)); + collection.on('remove', (...args) => calls.push(args)); + + collection.prop = 'val'; + + expect(collection.prop).to.eql('val'); + + expect(collection.length).to.eql(0); + expect(JSON.stringify(collection)).to.eql('[]'); + expect(calls).to.eql([]); + }); + + it('should check types when setting items', () => { + const collection = new TestCollection(); + + expect(() => { + collection[0] = { id: 'bar' }; + }).to.throw(); + }); + + it('should set new items', () => { + const collection = new TestCollection(); + + const callsChange = []; + const callsAdd = []; + const callsRemove = []; + + collection.on('change', (...args) => callsChange.push(args)); + collection.on('add', (...args) => callsAdd.push(args)); + collection.on('remove', (...args) => callsRemove.push(args)); + + const model = new TestModel(1); + collection[0] = model; + + expect(collection.length).to.eql(1); + expect(JSON.stringify(collection)).to.eql('[{"id":1}]'); + expect(callsChange).to.eql([[{ added: [model], removed: [] }, collection]]); + expect(callsAdd).to.eql([[model, collection]]); + expect(callsRemove).to.eql([]); + }); + + it('should set existing items', () => { + const model1 = new TestModel(1); + const model2 = new TestModel(2); + const collection = new TestCollection([model1, model2]); + + const callsChange = []; + const callsAdd = []; + const callsRemove = []; + + collection.on('change', (...args) => callsChange.push(args)); + collection.on('add', (...args) => callsAdd.push(args)); + collection.on('remove', (...args) => callsRemove.push(args)); + + expect(JSON.stringify(collection)).to.eql('[{"id":1},{"id":2}]'); + + const model3 = new TestModel(3); + collection[1] = model3; + + expect(collection.length).to.eql(2); + expect(JSON.stringify(collection)).to.eql('[{"id":1},{"id":3}]'); + expect(callsChange).to.eql([[{ added: [model3], removed: [model2] }, collection]]); + expect(callsAdd).to.eql([[model3, collection]]); + expect(callsRemove).to.eql([[model2, collection]]); + }); + + it('should get items by id', () => { + const model1 = new TestModel(1); + const model2 = new TestModel(2); + const collection = new TestCollection([model1, model2]); + + expect(collection.get(1)).to.eql(model1); + expect(collection.get(2)).to.eql(model2); + expect(collection.get(3)).to.be.undefined; + }); + + it('should remove items', () => { + const model1 = new TestModel(1); + const model2 = new TestModel(2); + const collection = new TestCollection([model1, model2]); + + const callsChange = []; + const callsRemove = []; + + collection.on('change', (...args) => callsChange.push(args)); + collection.on('remove', (...args) => callsRemove.push(args)); + + collection.remove(1); + + expect(JSON.stringify(collection)).to.eql('[{"id":2}]'); + expect(callsChange).to.eql([[{ added: [], removed: [model1] }, collection]]); + expect(callsRemove).to.eql([[model1, collection]]); + callsChange.length = 0; + callsRemove.length = 0; + + collection.remove(model2); + + expect(JSON.stringify(collection)).to.eql('[]'); + expect(callsChange).to.eql([[{ added: [], removed: [model2] }, collection]]); + expect(callsRemove).to.eql([[model2, collection]]); + }); + + it('should sort items by comparator', () => { + const model1 = new TestModel(1); + const model2 = new TestModel(2); + const collection = new TestCollection([model1, model2]); + + expect(JSON.stringify(collection)).to.eql('[{"id":1},{"id":2}]'); + + collection.comparator = (x, y) => x.id - y.id; + collection.sort(); + expect(JSON.stringify(collection)).to.eql('[{"id":1},{"id":2}]'); + + collection.comparator = (x, y) => y.id - x.id; + collection.sort(); + expect(JSON.stringify(collection)).to.eql('[{"id":2},{"id":1}]'); + }); + + it('should splice items', () => { + const model1 = new TestModel(1); + const model2 = new TestModel(2); + const model3 = new TestModel(3); + const collection = new TestCollection([model1, model2, model3]); + + const callsChange = []; + const callsAdd = []; + const callsRemove = []; + + collection.on('change', (...args) => callsChange.push(args)); + collection.on('add', (...args) => callsAdd.push(args)); + collection.on('remove', (...args) => callsRemove.push(args)); + + expect(JSON.stringify(collection)).to.eql('[{"id":1},{"id":2},{"id":3}]'); + + const model4 = new TestModel(4); + const model5 = new TestModel(5); + const model6 = new TestModel(6); + collection.splice(1, 1, model4, model5, model6); + + expect(collection.length).to.eql(5); + expect(JSON.stringify(collection)).to.eql('[{"id":1},{"id":4},{"id":5},{"id":6},{"id":3}]'); + expect(callsChange).to.eql([ + [{ added: [model4, model5, model6], removed: [model2] }, collection] + ]); + expect(callsAdd).to.eql([[model4, collection], [model5, collection], [model6, collection]]); + expect(callsRemove).to.eql([[model2, collection]]); + }); +}); diff --git a/test/test.webpack.config.js b/test/test.webpack.config.js index 08b22682..fc68d637 100644 --- a/test/test.webpack.config.js +++ b/test/test.webpack.config.js @@ -11,6 +11,7 @@ const rootDir = path.join(__dirname, '..'); module.exports = { mode: 'development', + devtool: 'source-map', entry: path.resolve(__dirname, 'index.js'), output: { path: path.resolve(__dirname, 'dist'), @@ -22,5 +23,8 @@ module.exports = { ...appConfig.resolve.alias, test: path.resolve(rootDir, 'test') } + }, + module: { + rules: appConfig.module.rules } };