@v4fire/client
Version:
V4Fire client core library
1,122 lines (872 loc) • 36.1 kB
JavaScript
/* 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
h = include('tests/helpers').default,
delay = require('delay'),
images = require('./const');
/**
* 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,
imageLoader;
let
imgNode,
divNode;
let
isClosed = false;
page.on('close', () => isClosed = true);
const handleImageRequest = (url, sleep = 0, base64Img = images.pngImage) => page.route(url, async (route) => {
await delay(sleep);
if (base64Img === '') {
await route.abort('failed');
return;
}
const
res = base64Img.split(',', 2)[1],
headers = route.request().headers();
headers['Content-Length'] = String(res?.length ?? 0);
if (isClosed) {
return;
}
await route.fulfill({
status: 200,
body: Buffer.from(res, 'base64'),
contentType: 'image/png',
headers
});
});
const
getRandomUrlPostfix = () => `${Math.random().toString().substr(10)}x${Math.random().toString().substr(10)}`,
getRandomImgUrl = () => `https://fakeim.pl/${getRandomUrlPostfix()}`,
abortImageRequest = (url, sleep = 0) => handleImageRequest(url, sleep, ''),
getNode = (target) => target === 'img' ? imgNode : divNode,
waitFor = h.utils.waitForFunction;
beforeAll(async () => {
componentNode = await h.dom.waitForEl(page, '#dummy-component');
component = await h.component.waitForComponent(page, '#dummy-component');
imageLoader = await component.evaluateHandle((ctx) => ctx.directives.image);
await component.evaluate((ctx) => globalThis.dummy = ctx);
});
beforeEach(async () => {
// eslint-disable-next-line no-inline-comments
await componentNode.evaluate((/** @type HTMLElement */ ctx) => {
ctx.innerHTML = '';
const image = new Image();
image.id = 'img-target';
image.setAttribute('data-test-ref', 'img-target');
const div = document.createElement('div');
div.id = 'div-target';
div.setAttribute('data-test-ref', 'div-target');
ctx.appendChild(image);
ctx.appendChild(div);
globalThis.tmp = undefined;
globalThis.tmpComponent = undefined;
globalThis.getSrc = (ctx) => {
if (ctx instanceof HTMLImageElement) {
return ctx.currentSrc;
}
return ctx.style.backgroundImage.match(/url\("(.*)"\)/)?.[1] ?? '';
};
// eslint-disable-next-line no-unused-expressions
document.getElementById('expected-picture')?.remove();
});
await imageLoader.evaluate((ctx) => {
ctx.clearElement(document.getElementById('div-target'));
ctx.clearElement(document.getElementById('img-target'));
});
imgNode = await componentNode.$('#img-target');
divNode = await componentNode.$('#div-target');
await page.setViewportSize({
width: 1024,
height: 1024
});
});
describe('v-image', () => {
['div', 'img'].forEach((tag) => {
describe(tag, () => {
it('with `src`', async () => {
await imageLoader.evaluate((imageLoaderCtx, [tag, images]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {src: images.pngImage, ctx: globalThis.dummy});
}, [tag, images]);
await h.bom.waitForIdleCallback(page);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).toBe(images.pngImage);
});
it('with `src` and `optionsResolver`', async () => {
const
imgUrl = getRandomImgUrl(),
newParam = '?size=42',
patchedImgUrl = imgUrl + newParam,
reqPromise = handleImageRequest(patchedImgUrl);
await imageLoader.evaluate((imageLoaderCtx, [tag, imgUrl, newParam]) => {
const target = document.getElementById(`${tag}-target`);
const optionsResolver = (options) => {
options.src += newParam;
return options;
};
imageLoaderCtx.init(target, {src: imgUrl, ctx: globalThis.dummy, optionsResolver});
}, [tag, imgUrl, newParam]);
await h.bom.waitForIdleCallback(page);
await reqPromise;
await expectAsync(
waitFor(getNode(tag), (ctx, patchedImgUrl) => globalThis.getSrc(ctx) === patchedImgUrl, patchedImgUrl)
).toBeResolved();
});
it('with `srcset`', async () => {
await imageLoader.evaluate((imageLoaderCtx, [tag, images]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {srcset: {'1x': images.pngImage}, ctx: globalThis.dummy});
}, [tag, images]);
await h.bom.waitForIdleCallback(page);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).toBe(images.pngImage);
});
it('`load` callback', async () => {
await imageLoader.evaluate((imageLoaderCtx, [tag, images]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: images.pngImage,
ctx: globalThis.dummy,
load: () => globalThis.tmp = true
});
}, [tag, images]);
await expectAsync(page.waitForFunction('globalThis.tmp === true')).toBeResolved();
});
it('`error` callback', async () => {
const imgUrl = getRandomImgUrl();
const pr = abortImageRequest(imgUrl);
await imageLoader.evaluate((imageLoaderCtx, [tag, imgUrl]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {src: imgUrl, ctx: globalThis.dummy, error: () => globalThis.tmp = false});
}, [tag, imgUrl]);
await expectAsync(page.waitForFunction('globalThis.tmp === false')).toBeResolved();
await pr;
});
it('`error` callback will not be called if loading was successful', async () => {
await imageLoader.evaluate((imageLoaderCtx, [tag, images]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: images.pngImage,
ctx: globalThis.dummy,
error: () => globalThis.tmp = false
});
}, [tag, images]);
await h.bom.waitForIdleCallback(page, {sleepAfterIdles: 1500});
expect(await page.evaluate(() => globalThis.tmp)).toBeUndefined();
});
it('update `src`', async () => {
await imageLoader.evaluate((imageLoaderCtx, [tag, images]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {src: images.pngImage, ctx: globalThis.dummy});
}, [tag, images]);
await h.bom.waitForIdleCallback(page);
await imageLoader.evaluate((imageLoaderCtx, [tag, images]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.update(target, {src: images.pngImage2x, ctx: globalThis.dummy, handleUpdate: true});
}, [tag, images]);
await h.bom.waitForIdleCallback(page);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).toBe(images.pngImage2x);
});
it('update `src` without ctx provided', async () => {
await imageLoader.evaluate((imageLoaderCtx, [tag, images]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {src: images.pngImage, handleUpdate: true});
}, [tag, images]);
await h.bom.waitForIdleCallback(page);
await imageLoader.evaluate((imageLoaderCtx, [tag, images]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.update(target, {src: images.pngImage2x, handleUpdate: true});
}, [tag, images]);
await h.bom.waitForIdleCallback(page);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).toBe(images.pngImage2x);
});
it('with `src` and preview with `src`', async () => {
const
imgUrl = getRandomImgUrl(),
reqPromise = handleImageRequest(imgUrl, 500);
await imageLoader.evaluate((imageLoaderCtx, [tag, images, imgUrl]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {src: imgUrl, ctx: globalThis.dummy, preview: images.preview});
}, [tag, images, imgUrl]);
await h.bom.waitForIdleCallback(page);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).toBe(images.preview);
await reqPromise;
await expectAsync(
waitFor(getNode(tag), (ctx, imgUrl) => globalThis.getSrc(ctx) === imgUrl, imgUrl)
).toBeResolved();
});
it('with loading error and broken with `src`', async () => {
const imgUrl = getRandomImgUrl();
const pr = abortImageRequest(imgUrl);
await imageLoader.evaluate((imageLoaderCtx, [tag, images, imgUrl]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {src: imgUrl, ctx: globalThis.dummy, broken: images.broken});
}, [tag, images, imgUrl]);
await expectAsync(
waitFor(getNode(tag), (ctx, broken) => globalThis.getSrc(ctx) === broken, images.broken)
).toBeResolved();
await pr;
});
it('with `src`, preview with `src`, broken with `src`', async () => {
const
imgUrl = getRandomImgUrl(),
reqPromise = handleImageRequest(imgUrl, 500);
await imageLoader.evaluate((imageLoaderCtx, [tag, images, imgUrl]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: imgUrl,
ctx: globalThis.dummy,
preview: images.preview,
broken: images.broken
});
}, [tag, images, imgUrl]);
await h.bom.waitForIdleCallback(page);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).toBe(images.preview);
await expectAsync(
waitFor(getNode(tag), (ctx, imgUrl) => globalThis.getSrc(ctx) === imgUrl, imgUrl)
).toBeResolved();
await reqPromise;
await h.bom.waitForIdleCallback(page);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).toBe(imgUrl);
});
it('with loading error, preview with `src`, broken with `src`', async () => {
const imgUrl = getRandomImgUrl();
const pr = abortImageRequest(imgUrl, 500);
await imageLoader.evaluate((imageLoaderCtx, [tag, images, imgUrl]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: imgUrl,
ctx: globalThis.dummy,
preview: images.preview,
broken: images.broken
});
}, [tag, images, imgUrl]);
await h.bom.waitForIdleCallback(page);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).toBe(images.preview);
await expectAsync(
waitFor(getNode(tag), (ctx, broken) => globalThis.getSrc(ctx) === broken, images.broken)
).toBeResolved();
await pr;
});
it('tag with `src`, preview with loading error, broken with `src`', async () => {
const
previewImgUrl = getRandomImgUrl(),
mainImgUrl = getRandomImgUrl();
const
abortReq = abortImageRequest(previewImgUrl, 100);
handleImageRequest(mainImgUrl, 500);
await imageLoader.evaluate((imageLoaderCtx, [tag, images, mainImgUrl, previewImgUrl]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainImgUrl,
ctx: globalThis.dummy,
preview: previewImgUrl,
broken: images.broken
});
}, [tag, images, mainImgUrl, previewImgUrl]);
await abortReq;
await h.bom.waitForIdleCallback(page);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).not.toBe(previewImgUrl);
await expectAsync(
waitFor(getNode(tag), (ctx, mainImgUrl) => globalThis.getSrc(ctx) === mainImgUrl, mainImgUrl)
).toBeResolved();
});
it('with loading error, preview with loading error, broken with loading error', async () => {
const
previewImgUrl = getRandomImgUrl(),
brokenImgUrl = getRandomImgUrl(),
mainImgUrl = getRandomImgUrl();
const reqPromises = [
abortImageRequest(previewImgUrl, 100),
abortImageRequest(brokenImgUrl, 100),
abortImageRequest(mainImgUrl, 100)
];
await imageLoader.evaluate((imageLoaderCtx, [tag, brokenImgUrl, mainImgUrl, previewImgUrl]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainImgUrl,
ctx: globalThis.dummy,
preview: previewImgUrl,
broken: brokenImgUrl
});
}, [tag, brokenImgUrl, mainImgUrl, previewImgUrl]);
await h.bom.waitForIdleCallback(page);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).not.toBe(previewImgUrl);
await Promise.all(reqPromises);
await h.bom.waitForIdleCallback(page);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).not.toBe(previewImgUrl);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).not.toBe(brokenImgUrl);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).not.toBe(mainImgUrl);
});
it('main with `load` callback will not be called if loading is failed', async () => {
const reqUrl = getRandomImgUrl();
abortImageRequest(reqUrl);
await imageLoader.evaluate((imageLoaderCtx, [tag, reqUrl]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {src: reqUrl, ctx: globalThis.dummy, load: () => globalThis.tmp = true});
}, [tag, reqUrl]);
await abortImageRequest;
await h.bom.waitForIdleCallback(page);
expect(await page.evaluate(() => globalThis.tmp)).toBeUndefined();
});
it('main with `src`, preview with `src` and load callback', async () => {
await imageLoader.evaluate((imageLoaderCtx, [tag, mainImgUrl, previewImgUrl]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainImgUrl,
ctx: globalThis.dummy,
preview: {
src: previewImgUrl,
load: () => globalThis.tmp = true
}
});
}, [tag, images.pngImage, images.preview]);
await expectAsync(page.waitForFunction('globalThis.tmp === true')).toBeResolved();
});
it('main with loading error, broken with `src` and load callback', async () => {
const mainImgUrl = getRandomImgUrl();
abortImageRequest(mainImgUrl);
await imageLoader.evaluate((imageLoaderCtx, [tag, mainImgUrl, brokenImgUrl]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainImgUrl,
ctx: globalThis.dummy,
broken: {
src: brokenImgUrl,
load: () => globalThis.tmp = true
}
});
}, [tag, mainImgUrl, images.preview]);
await expectAsync(page.waitForFunction('globalThis.tmp === true')).toBeResolved();
});
it('main with `src`, broken with loading error and error callback', async () => {
const
brokenImgUrl = getRandomImgUrl(),
mainImgUrl = getRandomImgUrl();
abortImageRequest(brokenImgUrl);
abortImageRequest(mainImgUrl);
await imageLoader.evaluate((imageLoaderCtx, [tag, mainImgUrl, brokenImgUrl]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainImgUrl,
ctx: globalThis.dummy,
broken: {
src: brokenImgUrl,
error: () => globalThis.tmp = false
}
});
}, [tag, mainImgUrl, brokenImgUrl]);
await expectAsync(page.waitForFunction('globalThis.tmp === false')).toBeResolved();
});
it('main with `src`, `sources` (srcset, media)', async () => {
await page.setViewportSize({
width: 580,
height: 480
});
await imageLoader.evaluate((imageLoaderCtx, [tag, pngImage2x, pngImage]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: pngImage2x,
ctx: globalThis.dummy,
sources: [{srcset: pngImage, media: '(max-width: 600px)'}]
});
}, [tag, images.pngImage2x, images.pngImage]);
await expectAsync(
waitFor(getNode(tag), (ctx, pngImage) => globalThis.getSrc(ctx) === pngImage, images.pngImage)
).toBeResolved();
});
it('main with `src`, `sources` (srcset, type)', async () => {
await page.evaluate(([png, webp]) => {
const pictHTML = `
<picture id="expected-picture">
<source srcset="${webp}" type="image/webp">
<img id="expected-img" src="${png}">
</picture>
`;
document.body.insertAdjacentHTML('beforeend', pictHTML);
}, [images.pngImage, images.webp]);
await imageLoader.evaluate((imageLoaderCtx, [tag, png, webp]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: png,
ctx: globalThis.dummy,
sources: [{srcset: webp, type: 'webp'}]
});
}, [tag, images.pngImage, images.webp]);
// @ts-expect-error
await expectAsync(waitFor(getNode(tag), (ctx) => globalThis.getSrc(ctx) === document.getElementById('expected-img').currentSrc)).toBeResolved();
});
it('main with `src`, `baseSrc`', async () => {
const
baseSrc = 'https://fakeim.pl',
src = '300x300',
reqUrl = 'https://fakeim.pl/300x300';
handleImageRequest(reqUrl);
await imageLoader.evaluate((imageLoaderCtx, [tag, baseSrc, src]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src,
baseSrc,
ctx: globalThis.dummy
});
}, [tag, baseSrc, src]);
await expectAsync(
waitFor(getNode(tag), (ctx, reqUrl) => globalThis.getSrc(ctx) === reqUrl, reqUrl)
).toBeResolved();
});
it('main with `src`, `baseSrc`, preview with `src`', async () => {
const
baseSrc = 'https://fakeim.pl',
mainSrc = getRandomUrlPostfix(),
previewSrc = getRandomUrlPostfix(),
mainReqUrl = `${baseSrc}/${mainSrc}`,
previewReqUrl = `${baseSrc}/${previewSrc}`;
handleImageRequest(previewReqUrl);
handleImageRequest(mainReqUrl, 500);
await imageLoader.evaluate((imageLoaderCtx, [tag, baseSrc, mainSrc, previewSrc]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainSrc,
baseSrc,
preview: previewSrc,
ctx: globalThis.dummy
});
}, [tag, baseSrc, mainSrc, previewSrc]);
await expectAsync(
waitFor(getNode(tag), (ctx, previewReqUrl) => globalThis.getSrc(ctx) === previewReqUrl, previewReqUrl)
).toBeResolved();
await expectAsync(
waitFor(getNode(tag), (ctx, mainReqUrl) => globalThis.getSrc(ctx) === mainReqUrl, mainReqUrl)
).toBeResolved();
});
it('main with `src`, `baseSrc`, `sources` (srcset, media)', async () => {
await page.setViewportSize({
width: 580,
height: 480
});
const
baseSrc = 'https://fakeim.pl',
mainSrc = getRandomUrlPostfix(),
sourceSrc = getRandomUrlPostfix(),
mainReqUrl = `${baseSrc}/${mainSrc}`,
sourceReqUrl = `${baseSrc}/${sourceSrc}`;
handleImageRequest(sourceReqUrl);
handleImageRequest(mainReqUrl);
await imageLoader.evaluate((imageLoaderCtx, [tag, baseSrc, mainSrc, sourceSrc]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainSrc,
baseSrc,
sources: [{srcset: sourceSrc, media: '(max-width: 600px)'}],
ctx: globalThis.dummy
});
}, [tag, baseSrc, mainSrc, sourceSrc]);
await expectAsync(
waitFor(getNode(tag), (ctx, sourceReqUrl) => globalThis.getSrc(ctx) === sourceReqUrl, sourceReqUrl)
).toBeResolved();
});
it('main with `src`, `baseSrc`, preview with `src`, `sources` (srcset, media)', async () => {
await page.setViewportSize({
width: 580,
height: 480
});
const
baseSrc = 'https://fakeim.pl',
mainSrc = getRandomUrlPostfix(),
previewSrc = getRandomUrlPostfix(),
sourceSrc = getRandomUrlPostfix(),
previewUrl = `${baseSrc}/${previewSrc}`,
mainReqUrl = `${baseSrc}/${mainSrc}`,
sourceReqUrl = `${baseSrc}/${sourceSrc}`;
handleImageRequest(sourceReqUrl);
handleImageRequest(mainReqUrl, 1000);
handleImageRequest(previewUrl);
await imageLoader.evaluate((imageLoaderCtx, [tag, baseSrc, mainSrc, previewSrc, sourceSrc]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainSrc,
baseSrc,
preview: {
src: previewSrc,
sources: [{srcset: sourceSrc, media: '(max-width: 600px)'}]
},
ctx: globalThis.dummy
});
}, [tag, baseSrc, mainSrc, previewSrc, sourceSrc]);
await expectAsync(
waitFor(getNode(tag), (ctx, sourceReqUrl) => globalThis.getSrc(ctx) === sourceReqUrl, sourceReqUrl)
).toBeResolved();
await expectAsync(
waitFor(getNode(tag), (ctx, mainReqUrl) => globalThis.getSrc(ctx) === mainReqUrl, mainReqUrl)
).toBeResolved();
});
it('main loading error with `src`, `baseSrc`, broken with `src`, `sources` (srcset, media)', async () => {
await page.setViewportSize({
width: 580,
height: 480
});
const
baseSrc = 'https://fakeim.pl',
mainSrc = getRandomUrlPostfix(),
brokenSrc = getRandomUrlPostfix(),
sourceSrc = getRandomUrlPostfix(),
brokeUrl = `${baseSrc}/${brokenSrc}`,
mainReqUrl = `${baseSrc}/${mainSrc}`,
sourceReqUrl = `${baseSrc}/${sourceSrc}`;
handleImageRequest(sourceReqUrl);
abortImageRequest(mainReqUrl, 1000);
handleImageRequest(brokeUrl);
await imageLoader.evaluate((imageLoaderCtx, [tag, baseSrc, mainSrc, brokenSrc, sourceSrc]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainSrc,
baseSrc,
broken: {
src: brokenSrc,
sources: [{srcset: sourceSrc, media: '(max-width: 600px)'}]
},
ctx: globalThis.dummy
});
}, [tag, baseSrc, mainSrc, brokenSrc, sourceSrc]);
await expectAsync(
waitFor(getNode(tag), (ctx, sourceReqUrl) => globalThis.getSrc(ctx) === sourceReqUrl, sourceReqUrl)
).toBeResolved();
});
it('main state classes with `src`, preview with `src`', async () => {
const
mainSrcUrl = getRandomImgUrl(),
previewSrcUrl = getRandomImgUrl();
handleImageRequest(previewSrcUrl);
handleImageRequest(mainSrcUrl, 100);
const
previewClass = await component.evaluate((ctx) => ctx.block.getFullElName('v-image', 'preview', 'true')),
mainClass = await component.evaluate((ctx) => ctx.block.getFullElName('v-image', 'main', 'true'));
await imageLoader.evaluate((imageLoaderCtx, [tag, mainSrcUrl, previewSrcUrl]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainSrcUrl,
preview: previewSrcUrl,
stageClasses: true,
ctx: globalThis.dummy
});
}, [tag, mainSrcUrl, previewSrcUrl]);
await expectAsync(
waitFor(getNode(tag), (ctx, previewClass) => ctx.classList.contains(previewClass), previewClass)
).toBeResolved();
await expectAsync(
waitFor(getNode(tag), (ctx, mainStateClass) => ctx.classList.contains(mainStateClass), mainClass)
).toBeResolved();
});
it('main with `stageClasses`, `src`, preview with `src`, broken with `src`', async () => {
const
mainSrcUrl = getRandomImgUrl(),
brokenSrcUrl = getRandomImgUrl(),
previewSrcUrl = getRandomImgUrl();
handleImageRequest(previewSrcUrl, 300);
handleImageRequest(brokenSrcUrl);
abortImageRequest(mainSrcUrl, 600);
const
brokenClass = await component.evaluate((ctx) => ctx.block.getFullElName('v-image', 'broken', 'true')),
previewClass = await component.evaluate((ctx) => ctx.block.getFullElName('v-image', 'preview', 'true')),
initialClass = await component.evaluate((ctx) => ctx.block.getFullElName('v-image', 'initial', 'true'));
await imageLoader.evaluate((imageLoaderCtx, [tag, mainSrcUrl, previewSrcUrl, brokenSrcUrl]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainSrcUrl,
preview: previewSrcUrl,
broken: brokenSrcUrl,
stageClasses: true,
ctx: globalThis.dummy
});
}, [tag, mainSrcUrl, previewSrcUrl, brokenSrcUrl]);
expect(
await getNode(tag).evaluate((ctx, initialClass) => ctx.classList.contains(initialClass), initialClass)
).toBeTrue();
await expectAsync(
waitFor(getNode(tag), (ctx, previewClass) => ctx.classList.contains(previewClass), previewClass)
).toBeResolved();
await expectAsync(
waitFor(getNode(tag), (ctx, brokenClass) => ctx.classList.contains(brokenClass), brokenClass)
).toBeResolved();
});
it('main with `src`, `load` will not call a `load` callback if context was destroyed', async () => {
const
mainSrcUrl = getRandomImgUrl(),
imgReq = handleImageRequest(mainSrcUrl, 300);
const targetComponent = await h.component.createComponent(page, 'b-dummy');
await targetComponent.evaluate((ctx) => globalThis.tmpComponent = ctx);
await imageLoader.evaluate((imageLoaderCtx, [tag, mainSrcUrl]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainSrcUrl,
load: () => globalThis.tmp = true,
ctx: globalThis.tmpComponent
});
}, [tag, mainSrcUrl]);
await targetComponent.evaluate((ctx) => ctx.$destroy());
await imgReq;
await h.bom.waitForIdleCallback(page);
expect(await page.evaluate(() => globalThis.tmp)).toBeUndefined();
});
it('default `broken` as string', async () => {
const
mainSrcUrl = getRandomImgUrl(),
brokenSrcUrl = getRandomImgUrl();
const requests = [
abortImageRequest(mainSrcUrl),
handleImageRequest(brokenSrcUrl)
];
await component.evaluate((ctx, [tag, mainSrcUrl, brokenSrcUrl]) => {
const imageLoaderCtx = ctx.directives.imageFactory({
broken: {
src: brokenSrcUrl
}
});
const div = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(div, {
src: mainSrcUrl,
ctx: globalThis.dummy
});
}, [tag, mainSrcUrl, brokenSrcUrl]);
await Promise.all(requests);
await expectAsync(
waitFor(getNode(tag), (ctx, sourceReqUrl) => globalThis.getSrc(ctx) === sourceReqUrl, brokenSrcUrl)
).toBeResolved();
});
it('default `preview` as string', async () => {
const
mainSrcUrl = getRandomImgUrl(),
previewSrcUrl = getRandomImgUrl();
handleImageRequest(mainSrcUrl, 3000);
handleImageRequest(previewSrcUrl);
await component.evaluate((ctx, [tag, mainSrcUrl, previewSrcUrl]) => {
const imageLoaderCtx = ctx.directives.imageFactory({
preview: {
src: previewSrcUrl
}
});
const div = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(div, {
src: mainSrcUrl,
ctx: globalThis.dummy
});
}, [tag, mainSrcUrl, previewSrcUrl]);
await expectAsync(
waitFor(getNode(tag), (ctx, sourceReqUrl) => globalThis.getSrc(ctx) === sourceReqUrl, previewSrcUrl)
).toBeResolved();
});
it('override default placeholders', async () => {
const
mainSrcUrl = getRandomImgUrl(),
previewSrcUrl = getRandomImgUrl();
const requests = [
handleImageRequest(mainSrcUrl, 2000),
handleImageRequest(previewSrcUrl)
];
await component.evaluate((ctx, [tag, mainSrcUrl, previewSrcUrl]) => {
const imageLoaderCtx = ctx.directives.imageFactory({
preview: {
src: previewSrcUrl
}
});
const div = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(div, {
src: mainSrcUrl,
preview: undefined,
ctx: globalThis.dummy
});
}, [tag, mainSrcUrl, previewSrcUrl]);
await h.bom.waitForIdleCallback(page);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).not.toBe(previewSrcUrl);
await h.bom.waitForIdleCallback(page);
expect(await getNode(tag).evaluate((ctx) => globalThis.getSrc(ctx))).not.toBe(previewSrcUrl);
await Promise.all(requests);
await expectAsync(
waitFor(getNode(tag), (ctx, sourceReqUrl) => globalThis.getSrc(ctx) === sourceReqUrl, mainSrcUrl)
).toBeResolved();
});
['string', 'object'].forEach((paramType) => {
it(`default \`broken\` as ${paramType}`, async () => {
const
mainSrcUrl = getRandomImgUrl(),
brokenSrcUrl = getRandomImgUrl();
const requests = [
abortImageRequest(mainSrcUrl),
handleImageRequest(brokenSrcUrl)
];
await component.evaluate((ctx, [tag, paramType, mainSrcUrl, brokenSrcUrl]) => {
const imageLoaderCtx = ctx.directives.imageFactory({
broken: paramType === 'string' ?
brokenSrcUrl :
{
src: brokenSrcUrl
}
});
const div = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(div, {
src: mainSrcUrl,
ctx: globalThis.dummy
});
}, [tag, paramType, mainSrcUrl, brokenSrcUrl]);
await Promise.all(requests);
await expectAsync(
waitFor(getNode(tag), (ctx, sourceReqUrl) => globalThis.getSrc(ctx) === sourceReqUrl, brokenSrcUrl)
).toBeResolved();
});
it(`default \`preview\` as ${paramType}`, async () => {
const
mainSrcUrl = getRandomImgUrl(),
previewSrcUrl = getRandomImgUrl();
handleImageRequest(mainSrcUrl, 3000);
handleImageRequest(previewSrcUrl);
await component.evaluate((ctx, [tag, paramType, mainSrcUrl, previewSrcUrl]) => {
const imageLoaderCtx = ctx.directives.imageFactory({
preview: paramType === 'string' ?
previewSrcUrl :
{
src: previewSrcUrl
}
});
const div = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(div, {
src: mainSrcUrl,
ctx: globalThis.dummy
});
}, [tag, paramType, mainSrcUrl, previewSrcUrl]);
await expectAsync(
waitFor(getNode(tag), (ctx, sourceReqUrl) => globalThis.getSrc(ctx) === sourceReqUrl, previewSrcUrl)
).toBeResolved();
});
});
});
});
it('div main with `src`, `bgOptions`', async () => {
const
tag = 'div',
beforeImg = 'linear-gradient(rgb(230, 100, 101), rgb(145, 152, 229))',
afterImg = 'linear-gradient(rgb(230, 97, 101), rgb(145, 40, 229))';
await imageLoader.evaluate((imageLoaderCtx, [tag, mainSrc, beforeImg, afterImg]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainSrc,
bgOptions: {size: 'contain', ratio: 100 / 50, beforeImg, afterImg, position: '47% 47%', repeat: 'no-repeat'},
ctx: globalThis.dummy
});
}, [tag, images.pngImage, beforeImg, afterImg]);
const
expected = `${(1 / (100 / 50)) * 100}%`;
await expectAsync(getNode(tag).evaluate((ctx, mainSrc) => globalThis.getSrc(ctx) === mainSrc, images.pngImage));
const
bg = await getNode(tag).evaluate((ctx) => ctx.style.backgroundImage);
expect(bg.startsWith(beforeImg)).toBeTrue();
expect(bg.endsWith(afterImg)).toBeTrue();
expect(await getNode(tag).evaluate((ctx) => ctx.style.paddingBottom)).toBe(expected);
expect(await getNode(tag).evaluate((ctx) => ctx.style.backgroundPosition)).toBe('47% 47%');
expect(await getNode(tag).evaluate((ctx) => ctx.style.backgroundSize)).toBe('contain');
expect(await getNode(tag).evaluate((ctx) => ctx.style.backgroundRepeat)).toBe('no-repeat');
});
it('div main with `src`, default ratio', async () => {
const
tag = 'div';
await imageLoader.evaluate((imageLoaderCtx, [tag, mainSrc]) => {
const target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainSrc,
ctx: globalThis.dummy
});
const testImg = document.createElement('img');
testImg.src = mainSrc;
// @ts-expect-error
testImg.onInit(() => {
if (testImg.naturalHeight > 0 || testImg.naturalWidth > 0) {
const ratio = testImg.naturalHeight === 0 ? 1 : testImg.naturalWidth / testImg.naturalHeight;
globalThis.tmp = `${(1 / ratio) * 100}%`;
}
});
}, [tag, images.pngImage]);
await h.bom.waitForIdleCallback(page);
await expectAsync(getNode(tag).evaluate((ctx) => ctx.style.paddingBottom === globalThis.tmp)).toBeResolved();
});
it('img tag with `src` and `alt`', async () => {
await imageLoader.evaluate((imageLoaderCtx, images) => {
const img = document.getElementById('img-target');
imageLoaderCtx.init(img, {src: images.pngImage, alt: 'alt text', ctx: globalThis.dummy});
}, images);
await h.dom.waitForRef(page, 'img-target');
expect(await imgNode.evaluate((ctx) => ctx.src)).toBe(images.pngImage);
expect(await imgNode.evaluate((ctx) => ctx.alt)).toBe('alt text');
});
it('div tag with `src` and `alt`', async () => {
await imageLoader.evaluate((imageLoaderCtx, images) => {
const div = document.getElementById('div-target');
imageLoaderCtx.init(div, {
src: images.pngImage,
alt: 'alt-text',
ctx: globalThis.dummy
});
}, images);
await h.bom.waitForIdleCallback(page);
expect(await divNode.evaluate((ctx) => globalThis.getSrc(ctx))).toBe(images.pngImage);
expect(await divNode.getAttribute('aria-label')).toBe('alt-text');
expect(await divNode.getAttribute('role')).toBe('img');
});
it('div tag initial padding bottom', async () => {
const
mainSrcUrl = getRandomImgUrl();
handleImageRequest(mainSrcUrl, 2000);
await imageLoader.evaluate((imageLoaderCtx, mainSrcUrl) => {
const div = document.getElementById('div-target');
imageLoaderCtx.init(div, {
src: mainSrcUrl,
bgOptions: {
ratio: 328 / 172
},
ctx: globalThis.dummy
});
}, mainSrcUrl);
await h.bom.waitForIdleCallback(page);
expect(await divNode.evaluate((ctx) => parseInt(ctx.style.paddingBottom, 10))).toBe(52);
});
it('div tag clearing all styles after unbinding', async () => {
const
tag = 'div',
beforeImg = 'linear-gradient(rgb(230, 100, 101), rgb(145, 152, 229))',
afterImg = 'linear-gradient(rgb(230, 97, 101), rgb(145, 40, 229))';
await imageLoader.evaluate((imageLoaderCtx, [tag, mainSrc, beforeImg, afterImg]) => {
const
target = document.getElementById(`${tag}-target`);
imageLoaderCtx.init(target, {
src: mainSrc,
bgOptions: {size: 'contain', ratio: 100 / 50, beforeImg, afterImg, position: '47% 47%', repeat: 'no-repeat'},
ctx: globalThis.dummy
});
}, [tag, images.pngImage, beforeImg, afterImg]);
await h.bom.waitForIdleCallback(page);
await imageLoader.evaluate((imageLoaderCtx, tag) => {
const
target = document.getElementById(`${tag}-target`);
imageLoaderCtx.clearElement(target);
}, tag);
await h.bom.waitForIdleCallback(page);
expect(await getNode(tag).evaluate((ctx) => ctx.style.backgroundImage)).toBe('');
expect(await getNode(tag).evaluate((ctx) => ctx.style.backgroundSize)).toBe('');
expect(await getNode(tag).evaluate((ctx) => ctx.style.backgroundPosition)).toBe('');
expect(await getNode(tag).evaluate((ctx) => ctx.style.backgroundRepeat)).toBe('');
expect(await getNode(tag).evaluate((ctx) => ctx.style.paddingBottom)).toBe('');
});
});
};