mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-20 06:10:35 +03:00
improve tests
This commit is contained in:
@@ -231,12 +231,11 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure
|
|||||||
|
|
||||||
const trinsicObserver = (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged);
|
const trinsicObserver = (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged);
|
||||||
const sizeObserver = createSizeObserver(_host, onSizeChanged, { _appear: true, _direction: !_nativeScrollbarStyling });
|
const sizeObserver = createSizeObserver(_host, onSizeChanged, { _appear: true, _direction: !_nativeScrollbarStyling });
|
||||||
const hostMutationObserver = createDOMObserver(_host, onHostMutation, {
|
const hostMutationObserver = createDOMObserver(_host, false, onHostMutation, {
|
||||||
_styleChangingAttributes: attrs,
|
_styleChangingAttributes: attrs,
|
||||||
_attributes: attrs,
|
_attributes: attrs,
|
||||||
});
|
});
|
||||||
const contentMutationObserver = createDOMObserver(_content || _viewport, onContentMutation, {
|
const contentMutationObserver = createDOMObserver(_content || _viewport, true, onContentMutation, {
|
||||||
_observeContent: true,
|
|
||||||
_styleChangingAttributes: attrs,
|
_styleChangingAttributes: attrs,
|
||||||
_attributes: attrs,
|
_attributes: attrs,
|
||||||
_eventContentChange: options!.updating!.elementEvents as [string, string][],
|
_eventContentChange: options!.updating!.elementEvents as [string, string][],
|
||||||
|
|||||||
@@ -17,37 +17,65 @@ import {
|
|||||||
} from 'support';
|
} from 'support';
|
||||||
|
|
||||||
type StringNullUndefined = string | null | undefined;
|
type StringNullUndefined = string | null | undefined;
|
||||||
|
|
||||||
|
type DOMContentObserverCallback = (contentChanged: boolean) => any;
|
||||||
|
|
||||||
|
type DOMTargetObserverCallback = (targetChangedAttrs: string[], targetStyleChanged: boolean) => any;
|
||||||
|
|
||||||
|
interface DOMObserverOptionsBase {
|
||||||
|
_attributes?: string[];
|
||||||
|
_styleChangingAttributes?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DOMContentObserverOptions extends DOMObserverOptionsBase {
|
||||||
|
_eventContentChange?: DOMObserverEventContentChange; // [selector, eventname | function returning eventname]
|
||||||
|
_nestedTargetSelector?: string;
|
||||||
|
_ignoreContentChange?: DOMObserverIgnoreContentChange; // function which will prevent marking certain dom changes as content change if it returns true
|
||||||
|
_ignoreNestedTargetChange?: DOMObserverIgnoreTargetChange; // a function which will prevent marking certain attributes as changed on nested targets if it returns true
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DOMTargetObserverOptions extends DOMObserverOptionsBase {
|
||||||
|
_ignoreTargetChange?: DOMObserverIgnoreTargetChange; // a function which will prevent marking certain attributes as changed if it returns true
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DOMObserverBase {
|
||||||
|
_destroy: () => void;
|
||||||
|
_update: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DOMContentObserver extends DOMObserverBase {
|
||||||
|
_updateEventContentChange: (newEventContentChange?: DOMObserverEventContentChange) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DOMTargetObserver extends DOMObserverBase {}
|
||||||
|
|
||||||
export type DOMObserverEventContentChange =
|
export type DOMObserverEventContentChange =
|
||||||
| Array<[StringNullUndefined, ((elms: Node[]) => string) | StringNullUndefined] | null | undefined>
|
| Array<[StringNullUndefined, ((elms: Node[]) => string) | StringNullUndefined] | null | undefined>
|
||||||
| false
|
| false
|
||||||
| null
|
| null
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
export type DOMObserverIgnoreContentChange = (
|
export type DOMObserverIgnoreContentChange = (
|
||||||
mutation: MutationRecord,
|
mutation: MutationRecord,
|
||||||
isNestedTarget: boolean,
|
isNestedTarget: boolean,
|
||||||
domObserverTarget: HTMLElement,
|
domObserverTarget: HTMLElement,
|
||||||
domObserverOptions: DOMObserverOptions | undefined
|
domObserverOptions: DOMContentObserverOptions | undefined
|
||||||
) => boolean;
|
) => boolean;
|
||||||
export type DOMObserverIgnoreTargetAttrChange = (
|
|
||||||
|
export type DOMObserverIgnoreTargetChange = (
|
||||||
target: Node,
|
target: Node,
|
||||||
attributeName: string,
|
attributeName: string,
|
||||||
oldAttributeValue: string | null,
|
oldAttributeValue: string | null,
|
||||||
newAttributeValue: string | null
|
newAttributeValue: string | null
|
||||||
) => boolean;
|
) => boolean;
|
||||||
export interface DOMObserverOptions {
|
|
||||||
_observeContent?: boolean; // do observe children and trigger content change
|
export type DOMObserverCallback<ContentObserver extends boolean> = ContentObserver extends true
|
||||||
_attributes?: string[]; // observed attributes
|
? DOMContentObserverCallback
|
||||||
_styleChangingAttributes?: string[]; // list of attributes that trigger a contentChange or a targetStyleChange if changed
|
: DOMTargetObserverCallback;
|
||||||
_eventContentChange?: DOMObserverEventContentChange; // [selector, eventname]
|
|
||||||
_nestedTargetSelector?: string;
|
export type DOMObserverOptions<ContentObserver extends boolean> = ContentObserver extends true ? DOMContentObserverOptions : DOMTargetObserverOptions;
|
||||||
_ignoreTargetAttrChange?: DOMObserverIgnoreTargetAttrChange;
|
|
||||||
_ignoreContentChange?: DOMObserverIgnoreContentChange;
|
export type DOMObserver<ContentObserver extends boolean> = ContentObserver extends true ? DOMContentObserver : DOMTargetObserver;
|
||||||
}
|
|
||||||
export interface DOMObserver {
|
|
||||||
_destroy: () => void;
|
|
||||||
_updateEventContentChange: (newEventContentChange?: DOMObserverEventContentChange) => void;
|
|
||||||
_update: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
// const styleChangingAttributes = ['id', 'class', 'style', 'open'];
|
// const styleChangingAttributes = ['id', 'class', 'style', 'open'];
|
||||||
// const mutationObserverAttrsTextarea = ['wrap', 'cols', 'rows'];
|
// const mutationObserverAttrsTextarea = ['wrap', 'cols', 'rows'];
|
||||||
@@ -63,25 +91,26 @@ const createEventContentChange = (target: Element, eventContentChange: DOMObserv
|
|||||||
let map: Map<Node, string> | undefined;
|
let map: Map<Node, string> | undefined;
|
||||||
let eventContentChangeRef: DOMObserverEventContentChange;
|
let eventContentChangeRef: DOMObserverEventContentChange;
|
||||||
const addEvent = (elm: Node, eventName: string) => {
|
const addEvent = (elm: Node, eventName: string) => {
|
||||||
const entry = map!.get(elm);
|
if (map) {
|
||||||
const newEntry = isUndefined(entry);
|
const entry = map.get(elm);
|
||||||
const registerEvent = () => {
|
const newEntry = isUndefined(entry);
|
||||||
map!.set(elm, eventName);
|
const changedExistingEntry = !newEntry && eventName !== entry;
|
||||||
on(elm, eventName, callback);
|
const register = newEntry || changedExistingEntry;
|
||||||
};
|
|
||||||
|
|
||||||
if (!newEntry && eventName !== entry) {
|
if (changedExistingEntry) {
|
||||||
off(elm, entry!, callback);
|
off(elm, entry!, callback);
|
||||||
registerEvent();
|
}
|
||||||
} else if (newEntry) {
|
if (register) {
|
||||||
registerEvent();
|
map.set(elm, eventName);
|
||||||
|
on(elm, eventName, callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const _destroy = () => {
|
const _destroy = () => {
|
||||||
map!.forEach((eventName: string, elm: Node) => {
|
if (map) {
|
||||||
off(elm, eventName, callback);
|
map.forEach((eventName: string, elm: Node) => off(elm, eventName, callback));
|
||||||
});
|
map.clear();
|
||||||
map!.clear();
|
}
|
||||||
};
|
};
|
||||||
const _updateElements = (getElements?: (selector: string) => Node[]) => {
|
const _updateElements = (getElements?: (selector: string) => Node[]) => {
|
||||||
if (eventContentChangeRef) {
|
if (eventContentChangeRef) {
|
||||||
@@ -127,37 +156,39 @@ const createEventContentChange = (target: Element, eventContentChange: DOMObserv
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a DOM observer which observes DOM changes to either the target element or its children. (not only direct children but also nested ones)
|
* Creates a DOM observer which observes DOM changes to either the target element or its children.
|
||||||
* @param target The element which shall be observed.
|
* @param target The element which shall be observed.
|
||||||
|
* @param isContentObserver Whether this observer is just observing the target or just the targets children. (not only direct children but also nested ones)
|
||||||
* @param callback The callback which gets called if a change was detected.
|
* @param callback The callback which gets called if a change was detected.
|
||||||
* @param options The options for DOM change detection.
|
* @param options The options for DOM change detection.
|
||||||
* @returns A object which represents the instance of the DOM observer.
|
* @returns A object which represents the instance of the DOM observer.
|
||||||
*/
|
*/
|
||||||
export const createDOMObserver = (
|
export const createDOMObserver = <ContentObserver extends boolean>(
|
||||||
target: HTMLElement,
|
target: HTMLElement,
|
||||||
callback: (targetChangedAttrs: string[], targetStyleChanged: boolean, contentChanged: boolean) => any,
|
isContentObserver: ContentObserver,
|
||||||
options?: DOMObserverOptions
|
callback: DOMObserverCallback<ContentObserver>,
|
||||||
): DOMObserver => {
|
options?: DOMObserverOptions<ContentObserver>
|
||||||
|
): DOMObserver<ContentObserver> => {
|
||||||
let isConnected = false;
|
let isConnected = false;
|
||||||
const {
|
const {
|
||||||
_observeContent,
|
|
||||||
_attributes,
|
_attributes,
|
||||||
_styleChangingAttributes,
|
_styleChangingAttributes,
|
||||||
_eventContentChange,
|
_eventContentChange,
|
||||||
_nestedTargetSelector,
|
_nestedTargetSelector,
|
||||||
_ignoreTargetAttrChange: _ignoreTargetChange,
|
_ignoreTargetChange,
|
||||||
|
_ignoreNestedTargetChange,
|
||||||
_ignoreContentChange,
|
_ignoreContentChange,
|
||||||
} = options || {};
|
} = (options as DOMContentObserverOptions & DOMTargetObserverOptions) || {};
|
||||||
const {
|
const {
|
||||||
_destroy: destroyEventContentChange,
|
_destroy: destroyEventContentChange,
|
||||||
_updateElements: updateEventContentChangeElements,
|
_updateElements: updateEventContentChangeElements,
|
||||||
_updateEventContentChange: updateEventContentChange,
|
_updateEventContentChange: updateEventContentChange,
|
||||||
} = createEventContentChange(
|
} = createEventContentChange(
|
||||||
target,
|
target,
|
||||||
_observeContent && _eventContentChange,
|
isContentObserver && _eventContentChange,
|
||||||
debounce(() => {
|
debounce(() => {
|
||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
callback([], false, true);
|
(callback as DOMContentObserverCallback)(true);
|
||||||
}
|
}
|
||||||
}, 84)
|
}, 84)
|
||||||
);
|
);
|
||||||
@@ -167,7 +198,7 @@ export const createDOMObserver = (
|
|||||||
const finalStyleChangingAttributes = _styleChangingAttributes || [];
|
const finalStyleChangingAttributes = _styleChangingAttributes || [];
|
||||||
const observedAttributes = finalAttributes.concat(finalStyleChangingAttributes);
|
const observedAttributes = finalAttributes.concat(finalStyleChangingAttributes);
|
||||||
const observerCallback = (mutations: MutationRecord[]) => {
|
const observerCallback = (mutations: MutationRecord[]) => {
|
||||||
const ignoreTargetChange = _ignoreTargetChange || noop;
|
const ignoreTargetChange = (isContentObserver ? _ignoreNestedTargetChange : _ignoreTargetChange) || noop;
|
||||||
const ignoreContentChange = _ignoreContentChange || noop;
|
const ignoreContentChange = _ignoreContentChange || noop;
|
||||||
const targetChangedAttrs: string[] = [];
|
const targetChangedAttrs: string[] = [];
|
||||||
const totalAddedNodes: Node[] = [];
|
const totalAddedNodes: Node[] = [];
|
||||||
@@ -181,19 +212,12 @@ export const createDOMObserver = (
|
|||||||
const targetIsMutationTarget = target === mutationTarget;
|
const targetIsMutationTarget = target === mutationTarget;
|
||||||
const attributeValue = isAttributesType && isString(attributeName) ? attr(mutationTarget as HTMLElement, attributeName!) : 0;
|
const attributeValue = isAttributesType && isString(attributeName) ? attr(mutationTarget as HTMLElement, attributeName!) : 0;
|
||||||
const attributeChanged = attributeValue !== 0 && oldValue !== attributeValue;
|
const attributeChanged = attributeValue !== 0 && oldValue !== attributeValue;
|
||||||
const targetAttrChanged =
|
|
||||||
attributeChanged &&
|
|
||||||
targetIsMutationTarget &&
|
|
||||||
!_observeContent &&
|
|
||||||
!ignoreTargetChange(mutationTarget, attributeName!, oldValue, attributeValue as string | null);
|
|
||||||
const styleChangingAttrChanged = indexOf(finalStyleChangingAttributes, attributeName) > -1 && attributeChanged;
|
const styleChangingAttrChanged = indexOf(finalStyleChangingAttributes, attributeName) > -1 && attributeChanged;
|
||||||
|
|
||||||
if (targetAttrChanged) {
|
// if is content observer and something changed in children
|
||||||
push(targetChangedAttrs, attributeName!);
|
if (isContentObserver && !targetIsMutationTarget) {
|
||||||
}
|
|
||||||
if (_observeContent) {
|
|
||||||
const notOnlyAttrChanged = !isAttributesType;
|
const notOnlyAttrChanged = !isAttributesType;
|
||||||
const contentAttrChanged = isAttributesType && styleChangingAttrChanged && !targetIsMutationTarget;
|
const contentAttrChanged = isAttributesType && styleChangingAttrChanged;
|
||||||
const isNestedTarget = contentAttrChanged && _nestedTargetSelector && is(mutationTarget, _nestedTargetSelector);
|
const isNestedTarget = contentAttrChanged && _nestedTargetSelector && is(mutationTarget, _nestedTargetSelector);
|
||||||
const baseAssertion = isNestedTarget
|
const baseAssertion = isNestedTarget
|
||||||
? !ignoreTargetChange(mutationTarget, attributeName!, oldValue, attributeValue as string | null)
|
? !ignoreTargetChange(mutationTarget, attributeName!, oldValue, attributeValue as string | null)
|
||||||
@@ -205,7 +229,11 @@ export const createDOMObserver = (
|
|||||||
contentChanged = contentChanged || contentFinalChanged;
|
contentChanged = contentChanged || contentFinalChanged;
|
||||||
childListChanged = childListChanged || isChildListType;
|
childListChanged = childListChanged || isChildListType;
|
||||||
}
|
}
|
||||||
targetStyleChanged = targetStyleChanged || (targetAttrChanged && styleChangingAttrChanged);
|
// else if is target observer and target attr changed
|
||||||
|
else if (attributeChanged && !ignoreTargetChange(mutationTarget, attributeName!, oldValue, attributeValue as string | null)) {
|
||||||
|
push(targetChangedAttrs, attributeName!);
|
||||||
|
targetStyleChanged = targetStyleChanged || styleChangingAttrChanged;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (childListChanged && !isEmptyArray(totalAddedNodes)) {
|
if (childListChanged && !isEmptyArray(totalAddedNodes)) {
|
||||||
@@ -217,8 +245,11 @@ export const createDOMObserver = (
|
|||||||
}, [])
|
}, [])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!isEmptyArray(targetChangedAttrs) || targetStyleChanged || contentChanged) {
|
|
||||||
callback(targetChangedAttrs, targetStyleChanged, contentChanged);
|
if (isContentObserver && contentChanged) {
|
||||||
|
(callback as DOMContentObserverCallback)(contentChanged);
|
||||||
|
} else if (!isEmptyArray(targetChangedAttrs) || targetStyleChanged) {
|
||||||
|
(callback as DOMTargetObserverCallback)(targetChangedAttrs, targetStyleChanged);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const mutationObserver: MutationObserver = new MutationObserverConstructor!(observerCallback);
|
const mutationObserver: MutationObserver = new MutationObserverConstructor!(observerCallback);
|
||||||
@@ -228,9 +259,9 @@ export const createDOMObserver = (
|
|||||||
attributes: true,
|
attributes: true,
|
||||||
attributeOldValue: true,
|
attributeOldValue: true,
|
||||||
attributeFilter: observedAttributes,
|
attributeFilter: observedAttributes,
|
||||||
subtree: _observeContent,
|
subtree: isContentObserver,
|
||||||
childList: _observeContent,
|
childList: isContentObserver,
|
||||||
characterData: _observeContent,
|
characterData: isContentObserver,
|
||||||
});
|
});
|
||||||
isConnected = true;
|
isConnected = true;
|
||||||
|
|
||||||
@@ -243,12 +274,12 @@ export const createDOMObserver = (
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
_updateEventContentChange: (newEventContentChange?: DOMObserverEventContentChange) => {
|
_updateEventContentChange: (newEventContentChange?: DOMObserverEventContentChange) => {
|
||||||
updateEventContentChange(isConnected && _observeContent && newEventContentChange);
|
updateEventContentChange(isConnected && isContentObserver && newEventContentChange);
|
||||||
},
|
},
|
||||||
_update: () => {
|
_update: () => {
|
||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
observerCallback(mutationObserver.takeRecords());
|
observerCallback(mutationObserver.takeRecords());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
} as DOMObserver<ContentObserver>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ import {
|
|||||||
|
|
||||||
import { createDOMObserver } from 'observers/domObserver';
|
import { createDOMObserver } from 'observers/domObserver';
|
||||||
|
|
||||||
interface DOMObserverResult {
|
type DOMContentObserverResult = boolean;
|
||||||
|
type DOMTargetObserverResult = {
|
||||||
changedTargetAttrs: string[];
|
changedTargetAttrs: string[];
|
||||||
styleChanged: boolean;
|
styleChanged: boolean;
|
||||||
contentChanged: boolean;
|
};
|
||||||
}
|
|
||||||
interface SeparateChangeThrough {
|
interface SeparateChangeThrough {
|
||||||
added?: DOMObserverResult[];
|
added?: DOMContentObserverResult[];
|
||||||
removed?: DOMObserverResult[];
|
removed?: DOMContentObserverResult[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetChangesCountSlot: HTMLElement | null = document.querySelector('#targetChanges');
|
const targetChangesCountSlot: HTMLElement | null = document.querySelector('#targetChanges');
|
||||||
@@ -68,23 +68,31 @@ const hostSelector = '.host';
|
|||||||
const ignorePrefix = 'ignore';
|
const ignorePrefix = 'ignore';
|
||||||
const attrs = ['id', 'class', 'style', 'open'];
|
const attrs = ['id', 'class', 'style', 'open'];
|
||||||
const contentChangeArr: Array<[string, string | ((elms: Node[]) => string)]> = [['img', 'load']];
|
const contentChangeArr: Array<[string, string | ((elms: Node[]) => string)]> = [['img', 'load']];
|
||||||
const targetElmObservations: DOMObserverResult[] = [];
|
const domTargetObserverObservations: DOMTargetObserverResult[] = [];
|
||||||
const targetElmContentElmObservations: DOMObserverResult[] = [];
|
const domContentObserverObservations: DOMContentObserverResult[] = [];
|
||||||
|
|
||||||
createDOMObserver(
|
const targetDomObserver = createDOMObserver(
|
||||||
document.querySelector('#target') as HTMLElement,
|
document.querySelector('#target')!,
|
||||||
(changedTargetAttrs: string[], styleChanged: boolean, contentChanged: boolean) => {
|
false,
|
||||||
targetElmObservations.push({ changedTargetAttrs, styleChanged, contentChanged });
|
(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.');
|
||||||
|
|
||||||
|
if (styleChanged && changedTargetAttrs.length === 0) {
|
||||||
|
should.ok(false, 'Style changing properties must always be inside the changedTargetAttrs array.');
|
||||||
|
}
|
||||||
|
|
||||||
|
domTargetObserverObservations.push({ changedTargetAttrs, styleChanged });
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (targetChangesCountSlot) {
|
if (targetChangesCountSlot) {
|
||||||
targetChangesCountSlot.textContent = `${targetElmObservations.length}`;
|
targetChangesCountSlot.textContent = `${domTargetObserverObservations.length}`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
_styleChangingAttributes: attrs,
|
_styleChangingAttributes: attrs,
|
||||||
_attributes: attrs.concat(['data-target']),
|
_attributes: attrs.concat(['data-target']),
|
||||||
_ignoreTargetAttrChange: (target, attrName, oldValue, newValue) => {
|
_ignoreTargetChange: (target, attrName, oldValue, newValue) => {
|
||||||
if (attrName === 'class' && oldValue && newValue) {
|
if (attrName === 'class' && oldValue && newValue) {
|
||||||
const diff = diffClass(oldValue, newValue);
|
const diff = diffClass(oldValue, newValue);
|
||||||
const ignore = diff.length === 1 && diff[0].startsWith(ignorePrefix);
|
const ignore = diff.length === 1 && diff[0].startsWith(ignorePrefix);
|
||||||
@@ -92,20 +100,35 @@ createDOMObserver(
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
_ignoreContentChange: () => {
|
||||||
|
// if param: isContentObserver = false, this function should never be called.
|
||||||
|
should.ok(false, 'A target dom observer must not call the _ignoreContentChange method.');
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
_ignoreNestedTargetChange: () => {
|
||||||
|
// if param: isContentObserver = false, this function should never be called.
|
||||||
|
should.ok(false, 'A target dom observer must not call the _ignoreNestedTargetChange method.');
|
||||||
|
return true;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const { _updateEventContentChange } = createDOMObserver(
|
|
||||||
document.querySelector('#target .content') as HTMLElement,
|
const contentDomObserver = createDOMObserver(
|
||||||
(changedTargetAttrs: string[], styleChanged: boolean, contentChanged: boolean) => {
|
document.querySelector('#target .content')!,
|
||||||
targetElmContentElmObservations.push({ changedTargetAttrs, styleChanged, contentChanged });
|
true,
|
||||||
|
(contentChanged: boolean) => {
|
||||||
|
should.equal(typeof contentChanged, 'boolean', 'The contentChanged parameter in a content dom observer must be a boolean.');
|
||||||
|
|
||||||
|
domContentObserverObservations.push(contentChanged);
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (contentChangesCountSlot) {
|
if (contentChangesCountSlot) {
|
||||||
contentChangesCountSlot.textContent = `${targetElmContentElmObservations.length}`;
|
contentChangesCountSlot.textContent = `${domContentObserverObservations.length}`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
_observeContent: true,
|
|
||||||
_styleChangingAttributes: attrs,
|
_styleChangingAttributes: attrs,
|
||||||
_attributes: attrs,
|
_attributes: attrs,
|
||||||
_eventContentChange: contentChangeArr,
|
_eventContentChange: contentChangeArr,
|
||||||
@@ -114,7 +137,7 @@ const { _updateEventContentChange } = createDOMObserver(
|
|||||||
const { target, attributeName } = mutation;
|
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;
|
||||||
},
|
},
|
||||||
_ignoreTargetAttrChange: (target, attrName, oldValue, newValue) => {
|
_ignoreNestedTargetChange: (target, attrName, oldValue, newValue) => {
|
||||||
if (attrName === 'class' && oldValue && newValue) {
|
if (attrName === 'class' && oldValue && newValue) {
|
||||||
const diff = diffClass(oldValue, newValue);
|
const diff = diffClass(oldValue, newValue);
|
||||||
const ignore = diff.length === 1 && diff[0].startsWith(ignorePrefix);
|
const ignore = diff.length === 1 && diff[0].startsWith(ignorePrefix);
|
||||||
@@ -122,15 +145,23 @@ const { _updateEventContentChange } = createDOMObserver(
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
_ignoreTargetChange: () => {
|
||||||
|
// if param: isContentObserver = true, this function should never be called.
|
||||||
|
should.ok(false, 'A content dom observer must not call the _ignoreTargetChange method.');
|
||||||
|
return true;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const getTotalObservations = () => targetElmObservations.length + targetElmContentElmObservations.length;
|
const getTotalObservations = () => domTargetObserverObservations.length + domContentObserverObservations.length;
|
||||||
const getLast = <T>(arr: T[], indexFromLast = 0): T => arr[arr.length - 1 - indexFromLast] || ({} as T);
|
const getLast = <T>(arr: T[], indexFromLast = 0): T => arr[arr.length - 1 - indexFromLast] || ({} as T);
|
||||||
const changedThrough = (observationLists?: Array<DOMObserverResult[]> | DOMObserverResult[]) => {
|
const changedThrough = <ChangeThrough extends DOMContentObserverResult | DOMTargetObserverResult>(
|
||||||
|
observationLists?: Array<ChangeThrough[]> | ChangeThrough[]
|
||||||
|
) => {
|
||||||
interface Stat {
|
interface Stat {
|
||||||
total: number;
|
total: number;
|
||||||
lists: Array<[DOMObserverResult[], number]>;
|
lists: Array<[ChangeThrough[], number]>;
|
||||||
}
|
}
|
||||||
const noObservationLists = observationLists === undefined;
|
const noObservationLists = observationLists === undefined;
|
||||||
let before: Stat;
|
let before: Stat;
|
||||||
@@ -139,13 +170,13 @@ const changedThrough = (observationLists?: Array<DOMObserverResult[]> | DOMObser
|
|||||||
observationLists = [];
|
observationLists = [];
|
||||||
}
|
}
|
||||||
if (isArray(observationLists) && !isArray(observationLists[0])) {
|
if (isArray(observationLists) && !isArray(observationLists[0])) {
|
||||||
observationLists = [observationLists] as Array<DOMObserverResult[]>;
|
observationLists = [observationLists] as Array<ChangeThrough[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStats = (): Stat => {
|
const getStats = (): Stat => {
|
||||||
return {
|
return {
|
||||||
total: getTotalObservations(),
|
total: getTotalObservations(),
|
||||||
lists: (observationLists as Array<DOMObserverResult[]>).map((list) => [list, list.length]),
|
lists: (observationLists as Array<ChangeThrough[]>).map((list) => [list, list.length]),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -156,7 +187,7 @@ const changedThrough = (observationLists?: Array<DOMObserverResult[]> | DOMObser
|
|||||||
after: () => {
|
after: () => {
|
||||||
after = getStats();
|
after = getStats();
|
||||||
},
|
},
|
||||||
compare: (comparisonTableOrNumber: number | Map<DOMObserverResult[], number> = 0) => {
|
compare: (comparisonTableOrNumber: number | Map<ChangeThrough[], number> = 0) => {
|
||||||
let totalDiff = 0;
|
let totalDiff = 0;
|
||||||
if (isNumber(comparisonTableOrNumber) || noObservationLists) {
|
if (isNumber(comparisonTableOrNumber) || noObservationLists) {
|
||||||
before.lists.forEach((_, index) => {
|
before.lists.forEach((_, index) => {
|
||||||
@@ -164,7 +195,11 @@ const changedThrough = (observationLists?: Array<DOMObserverResult[]> | DOMObser
|
|||||||
const [, afterCount] = after.lists[index];
|
const [, afterCount] = after.lists[index];
|
||||||
|
|
||||||
totalDiff += afterCount - beforeCount;
|
totalDiff += afterCount - beforeCount;
|
||||||
should(afterCount).equal(beforeCount + (noObservationLists ? 0 : (comparisonTableOrNumber as number)));
|
should.equal(
|
||||||
|
afterCount,
|
||||||
|
beforeCount + (noObservationLists ? 0 : (comparisonTableOrNumber as number)),
|
||||||
|
'Before and after changes for a certain observer are correct. (number)'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
before.lists.forEach((_, index) => {
|
before.lists.forEach((_, index) => {
|
||||||
@@ -172,10 +207,14 @@ const changedThrough = (observationLists?: Array<DOMObserverResult[]> | DOMObser
|
|||||||
const [, afterCount] = after.lists[index];
|
const [, afterCount] = after.lists[index];
|
||||||
|
|
||||||
totalDiff += afterCount - beforeCount;
|
totalDiff += afterCount - beforeCount;
|
||||||
should(afterCount).equal(beforeCount + (comparisonTableOrNumber.get(list) || 0));
|
should.equal(
|
||||||
|
afterCount,
|
||||||
|
beforeCount + (comparisonTableOrNumber.get(list) || 0),
|
||||||
|
'Before and after changes for a certain observer are correct. (Map)'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
should(after.total).equal(before.total + totalDiff);
|
should.equal(after.total, before.total + totalDiff, 'Total changes are correct.');
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -188,10 +227,10 @@ const attrChangeListener = (attrChangeTarget: HTMLElement | null) =>
|
|||||||
isClass && target.classList.add('something');
|
isClass && target.classList.add('something');
|
||||||
!isClass && target.setAttribute(selectedValue, 'something');
|
!isClass && target.setAttribute(selectedValue, 'something');
|
||||||
});
|
});
|
||||||
const iterateAttrChange = async (
|
const iterateAttrChange = async <ChangeThrough extends DOMContentObserverResult | DOMTargetObserverResult>(
|
||||||
select: HTMLSelectElement | null,
|
select: HTMLSelectElement | null,
|
||||||
changeThrough?: DOMObserverResult[],
|
changeThrough?: ChangeThrough[],
|
||||||
checkChange?: (observation: DOMObserverResult, selected: string) => any
|
checkChange?: (observation: ChangeThrough, selected: string) => any
|
||||||
) => {
|
) => {
|
||||||
const { before, after, compare } = changedThrough(changeThrough);
|
const { before, after, compare } = changedThrough(changeThrough);
|
||||||
|
|
||||||
@@ -214,10 +253,10 @@ const iterateAttrChange = async (
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const addRemoveElementsTest = async (slot: Element | null, changeThrough?: DOMObserverResult[] | SeparateChangeThrough) => {
|
const addRemoveElementsTest = async (slot: Element | null, changeThrough?: DOMContentObserverResult[] | SeparateChangeThrough) => {
|
||||||
if (slot) {
|
if (slot) {
|
||||||
let addChangeThrough: DOMObserverResult[] | undefined = changeThrough as DOMObserverResult[] | undefined;
|
let addChangeThrough: DOMContentObserverResult[] | undefined = changeThrough as DOMContentObserverResult[] | undefined;
|
||||||
let removeChangeThrough: DOMObserverResult[] | undefined = changeThrough as DOMObserverResult[] | undefined;
|
let removeChangeThrough: DOMContentObserverResult[] | undefined = changeThrough as DOMContentObserverResult[] | undefined;
|
||||||
if (changeThrough && !isArray(changeThrough)) {
|
if (changeThrough && !isArray(changeThrough)) {
|
||||||
addChangeThrough = (changeThrough as SeparateChangeThrough).added;
|
addChangeThrough = (changeThrough as SeparateChangeThrough).added;
|
||||||
removeChangeThrough = (changeThrough as SeparateChangeThrough).removed;
|
removeChangeThrough = (changeThrough as SeparateChangeThrough).removed;
|
||||||
@@ -236,11 +275,9 @@ const addRemoveElementsTest = async (slot: Element | null, changeThrough?: DOMOb
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (addChangeThrough) {
|
if (addChangeThrough) {
|
||||||
const { contentChanged, styleChanged, changedTargetAttrs } = getLast(addChangeThrough);
|
const contentChanged = getLast(addChangeThrough);
|
||||||
await waitForOrFailTest(() => {
|
await waitForOrFailTest(() => {
|
||||||
should(contentChanged).equal(true);
|
should.equal(contentChanged, true, 'Adding an content element must result in a content change.');
|
||||||
should(styleChanged).equal(false);
|
|
||||||
should(changedTargetAttrs.length).equal(0);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -259,10 +296,8 @@ const addRemoveElementsTest = async (slot: Element | null, changeThrough?: DOMOb
|
|||||||
compare(1);
|
compare(1);
|
||||||
|
|
||||||
if (removeChangeThrough) {
|
if (removeChangeThrough) {
|
||||||
const { changedTargetAttrs, styleChanged, contentChanged } = getLast(removeChangeThrough);
|
const contentChanged = getLast(removeChangeThrough);
|
||||||
should(changedTargetAttrs.length).equal(0);
|
should.equal(contentChanged, true, 'Removing an content element must result in a content change.');
|
||||||
should(styleChanged).equal(false);
|
|
||||||
should(contentChanged).equal(true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -277,7 +312,7 @@ const addRemoveElementsTest = async (slot: Element | null, changeThrough?: DOMOb
|
|||||||
await removeElm();
|
await removeElm();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const triggerSummaryElemet = async (summaryElm: HTMLElement | null, changeThrough?: DOMObserverResult[]) => {
|
const triggerSummaryElemet = async (summaryElm: HTMLElement | null, changeThrough?: DOMContentObserverResult[]) => {
|
||||||
// onyl do if summary is working (IE. exception)
|
// onyl do if summary is working (IE. exception)
|
||||||
if (summaryElm && (summaryElm.nextElementSibling as HTMLElement)?.offsetHeight === 0) {
|
if (summaryElm && (summaryElm.nextElementSibling as HTMLElement)?.offsetHeight === 0) {
|
||||||
const click = async () => {
|
const click = async () => {
|
||||||
@@ -302,17 +337,17 @@ const addRemoveTargetElmsFn = async () => {
|
|||||||
await addRemoveElementsTest(targetElmsSlot);
|
await addRemoveElementsTest(targetElmsSlot);
|
||||||
};
|
};
|
||||||
const addRemoveTargetContentElmsFn = async () => {
|
const addRemoveTargetContentElmsFn = async () => {
|
||||||
await addRemoveElementsTest(targetContentElmsSlot, targetElmContentElmObservations);
|
await addRemoveElementsTest(targetContentElmsSlot, domContentObserverObservations);
|
||||||
};
|
};
|
||||||
const addRemoveTargetContentBetweenElmsFn = async () => {
|
const addRemoveTargetContentBetweenElmsFn = async () => {
|
||||||
await addRemoveElementsTest(targetContentBetweenElmsSlot, targetElmContentElmObservations);
|
await addRemoveElementsTest(targetContentBetweenElmsSlot, domContentObserverObservations);
|
||||||
};
|
};
|
||||||
const addRemoveImgElmsFn = async () => {
|
const addRemoveImgElmsFn = async () => {
|
||||||
const add = async () => {
|
const add = async () => {
|
||||||
const img = new Image(1, 1);
|
const img = new Image(1, 1);
|
||||||
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
||||||
|
|
||||||
const { before, after, compare } = changedThrough(targetElmContentElmObservations);
|
const { before, after, compare } = changedThrough(domContentObserverObservations);
|
||||||
const imgHolder = createDiv('img');
|
const imgHolder = createDiv('img');
|
||||||
appendChildren(imgHolder, img);
|
appendChildren(imgHolder, img);
|
||||||
|
|
||||||
@@ -323,15 +358,11 @@ const addRemoveImgElmsFn = async () => {
|
|||||||
after();
|
after();
|
||||||
compare(2);
|
compare(2);
|
||||||
|
|
||||||
const mutationObserverObservation = getLast(targetElmContentElmObservations, 1);
|
const previousContentChanged = getLast(domContentObserverObservations, 1);
|
||||||
should(mutationObserverObservation.contentChanged).equal(true);
|
should.equal(previousContentChanged, true, 'Adding an content image must result in a content change.');
|
||||||
should(mutationObserverObservation.styleChanged).equal(false);
|
|
||||||
should(mutationObserverObservation.changedTargetAttrs.length).equal(0);
|
|
||||||
|
|
||||||
const eventObservation = getLast(targetElmContentElmObservations);
|
const lastContentChanged = getLast(domContentObserverObservations);
|
||||||
should(eventObservation.contentChanged).equal(true);
|
should.equal(lastContentChanged, true, 'The images load event must result in a content change.');
|
||||||
should(eventObservation.styleChanged).equal(false);
|
|
||||||
should(eventObservation.changedTargetAttrs.length).equal(0);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -341,7 +372,7 @@ const addRemoveImgElmsFn = async () => {
|
|||||||
|
|
||||||
// test event content change debounce
|
// test event content change debounce
|
||||||
const addMultiple = async () => {
|
const addMultiple = async () => {
|
||||||
const { before, after, compare } = changedThrough(targetElmContentElmObservations);
|
const { before, after, compare } = changedThrough(domContentObserverObservations);
|
||||||
const addMultipleItem = () => {
|
const addMultipleItem = () => {
|
||||||
const img = new Image(1, 1);
|
const img = new Image(1, 1);
|
||||||
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
||||||
@@ -362,15 +393,11 @@ const addRemoveImgElmsFn = async () => {
|
|||||||
after();
|
after();
|
||||||
compare(2);
|
compare(2);
|
||||||
|
|
||||||
const mutationObserverObservation = getLast(targetElmContentElmObservations, 1);
|
const previousContentChanged = getLast(domContentObserverObservations, 1);
|
||||||
should(mutationObserverObservation.contentChanged).equal(true);
|
should.equal(previousContentChanged, true, 'Adding mutliple content images must result in a single content change. (debounced)');
|
||||||
should(mutationObserverObservation.styleChanged).equal(false);
|
|
||||||
should(mutationObserverObservation.changedTargetAttrs.length).equal(0);
|
|
||||||
|
|
||||||
const eventObservation = getLast(targetElmContentElmObservations);
|
const lastContentChanged = getLast(domContentObserverObservations);
|
||||||
should(eventObservation.contentChanged).equal(true);
|
should.equal(lastContentChanged, true, 'Multiple images load events must result in a single cintent change. (debounced)');
|
||||||
should(eventObservation.styleChanged).equal(false);
|
|
||||||
should(eventObservation.changedTargetAttrs.length).equal(0);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -384,7 +411,7 @@ const addRemoveTransitionElmsFn = async () => {
|
|||||||
const startTransition = async (elm: Element, expectTransitionEndContentChange: boolean) => {
|
const startTransition = async (elm: Element, expectTransitionEndContentChange: boolean) => {
|
||||||
await timeout(50); // time for css to apply class a bit later to trigger transition
|
await timeout(50); // time for css to apply class a bit later to trigger transition
|
||||||
|
|
||||||
const { before: beforeTransition, after: afterTransition, compare: compareTransition } = changedThrough(targetElmContentElmObservations);
|
const { before: beforeTransition, after: afterTransition, compare: compareTransition } = changedThrough(domContentObserverObservations);
|
||||||
beforeTransition();
|
beforeTransition();
|
||||||
removeClass(elm, 'resetTransition'); // IE...
|
removeClass(elm, 'resetTransition'); // IE...
|
||||||
addClass(elm, 'active');
|
addClass(elm, 'active');
|
||||||
@@ -398,10 +425,8 @@ const addRemoveTransitionElmsFn = async () => {
|
|||||||
afterTransition();
|
afterTransition();
|
||||||
compareTransition(expectTransitionEndContentChange ? 2 : 1); // 2 because 1: added class mutation and 2: transition end event
|
compareTransition(expectTransitionEndContentChange ? 2 : 1); // 2 because 1: added class mutation and 2: transition end event
|
||||||
|
|
||||||
const eventObservation = getLast(targetElmContentElmObservations);
|
const contentChanged = getLast(domContentObserverObservations);
|
||||||
should(eventObservation.contentChanged).equal(true);
|
should.equal(contentChanged, true, 'The transitionend event must trigger a event content change.');
|
||||||
should(eventObservation.styleChanged).equal(false);
|
|
||||||
should(eventObservation.changedTargetAttrs.length).equal(0);
|
|
||||||
resolve(1);
|
resolve(1);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -414,7 +439,7 @@ const addRemoveTransitionElmsFn = async () => {
|
|||||||
};
|
};
|
||||||
const add = async (expectTransitionEndContentChange: boolean) => {
|
const add = async (expectTransitionEndContentChange: boolean) => {
|
||||||
const elm = createDiv(`transition ${expectTransitionEndContentChange ? 'highlight' : ''}`);
|
const elm = createDiv(`transition ${expectTransitionEndContentChange ? 'highlight' : ''}`);
|
||||||
const { before, after, compare } = changedThrough(targetElmContentElmObservations);
|
const { before, after, compare } = changedThrough(domContentObserverObservations);
|
||||||
|
|
||||||
before();
|
before();
|
||||||
|
|
||||||
@@ -424,14 +449,12 @@ const addRemoveTransitionElmsFn = async () => {
|
|||||||
after();
|
after();
|
||||||
compare(1);
|
compare(1);
|
||||||
|
|
||||||
const eventObservation = getLast(targetElmContentElmObservations);
|
const contentChanged = getLast(domContentObserverObservations);
|
||||||
should(eventObservation.contentChanged).equal(true);
|
should.equal(contentChanged, true, 'Adding an content element (transition) must result in a content change.');
|
||||||
should(eventObservation.styleChanged).equal(false);
|
|
||||||
should(eventObservation.changedTargetAttrs.length).equal(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await startTransition(elm, expectTransitionEndContentChange && true);
|
await startTransition(elm, expectTransitionEndContentChange && true);
|
||||||
_updateEventContentChange(contentChangeArr);
|
contentDomObserver._updateEventContentChange(contentChangeArr);
|
||||||
await startTransition(elm, expectTransitionEndContentChange && false);
|
await startTransition(elm, expectTransitionEndContentChange && false);
|
||||||
|
|
||||||
removeElements(elm);
|
removeElements(elm);
|
||||||
@@ -441,13 +464,13 @@ const addRemoveTransitionElmsFn = async () => {
|
|||||||
|
|
||||||
await add(false);
|
await add(false);
|
||||||
|
|
||||||
_updateEventContentChange(
|
contentDomObserver._updateEventContentChange(
|
||||||
contentChangeArr.concat([
|
contentChangeArr.concat([
|
||||||
[
|
[
|
||||||
'.transition',
|
'.transition',
|
||||||
(elms) => {
|
(elms) => {
|
||||||
elms.forEach((elm) => {
|
elms.forEach((elm) => {
|
||||||
should(hasClass(elm as Element, 'transition')).equal(true);
|
should.equal(hasClass(elm as Element, 'transition'), true, 'Every checked element must match the correpsonding selector.'); // in this case "".transition"
|
||||||
});
|
});
|
||||||
return 'transitionend';
|
return 'transitionend';
|
||||||
},
|
},
|
||||||
@@ -458,7 +481,10 @@ const addRemoveTransitionElmsFn = async () => {
|
|||||||
await add(true);
|
await add(true);
|
||||||
};
|
};
|
||||||
const ignoreTargetChangeFn = async () => {
|
const ignoreTargetChangeFn = async () => {
|
||||||
const check = async (target: Element | null, changeThrough: DOMObserverResult[]) => {
|
const check = async <ChangeThrough extends DOMContentObserverResult | DOMTargetObserverResult>(
|
||||||
|
target: Element | null,
|
||||||
|
changeThrough: ChangeThrough[]
|
||||||
|
) => {
|
||||||
const { before, after, compare } = changedThrough(changeThrough);
|
const { before, after, compare } = changedThrough(changeThrough);
|
||||||
before();
|
before();
|
||||||
|
|
||||||
@@ -473,24 +499,25 @@ const ignoreTargetChangeFn = async () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
await check(targetElm, targetElmObservations);
|
await check(targetElm, domTargetObserverObservations);
|
||||||
await check(targetElmContentElm, targetElmContentElmObservations);
|
await check(targetElmContentElm, domContentObserverObservations);
|
||||||
};
|
};
|
||||||
const iterateTargetAttrChange = async () => {
|
const iterateTargetAttrChange = async () => {
|
||||||
await iterateAttrChange(setTargetAttr, targetElmObservations, (observation, selected) => {
|
await iterateAttrChange(setTargetAttr, domTargetObserverObservations, (observation, selected) => {
|
||||||
const { changedTargetAttrs, styleChanged, contentChanged } = observation;
|
const { changedTargetAttrs, styleChanged } = observation;
|
||||||
should(changedTargetAttrs.includes(selected)).equal(true);
|
should.equal(
|
||||||
should(styleChanged).equal(true);
|
changedTargetAttrs.includes(selected),
|
||||||
should(contentChanged).equal(false);
|
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.');
|
||||||
});
|
});
|
||||||
await iterateAttrChange(setFilteredTargetAttr);
|
await iterateAttrChange(setFilteredTargetAttr);
|
||||||
};
|
};
|
||||||
const iterateContentAttrChange = async () => {
|
const iterateContentAttrChange = async () => {
|
||||||
await iterateAttrChange(setContentAttr, targetElmContentElmObservations, (observation) => {
|
await iterateAttrChange(setContentAttr, domContentObserverObservations, (observation) => {
|
||||||
const { changedTargetAttrs, styleChanged, contentChanged } = observation;
|
const contentChanged = observation;
|
||||||
should(changedTargetAttrs.length).equal(0);
|
should.equal(contentChanged, true, 'A attribute change inside the content must trigger a content change for a DOMContentObserver.');
|
||||||
should(styleChanged).equal(false);
|
|
||||||
should(contentChanged).equal(true);
|
|
||||||
});
|
});
|
||||||
await iterateAttrChange(setFilteredContentAttr);
|
await iterateAttrChange(setFilteredContentAttr);
|
||||||
};
|
};
|
||||||
@@ -499,16 +526,14 @@ const iterateContentBetweenAttrChange = async () => {
|
|||||||
await iterateAttrChange(setFilteredContentBetweenAttr);
|
await iterateAttrChange(setFilteredContentBetweenAttr);
|
||||||
};
|
};
|
||||||
const iterateContentHostElmAttrChange = async () => {
|
const iterateContentHostElmAttrChange = async () => {
|
||||||
await iterateAttrChange(setContentHostElmAttr, targetElmContentElmObservations, (observation) => {
|
await iterateAttrChange(setContentHostElmAttr, domContentObserverObservations, (observation) => {
|
||||||
const { changedTargetAttrs, styleChanged, contentChanged } = observation;
|
const contentChanged = observation;
|
||||||
should(changedTargetAttrs.length).equal(0);
|
should.equal(contentChanged, true, 'A attribute change for a nested target must trigger a content change for a DOMContentObserver.');
|
||||||
should(styleChanged).equal(false);
|
|
||||||
should(contentChanged).equal(true);
|
|
||||||
});
|
});
|
||||||
await iterateAttrChange(setFilteredContentHostElmAttr);
|
await iterateAttrChange(setFilteredContentHostElmAttr);
|
||||||
};
|
};
|
||||||
const triggerContentSummaryChange = async () => {
|
const triggerContentSummaryChange = async () => {
|
||||||
await triggerSummaryElemet(summaryContent, targetElmContentElmObservations);
|
await triggerSummaryElemet(summaryContent, domContentObserverObservations);
|
||||||
};
|
};
|
||||||
const triggerBetweenSummaryChange = async () => {
|
const triggerBetweenSummaryChange = async () => {
|
||||||
await triggerSummaryElemet(summaryBetween);
|
await triggerSummaryElemet(summaryBetween);
|
||||||
@@ -550,6 +575,16 @@ const start = async () => {
|
|||||||
await addRemoveImgElmsFn();
|
await addRemoveImgElmsFn();
|
||||||
|
|
||||||
setTestResult(true);
|
setTestResult(true);
|
||||||
|
|
||||||
|
targetDomObserver._update();
|
||||||
|
targetDomObserver._destroy();
|
||||||
|
targetDomObserver._update();
|
||||||
|
|
||||||
|
contentDomObserver._updateEventContentChange([]);
|
||||||
|
contentDomObserver._update();
|
||||||
|
contentDomObserver._destroy();
|
||||||
|
contentDomObserver._updateEventContentChange([]);
|
||||||
|
contentDomObserver._update();
|
||||||
};
|
};
|
||||||
|
|
||||||
startBtn?.addEventListener('click', start);
|
startBtn?.addEventListener('click', start);
|
||||||
|
|||||||
@@ -90,21 +90,27 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any)
|
|||||||
|
|
||||||
// no overflow if not needed
|
// no overflow if not needed
|
||||||
if (targetElm && newContentSize.w > 0) {
|
if (targetElm && newContentSize.w > 0) {
|
||||||
should.ok(observerElm.getBoundingClientRect().right <= targetElm.getBoundingClientRect().right);
|
should.ok(
|
||||||
|
observerElm.getBoundingClientRect().right <= targetElm.getBoundingClientRect().right,
|
||||||
|
'Generated observer element inst overflowing target element. (width)'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (targetElm && newContentSize.h > 0) {
|
if (targetElm && newContentSize.h > 0) {
|
||||||
should.ok(observerElm.getBoundingClientRect().bottom <= targetElm.getBoundingClientRect().bottom);
|
should.ok(
|
||||||
|
observerElm.getBoundingClientRect().bottom <= targetElm.getBoundingClientRect().bottom,
|
||||||
|
'Generated observer element inst overflowing target element. (height)'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dimensions && (offsetSizeChanged || contentSizeChanged || dirChanged)) {
|
if (dimensions && (offsetSizeChanged || contentSizeChanged || dirChanged)) {
|
||||||
await waitForOrFailTest(() => {
|
await waitForOrFailTest(() => {
|
||||||
if (offsetSizeChanged || contentSizeChanged) {
|
if (offsetSizeChanged || contentSizeChanged) {
|
||||||
should.equal(sizeIterations, currSizeIterations + 1);
|
should.equal(sizeIterations, currSizeIterations + 1, 'Size change was detected correctly.');
|
||||||
}
|
}
|
||||||
if (dirChanged) {
|
if (dirChanged) {
|
||||||
const expectedCacheValue = newDir === 'rtl';
|
const expectedCacheValue = newDir === 'rtl';
|
||||||
should.equal(directionIterations, currDirectionIterations + 1);
|
should.equal(directionIterations, currDirectionIterations + 1, 'Direction change was detected correctly.');
|
||||||
should.equal(sizeObserver._getCurrentCacheValues()._directionIsRTL._value, expectedCacheValue);
|
should.equal(sizeObserver._getCurrentCacheValues()._directionIsRTL._value, expectedCacheValue, 'Direction cache value is correct.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -158,8 +164,8 @@ const start = async () => {
|
|||||||
|
|
||||||
console.log('init direction changes:', directionIterations);
|
console.log('init direction changes:', directionIterations);
|
||||||
console.log('init size changes:', sizeIterations);
|
console.log('init size changes:', sizeIterations);
|
||||||
should.ok(directionIterations > 0);
|
should.ok(directionIterations > 0, 'Initial direction observations are fired.');
|
||||||
should.ok(sizeIterations > 0);
|
should.ok(sizeIterations > 0, 'Initial size observations are fired.');
|
||||||
|
|
||||||
targetElm?.removeAttribute('style');
|
targetElm?.removeAttribute('style');
|
||||||
await iterateDisplay();
|
await iterateDisplay();
|
||||||
@@ -176,7 +182,7 @@ const start = async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
sizeObserver._destroy();
|
sizeObserver._destroy();
|
||||||
should.equal(targetElm?.children.length, preInitChildren);
|
should.equal(targetElm?.children.length, preInitChildren, 'Destruction removes all generated elements.');
|
||||||
setTestResult(true);
|
setTestResult(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -65,9 +65,13 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any)
|
|||||||
|
|
||||||
await waitForOrFailTest(() => {
|
await waitForOrFailTest(() => {
|
||||||
if (trinsicHeightChanged) {
|
if (trinsicHeightChanged) {
|
||||||
should.equal(heightIterations, currHeightIterations + 1);
|
should.equal(heightIterations, currHeightIterations + 1, 'Height intrinsic change has been detected correctly.');
|
||||||
}
|
}
|
||||||
should.equal(trinsicObserver._getCurrentCacheValues()._heightIntrinsic._value, newHeightIntrinsic);
|
should.equal(
|
||||||
|
trinsicObserver._getCurrentCacheValues()._heightIntrinsic._value,
|
||||||
|
newHeightIntrinsic,
|
||||||
|
'Height intrinsic cache value is correct.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
afterEach,
|
afterEach,
|
||||||
@@ -93,7 +97,7 @@ const changeWhileHidden = async () => {
|
|||||||
selectOption(displaySelect as HTMLSelectElement, 'displayBlock');
|
selectOption(displaySelect as HTMLSelectElement, 'displayBlock');
|
||||||
|
|
||||||
await waitForOrFailTest(() => {
|
await waitForOrFailTest(() => {
|
||||||
should.equal(heightIntrinsic, false);
|
should.equal(heightIntrinsic, false, 'Trinsic sizing changes while hidden from intrinsic to extrinsic.');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -107,7 +111,7 @@ const changeWhileHidden = async () => {
|
|||||||
selectOption(displaySelect as HTMLSelectElement, 'displayBlock');
|
selectOption(displaySelect as HTMLSelectElement, 'displayBlock');
|
||||||
|
|
||||||
await waitForOrFailTest(() => {
|
await waitForOrFailTest(() => {
|
||||||
should.equal(heightIntrinsic, true);
|
should.equal(heightIntrinsic, true, 'Trinsic sizing changes while hidden from extrinsic to intrinsic.');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -129,7 +133,7 @@ const start = async () => {
|
|||||||
await changeWhileHidden();
|
await changeWhileHidden();
|
||||||
|
|
||||||
trinsicObserver._destroy();
|
trinsicObserver._destroy();
|
||||||
should.equal(targetElm?.children.length, preInitChildren);
|
should.equal(targetElm?.children.length, preInitChildren, 'After destruction all generated elements are removed.');
|
||||||
setTestResult(true);
|
setTestResult(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user