UNPKG

@v4fire/client

Version:

V4Fire client core library

460 lines (382 loc) • 9.69 kB
/* eslint-disable max-lines,max-lines-per-function,require-atomic-updates */ // @ts-check /*! * V4Fire Client Core * https://github.com/V4Fire/Client * * Released under the MIT license * https://github.com/V4Fire/Client/blob/master/LICENSE */ /** * @typedef {import('playwright').Page} Page */ const h = include('tests/helpers').default; /** @param {Page} page */ module.exports = (page) => { beforeEach(async () => { await page.evaluate(() => { globalThis.removeCreatedComponents(); }); }); describe('i-block watching for computed fields', () => { it('that depends on an external property', async () => { const target = await init(); const scan = await target.evaluate(async (ctx) => { ctx.r.isAuth = false; await ctx.nextTick(); const res = [ctx.componentName, ctx.remoteWatchableGetter]; ctx.watch('remoteWatchableGetter', (val) => { res.push(val); }); ctx.r.isAuth = true; await ctx.nextTick(); res.push(ctx.remoteWatchableGetter); return res; }); expect(scan).toEqual(['b-dummy-watch', false, true, true]); }); describe('without caching of old values', () => { it('non-deep watching', async () => { const target = await init(); const scan = await target.evaluate(async (ctx) => { ctx.r.isAuth = false; await ctx.nextTick(); const res = []; ctx.watch('smartComputed', (val, ...args) => { res.push([ Object.fastClone(val), Object.fastClone(args[0]), args[1].path, args[1].originalPath ]); }); ctx.complexObjStore = {a: {b: {c: 3}}}; ctx.systemComplexObjStore = {a: {b: {c: 2}}}; await ctx.nextTick(); ctx.r.isAuth = true; await ctx.nextTick(); ctx.complexObjStore.a.b.c++; ctx.complexObjStore.a.b.c++; await ctx.nextTick(); return res; }); expect(scan).toEqual([ [ {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: false}, undefined, ['smartComputed'], ['complexObjStore'] ], [ {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: true}, {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: false}, ['smartComputed'], ['isAuth'] ], [ {a: {b: {c: 5}}, b: 12, remoteWatchableGetter: true}, {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: true}, ['smartComputed'], ['complexObjStore', 'a', 'b', 'c'] ] ]); }); it('non-deep immediate watching', async () => { const target = await init(); const scan = await target.evaluate(async (ctx) => { ctx.r.isAuth = false; await ctx.nextTick(); const res = []; ctx.watch('smartComputed', {immediate: true}, (val, ...args) => { res.push([ Object.fastClone(val), Object.fastClone(args[0]), args[1]?.path, args[1]?.originalPath ]); }); ctx.complexObjStore = {a: {b: {c: 3}}}; ctx.systemComplexObjStore = {a: {b: {c: 2}}}; await ctx.nextTick(); ctx.r.isAuth = true; await ctx.nextTick(); ctx.complexObjStore.a.b.c++; ctx.complexObjStore.a.b.c++; await ctx.nextTick(); return res; }); expect(scan).toEqual([ [ {a: {b: {c: 1, d: 2}}, b: 11, remoteWatchableGetter: false}, undefined, undefined, undefined ], [ {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: false}, {a: {b: {c: 1, d: 2}}, b: 11, remoteWatchableGetter: false}, ['smartComputed'], ['complexObjStore'] ], [ {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: true}, {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: false}, ['smartComputed'], ['isAuth'] ], [ {a: {b: {c: 5}}, b: 12, remoteWatchableGetter: true}, {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: true}, ['smartComputed'], ['complexObjStore', 'a', 'b', 'c'] ] ]); }); it('watching for chained getters', async () => { const target = await init(); const scan = await target.evaluate(async (ctx) => { const res = []; ctx.watch('cachedComplexObj', (val, ...args) => { res.push([ Object.fastClone(val), Object.fastClone(args[0]), args[1]?.path, args[1]?.originalPath ]); }); ctx.watch('cachedComplexDecorator', (val, ...args) => { res.push([ Object.fastClone(val), Object.fastClone(args[0]), args[1]?.path, args[1]?.originalPath ]); }); ctx.complexObjStore = {a: 1}; await ctx.nextTick(); ctx.complexObjStore.a++; await ctx.nextTick(); return res; }); expect(scan).toEqual([ [ {a: 1}, undefined, ['cachedComplexDecorator'], ['complexObjStore'] ], [ {a: 1}, undefined, ['cachedComplexObj'], ['complexObjStore'] ], [ {a: 2}, {a: 1}, ['cachedComplexDecorator'], ['complexObjStore', 'a'] ], [ {a: 2}, {a: 1}, ['cachedComplexObj'], ['complexObjStore', 'a'] ] ]); }); }); describe('with caching of old values', () => { it('non-deep watching', async () => { const target = await init(); const scan = await target.evaluate(async (ctx) => { ctx.r.isAuth = false; await ctx.nextTick(); const res = []; ctx.watch('smartComputed', (val, oldVal, i) => { res.push([ Object.fastClone(val), Object.fastClone(oldVal), val === oldVal, i.path, i.originalPath ]); }); ctx.complexObjStore = {a: {b: {c: 3}}}; ctx.systemComplexObjStore = {a: {b: {c: 2}}}; await ctx.nextTick(); ctx.r.isAuth = true; await ctx.nextTick(); ctx.complexObjStore.a.b.c++; ctx.complexObjStore.a.b.c++; await ctx.nextTick(); return res; }); expect(scan).toEqual([ [ {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: false}, undefined, false, ['smartComputed'], ['complexObjStore'] ], [ {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: true}, {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: false}, false, ['smartComputed'], ['isAuth'] ], [ {a: {b: {c: 5}}, b: 12, remoteWatchableGetter: true}, {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: true}, false, ['smartComputed'], ['complexObjStore', 'a', 'b', 'c'] ] ]); }); it('non-deep immediate watching', async () => { const target = await init(); const scan = await target.evaluate(async (ctx) => { ctx.r.isAuth = false; await ctx.nextTick(); const res = []; ctx.watch('smartComputed', {immediate: true}, (val, oldVal, i) => { res.push([ Object.fastClone(val), Object.fastClone(oldVal), val === oldVal, i?.path, i?.originalPath ]); }); ctx.complexObjStore = {a: {b: {c: 3}}}; ctx.systemComplexObjStore = {a: {b: {c: 2}}}; await ctx.nextTick(); ctx.r.isAuth = true; await ctx.nextTick(); ctx.complexObjStore.a.b.c++; ctx.complexObjStore.a.b.c++; await ctx.nextTick(); return res; }); expect(scan).toEqual([ [ {a: {b: {c: 1, d: 2}}, b: 11, remoteWatchableGetter: false}, undefined, false, undefined, undefined ], [ {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: false}, {a: {b: {c: 1, d: 2}}, b: 11, remoteWatchableGetter: false}, false, ['smartComputed'], ['complexObjStore'] ], [ {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: true}, {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: false}, false, ['smartComputed'], ['isAuth'] ], [ {a: {b: {c: 5}}, b: 12, remoteWatchableGetter: true}, {a: {b: {c: 3}}, b: 12, remoteWatchableGetter: true}, false, ['smartComputed'], ['complexObjStore', 'a', 'b', 'c'] ] ]); }); it('watching for chained getters', async () => { const target = await init(); const scan = await target.evaluate(async (ctx) => { const res = []; ctx.watch('cachedComplexObj', (val, oldVal, i) => { res.push([ Object.fastClone(val), Object.fastClone(oldVal), val === oldVal, i?.path, i?.originalPath ]); }); ctx.watch('cachedComplexDecorator', (val, oldVal, i) => { res.push([ Object.fastClone(val), Object.fastClone(oldVal), val === oldVal, i?.path, i?.originalPath ]); }); ctx.complexObjStore = {a: 1}; await ctx.nextTick(); ctx.complexObjStore.a++; await ctx.nextTick(); return res; }); expect(scan).toEqual([ [ {a: 1}, undefined, false, ['cachedComplexDecorator'], ['complexObjStore'] ], [ {a: 1}, undefined, false, ['cachedComplexObj'], ['complexObjStore'] ], [ {a: 2}, {a: 1}, false, ['cachedComplexDecorator'], ['complexObjStore', 'a'] ], [ {a: 2}, {a: 1}, false, ['cachedComplexObj'], ['complexObjStore', 'a'] ] ]); }); }); }); async function init(attrs = {}) { await page.evaluate((attrs = {}) => { const scheme = [ { attrs: { id: 'target', ...attrs } } ]; globalThis.renderComponents('b-dummy-watch', scheme); }, attrs); return h.component.waitForComponent(page, '#target'); } };