UNPKG

ng-flex-layout

Version:
411 lines (403 loc) 15.1 kB
import { extendObject, applyCssPrefixes } from 'ng-flex-layout/_private-utils'; import { expect } from 'vitest'; import { CommonModule } from '@angular/common'; import { getDebugNode, Component, EnvironmentInjector, createEnvironmentInjector, createComponent } from '@angular/core'; import { TestBed } from '@angular/core/testing'; import { FlexLayoutModule } from 'ng-flex-layout'; import { BreakPointRegistry, MediaMarshaller, MediaObserver } from 'ng-flex-layout/core'; /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * Exported DOM accessor utility functions */ const _dom = { hasStyle, getDistributedNodes, getShadowRoot, getText, getStyle, childNodes, childNodesAsList, hasClass, hasAttribute, getAttribute, hasShadowRoot, isCommentNode, isElementNode, isPresent, isShadowRoot, tagName, lastElementChild }; // ****************************************************************************************** // These functions are cloned from // * @angular/platform-browser/src/browser/GenericBrowserDomAdapter // and are to be used ONLY internally in custom-matchers.ts and Unit Tests // ****************************************************************************************** function getStyle(element, stylename) { return element.style[stylename]; } function hasStyle(element, styleName, styleValue = '', inlineOnly = true) { let value = getStyle(element, styleName) || ''; if (!value && !inlineOnly) { // Search stylesheets value = typeof getComputedStyle === 'function' && getComputedStyle(element).getPropertyValue(styleName) || ''; } return styleValue ? value == styleValue : value.length > 0; } function getDistributedNodes(el) { return el.getDistributedNodes(); } function getShadowRoot(el) { return el.shadowRoot; } function getText(el) { return el.textContent || ''; } function childNodesAsList(el) { const list = el.childNodes; const res = new Array(list.length); for (let i = 0; i < list.length; i++) { res[i] = list[i]; } return res; } function hasClass(element, className) { return element.classList.contains(className); } function hasAttribute(element, attributeName) { return element.hasAttribute(attributeName); } function getAttribute(element, attributeName) { return element.getAttribute(attributeName); } function childNodes(el) { return el.childNodes; } function hasShadowRoot(node) { return isPresent(node.shadowRoot) && node instanceof HTMLElement; } function isCommentNode(node) { return node.nodeType === Node.COMMENT_NODE; } function isElementNode(node) { return node.nodeType === Node.ELEMENT_NODE; } function isShadowRoot(node) { return node instanceof DocumentFragment; } function isPresent(obj) { return obj != null; } function tagName(element) { return element.tagName; } // ****************************************************************************************** // These functions are part of the DOM API // and are to be used ONLY internally in custom-matchers.ts and Unit Tests // ****************************************************************************************** function lastElementChild(element) { return element.lastElementChild; } const _global = (typeof window === 'undefined' ? global : window); expect.extend({ toHaveText(received, expectedText) { const actualText = elementText(received); const pass = actualText === expectedText; return { pass, message: () => `Expected element text ${actualText} to ${pass ? 'not ' : ''}equal ${expectedText}`, }; }, toHaveCssClass(received, className) { const pass = _dom.hasClass(received, className); return { pass, message: () => `Expected element ${received.outerHTML} ${pass ? 'not ' : ''}to have class "${className}"`, }; }, toHaveMap(received, expected) { const allPassed = Object.entries(expected).every(([k, v]) => received[k] === v); return { pass: allPassed, message: () => `Expected map ${JSON.stringify(received)} ${allPassed ? 'not ' : ''}to match ${JSON.stringify(expected)}`, }; }, toHaveAttributes(received, expected) { const allPassed = Object.entries(expected).every(([name, value]) => _dom.hasAttribute(received, name) && _dom.getAttribute(received, name) === value); return { pass: allPassed, message: () => `Expected element ${received.outerHTML} ${allPassed ? 'not ' : ''}to have attributes ${JSON.stringify(expected)}`, }; }, toHaveStyle(received, styles, styler) { return buildCompareStyleFunction(true)(received, styles, styler); }, toHaveCSS(received, styles, styler) { return buildCompareStyleFunction(false)(received, styles, styler); }, }); function buildCompareStyleFunction(inlineOnly = true) { return function (actual, styles, styler) { const found = {}; const styleMap = {}; if (typeof styles === 'string') { styleMap[styles] = ''; } else { Object.assign(styleMap, styles); } let allPassed = Object.keys(styleMap).length !== 0; Object.keys(styleMap).forEach(prop => { const { elHasStyle, current } = hasPrefixedStyles(actual, prop, styleMap[prop], inlineOnly, styler); allPassed = allPassed && elHasStyle; if (!elHasStyle) { extendObject(found, current); } }); return { pass: allPassed, message: () => { const expectedValueStr = typeof styles === 'string' ? styleMap : JSON.stringify(styleMap, null, 2); const foundValueStr = inlineOnly ? actual.outerHTML : JSON.stringify(found); return `Expected ${foundValueStr}${!allPassed ? '' : ' not'} to contain the CSS ${typeof styles === 'string' ? 'property' : 'styles'} '${expectedValueStr}'`; } }; }; } /** * Validate presence of requested style or use fallback * to possible `prefixed` styles. Useful when some browsers * (Safari, IE, etc) will use prefixed style instead of defaults. */ function hasPrefixedStyles(actual, key, value, inlineOnly, styler) { const current = {}; if (value === '*') { return { elHasStyle: styler.lookupStyle(actual, key, inlineOnly) !== '', current }; } value = value.trim(); let elHasStyle = styler.lookupStyle(actual, key, inlineOnly) === value; if (!elHasStyle) { let prefixedStyles = applyCssPrefixes({ [key]: value }); Object.keys(prefixedStyles).forEach(prop => { // Search for optional prefixed values elHasStyle = elHasStyle || styler.lookupStyle(actual, prop, inlineOnly) === prefixedStyles[prop]; }); } // Return BOTH confirmation and current computed key values (if confirmation == false) return { elHasStyle, current }; } function elementText(n) { const hasNodes = (m) => { const children = _dom.childNodes(m); return children && children['length']; }; if (n instanceof Array) { return n.map(elementText).join(''); } if (_dom.isCommentNode(n)) { return ''; } if (_dom.isElementNode(n) && _dom.tagName(n) == 'CONTENT') { return elementText(Array.prototype.slice.apply(_dom.getDistributedNodes(n))); } if (_dom.hasShadowRoot(n)) { return elementText(_dom.childNodesAsList(_dom.getShadowRoot(n))); } if (hasNodes(n)) { return elementText(_dom.childNodesAsList(n)); } return _dom.getText(n); } function unwrapNativeElement(value) { return (value && typeof value === 'object' && 'nativeElement' in value) ? value.nativeElement : value; } expect.extend({ toHaveMap(received, expected) { const pass = Object.entries(expected).every(([k, v]) => received[k] === v); return { pass, message: () => `Expected map ${JSON.stringify(received)} ${pass ? 'not ' : ''}to match ${JSON.stringify(expected)}`, }; }, toHaveCssClass(received, className) { const el = unwrapNativeElement(received); const hasClass = el.classList.contains(className); return { pass: hasClass, message: () => `Expected element ${el.outerHTML} ${hasClass ? 'not ' : ''}to have class '${className}'` }; }, toHaveAttributes(received, expected) { const el = unwrapNativeElement(received); const allMatch = Object.entries(expected).every(([key, value]) => el.getAttribute(key) === value); return { pass: allMatch, message: () => `Expected element ${el.outerHTML} ${allMatch ? 'not ' : ''}to have attributes ${JSON.stringify(expected)}` }; } }); function getHostElement(fixture) { return fixture?.location?.nativeElement ?? fixture?.nativeElement ?? fixture?.componentRef?.location?.nativeElement; } function getInstance(fixture) { return fixture?.componentInstance ?? fixture?.instance ?? fixture?.componentRef?.instance; } function detectChanges(fixture) { const hostEl = fixture?.location?.nativeElement ?? fixture?.nativeElement ?? fixture?.componentRef?.location?.nativeElement; if (hostEl) { const wrapper = hostEl.querySelector('#testing-wrapper-node'); if (wrapper) { wrapper.dispatchEvent(new CustomEvent('change')); } } if (typeof fixture?.detectChanges === 'function') { fixture.detectChanges(); return; } if (fixture?.changeDetectorRef?.detectChanges) { fixture.changeDetectorRef.detectChanges(); return; } if (fixture?.componentRef?.changeDetectorRef?.detectChanges) { fixture.componentRef.changeDetectorRef.detectChanges(); } } function asFixtureLike(ref) { const fixture = ref; fixture.componentInstance ??= ref.instance; fixture.location ??= ref.location; fixture.injector ??= ref.injector; fixture.changeDetectorRef ??= ref.changeDetectorRef; fixture.nativeElement ??= ref.location.nativeElement; fixture.detectChanges ??= () => { const hostEl = fixture.nativeElement ?? fixture.location?.nativeElement; if (hostEl) { const wrapper = hostEl.querySelector('#testing-wrapper-node'); if (wrapper) { wrapper.dispatchEvent(new CustomEvent('change')); } } ref.changeDetectorRef.detectChanges(); }; const debugNode = getDebugNode(ref.location.nativeElement); if (debugNode && debugNode.children) { const wrapper = debugNode.children.find((c) => c.nativeElement?.id === 'testing-wrapper-node'); if (wrapper) { Object.defineProperty(debugNode, 'children', { get: () => wrapper.children }); Object.defineProperty(debugNode, 'childNodes', { get: () => wrapper.childNodes }); } fixture.debugElement ??= debugNode; } return fixture; } const TRACKED_REFS = []; let cleanupRegistered = false; function registerCleanup() { if (cleanupRegistered) { return; } cleanupRegistered = true; const afterEachHook = globalThis.afterEach; if (typeof afterEachHook !== 'function') { return; } afterEachHook(() => { while (TRACKED_REFS.length) { const tracked = TRACKED_REFS.pop(); try { tracked.componentRef.destroy(); } catch { // ignore } if (tracked.envInjector && tracked.envInjector !== tracked.componentRef.injector) { try { tracked.envInjector.destroy(); } catch { // ignore } } } }); } function makeCreateTestComponent(baseOrProviders = [], defaultProviders = []) { const hasBase = typeof baseOrProviders === 'function'; const BaseComponent = hasBase ? baseOrProviders() : class { }; const providers = [ ...(hasBase ? [] : baseOrProviders), ...defaultProviders ]; return function createTestComponent(templateStr, styles = [], additionalProviders = [], additionalImports = []) { registerCleanup(); const DynamicTestComponent = Component({ selector: 'test-wrapper', standalone: true, template: `<div (change)="0" id="testing-wrapper-node" style="display:contents">${templateStr}</div>`, styles, imports: [CommonModule, FlexLayoutModule, ...additionalImports] })(class extends BaseComponent { }); const parentInjector = TestBed.inject(EnvironmentInjector); const isolatedFlexProviders = [ { provide: BreakPointRegistry, useClass: BreakPointRegistry }, { provide: MediaMarshaller, useClass: MediaMarshaller }, { provide: MediaObserver, useClass: MediaObserver }, ]; const finalProviders = [...isolatedFlexProviders, ...providers, ...additionalProviders]; const envInjector = createEnvironmentInjector(finalProviders, parentInjector); const componentRef = createComponent(DynamicTestComponent, { environmentInjector: envInjector }); TRACKED_REFS.push({ componentRef, envInjector }); const fixture = asFixtureLike(componentRef); fixture.detectChanges?.(); return fixture; }; } function expectNativeEl(fixture, instanceOptions) { Object.assign(getInstance(fixture) ?? {}, instanceOptions || {}); detectChanges(fixture); const host = getHostElement(fixture); const wrapper = host.querySelector('#testing-wrapper-node'); const target = wrapper ? wrapper.children[0] : host.children[0]; return expect(target); } function expectEl(el) { const native = el?.nativeElement ?? el; return expect(native); } function queryFor(fixture, selector) { const host = getHostElement(fixture); const wrapper = host.querySelector('#testing-wrapper-node'); const target = wrapper ? wrapper : host; const nodes = Array.from(target.querySelectorAll(selector)); return nodes.map((node) => getDebugNode(node) ?? node); } /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * Generated bundle index. Do not edit. */ export { _dom, expectEl, expectNativeEl, makeCreateTestComponent, queryFor }; //# sourceMappingURL=ng-flex-layout-_private-utils-testing.mjs.map