UNPKG

reactant-share

Version:

A framework for building shared web applications with Reactant

909 lines (768 loc) 19.8 kB
/* eslint-disable no-console */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/ban-ts-comment */ /* eslint-disable prefer-destructuring */ /* eslint-disable @typescript-eslint/no-empty-function */ import { createSharedApp, injectable, state, action, delegate, mockPairTransports, ViewModule, PortDetector, fork, } from '..'; const increaseSymbol = Symbol('increase'); @injectable({ name: 'counter', }) class Counter extends ViewModule { setValue = 0; set(setValue: number) { this.setValue = setValue; } @state count = 0; @action _increase() { this.count += 1; } @action add(num: number) { this.count += num; } async increase() { await delegate(this as Counter, '_increase', []); } [increaseSymbol]() { this._increase(); } increaseFunc = () => { this._increase(); }; component() { return null; } } test('base: sync up full state', async () => { const transports = mockPairTransports(); const server = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'server', transports: { server: transports[0], }, }, }); await server.instance.increase(); expect(server.instance.count).toBe(1); await server.instance.increase(); expect(server.instance.count).toBe(2); const client0 = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'client', transports: { client: transports[1], }, }, }); await client0.bootstrap(); expect(client0.instance.count).toBe(2); expect(server.instance.count).toBe(2); await client0.instance.increase(); expect(server.instance.count).toBe(3); expect(client0.instance.count).toBe(3); await server.instance.increase(); expect(server.instance.count).toBe(4); expect(client0.instance.count).toBe(4); }); test('sync up full state and update from server', async () => { const transports = mockPairTransports(); const server = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'server', transports: { server: transports[0], }, }, }); await server.instance.increase(); expect(server.instance.count).toBe(1); await server.instance.increase(); expect(server.instance.count).toBe(2); const client0 = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'client', transports: { client: transports[1], }, }, }); await client0.bootstrap(); expect(client0.instance.count).toBe(2); expect(server.instance.count).toBe(2); await server.instance.increase(); expect(server.instance.count).toBe(3); expect(client0.instance.count).toBe(3); await server.instance.increase(); expect(server.instance.count).toBe(4); expect(client0.instance.count).toBe(4); }); test('switch same port', async () => { const transports = mockPairTransports(); const server = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'server', transports: { server: transports[0], }, }, }); await server.instance.increase(); expect(server.instance.count).toBe(1); await server.instance.increase(); expect(server.instance.count).toBe(2); const client0 = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'client', transports: { client: transports[1], }, }, }); await client0.bootstrap(); expect(client0.instance.count).toBe(2); expect(server.instance.count).toBe(2); await server.instance.increase(); expect(server.instance.count).toBe(3); expect(client0.instance.count).toBe(3); await client0.instance.increase(); expect(server.instance.count).toBe(4); expect(client0.instance.count).toBe(4); const transports0 = mockPairTransports(); const serverPortDetector = server.container.get(PortDetector); serverPortDetector.transform('server', transports0[0]); const clientPortDetector = client0.container.get(PortDetector); clientPortDetector.transform('client', transports0[1]); await server.instance.increase(); expect(server.instance.count).toBe(5); expect(client0.instance.count).toBe(5); await client0.instance.increase(); expect(server.instance.count).toBe(6); expect(client0.instance.count).toBe(6); }); test('switch diff port', async () => { const transports = mockPairTransports(); const server = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'server', transports: { server: transports[0], }, }, }); await server.instance.increase(); expect(server.instance.count).toBe(1); await server.instance.increase(); expect(server.instance.count).toBe(2); const client0 = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'client', transports: { client: transports[1], }, }, }); await client0.bootstrap(); expect(client0.instance.count).toBe(2); expect(server.instance.count).toBe(2); await server.instance.increase(); expect(server.instance.count).toBe(3); expect(client0.instance.count).toBe(3); await client0.instance.increase(); expect(server.instance.count).toBe(4); expect(client0.instance.count).toBe(4); const transports0 = mockPairTransports(); const portDetector0 = server.container.get(PortDetector); portDetector0.transform('client', transports0[0]); expect(portDetector0.isClient).toBeTruthy(); const portDetector1 = client0.container.get(PortDetector); portDetector1.transform('server', transports0[1]); expect(portDetector1.isServer).toBeTruthy(); await server.instance.increase(); expect(server.instance.count).toBe(5); expect(client0.instance.count).toBe(5); await client0.instance.increase(); expect(server.instance.count).toBe(6); expect(client0.instance.count).toBe(6); }); test('delegate error case0', async () => { const transports = mockPairTransports(); const server = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'server', transports: { server: transports[0], }, }, }); await server.instance.increase(); expect(server.instance.count).toBe(1); await server.instance.increase(); expect(server.instance.count).toBe(2); const client0 = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'client', transports: { client: transports[1], }, }, }); await client0.bootstrap(); expect(client0.instance.count).toBe(2); expect(server.instance.count).toBe(2); await server.instance.increase(); expect(server.instance.count).toBe(3); expect(client0.instance.count).toBe(3); await delegate(client0.instance, 'increaseFunc', []); expect(server.instance.count).toBe(4); expect(client0.instance.count).toBe(4); expect(() => { delegate(client0.instance, increaseSymbol, []); }).toThrowError(); expect(() => { delegate(client0.instance, 'count' as any, []); }).toThrowError(); expect(() => { // @ts-ignore delegate(client0.instance, 'increaseFunc', 0); }).toThrowError(); const portDetector1 = client0.container.get(PortDetector); portDetector1.transports.client = null as any; delegate(client0.instance, 'increaseFunc', [], { respond: true }).catch( (e) => { expect(e).toEqual( new Error('Detected that the current client transport does not exist.') ); } ); }); test('delegate error case1', async () => { @injectable() class Counter1 extends ViewModule { @state count = 0; @action _increase() { this.count += 1; } async increase() { await delegate(this as Counter1, '_increase', []); } [increaseSymbol]() { this._increase(); } increaseFunc = () => { this._increase(); }; component() { return null; } } const transports = mockPairTransports(); const server = await createSharedApp({ modules: [], main: Counter1, render: () => {}, share: { name: 'counter', type: 'Base', port: 'server', transports: { server: transports[0], }, }, }); expect(() => { delegate(server.instance, 'increaseFunc', [], { respond: true }); }).toThrowError(/a temporary string/); const client0 = await createSharedApp({ modules: [], main: Counter1, render: () => {}, share: { name: 'counter', type: 'Base', port: 'client', transports: { client: transports[1], }, }, }); await client0.bootstrap(); expect(() => { delegate(client0.instance, 'increaseFunc', [], { respond: true }); }).toThrowError(/a temporary string/); }); test('delegate error case2', async () => { @injectable({ name: Symbol('counter') as any, }) class Counter1 extends ViewModule { @state count = 0; @action _increase() { this.count += 1; } async increase() { await delegate(this as Counter1, '_increase', []); } [increaseSymbol]() { this._increase(); } increaseFunc = () => { this._increase(); }; component() { return null; } } const transports = mockPairTransports(); const server = await createSharedApp({ modules: [], main: Counter1, render: () => {}, share: { name: 'counter', type: 'Base', port: 'server', transports: { server: transports[0], }, }, }); expect(() => { delegate(server.instance, 'increaseFunc', [], { respond: true }); }).toThrowError(/a temporary string/); const client0 = await createSharedApp({ modules: [], main: Counter1, render: () => {}, share: { name: 'counter', type: 'Base', port: 'client', transports: { client: transports[1], }, }, }); await client0.bootstrap(); expect(() => { delegate(client0.instance, 'increaseFunc', [], { respond: true }); }).toThrowError(/a temporary string/); }); test('delegate with args', async () => { const transports = mockPairTransports(); const server = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'server', transports: { server: transports[0], }, }, }); await delegate(server.instance, 'add', [1]); expect(server.instance.count).toBe(1); await server.instance.increase(); expect(server.instance.count).toBe(2); const client0 = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'client', transports: { client: transports[1], }, }, }); await client0.bootstrap(); expect(client0.instance.count).toBe(2); expect(server.instance.count).toBe(2); await server.instance.increase(); expect(server.instance.count).toBe(3); expect(client0.instance.count).toBe(3); await delegate(client0.instance, 'add', [1]); expect(server.instance.count).toBe(4); expect(client0.instance.count).toBe(4); }); test('fork error case0', async () => { const transports = mockPairTransports(); const server = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'server', transports: { server: transports[0], }, }, }); const client0 = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'client', transports: { client: transports[1], }, }, }); await client0.bootstrap(); expect(client0.instance.count).toBe(0); expect(server.instance.count).toBe(0); await server.instance.increase(); expect(server.instance.count).toBe(1); expect(client0.instance.count).toBe(1); await fork(server.instance, 'add', [1]); expect(server.instance.count).toBe(1); expect(client0.instance.count).toBe(2); await fork(server.instance, 'set', [1]); expect(server.instance.setValue).toBe(0); expect(client0.instance.setValue).toBe(1); expect(() => { fork(server.instance, increaseSymbol, []); }).toThrowError(); expect(() => { fork(server.instance, 'count' as any, []); }).toThrowError(); expect(() => { // @ts-ignore fork(server.instance, 'increaseFunc', 0); }).toThrowError(); const portDetector1 = server.container.get(PortDetector); portDetector1.transports.server = null as any; fork(server.instance, 'increaseFunc', [], { respond: true }).catch((e) => { expect(e).toEqual( new Error('Detected that the current server transport does not exist.') ); }); }); test('fork error case1', async () => { jest.spyOn(console, 'error').mockImplementation(() => {}); @injectable() class Counter1 extends ViewModule { @state count = 0; @action _increase() { this.count += 1; } async increase() { await fork(this as Counter1, '_increase', []); } [increaseSymbol]() { this._increase(); } increaseFunc = () => { this._increase(); }; component() { return null; } } const transports = mockPairTransports(); const server = await createSharedApp({ modules: [], main: Counter1, render: () => {}, share: { name: 'counter', type: 'Base', port: 'server', transports: { server: transports[0], }, }, }); expect(console.error).toHaveBeenCalled(); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore expect(console.error.mock.calls[0][0]).toContain( `The decorator for class Counter1 should set "@injectable({ name: 'Counter1' })".` ); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore console.error.mockRestore(); expect(() => { fork(server.instance, 'increaseFunc', [], { respond: true }); }).toThrowError(/a temporary string/); const client0 = await createSharedApp({ modules: [], main: Counter1, render: () => {}, share: { name: 'counter', type: 'Base', port: 'client', transports: { client: transports[1], }, }, }); await client0.bootstrap(); expect(() => { fork(server.instance, 'increaseFunc', [], { respond: true }); }).toThrowError(/a temporary string/); expect(() => { fork(client0.instance, 'increaseFunc', [], { respond: true }); }).toThrowError(/should be running in server port/); }); test('fork error case2', async () => { @injectable({ name: Symbol('counter') as any, }) class Counter1 extends ViewModule { @state count = 0; @action _increase() { this.count += 1; } async increase() { await fork(this as Counter1, '_increase', []); } [increaseSymbol]() { this._increase(); } increaseFunc = () => { this._increase(); }; component() { return null; } } const transports = mockPairTransports(); const server = await createSharedApp({ modules: [], main: Counter1, render: () => {}, share: { name: 'counter', type: 'Base', port: 'server', transports: { server: transports[0], }, }, }); expect(() => { fork(server.instance, 'increaseFunc', [], { respond: true }); }).toThrowError(/a temporary string/); const client0 = await createSharedApp({ modules: [], main: Counter1, render: () => {}, share: { name: 'counter', type: 'Base', port: 'client', transports: { client: transports[1], }, }, }); await client0.bootstrap(); expect(() => { fork(server.instance, 'increaseFunc', [], { respond: true }); }).toThrowError(/a temporary string/); }); test('fork with args', async () => { const transports = mockPairTransports(); const server = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'server', transports: { server: transports[0], }, }, }); await server.instance.increase(); expect(server.instance.count).toBe(1); const client0 = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'client', transports: { client: transports[1], }, }, }); await client0.bootstrap(); await server.instance.increase(); expect(client0.instance.count).toBe(2); expect(server.instance.count).toBe(2); await server.instance.increase(); expect(server.instance.count).toBe(3); expect(client0.instance.count).toBe(3); await fork(server.instance, 'add', [1]); expect(server.instance.count).toBe(3); expect(client0.instance.count).toBe(4); }); test('fork with args with destroy on Client', async () => { const transports = mockPairTransports(); const server = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'server', transports: { server: transports[0], }, }, }); expect(server.instance.count).toBe(0); const client0 = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'client', transports: { client: transports[1], }, }, }); await client0.bootstrap(); expect(client0.instance.count).toBe(0); client0.destroy(); await server.instance.increase(); expect(server.instance.count).toBe(1); expect(client0.instance.count).toBe(0); }); test('fork with args with destroy on Server', async () => { const transports = mockPairTransports(); const server = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'server', transports: { server: transports[0], }, }, }); expect(server.instance.count).toBe(0); const client0 = await createSharedApp({ modules: [], main: Counter, render: () => {}, share: { name: 'counter', type: 'Base', port: 'client', transports: { client: transports[1], }, }, }); await client0.bootstrap(); expect(client0.instance.count).toBe(0); server.destroy(); await server.instance.increase(); // jest can send message to client after server destroyed expect(server.instance.count).toBe(1); expect(client0.instance.count).toBe(1); await expect( Promise.race([ client0.instance.increase(), new Promise((resolve) => { setTimeout(() => resolve('timeout'), 100); }), ]) ).resolves.toBe('timeout'); });