switch to playwright test runner, use esbuild for dev builds, switch to array destructuring

This commit is contained in:
Rene
2022-06-10 10:55:51 +02:00
parent 9d0dd41d7f
commit 35868511ff
156 changed files with 6693 additions and 7793 deletions
@@ -1,55 +0,0 @@
import 'jest-playwright-preset';
import 'expect-playwright';
import url from './.build/build.html';
describe('StructureLifecycle', () => {
beforeEach(async () => {
await jestPlaywright.resetPage();
await page.goto(url);
});
[false, true].forEach(async (nativeScrollbarStyling) => {
const withText = nativeScrollbarStyling ? 'with' : 'without';
const nss = async () => {
if (!nativeScrollbarStyling) {
await page.click('#nss');
await page.waitForTimeout(500);
}
};
describe(`structureLifecycles ${withText} native scrollbar styling`, () => {
test('default', async () => {
await nss();
await page.click('#start');
await expect(page).toHaveSelector('#testResult.passed');
});
test('without flexbox glue & css custom props', async () => {
await nss();
await page.click('#fbg');
await page.waitForTimeout(500);
await page.click('#ccp');
await page.waitForTimeout(500);
await page.click('#start');
await expect(page).toHaveSelector('#testResult.passed');
});
// firefox can't simulate partially overlaid scrollbars, boost speed by omitting webkit
test.jestPlaywrightSkip({ browsers: ['firefox', 'webkit'] }, 'with partially overlaid scrollbars', async () => {
await nss();
await page.click('#po');
await page.waitForTimeout(500);
await page.click('#start');
await expect(page).toHaveSelector('#testResult.passed');
});
test('with fully overlaid scrollbars', async () => {
await nss();
await page.click('#fo');
await page.waitForTimeout(500);
await page.click('#start');
await expect(page).toHaveSelector('#testResult.passed');
});
});
});
});
@@ -1,15 +0,0 @@
import 'jest-playwright-preset';
import 'expect-playwright';
import url from './.build/build.html';
describe('DOMObserver', () => {
beforeEach(async () => {
await jestPlaywright.resetPage();
await page.goto(url);
});
test('test', async () => {
await page.click('#start');
await expect(page).toHaveSelector('#testResult.passed');
});
});
@@ -1,22 +0,0 @@
import 'jest-playwright-preset';
import 'expect-playwright';
import url from './.build/build.html';
describe('SizeObserver', () => {
beforeEach(async () => {
await jestPlaywright.resetPage();
await page.goto(url);
});
test('with ResizeOserver', async () => {
await page.click('#start');
await expect(page).toHaveSelector('#testResult.passed');
});
test('with ResizeOserver polyfill', async () => {
await page.click('#roPolyfill');
await page.waitForTimeout(500);
await page.click('#start');
await expect(page).toHaveSelector('#testResult.passed');
});
});
@@ -1,31 +0,0 @@
import 'jest-playwright-preset';
import 'expect-playwright';
import url from './.build/build.html';
describe('TrinsicObserver', () => {
beforeEach(async () => {
await jestPlaywright.resetPage();
await page.goto(url);
});
test('with IntersectionObserver', async () => {
await page.click('#start');
await expect(page).toHaveSelector('#testResult.passed');
});
test('with ResizeObserver', async () => {
await page.click('#ioPolyfill');
await page.waitForTimeout(500);
await page.click('#start');
await expect(page).toHaveSelector('#testResult.passed');
});
test('with ResizeObserver polyfill', async () => {
await page.click('#ioPolyfill');
await page.waitForTimeout(500);
await page.click('#roPolyfill');
await page.waitForTimeout(500);
await page.click('#start');
await expect(page).toHaveSelector('#testResult.passed');
});
});
@@ -0,0 +1,902 @@
import {
Environment,
StructureInitializationStaticElement,
StructureInitializationDynamicElement,
} from 'environment';
import { OSTarget, StructureInitialization } from 'typings';
import { createStructureSetup, StructureSetup } from 'setups/structureSetup';
import { isHTMLElement } from 'support';
const mockGetEnvironment = jest.fn();
jest.mock('environment', () => {
return {
getEnvironment: jest.fn().mockImplementation(() => mockGetEnvironment()),
};
});
interface StructureSetupProxy {
input: OSTarget | StructureInitialization;
setup: StructureSetup;
}
const textareaId = 'textarea';
const textareaHostId = 'host';
const elementId = 'target';
const dynamicContent = 'text<p>paragraph</p>';
const textareaContent = `<textarea id="${textareaId}">text</textarea>`;
const getSnapshot = () => document.body.innerHTML;
const getTarget = (textarea?: boolean) =>
document.getElementById(textarea ? textareaId : elementId)!;
const fillBody = (textarea?: boolean, customDOM?: (content: string, hostId: string) => string) => {
document.body.innerHTML = `
<nav></nav>
${
customDOM
? customDOM(
textarea ? textareaContent : dynamicContent,
textarea ? textareaHostId : elementId
)
: textarea
? textareaContent
: `<div id="${elementId}">${dynamicContent}</div>`
}
<footer></footer>
`;
return getSnapshot();
};
const clearBody = () => {
document.body.innerHTML = '';
};
const getElements = (textarea?: boolean) => {
const target = getTarget(textarea);
const host = document.querySelector('.os-host')!;
const padding = document.querySelector('.os-padding')!;
const viewport = document.querySelector('.os-viewport')!;
const content = document.querySelector('.os-content')!;
return {
target,
host,
padding,
viewport,
content,
};
};
const assertCorrectDOMStructure = (textarea?: boolean) => {
const { target, host, padding, viewport, content } = getElements(textarea);
expect(host).toBeTruthy();
expect(viewport).toBeTruthy();
expect(viewport.parentElement).toBe(padding || host);
if (content) {
expect(content.parentElement).toBe(viewport);
}
if (padding) {
expect(padding.parentElement).toBe(host);
}
expect(host.parentElement).toBe(document.body);
expect(host.previousElementSibling).toBe(document.querySelector('nav'));
expect(host.nextElementSibling).toBe(document.querySelector('footer'));
const contentElm = content || viewport;
if (textarea) {
expect(target.parentElement).toBe(contentElm);
expect(contentElm.innerHTML).toBe(textareaContent);
} else {
expect(target).toBe(host);
expect(contentElm.innerHTML).toBe(dynamicContent);
}
};
const createStructureSetupProxy = (
target: OSTarget | StructureInitialization
): StructureSetupProxy => ({
input: target,
setup: createStructureSetup(target),
});
const assertCorrectSetup = (
textarea: boolean,
setupProxy: StructureSetupProxy,
environment: Environment
): StructureSetup => {
const { input, setup } = setupProxy;
const { _targetObj, _targetCtx, _destroy } = setup;
const { _target, _host, _padding, _viewport, _content } = _targetObj;
const { target, host, padding, viewport, content } = getElements(textarea);
const isTextarea = target.matches('textarea');
const isBody = target.matches('body');
expect(textarea).toBe(isTextarea);
expect(_target).toBe(target);
expect(_host).toBe(host);
if (padding || _padding) {
expect(_padding).toBe(padding);
} else {
expect(padding).toBeFalsy();
expect(_padding).toBeFalsy();
}
if (viewport || _viewport) {
expect(_viewport).toBe(viewport);
} else {
expect(viewport).toBeFalsy();
expect(_viewport).toBeFalsy();
}
if (content || _content) {
expect(_content).toBe(content);
} else {
expect(content).toBeFalsy();
expect(_content).toBeFalsy();
}
const { _isTextarea, _isBody, _bodyElm, _htmlElm, _documentElm, _windowElm } = _targetCtx;
expect(_isTextarea).toBe(isTextarea);
expect(_isBody).toBe(isBody);
expect(_windowElm).toBe(document.defaultView);
expect(_documentElm).toBe(document);
expect(_htmlElm).toBe(document.body.parentElement);
expect(_bodyElm).toBe(document.body);
expect(typeof _destroy).toBe('function');
const { _nativeScrollbarStyling, _cssCustomProperties, _getInitializationStrategy } = environment;
const {
_host: hostInitStrategy,
_viewport: viewportInitStrategy,
_padding: paddingInitStrategy,
_content: contentInitStrategy,
} = _getInitializationStrategy();
const inputIsElement = isHTMLElement(input);
const inputAsObj = input as StructureInitialization;
const styleElm = document.querySelector('style');
const checkStrategyDependendElements = (
elm: Element | null,
input: HTMLElement | boolean | undefined,
isStaticStrategy: boolean,
strategy: StructureInitializationStaticElement | StructureInitializationDynamicElement,
id: string
) => {
if (input) {
expect(elm).toBeTruthy();
} else {
if (input === false) {
expect(elm).toBeFalsy();
}
if (input === undefined) {
if (isStaticStrategy) {
strategy = strategy as StructureInitializationStaticElement;
if (typeof strategy === 'function') {
const result = strategy(target);
if (result) {
expect(result).toBe(elm);
} else {
expect(elm).toBeTruthy();
}
} else {
expect(elm).toBeTruthy();
}
} else {
strategy = strategy as StructureInitializationDynamicElement;
const expectDefaultValue = () => {
if (id === 'padding') {
if (_nativeScrollbarStyling) {
expect(elm).toBeFalsy();
} else {
expect(elm).toBeTruthy();
}
} else if (id === 'content') {
expect(elm).toBeFalsy();
}
};
if (typeof strategy === 'function') {
const result = strategy(target);
const resultIsBoolean = typeof result === 'boolean';
if (resultIsBoolean) {
if (result) {
expect(elm).toBeTruthy();
} else {
expect(elm).toBeFalsy();
}
} else if (result) {
expect(elm).toBe(result);
} else {
expectDefaultValue();
}
} else {
const strategyIsBoolean = typeof strategy === 'boolean';
if (strategyIsBoolean) {
if (strategy) {
expect(elm).toBeTruthy();
} else {
expect(elm).toBeFalsy();
}
} else {
expectDefaultValue();
}
}
}
}
}
};
if (_nativeScrollbarStyling || _cssCustomProperties) {
expect(styleElm).toBeFalsy();
} else {
expect(styleElm).toBeTruthy();
}
if (inputIsElement) {
checkStrategyDependendElements(padding, undefined, false, paddingInitStrategy, 'padding');
checkStrategyDependendElements(content, undefined, false, contentInitStrategy, 'content');
checkStrategyDependendElements(viewport, undefined, true, viewportInitStrategy, 'viewport');
checkStrategyDependendElements(host, undefined, true, hostInitStrategy, 'host');
} else {
const {
padding: inputPadding,
content: inputContent,
viewport: inputViewport,
host: inputHost,
} = inputAsObj;
checkStrategyDependendElements(padding, inputPadding, false, paddingInitStrategy, 'padding');
checkStrategyDependendElements(content, inputContent, false, contentInitStrategy, 'content');
checkStrategyDependendElements(viewport, inputViewport, true, viewportInitStrategy, 'viewport');
checkStrategyDependendElements(host, inputHost, true, hostInitStrategy, 'host');
}
return setup;
};
const assertCorrectDestroy = (snapshot: string, setup: StructureSetup) => {
const { _destroy } = setup;
_destroy();
// remove empty class attr
const elms = document.querySelectorAll('*');
Array.from(elms).forEach((elm) => {
const classAttr = elm.getAttribute('class');
if (classAttr === '') {
elm.removeAttribute('class');
}
});
expect(snapshot).toBe(getSnapshot());
};
const env: Environment = jest.requireActual('environment').getEnvironment();
const envDefault = {
name: 'default',
env,
};
const envNativeScrollbarStyling = {
name: 'native scrollbar styling',
env: {
...env,
_nativeScrollbarStyling: true,
},
};
const envCssCustomProperties = {
name: 'custom css properties',
env: {
...env,
_cssCustomProperties: true,
},
};
const envInitStrategyMin = {
name: 'initialization strategy min',
env: {
...env,
_getInitializationStrategy: () => ({
_host: null,
_viewport: () => null,
_content: () => false,
_padding: false,
_scrollbarsSlot: null,
}),
},
};
const envInitStrategyMax = {
name: 'initialization strategy max',
env: {
...env,
_getInitializationStrategy: () => ({
_host: null,
_viewport: null,
_content: true,
_padding: () => true,
_scrollbarsSlot: null,
}),
},
};
const envInitStrategyAssigned = {
name: 'initialization strategy assigned',
env: {
...env,
_getInitializationStrategy: () => ({
_host: () => document.querySelector('#host1') as HTMLElement,
_viewport: (target: HTMLElement) => target.querySelector('#viewport') as HTMLElement,
_content: (target: HTMLElement) => target.querySelector('#content') as HTMLElement,
_padding: (target: HTMLElement) => target.querySelector('#padding') as HTMLElement,
_scrollbarsSlot: null,
}),
},
};
describe('structureSetup', () => {
afterEach(() => clearBody());
[
envDefault,
envNativeScrollbarStyling,
envCssCustomProperties,
envInitStrategyMin,
envInitStrategyMax,
envInitStrategyAssigned,
].forEach((envWithName) => {
const { env: currEnv, name } = envWithName;
describe(`Environment: ${name}`, () => {
beforeAll(() => {
mockGetEnvironment.mockImplementation(() => currEnv);
});
[false, true].forEach((isTextarea) => {
describe(isTextarea ? 'textarea' : 'element', () => {
describe('basic', () => {
test('Element', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy(getTarget(isTextarea)),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('Object', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({ target: getTarget(isTextarea) }),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
describe('complex', () => {
describe('single assigned', () => {
test('padding', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
viewport: document.querySelector<HTMLElement>('#viewport')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="content">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
describe('multiple assigned', () => {
test('padding viewport content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding"><div id="viewport"><div id="content">${content}</div></div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('padding viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding"><div id="viewport">${content}</div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
viewport: document.querySelector<HTMLElement>('#viewport')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('padding content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding"><div id="content">${content}</div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('viewport content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport"><div id="content">${content}</div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
describe('single false', () => {
test('padding', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
target: getTarget(isTextarea),
padding: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('content', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
target: getTarget(isTextarea),
content: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
describe('single true', () => {
test('padding', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
target: getTarget(isTextarea),
padding: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('content', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
target: getTarget(isTextarea),
content: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
describe('multiple false', () => {
test('padding & content', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
target: getTarget(isTextarea),
padding: false,
content: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
describe('multiple true', () => {
test('padding & content', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
target: getTarget(isTextarea),
padding: true,
content: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
describe('mixed', () => {
test('false: padding & content | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: false,
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: padding & content | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: true,
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: content | false: padding | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: false,
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: padding | false: content | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: true,
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('false: padding | assigned: content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="content">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: false,
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: padding | assigned: content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="content">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: true,
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('false: padding | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: false,
viewport: document.querySelector<HTMLElement>('#viewport')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: padding | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: true,
viewport: document.querySelector<HTMLElement>('#viewport')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('false: padding | assigned: viewport & content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport"><div id="content">${content}</div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
viewport: document.querySelector<HTMLElement>('#viewport')!,
padding: false,
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: padding | assigned: viewport & content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport"><div id="content">${content}</div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
viewport: document.querySelector<HTMLElement>('#viewport')!,
padding: true,
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('false: content | assigned: padding', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
content: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: content | assigned: padding', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
content: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('false: content | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: content | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('false: content | assigned: padding & viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding"><div id="viewport">${content}</div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: content | assigned: padding & viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding"><div id="viewport">${content}</div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
});
});
});
});
});
});
@@ -1,870 +0,0 @@
import { Environment, StructureInitializationStaticElement, StructureInitializationDynamicElement } from 'environment';
import { OSTarget, StructureInitialization } from 'typings';
import { createStructureSetup, StructureSetup } from 'setups/structureSetup';
import { isHTMLElement } from 'support';
const mockGetEnvironment = jest.fn();
jest.mock('environment', () => {
return {
getEnvironment: jest.fn().mockImplementation(() => mockGetEnvironment()),
};
});
interface StructureSetupProxy {
input: OSTarget | StructureInitialization;
setup: StructureSetup;
}
const textareaId = 'textarea';
const textareaHostId = 'host';
const elementId = 'target';
const dynamicContent = 'text<p>paragraph</p>';
const textareaContent = `<textarea id="${textareaId}">text</textarea>`;
const getSnapshot = () => document.body.innerHTML;
const getTarget = (textarea?: boolean) => document.getElementById(textarea ? textareaId : elementId)!;
const fillBody = (textarea?: boolean, customDOM?: (content: string, hostId: string) => string) => {
document.body.innerHTML = `
<nav></nav>
${
customDOM
? customDOM(textarea ? textareaContent : dynamicContent, textarea ? textareaHostId : elementId)
: textarea
? textareaContent
: `<div id="${elementId}">${dynamicContent}</div>`
}
<footer></footer>
`;
return getSnapshot();
};
const clearBody = () => {
document.body.innerHTML = '';
};
const getElements = (textarea?: boolean) => {
const target = getTarget(textarea);
const host = document.querySelector('.os-host')!;
const padding = document.querySelector('.os-padding')!;
const viewport = document.querySelector('.os-viewport')!;
const content = document.querySelector('.os-content')!;
return {
target,
host,
padding,
viewport,
content,
};
};
const assertCorrectDOMStructure = (textarea?: boolean) => {
const { target, host, padding, viewport, content } = getElements(textarea);
expect(host).toBeTruthy();
expect(viewport).toBeTruthy();
expect(viewport.parentElement).toBe(padding || host);
if (content) {
expect(content.parentElement).toBe(viewport);
}
if (padding) {
expect(padding.parentElement).toBe(host);
}
expect(host.parentElement).toBe(document.body);
expect(host.previousElementSibling).toBe(document.querySelector('nav'));
expect(host.nextElementSibling).toBe(document.querySelector('footer'));
const contentElm = content || viewport;
if (textarea) {
expect(target.parentElement).toBe(contentElm);
expect(contentElm.innerHTML).toBe(textareaContent);
} else {
expect(target).toBe(host);
expect(contentElm.innerHTML).toBe(dynamicContent);
}
};
const createStructureSetupProxy = (target: OSTarget | StructureInitialization): StructureSetupProxy => ({
input: target,
setup: createStructureSetup(target),
});
const assertCorrectSetup = (textarea: boolean, setupProxy: StructureSetupProxy, environment: Environment): StructureSetup => {
const { input, setup } = setupProxy;
const { _targetObj, _targetCtx, _destroy } = setup;
const { _target, _host, _padding, _viewport, _content } = _targetObj;
const { target, host, padding, viewport, content } = getElements(textarea);
const isTextarea = target.matches('textarea');
const isBody = target.matches('body');
expect(textarea).toBe(isTextarea);
expect(_target).toBe(target);
expect(_host).toBe(host);
if (padding || _padding) {
expect(_padding).toBe(padding);
} else {
expect(padding).toBeFalsy();
expect(_padding).toBeFalsy();
}
if (viewport || _viewport) {
expect(_viewport).toBe(viewport);
} else {
expect(viewport).toBeFalsy();
expect(_viewport).toBeFalsy();
}
if (content || _content) {
expect(_content).toBe(content);
} else {
expect(content).toBeFalsy();
expect(_content).toBeFalsy();
}
const { _isTextarea, _isBody, _bodyElm, _htmlElm, _documentElm, _windowElm } = _targetCtx;
expect(_isTextarea).toBe(isTextarea);
expect(_isBody).toBe(isBody);
expect(_windowElm).toBe(document.defaultView);
expect(_documentElm).toBe(document);
expect(_htmlElm).toBe(document.body.parentElement);
expect(_bodyElm).toBe(document.body);
expect(typeof _destroy).toBe('function');
const { _nativeScrollbarStyling, _cssCustomProperties, _getInitializationStrategy } = environment;
const {
_host: hostInitStrategy,
_viewport: viewportInitStrategy,
_padding: paddingInitStrategy,
_content: contentInitStrategy,
} = _getInitializationStrategy();
const inputIsElement = isHTMLElement(input);
const inputAsObj = input as StructureInitialization;
const styleElm = document.querySelector('style');
const checkStrategyDependendElements = (
elm: Element | null,
input: HTMLElement | boolean | undefined,
isStaticStrategy: boolean,
strategy: StructureInitializationStaticElement | StructureInitializationDynamicElement,
id: string
) => {
if (input) {
expect(elm).toBeTruthy();
} else {
if (input === false) {
expect(elm).toBeFalsy();
}
if (input === undefined) {
if (isStaticStrategy) {
strategy = strategy as StructureInitializationStaticElement;
if (typeof strategy === 'function') {
const result = strategy(target);
if (result) {
expect(result).toBe(elm);
} else {
expect(elm).toBeTruthy();
}
} else {
expect(elm).toBeTruthy();
}
} else {
strategy = strategy as StructureInitializationDynamicElement;
const expectDefaultValue = () => {
if (id === 'padding') {
if (_nativeScrollbarStyling) {
expect(elm).toBeFalsy();
} else {
expect(elm).toBeTruthy();
}
} else if (id === 'content') {
expect(elm).toBeFalsy();
}
};
if (typeof strategy === 'function') {
const result = strategy(target);
const resultIsBoolean = typeof result === 'boolean';
if (resultIsBoolean) {
if (result) {
expect(elm).toBeTruthy();
} else {
expect(elm).toBeFalsy();
}
} else if (result) {
expect(elm).toBe(result);
} else {
expectDefaultValue();
}
} else {
const strategyIsBoolean = typeof strategy === 'boolean';
if (strategyIsBoolean) {
if (strategy) {
expect(elm).toBeTruthy();
} else {
expect(elm).toBeFalsy();
}
} else {
expectDefaultValue();
}
}
}
}
}
};
if (_nativeScrollbarStyling || _cssCustomProperties) {
expect(styleElm).toBeFalsy();
} else {
expect(styleElm).toBeTruthy();
}
if (inputIsElement) {
checkStrategyDependendElements(padding, undefined, false, paddingInitStrategy, 'padding');
checkStrategyDependendElements(content, undefined, false, contentInitStrategy, 'content');
checkStrategyDependendElements(viewport, undefined, true, viewportInitStrategy, 'viewport');
checkStrategyDependendElements(host, undefined, true, hostInitStrategy, 'host');
} else {
const { padding: inputPadding, content: inputContent, viewport: inputViewport, host: inputHost } = inputAsObj;
checkStrategyDependendElements(padding, inputPadding, false, paddingInitStrategy, 'padding');
checkStrategyDependendElements(content, inputContent, false, contentInitStrategy, 'content');
checkStrategyDependendElements(viewport, inputViewport, true, viewportInitStrategy, 'viewport');
checkStrategyDependendElements(host, inputHost, true, hostInitStrategy, 'host');
}
return setup;
};
const assertCorrectDestroy = (snapshot: string, setup: StructureSetup) => {
const { _destroy } = setup;
_destroy();
// remove empty class attr
const elms = document.querySelectorAll('*');
Array.from(elms).forEach((elm) => {
const classAttr = elm.getAttribute('class');
if (classAttr === '') {
elm.removeAttribute('class');
}
});
expect(snapshot).toBe(getSnapshot());
};
const env: Environment = jest.requireActual('environment').getEnvironment();
const envDefault = {
name: 'default',
env: env,
};
const envNativeScrollbarStyling = {
name: 'native scrollbar styling',
env: {
...env,
_nativeScrollbarStyling: true,
},
};
const envCssCustomProperties = {
name: 'custom css properties',
env: {
...env,
_cssCustomProperties: true,
},
};
const envInitStrategyMin = {
name: 'initialization strategy min',
env: {
...env,
_getInitializationStrategy: () => ({
_host: null,
_viewport: () => null,
_content: () => false,
_padding: false,
_scrollbarsSlot: null,
}),
},
};
const envInitStrategyMax = {
name: 'initialization strategy max',
env: {
...env,
_getInitializationStrategy: () => ({
_host: null,
_viewport: null,
_content: true,
_padding: () => true,
_scrollbarsSlot: null,
}),
},
};
const envInitStrategyAssigned = {
name: 'initialization strategy assigned',
env: {
...env,
_getInitializationStrategy: () => ({
_host: () => document.querySelector('#host1') as HTMLElement,
_viewport: (target: HTMLElement) => target.querySelector('#viewport') as HTMLElement,
_content: (target: HTMLElement) => target.querySelector('#content') as HTMLElement,
_padding: (target: HTMLElement) => target.querySelector('#padding') as HTMLElement,
_scrollbarsSlot: null,
}),
},
};
describe('structureSetup', () => {
afterEach(() => clearBody());
[envDefault, envNativeScrollbarStyling, envCssCustomProperties, envInitStrategyMin, envInitStrategyMax, envInitStrategyAssigned].forEach(
(envWithName) => {
const { env: currEnv, name } = envWithName;
describe(`Environment: ${name}`, () => {
beforeAll(() => {
mockGetEnvironment.mockImplementation(() => currEnv);
});
[false, true].forEach((isTextarea) => {
describe(isTextarea ? 'textarea' : 'element', () => {
describe('basic', () => {
test('Element', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(isTextarea, createStructureSetupProxy(getTarget(isTextarea)), currEnv);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('Object', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(isTextarea, createStructureSetupProxy({ target: getTarget(isTextarea) }), currEnv);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
describe('complex', () => {
describe('single assigned', () => {
test('padding', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
viewport: document.querySelector<HTMLElement>('#viewport')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="content">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
describe('multiple assigned', () => {
test('padding viewport content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding"><div id="viewport"><div id="content">${content}</div></div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('padding viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding"><div id="viewport">${content}</div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
viewport: document.querySelector<HTMLElement>('#viewport')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('padding content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding"><div id="content">${content}</div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('viewport content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport"><div id="content">${content}</div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
describe('single false', () => {
test('padding', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
target: getTarget(isTextarea),
padding: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('content', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
target: getTarget(isTextarea),
content: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
describe('single true', () => {
test('padding', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
target: getTarget(isTextarea),
padding: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('content', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
target: getTarget(isTextarea),
content: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
describe('multiple false', () => {
test('padding & content', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
target: getTarget(isTextarea),
padding: false,
content: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
describe('multiple true', () => {
test('padding & content', () => {
const snapshot = fillBody(isTextarea);
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
target: getTarget(isTextarea),
padding: true,
content: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
describe('mixed', () => {
test('false: padding & content | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: false,
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: padding & content | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: true,
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: content | false: padding | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: false,
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: padding | false: content | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: true,
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('false: padding | assigned: content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="content">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: false,
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: padding | assigned: content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="content">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: true,
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('false: padding | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: false,
viewport: document.querySelector<HTMLElement>('#viewport')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: padding | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: true,
viewport: document.querySelector<HTMLElement>('#viewport')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('false: padding | assigned: viewport & content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport"><div id="content">${content}</div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
viewport: document.querySelector<HTMLElement>('#viewport')!,
padding: false,
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: padding | assigned: viewport & content', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport"><div id="content">${content}</div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
viewport: document.querySelector<HTMLElement>('#viewport')!,
padding: true,
content: document.querySelector<HTMLElement>('#content')!,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('false: content | assigned: padding', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
content: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: content | assigned: padding', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
content: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('false: content | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: content | assigned: viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="viewport">${content}</div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('false: content | assigned: padding & viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding"><div id="viewport">${content}</div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: false,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
test('true: content | assigned: padding & viewport', () => {
const snapshot = fillBody(isTextarea, (content, hostId) => {
return `<div id="${hostId}"><div id="padding"><div id="viewport">${content}</div></div></div>`;
});
const setup = assertCorrectSetup(
isTextarea,
createStructureSetupProxy({
host: document.querySelector<HTMLElement>('#host')!,
target: getTarget(isTextarea),
padding: document.querySelector<HTMLElement>('#padding')!,
viewport: document.querySelector<HTMLElement>('#viewport')!,
content: true,
}),
currEnv
);
assertCorrectDOMStructure(isTextarea);
assertCorrectDestroy(snapshot, setup);
});
});
});
});
});
});
}
);
});
@@ -7,7 +7,17 @@ import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
import { generateClassChangeSelectCallback, iterateSelect } from '@/testing-browser/Select';
import { timeout } from '@/testing-browser/timeout';
import { OverlayScrollbars } from 'overlayscrollbars';
import { assignDeep, clientSize, from, getBoundingClientRect, style, parent, addClass, WH, removeAttr } from 'support';
import {
assignDeep,
clientSize,
from,
getBoundingClientRect,
style,
parent,
addClass,
WH,
removeAttr,
} from 'support';
interface Metrics {
offset: {
@@ -74,8 +84,12 @@ const getMetrics = (elm: HTMLElement): Metrics => {
return {
offset: {
left: rounding(comparisonBCR.left - comparisonEnvBCR.left).toFixed(Math.min(fixedDigitsOffset, fixedDigits)),
top: rounding(comparisonBCR.top - comparisonEnvBCR.top).toFixed(Math.min(fixedDigitsOffset, fixedDigits)),
left: rounding(comparisonBCR.left - comparisonEnvBCR.left).toFixed(
Math.min(fixedDigitsOffset, fixedDigits)
),
top: rounding(comparisonBCR.top - comparisonEnvBCR.top).toFixed(
Math.min(fixedDigitsOffset, fixedDigits)
),
},
size: {
width: rounding(comparisonBCR.width).toFixed(fixedDigits),
@@ -166,11 +180,11 @@ target!.querySelector('.os-viewport')?.addEventListener('scroll', (e) => {
resize(target!).addResizeListener((width, height) => {
style(comparison, { width, height });
});
//resize(comparison!).addResizeListener((width, height) => style(target, { width, height }));
// resize(comparison!).addResizeListener((width, height) => style(target, { width, height }));
resize(targetResize!).addResizeListener((width, height) => {
style(comparisonResize, { width, height });
});
//resize(comparisonRes!).addResizeListener((width, height) => style(targetRes, { width, height }));
// resize(comparisonRes!).addResizeListener((width, height) => style(targetRes, { width, height }));
const selectCallbackEnv = generateClassChangeSelectCallback(from(envElms));
const envWidthSelect = document.querySelector<HTMLSelectElement>('#envWidth');
@@ -230,58 +244,136 @@ const checkMetrics = async (checkComparison: CheckComparisonObj) => {
if (isFractionalPixelRatio()) {
should.ok(
plusMinusArr(targetMetrics.scroll.width, fractionalPixelRatioTollerance).indexOf(comparisonMetrics.scroll.width) > -1,
plusMinusArr(targetMetrics.scroll.width, fractionalPixelRatioTollerance).indexOf(
comparisonMetrics.scroll.width
) > -1,
`Scroll width equality. (+-${fractionalPixelRatioTollerance}) | Target: ${targetMetrics.scroll.width} | Comparison: ${comparisonMetrics.scroll.width}`
);
should.ok(
plusMinusArr(targetMetrics.scroll.height, fractionalPixelRatioTollerance).indexOf(comparisonMetrics.scroll.height) > -1,
plusMinusArr(targetMetrics.scroll.height, fractionalPixelRatioTollerance).indexOf(
comparisonMetrics.scroll.height
) > -1,
`Scroll height equality. (+-${fractionalPixelRatioTollerance}) | Target: ${targetMetrics.scroll.height} | Comparison: ${comparisonMetrics.scroll.height}`
);
should.ok(
plusMinusArr(osInstance.state()._overflowAmount.w, fractionalPixelRatioTollerance).indexOf(comparisonMetrics.scroll.width) > -1,
`Overflow amount width equality. (+-${fractionalPixelRatioTollerance}) | Amount: ${osInstance.state()._overflowAmount.w} | Comparison: ${
plusMinusArr(osInstance.state()._overflowAmount.w, fractionalPixelRatioTollerance).indexOf(
comparisonMetrics.scroll.width
}`
) > -1,
`Overflow amount width equality. (+-${fractionalPixelRatioTollerance}) | Amount: ${
osInstance.state()._overflowAmount.w
} | Comparison: ${comparisonMetrics.scroll.width}`
);
should.ok(
plusMinusArr(osInstance.state()._overflowAmount.h, fractionalPixelRatioTollerance).indexOf(comparisonMetrics.scroll.height) > -1,
`Overflow amount height equality. (+-${fractionalPixelRatioTollerance}) | Amount: ${osInstance.state()._overflowAmount.h} | Comparison: ${
plusMinusArr(osInstance.state()._overflowAmount.h, fractionalPixelRatioTollerance).indexOf(
comparisonMetrics.scroll.height
}`
) > -1,
`Overflow amount height equality. (+-${fractionalPixelRatioTollerance}) | Amount: ${
osInstance.state()._overflowAmount.h
} | Comparison: ${comparisonMetrics.scroll.height}`
);
} else {
should.equal(targetMetrics.scroll.width, comparisonMetrics.scroll.width, 'Scroll width equality.');
should.equal(targetMetrics.scroll.height, comparisonMetrics.scroll.height, 'Scroll height equality.');
should.equal(
targetMetrics.scroll.width,
comparisonMetrics.scroll.width,
'Scroll width equality.'
);
should.equal(
targetMetrics.scroll.height,
comparisonMetrics.scroll.height,
'Scroll height equality.'
);
should.equal(osInstance.state()._overflowAmount.w, comparisonMetrics.scroll.width, 'Overflow amount width equality.');
should.equal(osInstance.state()._overflowAmount.h, comparisonMetrics.scroll.height, 'Overflow amount height equality.');
should.equal(
osInstance.state()._overflowAmount.w,
comparisonMetrics.scroll.width,
'Overflow amount width equality.'
);
should.equal(
osInstance.state()._overflowAmount.h,
comparisonMetrics.scroll.height,
'Overflow amount height equality.'
);
should.equal(targetMetrics.hasOverflow.x, comparisonMetrics.hasOverflow.x, 'Has overflow x equality.');
should.equal(targetMetrics.hasOverflow.y, comparisonMetrics.hasOverflow.y, 'Has overflow y equality.');
should.equal(
targetMetrics.hasOverflow.x,
comparisonMetrics.hasOverflow.x,
'Has overflow x equality.'
);
should.equal(
targetMetrics.hasOverflow.y,
comparisonMetrics.hasOverflow.y,
'Has overflow y equality.'
);
}
if (targetMetrics.hasOverflow.x) {
should.equal(style(targetViewport!, 'overflowX'), 'scroll', 'Overflow-X should result in scroll.');
should.ok(osInstance.state()._overflowAmount.w > 0, 'Overflow amount width should be > 0 with overflow.');
should.equal(
style(targetViewport!, 'overflowX'),
'scroll',
'Overflow-X should result in scroll.'
);
should.ok(
osInstance.state()._overflowAmount.w > 0,
'Overflow amount width should be > 0 with overflow.'
);
} else {
should.notEqual(style(targetViewport!, 'overflowX'), 'scroll', 'No Overflow-X shouldnt result in scroll.');
should.equal(osInstance.state()._overflowAmount.w, 0, 'Overflow amount width should be 0 without overflow.');
should.notEqual(
style(targetViewport!, 'overflowX'),
'scroll',
'No Overflow-X shouldnt result in scroll.'
);
should.equal(
osInstance.state()._overflowAmount.w,
0,
'Overflow amount width should be 0 without overflow.'
);
}
if (targetMetrics.hasOverflow.y) {
should.equal(style(targetViewport!, 'overflowY'), 'scroll', 'Overflow-Y should result in scroll.');
should.ok(osInstance.state()._overflowAmount.h > 0, 'Overflow amount height should be > 0 with overflow.');
should.equal(
style(targetViewport!, 'overflowY'),
'scroll',
'Overflow-Y should result in scroll.'
);
should.ok(
osInstance.state()._overflowAmount.h > 0,
'Overflow amount height should be > 0 with overflow.'
);
} else {
should.notEqual(style(targetViewport!, 'overflowY'), 'scroll', 'No Overflow-Y shouldnt result in scroll.');
should.equal(osInstance.state()._overflowAmount.h, 0, 'Overflow amount height should be 0 without overflow.');
should.notEqual(
style(targetViewport!, 'overflowY'),
'scroll',
'No Overflow-Y shouldnt result in scroll.'
);
should.equal(
osInstance.state()._overflowAmount.h,
0,
'Overflow amount height should be 0 without overflow.'
);
}
should.equal(targetMetrics.percentElm.width, comparisonMetrics.percentElm.width, 'Percent Elements width equality.');
should.equal(targetMetrics.percentElm.height, comparisonMetrics.percentElm.height, 'Percent Elements height equality.');
should.equal(
targetMetrics.percentElm.width,
comparisonMetrics.percentElm.width,
'Percent Elements width equality.'
);
should.equal(
targetMetrics.percentElm.height,
comparisonMetrics.percentElm.height,
'Percent Elements height equality.'
);
should.equal(targetMetrics.endElm.width, comparisonMetrics.endElm.width, 'End Elements width equality.');
should.equal(targetMetrics.endElm.height, comparisonMetrics.endElm.height, 'End Elements height equality.');
should.equal(
targetMetrics.endElm.width,
comparisonMetrics.endElm.width,
'End Elements width equality.'
);
should.equal(
targetMetrics.endElm.height,
comparisonMetrics.endElm.height,
'End Elements height equality.'
);
await timeout(1);
@@ -307,31 +399,36 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any)
afterEach,
});
};
/*
const iterateEnvWidth = async (afterEach?: () => any) => {
await iterate(envWidthSelect, afterEach);
};
const iterateEnvHeight = async (afterEach?: () => any) => {
await iterate(envHeightSelect, afterEach);
};
*/
const iterateHeight = async (afterEach?: () => any) => {
await iterate(containerHeightSelect, afterEach);
};
const iterateWidth = async (afterEach?: () => any) => {
await iterate(containerWidthSelect, afterEach);
};
/*
const iterateFloat = async (afterEach?: () => any) => {
await iterate(containerFloatSelect, afterEach);
};
*/
const iteratePadding = async (afterEach?: () => any) => {
await iterate(containerPaddingSelect, afterEach);
};
const iterateBorder = async (afterEach?: () => any) => {
await iterate(containerBorderSelect, afterEach);
};
/*
const iterateMargin = async (afterEach?: () => any) => {
await iterate(containerMarginSelect, afterEach);
};
*/
const iterateBoxSizing = async (afterEach?: () => any) => {
await iterate(containerBoxSizingSelect, afterEach);
};
@@ -354,8 +451,10 @@ const overflowTest = async () => {
const computedStyle = window.getComputedStyle(elm);
const size = clientSize(elm);
return {
w: size.w - (parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight)),
h: size.h - (parseFloat(computedStyle.paddingTop) + parseFloat(computedStyle.paddingBottom)),
w:
size.w - (parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight)),
h:
size.h - (parseFloat(computedStyle.paddingTop) + parseFloat(computedStyle.paddingBottom)),
};
}
@@ -425,15 +524,29 @@ const overflowTest = async () => {
await waitForOrFailTest(() => {
if (width) {
should.ok(overflowAmountCheck.width >= addOverflow, 'Correct smallest possible overflow width. (?)');
should.ok(
overflowAmountCheck.width >= addOverflow,
'Correct smallest possible overflow width. (?)'
);
} else {
should.equal(overflowAmountCheck.width, 0, 'Correct smallest possible overflow width. (0)');
should.equal(
overflowAmountCheck.width,
0,
'Correct smallest possible overflow width. (0)'
);
}
if (height) {
should.ok(overflowAmountCheck.height >= addOverflow, 'Correct smallest possible overflow height. (?)');
should.ok(
overflowAmountCheck.height >= addOverflow,
'Correct smallest possible overflow height. (?)'
);
} else {
should.equal(overflowAmountCheck.height, 0, 'Correct smallest possible overflow height. (0)');
should.equal(
overflowAmountCheck.height,
0,
'Correct smallest possible overflow height. (0)'
);
}
});
@@ -0,0 +1,58 @@
// @ts-ignore
import { playwrightRollup, expectSuccess } from '@/playwright/rollup';
import { test, Page } from '@playwright/test';
playwrightRollup();
test.describe('StructureLifecycle', () => {
[false, true].forEach(async (nativeScrollbarStyling) => {
const withText = nativeScrollbarStyling ? 'with' : 'without';
const nss = async (page: Page) => {
if (!nativeScrollbarStyling) {
await page.click('#nss');
await page.waitForTimeout(500);
}
};
test.describe(`structureLifecycles ${withText} native scrollbar styling`, () => {
test.describe.configure({ mode: 'parallel' });
test('default', async ({ page }) => {
await nss(page);
await page.click('#start');
await expectSuccess(page);
});
test('without flexbox glue & css custom props', async ({ page }) => {
await nss(page);
await page.click('#fbg');
await page.waitForTimeout(500);
await page.click('#ccp');
await page.waitForTimeout(500);
await page.click('#start');
await expectSuccess(page);
});
test('with partially overlaid scrollbars', async ({ page, browserName }) => {
test.skip(
browserName === 'firefox' || browserName === 'webkit',
"firefox can't simulate partially overlaid scrollbars, boost speed by omitting webkit"
);
await nss(page);
await page.click('#po');
await page.waitForTimeout(500);
await page.click('#start');
await expectSuccess(page);
});
test('with fully overlaid scrollbars', async ({ page }) => {
await nss(page);
await page.click('#fo');
await page.waitForTimeout(500);
await page.click('#start');
await expectSuccess(page);
});
});
});
});
@@ -4,7 +4,19 @@ import should from 'should';
import { generateSelectCallback, iterateSelect } from '@/testing-browser/Select';
import { timeout } from '@/testing-browser/timeout';
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
import { appendChildren, createDiv, removeElements, children, isArray, isNumber, liesBetween, addClass, removeClass, diffClass, on } from 'support';
import {
appendChildren,
createDiv,
removeElements,
children,
isArray,
isNumber,
liesBetween,
addClass,
removeClass,
diffClass,
on,
} from 'support';
import { createDOMObserver } from 'observers/domObserver';
@@ -27,8 +39,12 @@ const targetElm: HTMLElement | null = document.querySelector('#target');
const trargetContentElm: HTMLElement | null = document.querySelector('#target .content');
const targetElmContentElm: HTMLElement | null = document.querySelector('#content-host');
const contentElmAttrChange: HTMLElement | null = document.querySelector('#target .content-nest');
const contentBetweenElmAttrChange: HTMLElement | null = document.querySelector('#content-host .padding-nest-item');
const contentHostElmAttrChange: HTMLElement | null = document.querySelector('#content-nest-item-host');
const contentBetweenElmAttrChange: HTMLElement | null = document.querySelector(
'#content-host .padding-nest-item'
);
const contentHostElmAttrChange: HTMLElement | null = document.querySelector(
'#content-nest-item-host'
);
const targetElmsSlot = document.querySelector('#target .host-nest-item');
const targetContentElmsSlot = document.querySelector('#target .content .content-nest');
@@ -36,20 +52,40 @@ const targetContentBetweenElmsSlot = document.querySelector('#content-host');
const imgElmsSlot = document.querySelector('#target .content-nest');
const transitionElmsSlot = document.querySelector('#content-host .content');
const addRemoveTargetElms: HTMLButtonElement | null = document.querySelector('#addRemoveTargetElms');
const addRemoveTargetContentElms: HTMLButtonElement | null = document.querySelector('#addRemoveTargetContentElms');
const addRemoveTargetContentBetweenElms: HTMLButtonElement | null = document.querySelector('#addRemoveTargetContentBetweenElms');
const addRemoveTargetElms: HTMLButtonElement | null = document.querySelector(
'#addRemoveTargetElms'
);
const addRemoveTargetContentElms: HTMLButtonElement | null = document.querySelector(
'#addRemoveTargetContentElms'
);
const addRemoveTargetContentBetweenElms: HTMLButtonElement | null = document.querySelector(
'#addRemoveTargetContentBetweenElms'
);
const addRemoveImgElms: HTMLButtonElement | null = document.querySelector('#addRemoveImgElms');
const addRemoveTransitionElms: HTMLButtonElement | null = document.querySelector('#addRemoveTransitionElms');
const addRemoveTransitionElms: HTMLButtonElement | null = document.querySelector(
'#addRemoveTransitionElms'
);
const ignoreTargetChange: HTMLButtonElement | null = document.querySelector('#ignoreTargetChange');
const setTargetAttr: HTMLSelectElement | null = document.querySelector('#setTargetAttr');
const setFilteredTargetAttr: HTMLSelectElement | null = document.querySelector('#setFilteredTargetAttr');
const setFilteredTargetAttr: HTMLSelectElement | null = document.querySelector(
'#setFilteredTargetAttr'
);
const setContentAttr: HTMLSelectElement | null = document.querySelector('#setContentAttr');
const setFilteredContentAttr: HTMLSelectElement | null = document.querySelector('#setFilteredContentAttr');
const setContentBetweenAttr: HTMLSelectElement | null = document.querySelector('#setContentBetweenAttr');
const setFilteredContentBetweenAttr: HTMLSelectElement | null = document.querySelector('#setFilteredContentBetweenAttr');
const setContentHostElmAttr: HTMLSelectElement | null = document.querySelector('#setContentHostElmAttr');
const setFilteredContentHostElmAttr: HTMLSelectElement | null = document.querySelector('#setFilteredContentHostElmAttr');
const setFilteredContentAttr: HTMLSelectElement | null = document.querySelector(
'#setFilteredContentAttr'
);
const setContentBetweenAttr: HTMLSelectElement | null = document.querySelector(
'#setContentBetweenAttr'
);
const setFilteredContentBetweenAttr: HTMLSelectElement | null = document.querySelector(
'#setFilteredContentBetweenAttr'
);
const setContentHostElmAttr: HTMLSelectElement | null = document.querySelector(
'#setContentHostElmAttr'
);
const setFilteredContentHostElmAttr: HTMLSelectElement | null = document.querySelector(
'#setFilteredContentHostElmAttr'
);
const summaryContent: HTMLElement | null = document.querySelector('#summary-content');
const summaryBetween: HTMLElement | null = document.querySelector('#summary-between');
@@ -66,11 +102,21 @@ const targetDomObserver = createDOMObserver(
document.querySelector('#target')!,
false,
(changedTargetAttrs: string[], styleChanged: boolean) => {
should.ok(Array.isArray(changedTargetAttrs), 'The changedTargetAttrs parameter in a target dom observer must be a array.');
should.equal(typeof styleChanged, 'boolean', 'The styleChanged parameter in a target dom observer must be a boolean.');
should.ok(
Array.isArray(changedTargetAttrs),
'The changedTargetAttrs parameter in a target dom observer must be a array.'
);
should.equal(
typeof styleChanged,
'boolean',
'The styleChanged parameter in a target dom observer must be a boolean.'
);
if (styleChanged && changedTargetAttrs.length === 0) {
should.ok(false, 'Style changing properties must always be inside the changedTargetAttrs array.');
should.ok(
false,
'Style changing properties must always be inside the changedTargetAttrs array.'
);
}
domTargetObserverObservations.push({ changedTargetAttrs, styleChanged });
@@ -106,14 +152,23 @@ const targetDomObserver = createDOMObserver(
}
);
const createContentDomOserver = (eventContentChange: Array<[string?, string?] | null | undefined>) => {
const createContentDomOserver = (
eventContentChange: Array<[string?, string?] | null | undefined>
) => {
return createDOMObserver(
trargetContentElm!,
true,
(contentChangedTroughEvent: boolean) => {
should.equal(typeof contentChangedTroughEvent, 'boolean', 'The contentChanged parameter in a content dom observer must be a boolean.');
should.equal(
typeof contentChangedTroughEvent,
'boolean',
'The contentChanged parameter in a content dom observer must be a boolean.'
);
domContentObserverObservations.push({ contentChange: true, troughEvent: contentChangedTroughEvent });
domContentObserverObservations.push({
contentChange: true,
troughEvent: contentChangedTroughEvent,
});
requestAnimationFrame(() => {
if (contentChangesCountSlot) {
contentChangesCountSlot.textContent = `${domContentObserverObservations.length}`;
@@ -127,7 +182,11 @@ const createContentDomOserver = (eventContentChange: Array<[string?, string?] |
_nestedTargetSelector: hostSelector,
_ignoreContentChange: (mutation, isNestedTarget) => {
const { target, attributeName } = mutation;
return isNestedTarget ? false : attributeName ? liesBetween(target as Element, hostSelector, '.content') : false;
return isNestedTarget
? false
: attributeName
? liesBetween(target as Element, hostSelector, '.content')
: false;
},
_ignoreNestedTargetChange: (target, attrName, oldValue, newValue) => {
if (attrName === 'class' && oldValue && newValue) {
@@ -149,8 +208,10 @@ const createContentDomOserver = (eventContentChange: Array<[string?, string?] |
let contentDomObserver = createContentDomOserver(contentChange);
const getTotalObservations = () => domTargetObserverObservations.length + domContentObserverObservations.length;
const getLast = <T>(arr: T[], indexFromLast = 0): T => arr[arr.length - 1 - indexFromLast] || ({} as T);
const getTotalObservations = () =>
domTargetObserverObservations.length + domContentObserverObservations.length;
const getLast = <T>(arr: T[], indexFromLast = 0): T =>
arr[arr.length - 1 - indexFromLast] || ({} as T);
const changedThrough = <ChangeThrough extends DOMContentObserverResult | DOMTargetObserverResult>(
observationLists?: Array<ChangeThrough[]> | ChangeThrough[]
) => {
@@ -222,7 +283,9 @@ const attrChangeListener = (attrChangeTarget: HTMLElement | null) =>
isClass && target.classList.add('something');
!isClass && target.setAttribute(selectedValue, 'something');
});
const iterateAttrChange = async <ChangeThrough extends DOMContentObserverResult | DOMTargetObserverResult>(
const iterateAttrChange = async <
ChangeThrough extends DOMContentObserverResult | DOMTargetObserverResult
>(
select: HTMLSelectElement | null,
changeThrough?: ChangeThrough[],
checkChange?: (observation: ChangeThrough, selected: string) => any
@@ -248,10 +311,17 @@ const iterateAttrChange = async <ChangeThrough extends DOMContentObserverResult
},
});
};
const addRemoveElementsTest = async (slot: Element | null, changeThrough?: DOMContentObserverResult[] | SeparateChangeThrough) => {
const addRemoveElementsTest = async (
slot: Element | null,
changeThrough?: DOMContentObserverResult[] | SeparateChangeThrough
) => {
if (slot) {
let addChangeThrough: DOMContentObserverResult[] | undefined = changeThrough as DOMContentObserverResult[] | undefined;
let removeChangeThrough: DOMContentObserverResult[] | undefined = changeThrough as DOMContentObserverResult[] | undefined;
let addChangeThrough: DOMContentObserverResult[] | undefined = changeThrough as
| DOMContentObserverResult[]
| undefined;
let removeChangeThrough: DOMContentObserverResult[] | undefined = changeThrough as
| DOMContentObserverResult[]
| undefined;
if (changeThrough && !isArray(changeThrough)) {
addChangeThrough = (changeThrough as SeparateChangeThrough).added;
removeChangeThrough = (changeThrough as SeparateChangeThrough).removed;
@@ -272,7 +342,11 @@ const addRemoveElementsTest = async (slot: Element | null, changeThrough?: DOMCo
if (addChangeThrough) {
const contentChanged = getLast(addChangeThrough);
await waitForOrFailTest(() => {
should.deepEqual(contentChanged, { contentChange: true, troughEvent: false }, 'Adding an content element must result in a content change.');
should.deepEqual(
contentChanged,
{ contentChange: true, troughEvent: false },
'Adding an content element must result in a content change.'
);
});
}
};
@@ -311,7 +385,10 @@ const addRemoveElementsTest = async (slot: Element | null, changeThrough?: DOMCo
await removeElm();
}
};
const triggerSummaryElemet = async (summaryElm: HTMLElement | null, changeThrough?: DOMContentObserverResult[]) => {
const triggerSummaryElemet = async (
summaryElm: HTMLElement | null,
changeThrough?: DOMContentObserverResult[]
) => {
// onyl do if summary is working (IE. exception)
if (summaryElm && (summaryElm.nextElementSibling as HTMLElement)?.offsetHeight === 0) {
const click = async () => {
@@ -370,7 +447,11 @@ const addRemoveImgElmsFn = async () => {
);
const lastContentChanged = getLast(domContentObserverObservations);
should.deepEqual(lastContentChanged, { contentChange: true, troughEvent: true }, 'The images load event must result in a content change.');
should.deepEqual(
lastContentChanged,
{ contentChange: true, troughEvent: true },
'The images load event must result in a content change.'
);
});
};
@@ -424,7 +505,9 @@ const addRemoveImgElmsFn = async () => {
await addMultiple();
// remove load event from image test
const addChanged = async (newEventContentChange: Array<[string?, string?] | null | undefined>) => {
const addChanged = async (
newEventContentChange: Array<[string?, string?] | null | undefined>
) => {
contentDomObserver._destroy();
contentDomObserver = createContentDomOserver(newEventContentChange);
@@ -449,7 +532,16 @@ const addRemoveImgElmsFn = async () => {
contentDomObserver = createContentDomOserver(contentChange);
};
await addChanged([['img', 'something'], ['img', 'something2'], ['img', ''], ['img', undefined], ['', ''], [undefined, undefined], null, undefined]);
await addChanged([
['img', 'something'],
['img', 'something2'],
['img', ''],
['img', undefined],
['', ''],
[undefined, undefined],
null,
undefined,
]);
await addChanged([]);
removeElements(document.querySelectorAll('.img'));
@@ -460,7 +552,11 @@ const addRemoveTransitionElmsFn = async () => {
const startTransition = async (elm: Element, expectTransitionEndContentChange: boolean) => {
await timeout(50); // time for css to apply class a bit later to trigger transition
const { before: beforeTransition, after: afterTransition, compare: compareTransition } = changedThrough(domContentObserverObservations);
const {
before: beforeTransition,
after: afterTransition,
compare: compareTransition,
} = changedThrough(domContentObserverObservations);
beforeTransition();
removeClass(elm, 'resetTransition'); // IE...
addClass(elm, 'active');
@@ -523,7 +619,9 @@ const addRemoveTransitionElmsFn = async () => {
await add(false);
contentDomObserver._destroy();
contentDomObserver = createContentDomOserver(contentChange.concat([['.transition', 'transitionend']]));
contentDomObserver = createContentDomOserver(
contentChange.concat([['.transition', 'transitionend']])
);
await add(true);
};
@@ -557,7 +655,11 @@ const iterateTargetAttrChange = async () => {
true,
'A attribute change on the target element for a DOMTargetObserver must be inside the changedTargetAttrs array.'
);
should.equal(styleChanged, true, 'A style changing attribute on the target element for a DOMTargetObserver must set styleChanged to true.');
should.equal(
styleChanged,
true,
'A style changing attribute on the target element for a DOMTargetObserver must set styleChanged to true.'
);
});
await iterateAttrChange(setFilteredTargetAttr);
};
@@ -605,9 +707,15 @@ setFilteredTargetAttr?.addEventListener('change', attrChangeListener(targetElm))
setContentAttr?.addEventListener('change', attrChangeListener(contentElmAttrChange));
setFilteredContentAttr?.addEventListener('change', attrChangeListener(contentElmAttrChange));
setContentBetweenAttr?.addEventListener('change', attrChangeListener(contentBetweenElmAttrChange));
setFilteredContentBetweenAttr?.addEventListener('change', attrChangeListener(contentBetweenElmAttrChange));
setFilteredContentBetweenAttr?.addEventListener(
'change',
attrChangeListener(contentBetweenElmAttrChange)
);
setContentHostElmAttr?.addEventListener('change', attrChangeListener(contentHostElmAttrChange));
setFilteredContentHostElmAttr?.addEventListener('change', attrChangeListener(contentHostElmAttrChange));
setFilteredContentHostElmAttr?.addEventListener(
'change',
attrChangeListener(contentHostElmAttrChange)
);
const start = async () => {
setTestResult(null);
@@ -0,0 +1,12 @@
// @ts-ignore
import { playwrightRollup, expectSuccess } from '@/playwright/rollup';
import { test } from '@playwright/test';
playwrightRollup();
test.describe('DOMObserver', () => {
test('test', async ({ page }) => {
await page.click('#start');
await expectSuccess(page);
});
});
@@ -2,7 +2,11 @@ import 'styles/overlayscrollbars.scss';
import './index.scss';
import './handleEnvironment';
import should from 'should';
import { generateClassChangeSelectCallback, iterateSelect, selectOption } from '@/testing-browser/Select';
import {
generateClassChangeSelectCallback,
iterateSelect,
selectOption,
} from '@/testing-browser/Select';
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
import { timeout } from '@/testing-browser/timeout';
import { hasDimensions, offsetSize, WH, style } from 'support';
@@ -15,8 +19,12 @@ const contentBox = (elm: HTMLElement | null): WH<number> => {
if (elm) {
const computedStyle = window.getComputedStyle(elm);
return {
w: elm.clientWidth - (parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight)),
h: elm.clientHeight - (parseFloat(computedStyle.paddingTop) + parseFloat(computedStyle.paddingBottom)),
w:
elm.clientWidth -
(parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight)),
h:
elm.clientHeight -
(parseFloat(computedStyle.paddingTop) + parseFloat(computedStyle.paddingBottom)),
};
}
@@ -37,7 +45,7 @@ const preInitChildren = targetElm?.children.length;
const sizeObserver = createSizeObserver(
targetElm as HTMLElement,
({ _directionIsRTLCache, _sizeChanged, _appear }) => {
({ _directionIsRTLCache, _sizeChanged }) => {
if (_sizeChanged) {
sizeIterations += 1;
}
@@ -45,6 +53,7 @@ const sizeObserver = createSizeObserver(
if (_directionIsRTLCache) {
directionIterations += 1;
}
requestAnimationFrame(() => {
if (resizesSlot) {
resizesSlot.textContent = (directionIterations + sizeIterations).toString();
@@ -83,13 +92,22 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any)
currBoxSizing,
};
},
async check({ currSizeIterations, currDirectionIterations, currOffsetSize, currContentSize, currDir, currBoxSizing }) {
async check({
currSizeIterations,
currDirectionIterations,
currOffsetSize,
currContentSize,
currDir,
currBoxSizing,
}) {
const newOffsetSize = offsetSize(targetElm as HTMLElement);
const newContentSize = contentBox(targetElm as HTMLElement);
const newDir = style(targetElm as HTMLElement, 'direction');
const newBoxSizing = style(targetElm as HTMLElement, 'box-sizing');
const offsetSizeChanged = currOffsetSize.w !== newOffsetSize.w || currOffsetSize.h !== newOffsetSize.h;
const contentSizeChanged = currContentSize.w !== newContentSize.w || currContentSize.h !== newContentSize.h;
const offsetSizeChanged =
currOffsetSize.w !== newOffsetSize.w || currOffsetSize.h !== newOffsetSize.h;
const contentSizeChanged =
currContentSize.w !== newContentSize.w || currContentSize.h !== newContentSize.h;
const dirChanged = currDir !== newDir;
const boxSizingChanged = currBoxSizing !== newBoxSizing;
const dimensions = hasDimensions(targetElm as HTMLElement);
@@ -113,20 +131,36 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any)
if (dirChanged) {
await waitForOrFailTest(() => {
const expectedCacheValue = newDir === 'rtl';
should.equal(directionIterations, currDirectionIterations + 1, 'Direction change was detected correctly.');
should.equal(sizeObserver._getCurrentCacheValues()._directionIsRTL._value, expectedCacheValue, 'Direction cache value is correct.');
should.equal(
directionIterations,
currDirectionIterations + 1,
'Direction change was detected correctly.'
);
should.equal(
sizeObserver._getCurrentCacheValues()._directionIsRTL[0],
expectedCacheValue,
'Direction cache value is correct.'
);
});
}
if (boxSizingChanged) {
await waitForOrFailTest(() => {
should.equal(sizeIterations, currSizeIterations + 1, 'BoxSizing change was detected correctly.');
should.equal(
sizeIterations,
currSizeIterations + 1,
'BoxSizing change was detected correctly.'
);
});
}
if (dimensions && (offsetSizeChanged || contentSizeChanged)) {
await waitForOrFailTest(() => {
should.equal(sizeIterations, currSizeIterations + 1, 'Size change was detected correctly.');
should.equal(
sizeIterations,
currSizeIterations + 1,
'Size change was detected correctly.'
);
});
}
@@ -229,7 +263,11 @@ const start = async () => {
await cleanBoxSizingChange();
sizeObserver._destroy();
should.equal(targetElm?.children.length, preInitChildren, 'Destruction removes all generated elements.');
should.equal(
targetElm?.children.length,
preInitChildren,
'Destruction removes all generated elements.'
);
setTestResult(true);
};
@@ -0,0 +1,19 @@
// @ts-ignore
import { playwrightRollup, expectSuccess } from '@/playwright/rollup';
import { test } from '@playwright/test';
playwrightRollup();
test.describe('SizeObserver', () => {
test('with ResizeOserver', async ({ page }) => {
await page.click('#start');
await expectSuccess(page);
});
test('with ResizeOserver polyfill', async ({ page }) => {
await page.click('#roPolyfill');
await page.waitForTimeout(500);
await page.click('#start');
await expectSuccess(page);
});
});
@@ -2,7 +2,11 @@ import 'styles/overlayscrollbars.scss';
import './index.scss';
import './handleEnvironment';
import should from 'should';
import { generateClassChangeSelectCallback, iterateSelect, selectOption } from '@/testing-browser/Select';
import {
generateClassChangeSelectCallback,
iterateSelect,
selectOption,
} from '@/testing-browser/Select';
import { timeout } from '@/testing-browser/timeout';
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
import { offsetSize } from 'support';
@@ -22,9 +26,10 @@ const changesSlot: HTMLButtonElement | null = document.querySelector('#changes')
const preInitChildren = targetElm?.children.length;
const trinsicObserver = createTrinsicObserver(targetElm as HTMLElement, (heightIntrinsicCache) => {
if (heightIntrinsicCache._changed) {
const [currentHeightIntrinsic, currentHeightIntrinsicChanged] = heightIntrinsicCache;
if (currentHeightIntrinsicChanged) {
heightIterations += 1;
heightIntrinsic = heightIntrinsicCache._value;
heightIntrinsic = currentHeightIntrinsic;
}
requestAnimationFrame(() => {
if (changesSlot) {
@@ -65,10 +70,14 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any)
await waitForOrFailTest(() => {
if (trinsicHeightChanged) {
should.equal(heightIterations, currHeightIterations + 1, 'Height intrinsic change has been detected correctly.');
should.equal(
heightIterations,
currHeightIterations + 1,
'Height intrinsic change has been detected correctly.'
);
}
should.equal(
trinsicObserver._getCurrentCacheValues()._heightIntrinsic._value,
trinsicObserver._getCurrentCacheValues()._heightIntrinsic[0],
newHeightIntrinsic,
'Height intrinsic cache value is correct.'
);
@@ -97,7 +106,11 @@ const changeWhileHidden = async () => {
selectOption(displaySelect as HTMLSelectElement, 'displayBlock');
await waitForOrFailTest(() => {
should.equal(heightIntrinsic, false, 'Trinsic sizing changes while hidden from intrinsic to extrinsic.');
should.equal(
heightIntrinsic,
false,
'Trinsic sizing changes while hidden from intrinsic to extrinsic.'
);
});
};
@@ -111,7 +124,11 @@ const changeWhileHidden = async () => {
selectOption(displaySelect as HTMLSelectElement, 'displayBlock');
await waitForOrFailTest(() => {
should.equal(heightIntrinsic, true, 'Trinsic sizing changes while hidden from extrinsic to intrinsic.');
should.equal(
heightIntrinsic,
true,
'Trinsic sizing changes while hidden from extrinsic to intrinsic.'
);
});
};
@@ -133,7 +150,11 @@ const start = async () => {
await changeWhileHidden();
trinsicObserver._destroy();
should.equal(targetElm?.children.length, preInitChildren, 'After destruction all generated elements are removed.');
should.equal(
targetElm?.children.length,
preInitChildren,
'After destruction all generated elements are removed.'
);
setTestResult(true);
};
@@ -0,0 +1,28 @@
// @ts-ignore
import { playwrightRollup, expectSuccess } from '@/playwright/rollup';
import { test } from '@playwright/test';
playwrightRollup();
test.describe('TrinsicObserver', () => {
test('with IntersectionObserver', async ({ page }) => {
await page.click('#start');
await expectSuccess(page);
});
test('with ResizeObserver', async ({ page }) => {
await page.click('#ioPolyfill');
await page.waitForTimeout(500);
await page.click('#start');
await expectSuccess(page);
});
test('with ResizeObserver polyfill', async ({ page }) => {
await page.click('#ioPolyfill');
await page.waitForTimeout(500);
await page.click('#roPolyfill');
await page.waitForTimeout(500);
await page.click('#start');
await expectSuccess(page);
});
});