UNPKG

@v4fire/client

Version:

V4Fire client core library

500 lines (392 loc) • 16 kB
/* eslint-disable 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; /** @param {Page} page */ module.exports = (page) => { const components = { renderNextWithSlot: undefined, renderNextNoSlot: undefined }; const nodes = { renderNextWithSlot: undefined, renderNextNoSlot: undefined }; const containers = { renderNextWithSlot: undefined, renderNextNoSlot: undefined }; const isNotHidden = async (selector, ctx) => { const el = await ctx.$(selector), state = await el.evaluate((ctx) => ctx.parentNode.style.display); return state === ''; }; const initialTimeout = globalThis.jasmine.DEFAULT_TIMEOUT_INTERVAL; beforeAll(() => { globalThis.jasmine.DEFAULT_TIMEOUT_INTERVAL = (20).seconds(); }); beforeEach(async () => { await h.utils.reloadAndWaitForIdle(page); await h.component.waitForComponent(page, '#root-component'); await page.evaluate(() => { const dummy = document.querySelector('#dummy-component'); if (dummy) { document.querySelector('#dummy-component').remove(); } globalThis.removeCreatedComponents(); const baseAttrs = { theme: 'demo', option: 'section', optionProps: ({current}) => ({'data-index': current.i}) }; const slots = { renderNext: { tag: 'div', attrs: { id: 'renderNext', 'data-test-ref': 'renderNext' } } }; const scheme = [ { attrs: { ...baseAttrs, dataProvider: 'demo.Pagination', loadStrategy: 'manual', id: 'renderNextNoSlot' } }, { attrs: { ...baseAttrs, dataProvider: 'demo.Pagination', loadStrategy: 'manual', id: 'renderNextWithSlot' }, content: { renderNext: slots.renderNext } } ]; globalThis.renderComponents('b-virtual-scroll', scheme, '.p-v4-components-demo'); }); await h.bom.waitForIdleCallback(page); await h.component.waitForComponentStatus(page, '.b-virtual-scroll', 'ready'); const allComponents = await page.$$('.b-virtual-scroll'); for (let i = 0; i < allComponents.length; i++) { await allComponents[i].evaluate((ctx) => ctx.style.display = 'none'); } for (let keys = Object.keys(components), i = 0; i < keys.length; i++) { const key = keys[i]; nodes[key] = await h.dom.waitForEl(page, `#${key}`); await nodes[key].evaluate((ctx) => ctx.style.display = ''); containers[key] = await h.dom.waitForRef(nodes[key], 'container'); // eslint-disable-next-line require-atomic-updates components[key] = await h.component.getComponentById(page, key); } await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = 'demo.Pagination'; ctx.request = {get: {}}; ctx.shouldStopRequest = (v) => v.isLastEmpty; return new Promise((res) => { if (ctx.isReady) { return res(); } ctx.localEmitter.on('localState.ready', res); }); }); await h.bom.waitForIdleCallback(page); }); afterAll(() => { globalThis.jasmine.DEFAULT_TIMEOUT_INTERVAL = initialTimeout; }); describe('b-virtual-scroll `renderNext` slot', () => { describe('not render', () => { it('if it is not set', async () => { expect(await components.renderNextNoSlot.evaluate((ctx) => Boolean(ctx.vdom.getSlot('empty')))).toBe(false); expect(await h.dom.getRef(nodes.renderNextNoSlot, 'empty')).toBeFalsy(); }); it('there are no loaded data', async () => { await components.renderNextWithSlot.evaluate((ctx) => { ctx.dbConverter = () => ({data: []}); ctx.request = {get: {total: 0, chunkSize: 0, id: Math.random()}}; return new Promise((res) => { ctx.localEmitter.on('localState.ready', res); }); }); await h.bom.waitForIdleCallback(page); await h.bom.waitForRAF(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeFalse(); }); it('there are no data', async () => { await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = undefined; ctx.options = []; return new Promise((res) => { ctx.localEmitter.on('localState.ready', res); }); }); await h.bom.waitForRAF(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeFalse(); }); it('if initial loading in progress', async () => { await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = 'demo.Pagination'; ctx.request = {get: {chunkSize: 10, sleep: 1000}}; }); await h.bom.waitForIdleCallback(page); await h.bom.waitForRAF(page); await expectAsync(page.waitForFunction(() => { const node = document.querySelector('#renderNext'), parent = node.parentElement; return parent.style.display === 'none'; })).toBeResolved(); }); it('if the second batch of data loading in progress', async () => { await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = 'demo.Pagination'; ctx.request = {get: {total: 20, chunkSize: 10, id: Math.random(), sleep: 500}}; ctx.chunkSize = 10; }); await h.dom.waitForEl(containers.renderNextWithSlot, 'section'); await h.scroll.scrollToBottom(page); await h.dom.waitForRef(nodes.renderNextWithSlot, 'renderNext'); await components.renderNextWithSlot.evaluate((ctx) => ctx.renderNext()); await h.bom.waitForRAF(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeFalse(); }); it('if all data were loaded after the initial request', async () => { await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = 'demo.Pagination'; ctx.request = {get: {total: 10, chunkSize: 10, id: Math.random()}}; ctx.shouldStopRequest = () => true; ctx.chunkSize = 10; }); await h.dom.waitForEl(containers.renderNextWithSlot, 'section'); await h.bom.waitForIdleCallback(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeFalse(); }); it('if all data were loaded after the second batch load', async () => { await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = 'demo.Pagination'; ctx.request = {get: {total: 20, chunkSize: 10, id: Math.random(), sleep: 50}}; ctx.shouldStopRequest = ({data}) => data.length === 20; ctx.chunkSize = 10; }); await h.dom.waitForEl(containers.renderNextWithSlot, 'section'); await h.dom.waitForRef(nodes.renderNextWithSlot, 'renderNext'); await components.renderNextWithSlot.evaluate((ctx) => ctx.renderNext()); await h.dom.waitForEl(containers.renderNextWithSlot, 'section:nth-child(11)'); await h.bom.waitForIdleCallback(page); await h.bom.waitForRAF(page); expect(await (await nodes.renderNextWithSlot.$('#renderNext')).evaluate((ctx) => ctx.parentNode.style.display)).toBe('none'); }); it('if all items were rendered', async () => { await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = undefined; ctx.chunkSize = 10; // @ts-ignore ctx.options = Array.from(Array(10), (v, i) => ({i})); }); await h.dom.waitForEl(containers.renderNextWithSlot, 'section'); await h.bom.waitForRAF(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeFalse(); }); it('if all items were rendered after second render', async () => { await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = undefined; ctx.chunkSize = 10; ctx.options = Array.from(Array(20), (v, i) => ({i})); }); await h.dom.waitForEl(containers.renderNextWithSlot, 'section'); await h.dom.waitForRef(nodes.renderNextWithSlot, 'renderNext'); await components.renderNextWithSlot.evaluate((ctx) => ctx.renderNext()); await h.dom.waitForEl(containers.renderNextWithSlot, 'section:nth-child(11)'); await h.bom.waitForIdleCallback(page); await h.bom.waitForRAF(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeFalse(); }); it('if all data were rendered and loaded', async () => { await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = 'demo.Pagination'; ctx.shouldStopRequest = ({data}) => data.length === 80; ctx.request = {get: {total: 80, chunkSize: 40, id: Math.random()}}; ctx.chunkSize = 20; return new Promise((res) => { ctx.localEmitter.once('localState.ready', res); }); }); await h.dom.waitForEl(containers.renderNextWithSlot, 'section'); let renders = 1; const totalRenders = 4; while (renders < totalRenders) { await h.dom.waitForRef(nodes.renderNextWithSlot, 'renderNext'); await components.renderNextWithSlot.evaluate((ctx) => ctx.renderNext()); await h.dom.waitForEl(containers.renderNextWithSlot, `section:nth-child(${(renders * 20) + 1})`); await h.bom.waitForIdleCallback(page); await h.bom.waitForRAF(page); renders++; } expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeFalse(); }); it('if an error appears on the initial loading', async () => { await components.renderNextWithSlot.evaluate((ctx) => { const p = new Promise((res) => { ctx.watch(':onRequestError', res); }); ctx.dataProvider = 'demo.Pagination'; ctx.request = {get: {total: 20, chunkSize: 10, id: Math.random(), failOn: 0, sleep: 500}}; ctx.chunkSize = 10; return p; }); await h.bom.waitForIdleCallback(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeFalse(); }); it('if an error appears on the second data batch loading', async () => { const requestErrorPromise = components.renderNextWithSlot.evaluate((ctx) => new Promise((res) => { ctx.watch(':requestError', res); })); await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = 'demo.Pagination'; ctx.request = {get: {total: 40, chunkSize: 10, id: Math.random(), failOn: 1, sleep: 50}}; ctx.chunkSize = 10; }); await h.dom.waitForEl(containers.renderNextWithSlot, 'section'); await h.dom.waitForRef(nodes.renderNextWithSlot, 'renderNext'); await components.renderNextWithSlot.evaluate((ctx) => ctx.renderNext()); await requestErrorPromise; await h.bom.waitForIdleCallback(page); await h.bom.waitForRAF(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeFalse(); }); }); describe('render', () => { it('after initial loading', async () => { await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = 'demo.Pagination'; ctx.request = {get: {total: 20, chunkSize: 10, id: Math.random()}}; ctx.chunkSize = 10; return new Promise((res) => { ctx.localEmitter.on('localState.ready', res); }); }); await h.bom.waitForIdleCallback(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeTrue(); }); it('after loading of the second data batch', async () => { await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = 'demo.Pagination'; ctx.request = {get: {total: 40, chunkSize: 10, id: Math.random()}}; ctx.chunkSize = 10; return new Promise((res) => { ctx.localEmitter.on('localState.ready', res); }); }); await h.dom.waitForEl(containers.renderNextWithSlot, 'section'); await h.dom.waitForRef(nodes.renderNextWithSlot, 'renderNext'); await components.renderNextWithSlot.evaluate((ctx) => ctx.renderNext()); await h.dom.waitForEl(containers.renderNextWithSlot, 'section:nth-child(11)'); await h.bom.waitForIdleCallback(page); await h.bom.waitForRAF(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeTrue(); }); it('after the initial rendering with items provided', async () => { await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = undefined; // @ts-ignore ctx.options = Array.from(Array(20), (v, i) => ({i})); }); await h.dom.waitForEl(containers.renderNextWithSlot, 'section'); await h.bom.waitForIdleCallback(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeTrue(); }); it('after the second rendering with items provided', async () => { await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = undefined; // @ts-ignore ctx.options = Array.from(Array(40), (v, i) => ({i})); }); await h.dom.waitForEl(containers.renderNextWithSlot, 'section'); await h.dom.waitForRef(nodes.renderNextWithSlot, 'renderNext'); await components.renderNextWithSlot.evaluate((ctx) => ctx.renderNext()); await h.dom.waitForEl(containers.renderNextWithSlot, 'section:nth-child(11)'); await h.bom.waitForRAF(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeTrue(); }); it('until all data are rendered', async () => { await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = 'demo.Pagination'; ctx.shouldStopRequest = ({data}) => data.length === 60; ctx.request = {get: {total: 60, chunkSize: 30, id: Math.random()}}; ctx.chunkSize = 10; return new Promise((res) => { ctx.localEmitter.on('localState.ready', res); }); }); await h.dom.waitForEl(containers.renderNextWithSlot, 'section'); let renders = 1; const totalRenders = 5; while (renders < totalRenders) { await h.dom.waitForRef(nodes.renderNextWithSlot, 'renderNext'); await components.renderNextWithSlot.evaluate((ctx) => ctx.renderNext()); await h.dom.waitForEl(containers.renderNextWithSlot, `section:nth-child(${(renders * 10) + 1})`); await h.bom.waitForRAF(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeTrue(); renders++; } }); it('if there was an error on the initial loading, but after retrying all fine', async () => { const requestErrorPromise = components.renderNextWithSlot.evaluate((ctx) => new Promise((res) => { ctx.watch(':requestError', res); })); await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = 'demo.Pagination'; ctx.request = {get: {total: 40, chunkSize: 10, id: Math.random(), failOn: 0, failCount: 1, sleep: 50}}; ctx.chunkSize = 10; }); await requestErrorPromise; await components.renderNextWithSlot.evaluate((ctx) => ctx.reloadLast()); await h.dom.waitForEl(containers.renderNextWithSlot, 'section'); await h.bom.waitForRAF(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeTrue(); }); it('if there was an error on the second data batch loading, but after retrying all fine', async () => { const requestErrorPromise = components.renderNextWithSlot.evaluate((ctx) => new Promise((res) => { ctx.watch(':requestError', res); })); await components.renderNextWithSlot.evaluate((ctx) => { ctx.dataProvider = 'demo.Pagination'; ctx.request = {get: {total: 40, chunkSize: 10, id: Math.random(), failOn: 1, failCount: 1, sleep: 50}}; ctx.chunkSize = 10; }); await h.dom.waitForEl(containers.renderNextWithSlot, 'section'); await components.renderNextWithSlot.evaluate((ctx) => ctx.renderNext()); await requestErrorPromise; await h.bom.waitForIdleCallback(page); await components.renderNextWithSlot.evaluate((ctx) => ctx.reloadLast()); await h.dom.waitForEl(containers.renderNextWithSlot, 'section:nth-child(11)'); await h.bom.waitForRAF(page); expect(await isNotHidden('#renderNext', nodes.renderNextWithSlot)).toBeTrue(); }); }); }); };