UNPKG

@v4fire/client

Version:

V4Fire client core library

863 lines (676 loc) • 21 kB
/* eslint-disable max-lines,max-lines-per-function */ // @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; /** * Starts a test * * @param {Page} page * @param {!Object} params * @returns {!Promise<void>} */ module.exports = async (page, params) => { await h.utils.setup(page, params.context); let target; beforeEach(async () => { await page.evaluate(() => { const scheme = [ { attrs: { id: 'target' } } ]; globalThis.renderComponents('b-dummy-sync', scheme); }); target = await h.component.waitForComponent(page, '#target'); }); afterEach(async () => { await page.evaluate(() => { globalThis.removeCreatedComponents(); }); }); describe('`iBlock.sync`', () => { it('checking the initial values', async () => { expect( await target.evaluate((ctx) => ({ dict: Object.fastClone(ctx.dict), linkToNestedFieldWithInitializer: ctx.linkToNestedFieldWithInitializer, watchableObject: Object.fastClone(ctx.watchableObject) })) ).toEqual({ dict: {a: {b: 2, c: 3}}, linkToNestedFieldWithInitializer: 3, watchableObject: { dict: {a: {b: 2, c: 3}}, linkToNestedFieldWithInitializer: 6, linkToPath: 2, linkToPathWithInitializer: 6 } }); }); it('changing some values', async () => { expect( await target.evaluate(async (ctx) => { ctx.dict.a.b++; ctx.dict.a.c++; await ctx.nextTick(); return { dict: Object.fastClone(ctx.dict), linkToNestedFieldWithInitializer: ctx.linkToNestedFieldWithInitializer, watchableObject: Object.fastClone(ctx.watchableObject) }; }) ).toEqual({ dict: {a: {b: 3, c: 4}}, linkToNestedFieldWithInitializer: 4, watchableObject: { dict: {a: {b: 3, c: 4}}, linkToNestedFieldWithInitializer: 8, linkToPath: 3, linkToPathWithInitializer: 8 } }); }); describe('`link`', () => { describe('by using a decorator', () => { it('linking to a nested field', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ctx.linkToNestedField]; ctx.dict.a.b++; await ctx.nextTick(); res.push(ctx.linkToNestedField); ctx.dict.a.b++; await ctx.nextTick(); res.push(ctx.linkToNestedField); ctx.dict.a = {e: 1}; await ctx.nextTick(); res.push(ctx.linkToNestedField); return res; }); expect(scan).toEqual([2, 3, 4, undefined]); }); it('linking to a nested field with an initializer', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ctx.linkToNestedFieldWithInitializer]; ctx.dict.a.b++; await ctx.nextTick(); res.push(ctx.linkToNestedFieldWithInitializer); ctx.dict.a.b++; await ctx.nextTick(); res.push(ctx.linkToNestedFieldWithInitializer); ctx.dict.a = {e: 1}; await ctx.nextTick(); res.push(ctx.linkToNestedFieldWithInitializer); return res; }); expect(scan).toEqual([3, 4, 5, NaN]); }); it('immediate linking to a nested field with an initializer from @system to @field', async () => { const scan = await target.evaluate((ctx) => { const res = [ctx.immediateLinkToNestedFieldWithInitializerFromSystemToField]; ctx.dict.a.b++; res.push(ctx.immediateLinkToNestedFieldWithInitializerFromSystemToField); ctx.dict.a.b++; res.push(ctx.immediateLinkToNestedFieldWithInitializerFromSystemToField); ctx.dict.a = {e: 1}; res.push(ctx.immediateLinkToNestedFieldWithInitializerFromSystemToField); return res; }); expect(scan).toEqual([3, 4, 5, NaN]); }); }); describe('without using a decorator', () => { it('linking to an event', async () => { const scan = await target.evaluate((ctx) => { const res = [ctx.sync.link(['bla', 'localEmitter:foo'])]; ctx.localEmitter.emit('foo', 1); res.push(ctx.bla); ctx.localEmitter.emit('foo', 2); res.push(ctx.bla); ctx.localEmitter.emit('foo', 3); res.push(ctx.bla); return res; }); expect(scan).toEqual([undefined, 1, 2, 3]); }); it('linking to an event with an initializer', async () => { const scan = await target.evaluate((ctx) => { const res = [ctx.sync.link(['bla', 'localEmitter:foo'], (val) => val + 1)]; ctx.localEmitter.emit('foo', 1); res.push(ctx.bla); ctx.localEmitter.emit('foo', 2); res.push(ctx.bla); ctx.localEmitter.emit('foo', 3); res.push(ctx.bla); return res; }); expect(scan).toEqual([NaN, 2, 3, 4]); }); it('linking to a field', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ Object.fastClone(ctx.dict), Object.fastClone(ctx.sync.link(['bla', 'dict'])) ]; ctx.dict.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.dict.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.dict.a = {e: 1}; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([ {a: {b: 2, c: 3}}, {a: {b: 2, c: 3}}, {a: {b: 3, c: 3}}, {a: {b: 4, c: 3}}, {a: {e: 1}} ]); }); it('immediate linking to a field', async () => { const scan = await target.evaluate((ctx) => { const res = [ Object.fastClone(ctx.dict), Object.fastClone(ctx.sync.link(['bla', 'dict'])) ]; ctx.dict.a.b++; res.push(Object.fastClone(ctx.bla)); ctx.dict.a.b++; res.push(Object.fastClone(ctx.bla)); ctx.dict.a = {e: 1}; res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([ {a: {b: 2, c: 3}}, {a: {b: 2, c: 3}}, {a: {b: 3, c: 3}}, {a: {b: 4, c: 3}}, {a: {e: 1}} ]); }); it('linking to a nested field', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ ctx.dict.a.b, ctx.sync.link(['bla', 'dict.a.b']) ]; ctx.dict.a.b++; await ctx.nextTick(); res.push(ctx.bla); ctx.dict.a.b++; await ctx.nextTick(); res.push(ctx.bla); ctx.dict.a = {e: 1}; await ctx.nextTick(); res.push(ctx.bla); return res; }); expect(scan).toEqual([2, 2, 3, 4, undefined]); }); it('linking to a nested field with an initializer', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ ctx.dict.a.b, ctx.sync.link(['bla', 'dict.a.b'], (val) => val + 1) ]; ctx.dict.a.b++; await ctx.nextTick(); res.push(ctx.bla); ctx.dict.a.b++; await ctx.nextTick(); res.push(ctx.bla); ctx.dict.a = {e: 1}; await ctx.nextTick(); res.push(ctx.bla); return res; }); expect(scan).toEqual([2, 3, 4, 5, NaN]); }); it('linking to a field from the mounted watcher passed by a path', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ Object.fastClone(ctx.mountedWatcher), Object.fastClone(ctx.sync.link(['bla', 'mountedWatcher'])) ]; ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.mountedWatcher.a = {e: 1}; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([ {a: {b: 1}}, {a: {b: 1}}, {a: {b: 2}}, {a: {b: 3}}, {a: {e: 1}} ]); }); it('linking to a field from the mounted watcher passed by a link', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ Object.fastClone(ctx.mountedWatcher), Object.fastClone(ctx.sync.link(['bla', ctx.mountedWatcher])) ]; ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.mountedWatcher.a = {e: 1}; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([ {a: {b: 1}}, {a: {b: 1}}, {a: {b: 2}}, {a: {b: 3}}, {a: {e: 1}} ]); }); it('linking to a nested field from the mounted watcher passed by a path', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ ctx.mountedWatcher.a.b, ctx.sync.link(['bla', 'mountedWatcher.a.b']) ]; ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(ctx.bla); ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(ctx.bla); ctx.mountedWatcher.a = {e: 1}; await ctx.nextTick(); res.push(ctx.bla); return res; }); expect(scan).toEqual([1, 1, 2, 3, undefined]); }); it('linking to a nested field from the mounted watcher passed by a link', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ Object.fastClone(ctx.mountedWatcher.a), Object.fastClone(ctx.sync.link(['bla', {ctx: ctx.mountedWatcher, path: 'a'}])) ]; ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.mountedWatcher.a = {e: 1}; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([{b: 1}, {b: 1}, {b: 2}, {b: 3}, {e: 1}]); }); }); }); describe('`object`', () => { describe('without using a decorator', () => { it('linking to an event', async () => { const scan = await target.evaluate((ctx) => { const res = [Object.fastClone(ctx.sync.object('bla', [['foo', 'localEmitter:foo']]))]; ctx.localEmitter.emit('foo', 1); res.push(Object.fastClone(ctx.bla)); ctx.localEmitter.emit('foo', 2); res.push(Object.fastClone(ctx.bla)); ctx.localEmitter.emit('foo', 3); res.push(Object.fastClone(ctx.bla)); ctx.localEmitter.emit('foo', undefined); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([{foo: undefined}, {foo: 1}, {foo: 2}, {foo: 3}, {foo: undefined}]); }); it('linking to an event with an initializer', async () => { const scan = await target.evaluate((ctx) => { const res = [Object.fastClone(ctx.sync.object('bla', [['foo', 'localEmitter:foo', (val) => val + 1]]))]; ctx.localEmitter.emit('foo', 1); res.push(Object.fastClone(ctx.bla)); ctx.localEmitter.emit('foo', 2); res.push(Object.fastClone(ctx.bla)); ctx.localEmitter.emit('foo', 3); res.push(Object.fastClone(ctx.bla)); ctx.localEmitter.emit('foo', undefined); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([{foo: NaN}, {foo: 2}, {foo: 3}, {foo: 4}, {foo: NaN}]); }); it('linking to a field', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ Object.fastClone(ctx.dict), Object.fastClone(ctx.sync.object('bla', ['dict'])) ]; ctx.dict.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.dict.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.dict.a = {e: 1}; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([ {a: {b: 2, c: 3}}, {dict: {a: {b: 2, c: 3}}}, {dict: {a: {b: 3, c: 3}}}, {dict: {a: {b: 4, c: 3}}}, {dict: {a: {e: 1}}} ]); }); it('linking to a nested field', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ ctx.dict.a.b, Object.fastClone(ctx.sync.object('bla', [['foo', 'dict.a.b']])) ]; ctx.dict.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.dict.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.dict.a = {e: 1}; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([2, {foo: 2}, {foo: 3}, {foo: 4}, {foo: undefined}]); }); it('immediate linking to a nested field', async () => { const scan = await target.evaluate((ctx) => { const res = [ ctx.dict.a.b, Object.fastClone(ctx.sync.object('bla', {immediate: true}, [['foo', 'dict.a.b']])) ]; ctx.dict.a.b++; res.push(Object.fastClone(ctx.bla)); ctx.dict.a.b++; res.push(Object.fastClone(ctx.bla)); ctx.dict.a = {e: 1}; res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([2, {foo: 2}, {foo: 3}, {foo: 4}, {foo: undefined}]); }); it('linking to a nested field with an initializer', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ ctx.dict.a.b, ctx.sync.object('bla.bar', [['foo', 'dict.a.b', (val) => val + 1]]) ]; ctx.dict.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.dict.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.dict.a = {e: 1}; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([ 2, {bar: {foo: NaN}}, {bar: {foo: 4}}, {bar: {foo: 5}}, {bar: {foo: NaN}} ]); }); it('checking `r.isAuth`', async () => { const scan = await target.evaluate(async (ctx) => { const res = [Object.isBoolean(ctx.r.isAuth)]; ctx.r.remoteState.isAuth = true; await ctx.nextTick(); res.push(ctx.r.isAuth); ctx.r.remoteState.isAuth = false; await ctx.nextTick(); res.push(ctx.r.isAuth); return res; }); expect(scan).toEqual([ true, true, false ]); }); it('linking to a field from the mounted watcher passed by a path', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ Object.fastClone(ctx.mountedWatcher), Object.fastClone(ctx.sync.object('bla', [['bla', 'mountedWatcher']])) ]; ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.mountedWatcher.a = {e: 1}; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([ {a: {b: 1}}, {bla: {a: {b: 1}}}, {bla: {a: {b: 2}}}, {bla: {a: {b: 3}}}, {bla: {a: {e: 1}}} ]); }); it('linking to a field from the mounted watcher passed by a link', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ Object.fastClone(ctx.mountedWatcher), Object.fastClone(ctx.sync.object('bla', [['bla', ctx.mountedWatcher]])) ]; ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.mountedWatcher.a = {e: 1}; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([ {a: {b: 1}}, {bla: {a: {b: 1}}}, {bla: {a: {b: 2}}}, {bla: {a: {b: 3}}}, {bla: {a: {e: 1}}} ]); }); it('linking to a nested field from the mounted watcher passed by a path', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ ctx.mountedWatcher.a.b, Object.fastClone(ctx.sync.object('bla', [['bla', 'mountedWatcher.a.b']])) ]; ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.mountedWatcher.a = {e: 1}; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([1, {bla: 1}, {bla: 2}, {bla: 3}, {bla: undefined}]); }); it('linking to a nested field from the mounted watcher passed by a link', async () => { const scan = await target.evaluate(async (ctx) => { const res = [ Object.fastClone(ctx.mountedWatcher.a), Object.fastClone(ctx.sync.object('bla', [['bla', {ctx: ctx.mountedWatcher, path: 'a'}]])) ]; ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.mountedWatcher.a.b++; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); ctx.mountedWatcher.a = {e: 1}; await ctx.nextTick(); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([ {b: 1}, {bla: {b: 1}}, {bla: {b: 2}}, {bla: {b: 3}}, {bla: {e: 1}} ]); }); }); }); describe('`syncLinks`', () => { it('global synchronization', async () => { const scan = await target.evaluate((ctx) => { const res = [ Object.fastClone(ctx.dict), Object.fastClone(ctx.sync.link(['bla', 'dict'], {immediate: true})) ]; ctx.dict.a.b++; res.push(Object.fastClone(ctx.bla)); ctx.bla = {c: 1}; res.push(Object.fastClone(ctx.bla)); ctx.sync.syncLinks(); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([ {a: {b: 2, c: 3}}, {a: {b: 2, c: 3}}, {a: {b: 3, c: 3}}, {c: 1}, {a: {b: 3, c: 3}} ]); }); it('synchronization by a main name', async () => { const scan = await target.evaluate((ctx) => { const res = [ Object.fastClone(ctx.dict), Object.fastClone(ctx.sync.link(['bla', 'dict'], {immediate: true})) ]; ctx.dict.a.b++; res.push(Object.fastClone(ctx.bla)); ctx.bla = {c: 1}; res.push(Object.fastClone(ctx.bla)); ctx.sync.syncLinks('dict'); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([ {a: {b: 2, c: 3}}, {a: {b: 2, c: 3}}, {a: {b: 3, c: 3}}, {c: 1}, {a: {b: 3, c: 3}} ]); }); it('synchronization by a link name', async () => { const scan = await target.evaluate((ctx) => { const res = [ Object.fastClone(ctx.dict), Object.fastClone(ctx.sync.link(['bla', 'dict'], {immediate: true})) ]; ctx.dict.a.b++; res.push(Object.fastClone(ctx.bla)); ctx.bla = {c: 1}; res.push(Object.fastClone(ctx.bla)); ctx.sync.syncLinks(['bla']); res.push(Object.fastClone(ctx.bla)); ctx.bla = {c: 1}; res.push(Object.fastClone(ctx.bla)); ctx.sync.syncLinks(['bla', 'dict']); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([ {a: {b: 2, c: 3}}, {a: {b: 2, c: 3}}, {a: {b: 3, c: 3}}, {c: 1}, {a: {b: 3, c: 3}}, {c: 1}, {a: {b: 3, c: 3}} ]); }); it('providing a value', async () => { const scan = await target.evaluate((ctx) => { const res = [ Object.fastClone(ctx.dict), Object.fastClone(ctx.sync.link(['bla', 'dict'], {immediate: true})) ]; ctx.dict.a.b++; res.push(Object.fastClone(ctx.bla)); ctx.bla = {c: 1}; res.push(Object.fastClone(ctx.bla)); ctx.sync.syncLinks('dict', {e: 1}); res.push(Object.fastClone(ctx.bla)); return res; }); expect(scan).toEqual([ {a: {b: 2, c: 3}}, {a: {b: 2, c: 3}}, {a: {b: 3, c: 3}}, {c: 1}, {e: 1} ]); }); }); describe('`mod`', () => { it('checking the initial value', async () => { expect(await target.evaluate((ctx) => ctx.mods.foo)).toBe('bar'); }); it('changing the tied field', async () => { expect(await target.evaluate(async (ctx) => { ctx.dict.a.b++; await ctx.nextTick(); return ctx.mods.foo; })).toBe('bla'); }); }); }); };