diff --git a/package.json b/package.json index a83f66c2135..efa51df6fc5 100644 --- a/package.json +++ b/package.json @@ -13,22 +13,22 @@ "build:docs": "dumi build", "start": "dumi dev", "test": "jest --coverage", - "test:reactive": "jest packages/reactive", - "test:validator": "jest packages/validator", - "test:core": "jest packages/core", + "test:reactive": "jest packages/reactive/", + "test:validator": "jest packages/validator/", + "test:core": "jest packages/core/", "test:core:watch": "npm run test:core --- --watch", - "test:schema": "jest packages/json-schema", + "test:schema": "jest packages/json-schema/", "test:schema:watch": "npm run test:schema --- --watch", - "test:react": "jest packages/react", - "test:shared": "jest packages/shared", - "test:path": "jest packages/path", + "test:react": "jest packages/react/", + "test:shared": "jest packages/shared/", + "test:path": "jest packages/path/", "test:react:watch": "npm run test:react --- --watch", - "test:vue": "jest packages/vue", + "test:vue": "jest packages/vue/", "test:vue:watch": "npm run test:vue --- --watch", - "test:reactive-vue": "jest packages/reactive-vue", + "test:reactive-vue": "jest packages/reactive-vue/", "test:reactive-vue:watch": "npm run test:reactive-vue --- --watch", - "test:antd": "jest packages/antd", - "test:next": "jest packages/next", + "test:antd": "jest packages/antd/", + "test:next": "jest packages/next/", "test:watch": "jest --watch", "test:prod": "jest --coverage --silent", "preversion": "yarn install --ignore-engines && git add -A && npm run build && npm run lint && npm run test", @@ -183,4 +183,4 @@ "dependencies": { "@ant-design/icons": "^4.0.2" } -} \ No newline at end of file +} diff --git a/packages/reactive/src/__tests__/action.spec.ts b/packages/reactive/src/__tests__/action.spec.ts index dec09c7d992..8bf27dac1e6 100644 --- a/packages/reactive/src/__tests__/action.spec.ts +++ b/packages/reactive/src/__tests__/action.spec.ts @@ -487,8 +487,8 @@ test('nested action to reaction', () => { obs.aa = 4 }) }) - expect(handler).toBeCalledWith(2) - expect(handler).toBeCalledWith(4) + expect(handler).nthCalledWith(1, 2) + expect(handler).nthCalledWith(2, 4) expect(handler).toBeCalledTimes(2) }) @@ -525,7 +525,7 @@ test('nested action/batch to reaction', () => { obs.aa = 4 }) }) - expect(handler).toBeCalledWith(2) - expect(handler).toBeCalledWith(4) + expect(handler).nthCalledWith(1, 2) + expect(handler).nthCalledWith(2, 4) expect(handler).toBeCalledTimes(2) }) diff --git a/packages/reactive/src/__tests__/annotations.spec.ts b/packages/reactive/src/__tests__/annotations.spec.ts index f2723086c6f..7336ba2008e 100644 --- a/packages/reactive/src/__tests__/annotations.spec.ts +++ b/packages/reactive/src/__tests__/annotations.spec.ts @@ -30,9 +30,9 @@ test('shallow annotation', () => { handler(obs.aa) }) obs.aa = { bb: { cc: 123 } } - expect(isObservable(obs)).toBeTruthy() - expect(isObservable(obs.aa)).toBeFalsy() - expect(isObservable(obs.aa.bb)).toBeFalsy() + expect(isObservable(obs)).toBe(true) + expect(isObservable(obs.aa)).toBe(false) + expect(isObservable(obs.aa.bb)).toBe(false) obs.aa.bb = 333 obs.cc = 444 expect(handler).toBeCalledTimes(2) @@ -67,8 +67,8 @@ test('ref annotation', () => { handler(obs.value) }) obs.value = 333 - expect(handler).toBeCalledWith(123) - expect(handler).toBeCalledWith(123) + expect(handler).nthCalledWith(1, 123) + expect(handler).nthCalledWith(2, 333) expect(handler1).toBeCalledTimes(1) }) @@ -99,8 +99,8 @@ test('no action annotation', () => { }, handler) setData() expect(handler).toBeCalledTimes(2) - expect(handler).toBeCalledWith([123, undefined], [undefined, undefined]) - expect(handler).toBeCalledWith([123, 321], [123, undefined]) + expect(handler).nthCalledWith(1, [123, undefined], [undefined, undefined]) + expect(handler).nthCalledWith(2, [123, 321], [123, undefined]) }) test('computed annotation', () => { @@ -241,12 +241,16 @@ test('computed with computed array length', () => { handler(obs.isNotEmpty) handler2(obs.arr2) }) - expect(handler).toBeCalledWith(false) expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(false) + expect(handler2).toBeCalledTimes(1) + expect(handler2.mock.calls[0][0]).toEqual([]) obs.arr.push(1) - expect(handler).toBeCalledWith(true) + expect(handler).lastCalledWith(true) + expect(handler2.mock.calls[1][0]).toEqual([2]) obs.arr = [] - expect(handler).toBeCalledWith(false) + expect(handler).lastCalledWith(false) + expect(handler2.mock.calls[2][0]).toEqual([]) }) test('computed recollect dependencies', () => { diff --git a/packages/reactive/src/__tests__/array.spec.ts b/packages/reactive/src/__tests__/array.spec.ts index cc48692a963..68a82c86628 100644 --- a/packages/reactive/src/__tests__/array.spec.ts +++ b/packages/reactive/src/__tests__/array.spec.ts @@ -11,7 +11,30 @@ test('ArraySet', () => { const set = new ArraySet() set.add(11) set.add(11) - expect(set.value).toEqual([11]) + set.add(22) + expect(set.value).toEqual([11, 22]) + + const handler = jest.fn() + set.forEach(handler) + expect(handler).toBeCalledTimes(2) + expect(handler).nthCalledWith(1, 11) + expect(handler).nthCalledWith(2, 22) + expect(set.value).toEqual([11, 22]) + + expect(set.has(11)).toBe(true) + set.delete(11) + expect(set.has(11)).toBe(false) + expect(set.value).toEqual([22]) + set.clear() expect(set.value).toEqual([]) + + const handler1 = jest.fn() + set.add(11) + set.add(22) + set.forEachDelete(handler1) + expect(handler1).toBeCalledTimes(2) + expect(handler1).nthCalledWith(1, 11) + expect(handler1).nthCalledWith(2, 22) + expect(set.value).toEqual([]) }) diff --git a/packages/reactive/src/__tests__/autorun.spec.ts b/packages/reactive/src/__tests__/autorun.spec.ts index f74c9fba312..2e74766b38a 100644 --- a/packages/reactive/src/__tests__/autorun.spec.ts +++ b/packages/reactive/src/__tests__/autorun.spec.ts @@ -59,6 +59,7 @@ test('reaction fireImmediately', () => { fireImmediately: true, } ) + expect(handler).toBeCalledTimes(1) obs.aa.bb = 123 expect(handler).toBeCalledTimes(1) obs.aa.bb = 111 @@ -122,6 +123,7 @@ test('reaction with shallow equals', () => { }, handler) obs.aa = { bb: 123 } expect(handler).toBeCalledTimes(1) + expect(handler.mock.calls[0][0]).toEqual({ bb: 123 }) }) test('reaction with deep equals', () => { @@ -165,8 +167,7 @@ test('autorun direct recursive react with if', () => { fn(obs1.value, obs2.value) }) obs2.value = '222' - expect(fn).not.toHaveBeenCalledWith('111', undefined) - expect(fn).not.toHaveBeenCalledWith('111', '222') + expect(fn).toBeCalledTimes(0) }) test('autorun indirect recursive react', () => { @@ -186,7 +187,10 @@ test('autorun indirect recursive react', () => { obs3.value = 0 } }) + expect(obs2.value).toEqual(1) + expect(obs1.value).toEqual(2) obs3.value = 1 + expect(obs2.value).toEqual(2) expect(obs1.value).toEqual(3) }) @@ -228,8 +232,8 @@ test('autorun direct recursive react with head track', () => { fn(obs1.value, obs2Value) }) obs2.value = '222' - expect(fn).not.toHaveBeenCalledWith('111', undefined) - expect(fn).toHaveBeenCalledWith('111', '222') + expect(fn).toBeCalledTimes(1) + expect(fn).lastCalledWith('111', '222') }) test('autorun.memo', () => { @@ -248,8 +252,11 @@ test('autorun.memo', () => { obs.bb++ obs.bb++ expect(fn).toBeCalledTimes(5) - expect(fn).toBeCalledWith(3, 3) - expect(fn).toBeCalledWith(4, 4) + expect(fn).nthCalledWith(1, 0, 0) + expect(fn).nthCalledWith(2, 1, 1) + expect(fn).nthCalledWith(3, 2, 2) + expect(fn).nthCalledWith(4, 3, 3) + expect(fn).nthCalledWith(5, 4, 4) }) test('autorun.memo with observable', () => { @@ -269,10 +276,10 @@ test('autorun.memo with observable', () => { obs1.aa++ obs1.aa++ expect(fn).toBeCalledTimes(4) - expect(fn).toBeCalledWith(0, 0) - expect(fn).toBeCalledWith(1, 1) - expect(fn).toBeCalledWith(2, 2) - expect(fn).toBeCalledWith(3, 3) + expect(fn).nthCalledWith(1, 0, 0) + expect(fn).nthCalledWith(2, 1, 1) + expect(fn).nthCalledWith(3, 2, 2) + expect(fn).nthCalledWith(4, 3, 3) dispose() obs1.aa++ expect(fn).toBeCalledTimes(4) @@ -297,13 +304,13 @@ test('autorun.memo with observable and effect', async () => { obs1.aa++ obs1.aa++ obs1.aa++ - await sleep(100) + await sleep(0) expect(fn).toBeCalledTimes(5) - expect(fn).toBeCalledWith(0, 0) - expect(fn).toBeCalledWith(1, 1) - expect(fn).toBeCalledWith(2, 2) - expect(fn).toBeCalledWith(3, 3) - expect(fn).toBeCalledWith(3, 5) + expect(fn).nthCalledWith(1, 0, 0) + expect(fn).nthCalledWith(2, 1, 1) + expect(fn).nthCalledWith(3, 2, 2) + expect(fn).nthCalledWith(4, 3, 3) + expect(fn).nthCalledWith(5, 3, 5) dispose() obs1.aa++ expect(fn).toBeCalledTimes(5) @@ -329,11 +336,14 @@ test('autorun.memo with deps', () => { obs.bb++ obs.bb++ expect(fn).toBeCalledTimes(5) - expect(fn).toBeCalledWith(3, 3) - expect(fn).toBeCalledWith(4, 4) + expect(fn).nthCalledWith(1, 0, 0) + expect(fn).nthCalledWith(2, 1, 1) + expect(fn).nthCalledWith(3, 2, 2) + expect(fn).nthCalledWith(4, 3, 3) + expect(fn).nthCalledWith(5, 4, 4) obs.cc++ expect(fn).toBeCalledTimes(6) - expect(fn).toBeCalledWith(4, 0) + expect(fn).nthCalledWith(6, 4, 0) }) test('autorun.memo with deps and dispose', () => { @@ -356,11 +366,10 @@ test('autorun.memo with deps and dispose', () => { obs.bb++ obs.bb++ expect(fn).toBeCalledTimes(5) - expect(fn).toBeCalledWith(3, 3) - expect(fn).toBeCalledWith(4, 4) + expect(fn).lastCalledWith(4, 4) obs.cc++ expect(fn).toBeCalledTimes(6) - expect(fn).toBeCalledWith(4, 0) + expect(fn).lastCalledWith(4, 0) dispose() obs.bb++ obs.cc++ @@ -381,7 +390,7 @@ test('autorun.memo with invalid params', () => { obs.bb++ obs.bb++ expect(fn).toBeCalledTimes(5) - expect(fn).toBeCalledWith(4, undefined) + expect(fn).lastCalledWith(4, undefined) }) test('autorun.memo not in autorun', () => { @@ -404,8 +413,11 @@ test('autorun no memo', () => { obs.bb++ obs.bb++ expect(fn).toBeCalledTimes(5) - expect(fn).toBeCalledWith(3, 0) - expect(fn).toBeCalledWith(4, 0) + expect(fn).nthCalledWith(1, 0, 0) + expect(fn).nthCalledWith(2, 1, 0) + expect(fn).nthCalledWith(3, 2, 0) + expect(fn).nthCalledWith(4, 3, 0) + expect(fn).nthCalledWith(5, 4, 0) }) test('autorun.effect', async () => { @@ -426,12 +438,15 @@ test('autorun.effect', async () => { obs.bb++ obs.bb++ obs.bb++ - await sleep(16) + + await sleep(0) expect(fn).toBeCalledTimes(5) - expect(fn).toBeCalledWith(4) + expect(fn).lastCalledWith(4) expect(effect).toBeCalledTimes(1) expect(disposer).toBeCalledTimes(0) + dispose() + await sleep(0) expect(effect).toBeCalledTimes(1) expect(disposer).toBeCalledTimes(1) }) @@ -454,10 +469,11 @@ test('autorun.effect dispose when autorun dispose', async () => { obs.bb++ obs.bb++ obs.bb++ + dispose() - await sleep(16) + await sleep(0) expect(fn).toBeCalledTimes(5) - expect(fn).toBeCalledWith(4) + expect(fn).lastCalledWith(4) expect(effect).toBeCalledTimes(0) expect(disposer).toBeCalledTimes(0) }) @@ -480,17 +496,18 @@ test('autorun.effect with deps', async () => { obs.bb++ obs.bb++ expect(effect).toBeCalledTimes(0) - await sleep(16) + await sleep(0) expect(fn).toBeCalledTimes(5) - expect(fn).toBeCalledWith(4) + expect(fn).lastCalledWith(4) expect(effect).toBeCalledTimes(1) obs.cc++ expect(effect).toBeCalledTimes(1) - await sleep(16) + await sleep(0) expect(fn).toBeCalledTimes(6) - expect(fn).toBeCalledWith(4) + expect(fn).lastCalledWith(4) expect(effect).toBeCalledTimes(2) dispose() + await sleep(0) expect(effect).toBeCalledTimes(2) }) @@ -511,11 +528,12 @@ test('autorun.effect with default deps', async () => { obs.bb++ obs.bb++ expect(effect).toBeCalledTimes(0) - await sleep(100) + await sleep(0) expect(fn).toBeCalledTimes(5) - expect(fn).toBeCalledWith(4) + expect(fn).lastCalledWith(4) expect(effect).toBeCalledTimes(5) dispose() + await sleep(0) expect(effect).toBeCalledTimes(5) }) @@ -560,8 +578,8 @@ test('set value by computed depend', () => { cc: 321, } expect(handler).toBeCalledTimes(2) - expect(handler).toBeCalledWith(undefined, undefined) - expect(handler).toBeCalledWith(123, 321) + expect(handler).nthCalledWith(1, undefined, undefined) + expect(handler).nthCalledWith(2, 123, 321) }) test('delete value by computed depend', () => { @@ -583,8 +601,8 @@ test('delete value by computed depend', () => { }) delete obs.a expect(handler).toBeCalledTimes(2) - expect(handler).toBeCalledWith(1, 2) - expect(handler).toBeCalledWith(undefined, undefined) + expect(handler).nthCalledWith(1, 1, 2) + expect(handler).nthCalledWith(2, undefined, undefined) }) test('set Set value by computed depend', () => { @@ -603,8 +621,8 @@ test('set Set value by computed depend', () => { }) obs.set.add(1) expect(handler).toBeCalledTimes(2) - expect(handler).toBeCalledWith(false, 0) - expect(handler).toBeCalledWith(true, 1) + expect(handler).nthCalledWith(1, false, 0) + expect(handler).nthCalledWith(2, true, 1) }) test('delete Set by computed depend', () => { @@ -623,8 +641,8 @@ test('delete Set by computed depend', () => { }) obs.set.delete(1) expect(handler).toBeCalledTimes(2) - expect(handler).toBeCalledWith(true, 1) - expect(handler).toBeCalledWith(false, 0) + expect(handler).nthCalledWith(1, true, 1) + expect(handler).nthCalledWith(2, false, 0) }) test('set Map value by computed depend', () => { @@ -643,8 +661,8 @@ test('set Map value by computed depend', () => { }) obs.map.set(1, 1) expect(handler).toBeCalledTimes(2) - expect(handler).toBeCalledWith(false, 0) - expect(handler).toBeCalledWith(true, 1) + expect(handler).nthCalledWith(1, false, 0) + expect(handler).nthCalledWith(2, true, 1) }) test('delete Map by computed depend', () => { @@ -663,8 +681,8 @@ test('delete Map by computed depend', () => { }) obs.map.delete(1) expect(handler).toBeCalledTimes(2) - expect(handler).toBeCalledWith(true, 1) - expect(handler).toBeCalledWith(false, 0) + expect(handler).nthCalledWith(1, true, 1) + expect(handler).nthCalledWith(2, false, 0) }) test('autorun recollect dependencies', () => { diff --git a/packages/reactive/src/__tests__/batch.spec.ts b/packages/reactive/src/__tests__/batch.spec.ts index 288a6e2dd09..ad908531e4e 100644 --- a/packages/reactive/src/__tests__/batch.spec.ts +++ b/packages/reactive/src/__tests__/batch.spec.ts @@ -35,6 +35,7 @@ describe('normal batch', () => { obs.aa.bb = 111 obs.aa.bb = 222 expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(222) batch(() => { obs.aa.bb = 333 obs.aa.bb = 444 @@ -42,6 +43,7 @@ describe('normal batch', () => { batch(() => {}) batch() expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(444) }) test('batch track', () => { @@ -84,9 +86,11 @@ describe('normal batch', () => { obs.aa.bb = 111 obs.aa.bb = 222 expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(222) setData() batch(() => {}) expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(444) }) test('batch.bound track', () => { @@ -133,6 +137,10 @@ describe('normal batch', () => { }) expect(handler).toBeCalledTimes(4) + expect(handler).nthCalledWith(1, undefined, undefined, undefined, undefined) + expect(handler).nthCalledWith(2, 123, undefined, undefined, undefined) + expect(handler).nthCalledWith(3, 123, undefined, 'ccccc', undefined) + expect(handler).nthCalledWith(4, 123, 321, 'ccccc', 'ddddd') }) test('batch.scope bound', () => { @@ -157,6 +165,10 @@ describe('normal batch', () => { }) expect(handler).toBeCalledTimes(4) + expect(handler).nthCalledWith(1, undefined, undefined, undefined, undefined) + expect(handler).nthCalledWith(2, 123, undefined, undefined, undefined) + expect(handler).nthCalledWith(3, 123, undefined, 'ccccc', undefined) + expect(handler).nthCalledWith(4, 123, 321, 'ccccc', 'ddddd') }) test('batch.scope track', () => { @@ -242,8 +254,10 @@ describe('annotation batch', () => { obs.aa.bb = 111 obs.aa.bb = 222 expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(222) obs.setData() expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(444) }) test('batch track', () => { @@ -299,8 +313,10 @@ describe('annotation batch', () => { obs.aa.bb = 111 obs.aa.bb = 222 expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(222) obs.setData() expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(444) }) test('batch.bound track', () => { @@ -371,6 +387,10 @@ describe('annotation batch', () => { }) expect(handler).toBeCalledTimes(4) + expect(handler).nthCalledWith(1, null, null, null, null) + expect(handler).nthCalledWith(2, 123, null, null, null) + expect(handler).nthCalledWith(3, 123, null, 'ccccc', null) + expect(handler).nthCalledWith(4, 123, 321, 'ccccc', 'ddddd') }) test('batch.scope bound', () => { @@ -411,6 +431,10 @@ describe('annotation batch', () => { }) expect(handler).toBeCalledTimes(4) + expect(handler).nthCalledWith(1, null, null, null, null) + expect(handler).nthCalledWith(2, 123, null, null, null) + expect(handler).nthCalledWith(3, 123, null, 'ccccc', null) + expect(handler).nthCalledWith(4, 123, 321, 'ccccc', 'ddddd') }) test('batch.scope track', () => { diff --git a/packages/reactive/src/__tests__/collections-map.spec.ts b/packages/reactive/src/__tests__/collections-map.spec.ts new file mode 100644 index 00000000000..d884dc2aafa --- /dev/null +++ b/packages/reactive/src/__tests__/collections-map.spec.ts @@ -0,0 +1,330 @@ +import { observable, autorun, raw } from '..' + +describe('Map', () => { + test('should be a proper JS Map', () => { + const map = observable(new Map()) + expect(map).toBeInstanceOf(Map) + expect(raw(map)).toBeInstanceOf(Map) + }) + + test('should autorun mutations', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => handler(map.get('key'))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(undefined) + map.set('key', 'value') + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith('value') + map.set('key', 'value2') + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith('value2') + map.delete('key') + expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(undefined) + }) + + test('should autorun size mutations', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => handler(map.size)) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + map.set('key1', 'value') + map.set('key2', 'value2') + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(2) + map.delete('key1') + expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(1) + map.clear() + expect(handler).toBeCalledTimes(5) + expect(handler).lastCalledWith(0) + }) + + test('should autorun for of iteration', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => { + let sum = 0 + // eslint-disable-next-line no-unused-vars + for (let [, num] of map) { + sum += num + } + handler(sum) + }) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + map.set('key0', 3) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(3) + map.set('key1', 2) + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(5) + map.delete('key0') + expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(2) + map.clear() + expect(handler).toBeCalledTimes(5) + expect(handler).lastCalledWith(0) + }) + + test('should autorun forEach iteration', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => { + let sum = 0 + map.forEach((num) => (sum += num)) + handler(sum) + }) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + map.set('key0', 3) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(3) + map.set('key1', 2) + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(5) + map.delete('key0') + expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(2) + map.clear() + expect(handler).toBeCalledTimes(5) + expect(handler).lastCalledWith(0) + }) + + test('should autorun keys iteration', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => { + let sum = 0 + for (let key of map.keys()) { + sum += key + } + handler(sum) + }) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + map.set(3, 3) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(3) + map.set(2, 2) + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(5) + map.delete(3) + expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(2) + map.clear() + expect(handler).toBeCalledTimes(5) + expect(handler).lastCalledWith(0) + }) + + test('should autorun values iteration', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => { + let sum = 0 + for (let num of map.values()) { + sum += num + } + handler(sum) + }) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + map.set('key0', 3) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(3) + map.set('key1', 2) + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(5) + map.delete('key0') + expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(2) + map.clear() + expect(handler).toBeCalledTimes(5) + expect(handler).lastCalledWith(0) + }) + + test('should autorun entries iteration', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => { + let sum = 0 + // eslint-disable-next-line no-unused-vars + for (let [, num] of map.entries()) { + sum += num + } + handler(sum) + }) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + map.set('key0', 3) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(3) + map.set('key1', 2) + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(5) + map.delete('key0') + expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(2) + map.clear() + expect(handler).toBeCalledTimes(5) + expect(handler).lastCalledWith(0) + }) + + test('should be triggered by clearing', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => handler(map.get('key'))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(undefined) + map.set('key', 3) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(3) + map.clear() + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(undefined) + }) + + test('should not autorun custom property mutations', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => handler(map['customProp'])) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(undefined) + map['customProp'] = 'Hello World' + expect(handler).toBeCalledTimes(1) + }) + + test('should not autorun non value changing mutations', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => handler(map.get('key'))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(undefined) + map.set('key', 'value') + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith('value') + map.set('key', 'value') + expect(handler).toBeCalledTimes(2) + map.delete('key') + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(undefined) + map.delete('key') + expect(handler).toBeCalledTimes(3) + map.clear() + expect(handler).toBeCalledTimes(3) + }) + + test('should not autorun raw data', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => handler(raw(map).get('key'))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(undefined) + map.set('key', 'Hello') + expect(handler).toBeCalledTimes(1) + map.delete('key') + expect(handler).toBeCalledTimes(1) + }) + + test('should not autorun raw iterations', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => { + let sum = 0 + // eslint-disable-next-line no-unused-vars + for (let [, num] of raw(map).entries()) { + sum += num + } + for (let key of raw(map).keys()) { + sum += raw(map).get(key) + } + for (let num of raw(map).values()) { + sum += num + } + raw(map).forEach((num) => { + sum += num + }) + // eslint-disable-next-line no-unused-vars + for (let [, num] of raw(map)) { + sum += num + } + handler(sum) + }) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + map.set('key1', 2) + map.set('key2', 3) + expect(handler).toBeCalledTimes(1) + map.delete('key1') + expect(handler).toBeCalledTimes(1) + }) + + test('should not be triggered by raw mutations', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => handler(map.get('key'))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(undefined) + raw(map).set('key', 'Hello') + expect(handler).toBeCalledTimes(1) + raw(map).delete('key') + expect(handler).toBeCalledTimes(1) + raw(map).clear() + expect(handler).toBeCalledTimes(1) + }) + + test('should not autorun raw size mutations', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => handler(raw(map).size)) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + map.set('key', 'value') + expect(handler).toBeCalledTimes(1) + }) + + test('should not be triggered by raw size mutations', () => { + const handler = jest.fn() + const map = observable(new Map()) + autorun(() => handler(map.size)) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + raw(map).set('key', 'value') + expect(handler).toBeCalledTimes(1) + }) + + test('should support objects as key', () => { + const handler = jest.fn() + const key = {} + const map = observable(new Map()) + autorun(() => handler(map.get(key))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(undefined) + + map.set(key, 1) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(1) + + map.set({}, 2) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(1) + }) +}) diff --git a/packages/reactive/src/__tests__/collections-set.spec.ts b/packages/reactive/src/__tests__/collections-set.spec.ts new file mode 100644 index 00000000000..c61e2ab5722 --- /dev/null +++ b/packages/reactive/src/__tests__/collections-set.spec.ts @@ -0,0 +1,294 @@ +import { observable, autorun, raw } from '..' + +describe('Set', () => { + test('should be a proper JS Set', () => { + const set = observable(new Set()) + expect(set).toBeInstanceOf(Set) + expect(raw(set)).toBeInstanceOf(Set) + }) + + test('should autorun mutations', () => { + const handler = jest.fn() + const set = observable(new Set()) + autorun(() => handler(set.has('value'))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(false) + set.add('value') + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(true) + set.delete('value') + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(false) + }) + + test('should autorun size mutations', () => { + const handler = jest.fn() + const set = observable(new Set()) + autorun(() => handler(set.size)) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + set.add('value') + set.add('value2') + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(2) + set.delete('value') + expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(1) + set.clear() + expect(handler).toBeCalledTimes(5) + expect(handler).lastCalledWith(0) + }) + + test('should autorun for of iteration', () => { + const handler = jest.fn() + const set = observable(new Set()) + autorun(() => { + let sum = 0 + // eslint-disable-next-line no-unused-vars + for (let num of set) { + sum += num + } + handler(sum) + }) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + set.add(3) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(3) + set.add(2) + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(5) + set.delete(3) + expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(2) + set.clear() + expect(handler).toBeCalledTimes(5) + expect(handler).lastCalledWith(0) + }) + + test('should autorun forEach iteration', () => { + const handler = jest.fn() + const set = observable(new Set()) + autorun(() => { + let sum = 0 + set.forEach((num) => (sum += num)) + handler(sum) + }) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + set.add(3) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(3) + set.add(2) + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(5) + set.delete(3) + expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(2) + set.clear() + expect(handler).toBeCalledTimes(5) + expect(handler).lastCalledWith(0) + }) + + test('should autorun keys iteration', () => { + const handler = jest.fn() + const set = observable(new Set()) + autorun(() => { + let sum = 0 + for (let key of set.keys()) { + sum += key + } + handler(sum) + }) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + set.add(3) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(3) + set.add(2) + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(5) + set.delete(3) + expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(2) + set.clear() + expect(handler).toBeCalledTimes(5) + expect(handler).lastCalledWith(0) + }) + + test('should autorun values iteration', () => { + const handler = jest.fn() + const set = observable(new Set()) + autorun(() => { + let sum = 0 + for (let num of set.values()) { + sum += num + } + handler(sum) + }) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + set.add(3) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(3) + set.add(2) + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(5) + set.delete(3) + expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(2) + set.clear() + expect(handler).toBeCalledTimes(5) + expect(handler).lastCalledWith(0) + }) + + test('should autorun entries iteration', () => { + const handler = jest.fn() + const set = observable(new Set()) + autorun(() => { + let sum = 0 + // eslint-disable-next-line no-unused-vars + for (let [, num] of set.entries()) { + sum += num + } + handler(sum) + }) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + set.add(3) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(3) + set.add(2) + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(5) + set.delete(3) + expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(2) + set.clear() + expect(handler).toBeCalledTimes(5) + expect(handler).lastCalledWith(0) + }) + + test('should not autorun custom property mutations', () => { + const handler = jest.fn() + const set = observable(new Set()) + autorun(() => handler(set['customProp'])) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(undefined) + set['customProp'] = 'Hello World' + expect(handler).toBeCalledTimes(1) + }) + + test('should not autorun non value changing mutations', () => { + const handler = jest.fn() + const set = observable(new Set()) + autorun(() => handler(set.has('value'))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(false) + set.add('value') + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(true) + set.add('value') + expect(handler).toBeCalledTimes(2) + set.delete('value') + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(false) + set.delete('value') + expect(handler).toBeCalledTimes(3) + set.clear() + expect(handler).toBeCalledTimes(3) + }) + + test('should not autorun raw data', () => { + const handler = jest.fn() + const set = observable(new Set()) + autorun(() => handler(raw(set).has('value'))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(false) + set.add('value') + expect(handler).toBeCalledTimes(1) + set.delete('value') + expect(handler).toBeCalledTimes(1) + }) + + test('should not autorun raw iterations', () => { + const handler = jest.fn() + const set = observable(new Set()) + autorun(() => { + let sum = 0 + // eslint-disable-next-line no-unused-vars + for (let [, num] of raw(set).entries()) { + sum += num + } + for (let key of raw(set).keys()) { + sum += key + } + for (let num of raw(set).values()) { + sum += num + } + raw(set).forEach((num) => { + sum += num + }) + // eslint-disable-next-line no-unused-vars + for (let num of raw(set)) { + sum += num + } + handler(sum) + }) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + set.add(2) + set.add(3) + expect(handler).toBeCalledTimes(1) + set.delete(2) + expect(handler).toBeCalledTimes(1) + }) + + test('should not be triggered by raw mutations', () => { + const handler = jest.fn() + const set = observable(new Set()) + autorun(() => handler(set.has('value'))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(false) + raw(set).add('value') + expect(handler).toBeCalledTimes(1) + raw(set).delete('value') + expect(handler).toBeCalledTimes(1) + raw(set).clear() + expect(handler).toBeCalledTimes(1) + }) + + test('should not autorun raw size mutations', () => { + const handler = jest.fn() + const set = observable(new Set()) + autorun(() => handler(raw(set).size)) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + set.add('value') + expect(handler).toBeCalledTimes(1) + }) + + test('should not be triggered by raw size mutations', () => { + const handler = jest.fn() + const set = observable(new Set()) + autorun(() => handler(set.size)) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(0) + raw(set).add('value') + expect(handler).toBeCalledTimes(1) + }) +}) diff --git a/packages/reactive/src/__tests__/collections-weakmap.spec.ts b/packages/reactive/src/__tests__/collections-weakmap.spec.ts new file mode 100644 index 00000000000..962ddde33d5 --- /dev/null +++ b/packages/reactive/src/__tests__/collections-weakmap.spec.ts @@ -0,0 +1,87 @@ +import { observable, autorun, raw } from '..' + +describe('WeakMap', () => { + test('should be a proper JS WeakMap', () => { + const weakMap = observable(new WeakMap()) + expect(weakMap).toBeInstanceOf(WeakMap) + expect(raw(weakMap)).toBeInstanceOf(WeakMap) + }) + + test('should autorun mutations', () => { + const handler = jest.fn() + const key = {} + const weakMap = observable(new WeakMap()) + autorun(() => handler(weakMap.get(key))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(undefined) + weakMap.set(key, 'value') + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith('value') + weakMap.set(key, 'value2') + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith('value2') + weakMap.delete(key) + expect(handler).toBeCalledTimes(4) + expect(handler).lastCalledWith(undefined) + }) + + test('should not autorun custom property mutations', () => { + const handler = jest.fn() + const weakMap = observable(new WeakMap()) + autorun(() => handler(weakMap['customProp'])) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(undefined) + weakMap['customProp'] = 'Hello World' + expect(handler).toBeCalledTimes(1) + }) + + test('should not autorun non value changing mutations', () => { + const handler = jest.fn() + const key = {} + const weakMap = observable(new WeakMap()) + autorun(() => handler(weakMap.get(key))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(undefined) + weakMap.set(key, 'value') + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith('value') + weakMap.set(key, 'value') + expect(handler).toBeCalledTimes(2) + weakMap.delete(key) + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(undefined) + weakMap.delete(key) + expect(handler).toBeCalledTimes(3) + }) + + test('should not autorun raw data', () => { + const handler = jest.fn() + const key = {} + const weakMap = observable(new WeakMap()) + autorun(() => handler(raw(weakMap).get(key))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(undefined) + weakMap.set(key, 'Hello') + expect(handler).toBeCalledTimes(1) + weakMap.delete(key) + expect(handler).toBeCalledTimes(1) + }) + + test('should not be triggered by raw mutations', () => { + const handler = jest.fn() + const key = {} + const weakMap = observable(new WeakMap()) + autorun(() => handler(weakMap.get(key))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(undefined) + raw(weakMap).set(key, 'Hello') + expect(handler).toBeCalledTimes(1) + raw(weakMap).delete(key) + expect(handler).toBeCalledTimes(1) + }) +}) diff --git a/packages/reactive/src/__tests__/collections-weakset.spec.ts b/packages/reactive/src/__tests__/collections-weakset.spec.ts new file mode 100644 index 00000000000..757e23dc5cc --- /dev/null +++ b/packages/reactive/src/__tests__/collections-weakset.spec.ts @@ -0,0 +1,84 @@ +import { observable, autorun, raw } from '..' + +describe('WeakSet', () => { + test('should be a proper JS WeakSet', () => { + const weakSet = observable(new WeakSet()) + expect(weakSet).toBeInstanceOf(WeakSet) + expect(raw(weakSet)).toBeInstanceOf(WeakSet) + }) + + test('should autorun mutations', () => { + const handler = jest.fn() + const value = {} + const weakSet = observable(new WeakSet()) + autorun(() => handler(weakSet.has(value))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(false) + weakSet.add(value) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(true) + weakSet.delete(value) + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(false) + }) + + test('should not autorun custom property mutations', () => { + const handler = jest.fn() + const weakSet = observable(new WeakSet()) + autorun(() => handler(weakSet['customProp'])) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(undefined) + weakSet['customProp'] = 'Hello World' + expect(handler).toBeCalledTimes(1) + }) + + test('should not autorun non value changing mutations', () => { + const handler = jest.fn() + const value = {} + const weakSet = observable(new WeakSet()) + autorun(() => handler(weakSet.has(value))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(false) + weakSet.add(value) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(true) + weakSet.add(value) + expect(handler).toBeCalledTimes(2) + weakSet.delete(value) + expect(handler).toBeCalledTimes(3) + expect(handler).lastCalledWith(false) + weakSet.delete(value) + expect(handler).toBeCalledTimes(3) + }) + + test('should not autorun raw data', () => { + const handler = jest.fn() + const value = {} + const weakSet = observable(new WeakSet()) + autorun(() => handler(raw(weakSet).has(value))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(false) + weakSet.add(value) + expect(handler).toBeCalledTimes(1) + weakSet.delete(value) + expect(handler).toBeCalledTimes(1) + }) + + test('should not be triggered by raw mutations', () => { + const handler = jest.fn() + const value = {} + const weakSet = observable(new WeakSet()) + autorun(() => handler(weakSet.has(value))) + + expect(handler).toBeCalledTimes(1) + expect(handler).lastCalledWith(false) + raw(weakSet).add(value) + expect(handler).toBeCalledTimes(1) + raw(weakSet).delete(value) + expect(handler).toBeCalledTimes(1) + }) +}) diff --git a/packages/reactive/src/__tests__/collections.spec.ts b/packages/reactive/src/__tests__/collections.spec.ts deleted file mode 100644 index abb2c6d12f9..00000000000 --- a/packages/reactive/src/__tests__/collections.spec.ts +++ /dev/null @@ -1,288 +0,0 @@ -import { observable, autorun, raw } from '..' - -describe('Map', () => { - test('should be a proper JS Map', () => { - const map = observable(new Map()) - expect(map).toBeInstanceOf(Map) - expect(raw(map)).toBeInstanceOf(Map) - }) - - test('should autorun mutations', () => { - let dummy - const map = observable(new Map()) - autorun(() => (dummy = map.get('key'))) - - expect(dummy).toEqual(undefined) - map.set('key', 'value') - expect(dummy).toEqual('value') - map.set('key', 'value2') - expect(dummy).toEqual('value2') - map.delete('key') - expect(dummy).toEqual(undefined) - }) - - test('should autorun size mutations', () => { - let dummy - const map = observable(new Map()) - autorun(() => (dummy = map.size)) - - expect(dummy).toEqual(0) - map.set('key1', 'value') - map.set('key2', 'value2') - expect(dummy).toEqual(2) - map.delete('key1') - expect(dummy).toEqual(1) - map.clear() - expect(dummy).toEqual(0) - }) - - test('should autorun for of iteration', () => { - let dummy - const map = observable(new Map()) - autorun(() => { - dummy = 0 - // eslint-disable-next-line no-unused-vars - for (let [, num] of map) { - dummy += num - } - }) - - expect(dummy).toEqual(0) - map.set('key0', 3) - expect(dummy).toEqual(3) - map.set('key1', 2) - expect(dummy).toEqual(5) - map.delete('key0') - expect(dummy).toEqual(2) - map.clear() - expect(dummy).toEqual(0) - }) - - test('should autorun forEach iteration', () => { - let dummy - const map = observable(new Map()) - autorun(() => { - dummy = 0 - map.forEach((num) => (dummy += num)) - }) - - expect(dummy).toEqual(0) - map.set('key0', 3) - expect(dummy).toEqual(3) - map.set('key1', 2) - expect(dummy).toEqual(5) - map.delete('key0') - expect(dummy).toEqual(2) - map.clear() - expect(dummy).toEqual(0) - }) - - test('should autorun keys iteration', () => { - let dummy - const map = observable(new Map()) - autorun(() => { - dummy = 0 - for (let key of map.keys()) { - dummy += key - } - }) - - expect(dummy).toEqual(0) - map.set(3, 3) - expect(dummy).toEqual(3) - map.set(2, 2) - expect(dummy).toEqual(5) - map.delete(3) - expect(dummy).toEqual(2) - map.clear() - expect(dummy).toEqual(0) - }) - - test('should autorun values iteration', () => { - let dummy - const map = observable(new Map()) - autorun(() => { - dummy = 0 - for (let num of map.values()) { - dummy += num - } - }) - - expect(dummy).toEqual(0) - map.set('key0', 3) - expect(dummy).toEqual(3) - map.set('key1', 2) - expect(dummy).toEqual(5) - map.delete('key0') - expect(dummy).toEqual(2) - map.clear() - expect(dummy).toEqual(0) - }) - - test('should autorun entries iteration', () => { - let dummy - const map = observable(new Map()) - autorun(() => { - dummy = 0 - // eslint-disable-next-line no-unused-vars - for (let [, num] of map.entries()) { - dummy += num - } - }) - - expect(dummy).toEqual(0) - map.set('key0', 3) - expect(dummy).toEqual(3) - map.set('key1', 2) - expect(dummy).toEqual(5) - map.delete('key0') - expect(dummy).toEqual(2) - map.clear() - expect(dummy).toEqual(0) - }) - - test('should be triggered by clearing', () => { - let dummy - const map = observable(new Map()) - autorun(() => (dummy = map.get('key'))) - - expect(dummy).toEqual(undefined) - map.set('key', 3) - expect(dummy).toEqual(3) - map.clear() - expect(dummy).toEqual(undefined) - }) - - test('should not autorun custom property mutations', () => { - let dummy - const map = observable(new Map()) - autorun(() => (dummy = map['customProp'])) - - expect(dummy).toEqual(undefined) - map['customProp'] = 'Hello World' - expect(dummy).toEqual(undefined) - }) - - test('should not autorun non value changing mutations', () => { - let dummy - const map = observable(new Map()) - const mapSpy = jest.fn(() => (dummy = map.get('key'))) - autorun(mapSpy) - - expect(dummy).toEqual(undefined) - expect(mapSpy).toBeCalledTimes(1) - map.set('key', 'value') - expect(dummy).toEqual('value') - expect(mapSpy).toBeCalledTimes(2) - map.set('key', 'value') - expect(dummy).toEqual('value') - expect(mapSpy).toBeCalledTimes(2) - map.delete('key') - expect(dummy).toEqual(undefined) - expect(mapSpy).toBeCalledTimes(3) - map.delete('key') - expect(dummy).toEqual(undefined) - expect(mapSpy).toBeCalledTimes(3) - map.clear() - expect(dummy).toEqual(undefined) - expect(mapSpy).toBeCalledTimes(3) - }) - - test('should not autorun raw data', () => { - let dummy - const map = observable(new Map()) - autorun(() => (dummy = raw(map).get('key'))) - - expect(dummy).toEqual(undefined) - map.set('key', 'Hello') - expect(dummy).toEqual(undefined) - map.delete('key') - expect(dummy).toEqual(undefined) - }) - - test('should not autorun raw iterations', () => { - let dummy = 0 - const map = observable(new Map()) - autorun(() => { - dummy = 0 - // eslint-disable-next-line no-unused-vars - for (let [, num] of raw(map).entries()) { - dummy += num - } - for (let key of raw(map).keys()) { - dummy += raw(map).get(key) - } - for (let num of raw(map).values()) { - dummy += num - } - raw(map).forEach((num) => { - dummy += num - }) - // eslint-disable-next-line no-unused-vars - for (let [, num] of raw(map)) { - dummy += num - } - }) - - expect(dummy).toEqual(0) - map.set('key1', 2) - map.set('key2', 3) - expect(dummy).toEqual(0) - map.delete('key1') - expect(dummy).toEqual(0) - }) - - test('should not be triggered by raw mutations', () => { - let dummy - const map = observable(new Map()) - autorun(() => (dummy = map.get('key'))) - - expect(dummy).toEqual(undefined) - raw(map).set('key', 'Hello') - expect(dummy).toEqual(undefined) - dummy = 'Thing' - raw(map).delete('key') - expect(dummy).toEqual('Thing') - raw(map).clear() - expect(dummy).toEqual('Thing') - }) - - test('should not autorun raw size mutations', () => { - let dummy - const map = observable(new Map()) - autorun(() => (dummy = raw(map).size)) - - expect(dummy).toEqual(0) - map.set('key', 'value') - expect(dummy).toEqual(0) - }) - - test('should not be triggered by raw size mutations', () => { - let dummy - const map = observable(new Map()) - autorun(() => (dummy = map.size)) - - expect(dummy).toEqual(0) - raw(map).set('key', 'value') - expect(dummy).toEqual(0) - }) - - test('should support objects as key', () => { - let dummy - const key = {} - const map = observable(new Map()) - const mapSpy = jest.fn(() => (dummy = map.get(key))) - autorun(mapSpy) - - expect(dummy).toEqual(undefined) - expect(mapSpy).toBeCalledTimes(1) - - map.set(key, 1) - expect(dummy).toEqual(1) - expect(mapSpy).toBeCalledTimes(2) - - map.set({}, 2) - expect(dummy).toEqual(1) - expect(mapSpy).toBeCalledTimes(2) - }) -}) diff --git a/packages/reactive/src/__tests__/define.spec.ts b/packages/reactive/src/__tests__/define.spec.ts index 62c88ffe9d4..334b9022ef0 100644 --- a/packages/reactive/src/__tests__/define.spec.ts +++ b/packages/reactive/src/__tests__/define.spec.ts @@ -21,7 +21,10 @@ describe('makeObservable', () => { observe(target.aa, handler2) target.aa.bb = { cc: { dd: { ee: 123 } } } target.aa = { hh: 123 } - expect(handler).toBeCalledWith({ dd: { ee: 123 } }) + expect(handler).toBeCalledTimes(3) + expect(handler).nthCalledWith(1, undefined) + expect(handler).nthCalledWith(2, { dd: { ee: 123 } }) + expect(handler).nthCalledWith(3, undefined) expect(handler1).toBeCalledTimes(2) expect(handler2).toBeCalledTimes(2) }) @@ -43,8 +46,10 @@ describe('makeObservable', () => { target.aa.bb = { cc: { dd: { ee: 123 } } } target.aa.bb.cc.kk = 333 target.aa = { hh: 123 } - expect(handler).toBeCalledWith({ dd: { ee: 123 }, kk: 333 }) expect(handler).toBeCalledTimes(3) + expect(handler).nthCalledWith(1, undefined) + expect(handler).nthCalledWith(2, { dd: { ee: 123 }, kk: 333 }) + expect(handler).nthCalledWith(3, undefined) expect(handler1).toBeCalledTimes(2) expect(handler2).toBeCalledTimes(2) }) @@ -61,8 +66,11 @@ describe('makeObservable', () => { }) observe(target, handler1) observe(target.aa, handler2) + + expect(handler).lastCalledWith(undefined) target.aa.set(123) - expect(handler).toBeCalledWith(123) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(123) expect(handler1).toBeCalledTimes(1) expect(handler2).toBeCalledTimes(1) }) @@ -77,8 +85,10 @@ describe('makeObservable', () => { handler(target.aa) }) observe(target, handler1) + expect(handler).lastCalledWith(undefined) target.aa = 123 - expect(handler).toBeCalledWith(123) + expect(handler).toBeCalledTimes(2) + expect(handler).lastCalledWith(123) expect(handler1).toBeCalledTimes(1) }) test('action annotation', () => { @@ -100,6 +110,7 @@ describe('makeObservable', () => { autorun(() => { handler([target.aa.bb, target.aa.cc]) }) + expect(handler).toBeCalledTimes(1) target.setData() expect(handler).toBeCalledTimes(2) }) @@ -123,13 +134,9 @@ describe('makeObservable', () => { }) expect(handler).toBeCalledTimes(1) expect(target.cc).toEqual(33) - expect(handler).toBeCalledTimes(1) - expect(target.cc).toEqual(33) - expect(handler).toBeCalledTimes(1) target.aa = 22 expect(handler).toBeCalledTimes(2) expect(target.cc).toEqual(44) - expect(handler).toBeCalledTimes(2) }) }) diff --git a/packages/reactive/src/__tests__/externals.spec.ts b/packages/reactive/src/__tests__/externals.spec.ts index 4321455f6bc..2dc35b40f10 100644 --- a/packages/reactive/src/__tests__/externals.spec.ts +++ b/packages/reactive/src/__tests__/externals.spec.ts @@ -9,43 +9,46 @@ import { test('is support observable', () => { const obs = observable({ aa: 111 }) - expect(isSupportObservable(obs)).toBeTruthy() - expect(isSupportObservable(null)).toBeFalsy() - expect(isSupportObservable([])).toBeTruthy() - expect(isSupportObservable({})).toBeTruthy() - expect(isSupportObservable({ $$typeof: {}, _owner: {} })).toBeFalsy() - expect(isSupportObservable({ _isAMomentObject: {} })).toBeFalsy() - expect(isSupportObservable({ _isJSONSchemaObject: {} })).toBeFalsy() - expect(isSupportObservable({ toJS: () => {} })).toBeFalsy() - expect(isSupportObservable({ toJSON: () => {} })).toBeFalsy() - expect(isSupportObservable(new Map())).toBeTruthy() - expect(isSupportObservable(new WeakMap())).toBeTruthy() - expect(isSupportObservable(new Set())).toBeTruthy() - expect(isSupportObservable(new WeakSet())).toBeTruthy() + class Class {} + + expect(isSupportObservable(obs)).toBe(true) + expect(isSupportObservable(new Class())).toBe(true) + expect(isSupportObservable(null)).toBe(false) + expect(isSupportObservable([])).toBe(true) + expect(isSupportObservable({})).toBe(true) + expect(isSupportObservable({ $$typeof: {}, _owner: {} })).toBe(false) + expect(isSupportObservable({ _isAMomentObject: {} })).toBe(false) + expect(isSupportObservable({ _isJSONSchemaObject: {} })).toBe(false) + expect(isSupportObservable({ toJS: () => {} })).toBe(false) + expect(isSupportObservable({ toJSON: () => {} })).toBe(false) + expect(isSupportObservable(new Map())).toBe(true) + expect(isSupportObservable(new WeakMap())).toBe(true) + expect(isSupportObservable(new Set())).toBe(true) + expect(isSupportObservable(new WeakSet())).toBe(true) }) describe('mark operation', () => { test('plain object should be observable', () => { const obs = observable({ aa: 111 }) - expect(isObservable(obs)).toBeTruthy() + expect(isObservable(obs)).toBe(true) }) test('class instance should be observable', () => { class Class {} const obs = observable(new Class()) const obs2 = observable(new Class()) - expect(isObservable(obs)).toBeTruthy() - expect(isObservable(obs2)).toBeTruthy() + expect(isObservable(obs)).toBe(true) + expect(isObservable(obs2)).toBe(true) }) test('object with toJS function should NOT be observable', () => { const obs = observable({ aa: 111, toJS: () => {} }) - expect(isObservable(obs)).toBeFalsy() + expect(isObservable(obs)).toBe(false) }) test('plain object marked as raw should NOT be observable', () => { const obs = observable(markRaw({ aa: 111 })) - expect(isObservable(obs)).toBeFalsy() + expect(isObservable(obs)).toBe(false) }) test('class marked as raw instance should NOT be observable', () => { @@ -53,23 +56,23 @@ describe('mark operation', () => { markRaw(Class) const obs = observable(new Class()) const obs2 = observable(new Class()) - expect(isObservable(obs)).toBeFalsy() - expect(isObservable(obs2)).toBeFalsy() + expect(isObservable(obs)).toBe(false) + expect(isObservable(obs2)).toBe(false) }) test('object with toJS function marked as observable should be observable', () => { const obs = observable(markObservable({ aa: 111, toJS: () => {} })) - expect(isObservable(obs)).toBeTruthy() + expect(isObservable(obs)).toBe(true) }) test('plain object marked as raw and observable should NOT be observable', () => { const obs = observable(markRaw(markObservable({ aa: 111 }))) - expect(isObservable(obs)).toBeFalsy() + expect(isObservable(obs)).toBe(false) }) test('plain object marked as observable and raw should NOT be observable', () => { const obs = observable(markObservable(markRaw({ aa: 111 }))) - expect(isObservable(obs)).toBeFalsy() + expect(isObservable(obs)).toBe(false) }) }) diff --git a/packages/reactive/src/__tests__/hasCollected.spec.ts b/packages/reactive/src/__tests__/hasCollected.spec.ts index 34e5ad70c36..6dbc10b6ecd 100644 --- a/packages/reactive/src/__tests__/hasCollected.spec.ts +++ b/packages/reactive/src/__tests__/hasCollected.spec.ts @@ -7,8 +7,8 @@ test('hasCollected', () => { hasCollected(() => { obs.value }) - ).toBeTruthy() - expect(hasCollected(() => {})).toBeFalsy() - expect(hasCollected()).toBeFalsy() + ).toBe(true) + expect(hasCollected(() => {})).toBe(false) + expect(hasCollected()).toBe(false) }) }) diff --git a/packages/reactive/src/__tests__/observable.spec.ts b/packages/reactive/src/__tests__/observable.spec.ts index b266194bc69..c67663fa529 100644 --- a/packages/reactive/src/__tests__/observable.spec.ts +++ b/packages/reactive/src/__tests__/observable.spec.ts @@ -8,14 +8,27 @@ test('array mutation', () => { }) test('observable contains', () => { - const element = { aa: 123 } - const other = { bb: 321 } + const subElement = { cc: 333 } + const element = { aa: subElement } const arr = observable([element, 2, 3, 4]) + expect(contains(arr, arr[0])).toBe(true) + expect(contains(arr, arr[0].aa)).toBe(true) + expect(contains(arr, element)).toBe(true) + expect(contains(arr, subElement)).toBe(true) + expect(contains(element, subElement)).toBe(true) + expect(contains(element, arr[0].aa)).toBe(true) + expect(contains(arr[0], subElement)).toBe(true) + const obj = observable({}) - expect(contains(arr, arr[0])).toBeTruthy() - expect(contains(obj, obj.other)).toBeFalsy() + const other = { bb: 321 } + expect(contains(obj, obj.other)).toBe(false) obj.other = other obj.arr = arr - expect(contains(obj, obj.other)).toBeTruthy() - expect(contains(obj, obj.arr)).toBeFalsy() + + expect(contains(obj, obj.other)).toBe(true) + expect(contains(obj, other)).toBe(true) + + // Are these expected behaviors? + expect(contains(obj, obj.arr)).toBe(false) + expect(contains(obj, arr)).toBe(false) }) diff --git a/packages/reactive/src/__tests__/observe.spec.ts b/packages/reactive/src/__tests__/observe.spec.ts index 8a86ceb924b..53d78e8e99c 100644 --- a/packages/reactive/src/__tests__/observe.spec.ts +++ b/packages/reactive/src/__tests__/observe.spec.ts @@ -8,6 +8,7 @@ test('deep observe', () => { cc: [11, 22, 33], }, }, + ee: observable([]), }) const handler = jest.fn() observe(obs, handler) @@ -17,6 +18,14 @@ test('deep observe', () => { expect(handler).toHaveBeenCalledTimes(2) delete obs.aa expect(handler).toHaveBeenCalledTimes(3) + + // Are these expected behaviors? + obs.ee.push(11) + expect(handler).toHaveBeenCalledTimes(3) + obs.ee = [] + expect(handler).toHaveBeenCalledTimes(4) + obs.ee.push(11) + expect(handler).toHaveBeenCalledTimes(5) }) test('shallow observe', () => { diff --git a/packages/reactive/src/__tests__/tracker.spec.ts b/packages/reactive/src/__tests__/tracker.spec.ts index ab788855185..17b25e7d7d5 100644 --- a/packages/reactive/src/__tests__/tracker.spec.ts +++ b/packages/reactive/src/__tests__/tracker.spec.ts @@ -6,15 +6,15 @@ test('base tracker', () => { const view = () => { fn(obs.value) } - const handler = () => { + const scheduler = () => { tracker.track(view) } - const tracker = new Tracker(handler) + const tracker = new Tracker(scheduler) tracker.track(view) obs.value = 123 - expect(fn).toBeCalledWith(undefined) - expect(fn).toBeCalledWith(123) + expect(fn).nthCalledWith(1, undefined) + expect(fn).nthCalledWith(2, 123) tracker.dispose() }) @@ -25,16 +25,17 @@ test('nested tracker', () => { obs.value = obs.value || 321 fn(obs.value) } - const handler = () => { + const scheduler = () => { tracker.track(view) } - const tracker = new Tracker(handler) + const tracker = new Tracker(scheduler) tracker.track(view) + expect(fn).toBeCalledTimes(1) + expect(fn).nthCalledWith(1, 321) obs.value = 123 - expect(fn).toBeCalledWith(321) - expect(fn).toBeCalledWith(123) expect(fn).toBeCalledTimes(2) + expect(fn).nthCalledWith(2, 123) tracker.dispose() }) @@ -52,10 +53,10 @@ test('tracker recollect dependencies', () => { } return obs.cc } - const handler = () => { + const scheduler = () => { tracker.track(view) } - const tracker = new Tracker(handler) + const tracker = new Tracker(scheduler) tracker.track(view) obs.aa = '111' diff --git a/packages/reactive/src/__tests__/untracked.spec.ts b/packages/reactive/src/__tests__/untracked.spec.ts index d6c3b33f6c4..03debe79893 100644 --- a/packages/reactive/src/__tests__/untracked.spec.ts +++ b/packages/reactive/src/__tests__/untracked.spec.ts @@ -8,6 +8,8 @@ test('basic untracked', () => { fn(obs.value) }) }) + + expect(fn).toBeCalledTimes(1) obs.value = 123 expect(fn).toBeCalledTimes(1) }) diff --git a/tsconfig.json b/tsconfig.json index c8fe9d0dc88..025167e7072 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,7 @@ "baseUrl": ".", "paths": { "@formily/*": ["packages/*/src", "devtools/*/src"] - } + }, + "lib": ["ESNext"] } }