UNPKG

@v4fire/client

Version:

V4Fire client core library

345 lines (272 loc) • 9.64 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 */ const delay = require('delay'), h = include('tests/helpers').default; /** * Starts a test * * @param {Playwright.Page} page * @param {!Object} params * @returns {!Promise<void>} */ module.exports = async (page, params) => { await h.utils.setup(page, params.context); let componentNode, component, inViewMutation, inViewObserver, divNode; const strategies = ['mutation', 'observer'], getInView = (strategy) => strategy === 'mutation' ? inViewMutation : inViewObserver; beforeAll(async () => { componentNode = await h.dom.waitForEl(page, '#dummy-component'); component = await h.component.waitForComponent(page, '#dummy-component'); inViewMutation = await component.evaluateHandle((ctx) => ctx.directives.inViewMutation); inViewObserver = await component.evaluateHandle((ctx) => ctx.directives.inViewObserver); await component.evaluate((ctx) => globalThis.dummy = ctx); }); beforeEach(async () => { await page.setViewportSize({ width: 1024, height: 1024 }); for (let i = 0; i < strategies.length; i++) { await getInView(strategies[i]).evaluate((ctx) => ctx.remove(globalThis.target)); } // eslint-disable-next-line no-inline-comments await componentNode.evaluate((/** @type HTMLElement */ ctx) => { ctx.innerHTML = ''; const div = document.createElement('div'); div.id = 'div-target'; ctx.appendChild(div); Object.assign(div.style, { height: '200px', width: '200px', display: 'block' }); globalThis.tmp = undefined; globalThis.tmpTime = undefined; globalThis.tmpTotalTime = undefined; globalThis.target = div; }); divNode = await componentNode.$('#div-target'); }); strategies.forEach((strategy) => { describe(`core/dom/in-view ${strategy} strategy`, () => { it('with `callback`', async () => { await getInView(strategy).evaluate((ctx) => { ctx.observe(globalThis.target, { callback: () => globalThis.tmp = true }); }); await expectAsync(page.waitForFunction('globalThis.tmp === true')).toBeResolved(); }); it('with `callback` and `delay`', async () => { await getInView(strategy).evaluate((ctx) => { globalThis.tmpTime = performance.now(); ctx.observe(globalThis.target, { callback: () => { globalThis.tmp = true; globalThis.tmpTotalTime = performance.now() - globalThis.tmpTime; }, delay: 1000 }); }); await h.bom.waitForIdleCallback(page); expect(await page.evaluate(() => globalThis.tmp)).toBeUndefined(); await expectAsync(page.waitForFunction('globalThis.tmp === true')).toBeResolved(); expect(await page.evaluate(() => globalThis.tmpTotalTime)).toBeGreaterThanOrEqual(1000); expect(await page.evaluate(() => globalThis.tmpTotalTime)).not.toBeGreaterThanOrEqual(2000); }); it('with `callback`: doesn\'t fire the callback on a hidden element', async () => { await page.evaluate(() => { globalThis.target.style.height = '0'; globalThis.target.style.width = '0'; globalThis.target.style.display = 'none'; }); await getInView(strategy).evaluate((ctx) => { ctx.observe(globalThis.target, { callback: () => globalThis.tmp = true }); }); await h.bom.waitForIdleCallback(page); expect(await page.evaluate(() => globalThis.tmp)).toBeUndefined(); }); it('with `callback` and `polling`', async () => { await getInView(strategy).evaluate((ctx) => { ctx.observe(globalThis.target, { callback: () => globalThis.tmp = true, delay: 1, polling: true }); }); await expectAsync(page.waitForFunction('globalThis.tmp === true')).toBeResolved(); }); it('with `callback` and `polling`: doesn\'t fire the callback on a hidden element', async () => { await page.evaluate(() => { globalThis.target.style.height = '0'; globalThis.target.style.width = '0'; globalThis.target.style.display = 'none'; }); await getInView(strategy).evaluate((ctx) => { ctx.observe(globalThis.target, { callback: () => globalThis.tmp = true, polling: true }); }); await h.bom.waitForIdleCallback(page); expect(await page.evaluate(() => globalThis.tmp)).toBeUndefined(); }); it('with `threshold: 0.5`: the element is positioned at the bottom of the page', async () => { await page.setViewportSize({ width: 600, height: 300 }); await divNode.evaluate((ctx) => ctx.style.marginTop = '200px'); await getInView(strategy).evaluate((ctx) => { ctx.observe(globalThis.target, { callback: () => globalThis.tmp = true, threshold: 0.5 }); }); await expectAsync(page.waitForFunction('globalThis.tmp === true')).toBeResolved(); }); it('with `threshold: 0.5` and `polling`: the element is positioned at the bottom of the page', async () => { await page.setViewportSize({ width: 600, height: 300 }); await divNode.evaluate((ctx) => ctx.style.marginTop = '200px'); await getInView(strategy).evaluate((ctx) => { ctx.observe(globalThis.target, { callback: () => globalThis.tmp = true, threshold: 0.5, polling: true }); }); await expectAsync(page.waitForFunction('globalThis.tmp === true')).toBeResolved(); }); it('with `threshold: 0.5` and the element that is 0.2 visible: doesn\'t fire the callback', async () => { await page.setViewportSize({ width: 600, height: 300 }); await divNode.evaluate((ctx) => ctx.style.marginTop = '250px'); await getInView(strategy).evaluate((ctx) => { ctx.observe(globalThis.target, { callback: () => globalThis.tmp = true, threshold: 0.5, delay: 100 }); }); await delay(200); expect(await page.evaluate(() => globalThis.tmp)).toBeUndefined(); }); it('removing an element from observing', async () => { await getInView(strategy).evaluate((ctx) => { ctx.observe(globalThis.target, { callback: () => globalThis.tmp = true, delay: 500 }); ctx.remove(globalThis.target); }); expect(await page.evaluate(() => globalThis.tmp)).toBeUndefined(); }); it('suspending with `callback`: doesn\'t fire the callback', async () => { await getInView(strategy).evaluate((ctx) => { ctx.observe(globalThis.target, { callback: () => globalThis.tmp = true, delay: 200, group: 'test' }); setTimeout(() => ctx.suspend('test'), 0); }); await delay(300); expect(await page.evaluate(() => globalThis.tmp)).toBeUndefined(); }); it('suspending first observable by the group doesn\'t fire a callback, second observable without group fire a callback', async () => { await page.evaluate(() => globalThis.tmp = {}); await getInView(strategy).evaluate((ctx) => { ctx.observe(globalThis.target, { callback: () => globalThis.tmp[1] = true, delay: 200, threshold: 0.7, group: 'test' }); ctx.observe(globalThis.target, { callback: () => globalThis.tmp[2] = true, delay: 100, threshold: 0.8 }); setTimeout(() => ctx.suspend('test'), 0); }); await delay(200); await page.waitForFunction('globalThis.tmp[2] === true'); expect(await page.evaluate(() => globalThis.tmp)).toEqual({2: true}); }); it('suspending/unsuspecting with `callback`: fires the callback', async () => { await getInView(strategy).evaluate((ctx) => { ctx.observe(globalThis.target, { callback: () => globalThis.tmp = true, delay: 200, group: 'test' }); setTimeout(() => ctx.suspend('test'), 0); }); await delay(300); expect(await page.evaluate(() => globalThis.tmp)).toBeUndefined(); await getInView(strategy).evaluate((ctx) => ctx.unsuspend('test')); await expectAsync(page.waitForFunction('globalThis.tmp === true')).toBeResolved(); }); it('re-observing with an element and threshold provided', async () => { await page.evaluate(() => globalThis.tmp = 0); await getInView(strategy).evaluate((ctx) => { ctx.observe(globalThis.target, { callback: () => globalThis.tmp += 1, delay: 100, threshold: 0.7 }); setTimeout(() => ctx.reObserve(globalThis.target, 0.7), 400); }); await expectAsync(page.waitForFunction('globalThis.tmp === 2')).toBeResolved(); }); it('re-observing with a group provided', async () => { await page.evaluate(() => globalThis.tmp = 0); await getInView(strategy).evaluate((ctx) => { ctx.observe(globalThis.target, { callback: () => globalThis.tmp += 1, delay: 100, threshold: 0.7, group: 'test-group' }); setTimeout(() => ctx.reObserve('test-group'), 400); }); await expectAsync(page.waitForFunction('globalThis.tmp === 2')).toBeResolved(); }); it('disconnected element doesn\'t invokes a callback', async () => { await page.evaluate(() => globalThis.tmp = 0); await getInView(strategy).evaluate((ctx) => { ctx.observe(globalThis.target, { callback: () => globalThis.tmp = 1, delay: 100, threshold: 0.7, group: 'test-group' }); globalThis.target.remove(); return new Promise((res) => setTimeout(res, 300)); }); expect(await page.evaluate(() => globalThis.tmp)).toBe(0); }); }); }); };