UNPKG

@bespunky/angular-zen

Version:

The Angular tools you always wished were there.

139 lines (131 loc) 5.36 kB
import { TestBed } from '@angular/core/testing'; import { NgZone } from '@angular/core'; import { CoreModule, DOCUMENT } from '@bespunky/angular-zen/core'; class MockElement { constructor(tagName) { this.tagName = tagName; this.children = []; } remove() { if (this.parentElement) this.parentElement.removeChild(this); } removeChild(node) { if (node.parentElement !== this) return; const index = this.children.indexOf(node); if (index > -1) { this.children.splice(index, 1); node.parentElement = null; } } appendChild(node) { this.children.push(node); node.parentElement = this; if (node.onload instanceof Function) setTimeout(node.onload, 0); } querySelectorAll(selector) { throw new Error(` Providing a general implementation for querySelectorAll() to support all cases is to complex. Use jest.spyOn() and fake this to provide an implementation for the specific use case. See MockElement.extractXXXFromSelector() methods for utils. `); } /** * Extracts an array of {name, value} objects mapping the attributes from the specified selector string. * Attributes with no value will be mapped with wildcard value (i.e. '**'). * * @param {string} selector * @returns {*} */ extractAttributesFromSelector(selector) { // Searches for [key="value"] and [key] groups and extracts the attribute and value from each const regex = /(?:(\[(?<attr>\w+)(?:="(?<value>[^\]]+)")?)\]*)/g; let match; const attributes = []; while ((match = regex.exec(selector)) !== null) { // This is necessary to avoid infinite loops with zero-width matches if (match.index === regex.lastIndex) regex.lastIndex++; attributes.push({ name: match.groups?.['attr'], value: match.groups?.['value'] || '**' }); } return attributes; } } class MockScriptElement extends MockElement { constructor() { super('script'); } } class MockLinkElement extends MockElement { constructor() { super('link'); this.relList = { add: (...tokens) => this.rel = tokens.join(' ') }; } } class MockHeadElement extends MockElement { constructor() { super('head'); } } /** * Configures a testing module provided with a ready-to-use mock for [`DOCUMENT`](/miscellaneous/variables.html#DOCUMENT). * * Any element created using `DocumentRef.nativeDocument.createElement()` will go through this mock. * If a script element was requested, a `MockScriptElement` object is returned. * If a link element was requested, the `MockLinkElement` object is returned. * If any other tag name is requested, a new `MockElement` object is returned. * * Used when testing head related services (e.g. HeadService, LazyLoaderService). * * Internally, this plants the following structure in [`DocumentRef`](/additional-documentation/coremodule/documentref.html): * * `DocumentRef.nativeDocument.head -> MockHeadElement` * * `DocumentRef.nativeDocument.createElement -> () => MockScriptElement | MockLinkElement | MockElement(<tagName>)` * * The returned mocks can be deconstructed like so: * @example * let mockHeadElement: MockHeadElement; * let mockDocument : any; * ({ mockHeadElement, mockDocument } = setupDocumentRefMock()); // mockDocument is also a jest.SpyInstance */ function setupDocumentRefMock() { const createElement = jest.fn((tagName) => { return tagName === 'script' ? new MockScriptElement() : tagName === 'link' ? new MockLinkElement() : new MockElement(tagName); }); // Create a stub for the head element const mockHeadElement = new MockHeadElement(); // Mock for the DocumentRef.nativeDocument object // Create the document object allowing to spy on its createElement() function. // When an element should be created, substitute it for the appropriate mock const mockDocument = { createElement, head: mockHeadElement }; TestBed.configureTestingModule({ imports: [CoreModule], providers: [ { provide: DOCUMENT, useValue: mockDocument } ] }); return { mockHeadElement, mockDocument }; } /** * Wraps the `navigate` and `navigateByUrl` methods of the router with a call to `NgZone.run()`. * Fixes the warning when using the router in unit tests. * * @export * @param {Router} router The router instance. */ function forceRoutingInsideAngularZone(router) { const zone = TestBed.inject(NgZone); const navigate = router.navigate.bind(router); const navigateByUrl = router.navigateByUrl.bind(router); // Fix for angular's warning of running navigation outside angular's zone jest.spyOn(router, 'navigate').mockImplementation((...args) => zone.run(() => navigate(...args))); jest.spyOn(router, 'navigateByUrl').mockImplementation((...args) => zone.run(() => navigateByUrl(...args))); } /** * Generated bundle index. Do not edit. */ export { MockElement, MockHeadElement, MockLinkElement, MockScriptElement, forceRoutingInsideAngularZone, setupDocumentRefMock }; //# sourceMappingURL=bespunky-angular-zen-core-testing.mjs.map