@angular/cdk
Version:
Angular Material Component Development Kit
1 lines • 86.3 kB
Source Map (JSON)
{"version":3,"file":"testing.mjs","sources":["../../../../../darwin_arm64-fastbuild-ST-46c76129e412/bin/src/cdk/testing/change-detection.ts","../../../../../darwin_arm64-fastbuild-ST-46c76129e412/bin/src/cdk/testing/component-harness.ts","../../../../../darwin_arm64-fastbuild-ST-46c76129e412/bin/src/cdk/testing/harness-environment.ts","../../../../../darwin_arm64-fastbuild-ST-46c76129e412/bin/src/cdk/testing/test-element.ts","../../../../../darwin_arm64-fastbuild-ST-46c76129e412/bin/src/cdk/testing/test-element-errors.ts","../../../../../darwin_arm64-fastbuild-ST-46c76129e412/bin/src/cdk/testing/text-filtering.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {BehaviorSubject, Subscription} from 'rxjs';\n\n/**\n * The status of the test harness auto change detection. If not diabled test harnesses will\n * automatically trigger change detection after every action (such as a click) and before every read\n * (such as getting the text of an element).\n */\nexport interface AutoChangeDetectionStatus {\n /** Whether auto change detection is disabled. */\n isDisabled: boolean;\n /**\n * An optional callback, if present it indicates that change detection should be run immediately,\n * while handling the status change. The callback should then be called as soon as change\n * detection is done.\n */\n onDetectChangesNow?: () => void;\n}\n\n/** Subject used to dispatch and listen for changes to the auto change detection status . */\nconst autoChangeDetectionSubject = new BehaviorSubject<AutoChangeDetectionStatus>({\n isDisabled: false,\n});\n\n/** The current subscription to `autoChangeDetectionSubject`. */\nlet autoChangeDetectionSubscription: Subscription | null;\n\n/**\n * The default handler for auto change detection status changes. This handler will be used if the\n * specific environment does not install its own.\n * @param status The new auto change detection status.\n */\nfunction defaultAutoChangeDetectionHandler(status: AutoChangeDetectionStatus) {\n status.onDetectChangesNow?.();\n}\n\n/**\n * Allows a test `HarnessEnvironment` to install its own handler for auto change detection status\n * changes.\n * @param handler The handler for the auto change detection status.\n */\nexport function handleAutoChangeDetectionStatus(\n handler: (status: AutoChangeDetectionStatus) => void,\n) {\n stopHandlingAutoChangeDetectionStatus();\n autoChangeDetectionSubscription = autoChangeDetectionSubject.subscribe(handler);\n}\n\n/** Allows a `HarnessEnvironment` to stop handling auto change detection status changes. */\nexport function stopHandlingAutoChangeDetectionStatus() {\n autoChangeDetectionSubscription?.unsubscribe();\n autoChangeDetectionSubscription = null;\n}\n\n/**\n * Batches together triggering of change detection over the duration of the given function.\n * @param fn The function to call with batched change detection.\n * @param triggerBeforeAndAfter Optionally trigger change detection once before and after the batch\n * operation. If false, change detection will not be triggered.\n * @return The result of the given function.\n */\nasync function batchChangeDetection<T>(fn: () => Promise<T>, triggerBeforeAndAfter: boolean) {\n // If change detection batching is already in progress, just run the function.\n if (autoChangeDetectionSubject.getValue().isDisabled) {\n return await fn();\n }\n\n // If nothing is handling change detection batching, install the default handler.\n if (!autoChangeDetectionSubscription) {\n handleAutoChangeDetectionStatus(defaultAutoChangeDetectionHandler);\n }\n\n if (triggerBeforeAndAfter) {\n await new Promise(resolve =>\n autoChangeDetectionSubject.next({\n isDisabled: true,\n onDetectChangesNow: resolve as () => void,\n }),\n );\n // The function passed in may throw (e.g. if the user wants to make an expectation of an error\n // being thrown. If this happens, we need to make sure we still re-enable change detection, so\n // we wrap it in a `finally` block.\n try {\n return await fn();\n } finally {\n await new Promise(resolve =>\n autoChangeDetectionSubject.next({\n isDisabled: false,\n onDetectChangesNow: resolve as () => void,\n }),\n );\n }\n } else {\n autoChangeDetectionSubject.next({isDisabled: true});\n // The function passed in may throw (e.g. if the user wants to make an expectation of an error\n // being thrown. If this happens, we need to make sure we still re-enable change detection, so\n // we wrap it in a `finally` block.\n try {\n return await fn();\n } finally {\n autoChangeDetectionSubject.next({isDisabled: false});\n }\n }\n}\n\n/**\n * Disables the harness system's auto change detection for the duration of the given function.\n * @param fn The function to disable auto change detection for.\n * @return The result of the given function.\n */\nexport async function manualChangeDetection<T>(fn: () => Promise<T>) {\n return batchChangeDetection(fn, false);\n}\n\n/**\n * Resolves the given list of async values in parallel (i.e. via Promise.all) while batching change\n * detection over the entire operation such that change detection occurs exactly once before\n * resolving the values and once after.\n * @param values A getter for the async values to resolve in parallel with batched change detection.\n * @return The resolved values.\n */\nexport function parallel<T1, T2, T3, T4, T5>(\n values: () => [\n T1 | PromiseLike<T1>,\n T2 | PromiseLike<T2>,\n T3 | PromiseLike<T3>,\n T4 | PromiseLike<T4>,\n T5 | PromiseLike<T5>,\n ],\n): Promise<[T1, T2, T3, T4, T5]>;\n\n/**\n * Resolves the given list of async values in parallel (i.e. via Promise.all) while batching change\n * detection over the entire operation such that change detection occurs exactly once before\n * resolving the values and once after.\n * @param values A getter for the async values to resolve in parallel with batched change detection.\n * @return The resolved values.\n */\nexport function parallel<T1, T2, T3, T4>(\n values: () => [\n T1 | PromiseLike<T1>,\n T2 | PromiseLike<T2>,\n T3 | PromiseLike<T3>,\n T4 | PromiseLike<T4>,\n ],\n): Promise<[T1, T2, T3, T4]>;\n\n/**\n * Resolves the given list of async values in parallel (i.e. via Promise.all) while batching change\n * detection over the entire operation such that change detection occurs exactly once before\n * resolving the values and once after.\n * @param values A getter for the async values to resolve in parallel with batched change detection.\n * @return The resolved values.\n */\nexport function parallel<T1, T2, T3>(\n values: () => [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>],\n): Promise<[T1, T2, T3]>;\n\n/**\n * Resolves the given list of async values in parallel (i.e. via Promise.all) while batching change\n * detection over the entire operation such that change detection occurs exactly once before\n * resolving the values and once after.\n * @param values A getter for the async values to resolve in parallel with batched change detection.\n * @return The resolved values.\n */\nexport function parallel<T1, T2>(\n values: () => [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>],\n): Promise<[T1, T2]>;\n\n/**\n * Resolves the given list of async values in parallel (i.e. via Promise.all) while batching change\n * detection over the entire operation such that change detection occurs exactly once before\n * resolving the values and once after.\n * @param values A getter for the async values to resolve in parallel with batched change detection.\n * @return The resolved values.\n */\nexport function parallel<T>(values: () => (T | PromiseLike<T>)[]): Promise<T[]>;\n\n/**\n * Resolves the given list of async values in parallel (i.e. via Promise.all) while batching change\n * detection over the entire operation such that change detection occurs exactly once before\n * resolving the values and once after.\n * @param values A getter for the async values to resolve in parallel with batched change detection.\n * @return The resolved values.\n */\nexport async function parallel<T>(values: () => Iterable<T | PromiseLike<T>>): Promise<T[]> {\n return batchChangeDetection(() => Promise.all(values()), true);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {parallel} from './change-detection';\nimport {TestElement} from './test-element';\n\n/**\n * An async function that returns a promise when called.\n * @deprecated This was just an alias for `() => Promise<T>`. Use that instead.\n * @breaking-change 21.0.0 Remove this alias.\n * @docs-private\n */\nexport type AsyncFactoryFn<T> = () => Promise<T>;\n\n/** An async function that takes an item and returns a boolean promise */\nexport type AsyncPredicate<T> = (item: T) => Promise<boolean>;\n\n/** An async function that takes an item and an option value and returns a boolean promise. */\nexport type AsyncOptionPredicate<T, O> = (item: T, option: O) => Promise<boolean>;\n\n/**\n * A query for a `ComponentHarness`, which is expressed as either a `ComponentHarnessConstructor` or\n * a `HarnessPredicate`.\n */\nexport type HarnessQuery<T extends ComponentHarness> =\n | ComponentHarnessConstructor<T>\n | HarnessPredicate<T>;\n\n/**\n * The result type obtained when searching using a particular list of queries. This type depends on\n * the particular items being queried.\n * - If one of the queries is for a `ComponentHarnessConstructor<C1>`, it means that the result\n * might be a harness of type `C1`\n * - If one of the queries is for a `HarnessPredicate<C2>`, it means that the result might be a\n * harness of type `C2`\n * - If one of the queries is for a `string`, it means that the result might be a `TestElement`.\n *\n * Since we don't know for sure which query will match, the result type if the union of the types\n * for all possible results.\n *\n * @usageNotes\n * ### Example\n *\n * The type:\n * ```ts\n * LocatorFnResult<[\n * ComponentHarnessConstructor<MyHarness>,\n * HarnessPredicate<MyOtherHarness>,\n * string\n * ]>\n * ```\n *\n * is equivalent to:\n *\n * ```ts\n * MyHarness | MyOtherHarness | TestElement\n * ```\n */\nexport type LocatorFnResult<T extends (HarnessQuery<any> | string)[]> = {\n [I in keyof T]: T[I] extends new (...args: any[]) => infer C // Map `ComponentHarnessConstructor<C>` to `C`.\n ? C\n : // Map `HarnessPredicate<C>` to `C`.\n T[I] extends {harnessType: new (...args: any[]) => infer C}\n ? C\n : // Map `string` to `TestElement`.\n T[I] extends string\n ? TestElement\n : // Map everything else to `never` (should not happen due to the type constraint on `T`).\n never;\n}[number];\n\n/**\n * Interface used to load ComponentHarness objects. This interface is used by test authors to\n * instantiate `ComponentHarness`es.\n */\nexport interface HarnessLoader {\n /**\n * Searches for an element with the given selector under the current instances's root element,\n * and returns a `HarnessLoader` rooted at the matching element. If multiple elements match the\n * selector, the first is used. If no elements match, an error is thrown.\n * @param selector The selector for the root element of the new `HarnessLoader`\n * @return A `HarnessLoader` rooted at the element matching the given selector.\n * @throws If a matching element can't be found.\n */\n getChildLoader(selector: string): Promise<HarnessLoader>;\n\n /**\n * Searches for all elements with the given selector under the current instances's root element,\n * and returns an array of `HarnessLoader`s, one for each matching element, rooted at that\n * element.\n * @param selector The selector for the root element of the new `HarnessLoader`\n * @return A list of `HarnessLoader`s, one for each matching element, rooted at that element.\n */\n getAllChildLoaders(selector: string): Promise<HarnessLoader[]>;\n\n /**\n * Searches for an instance of the component corresponding to the given harness type under the\n * `HarnessLoader`'s root element, and returns a `ComponentHarness` for that instance. If multiple\n * matching components are found, a harness for the first one is returned. If no matching\n * component is found, an error is thrown.\n * @param query A query for a harness to create\n * @return An instance of the given harness type\n * @throws If a matching component instance can't be found.\n */\n getHarness<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<T>;\n\n /**\n * Searches for an instance of the component corresponding to the given harness type under the\n * `HarnessLoader`'s root element, and returns a `ComponentHarness` for that instance. If multiple\n * matching components are found, a harness for the first one is returned. If no matching\n * component is found, null is returned.\n * @param query A query for a harness to create\n * @return An instance of the given harness type (or null if not found).\n */\n getHarnessOrNull<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<T | null>;\n\n /**\n * Searches for all instances of the component corresponding to the given harness type under the\n * `HarnessLoader`'s root element, and returns a list `ComponentHarness` for each instance.\n * @param query A query for a harness to create\n * @return A list instances of the given harness type.\n */\n getAllHarnesses<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<T[]>;\n\n /**\n * Searches for an instance of the component corresponding to the given harness type under the\n * `HarnessLoader`'s root element, and returns a boolean indicating if any were found.\n * @param query A query for a harness to create\n * @return A boolean indicating if an instance was found.\n */\n hasHarness<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<boolean>;\n}\n\n/**\n * Interface used to create asynchronous locator functions used find elements and component\n * harnesses. This interface is used by `ComponentHarness` authors to create locator functions for\n * their `ComponentHarness` subclass.\n */\nexport interface LocatorFactory {\n /** Gets a locator factory rooted at the document root. */\n documentRootLocatorFactory(): LocatorFactory;\n\n /** The root element of this `LocatorFactory` as a `TestElement`. */\n rootElement: TestElement;\n\n /**\n * Creates an asynchronous locator function that can be used to find a `ComponentHarness` instance\n * or element under the root element of this `LocatorFactory`.\n *\n * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'`\n *\n * ```html\n * <div id=\"d1\"></div><div id=\"d2\"></div>\n * ```\n *\n * then we expect:\n *\n * ```ts\n * await lf.locatorFor(DivHarness, 'div')() // Gets a `DivHarness` instance for #d1\n * await lf.locatorFor('div', DivHarness)() // Gets a `TestElement` instance for #d1\n * await lf.locatorFor('span')() // Throws because the `Promise` rejects\n * ```\n *\n * @param queries A list of queries specifying which harnesses and elements to search for:\n * - A `string` searches for elements matching the CSS selector specified by the string.\n * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the\n * given class.\n * - A `HarnessPredicate` searches for `ComponentHarness` instances matching the given\n * predicate.\n * @return An asynchronous locator function that searches for and returns a `Promise` for the\n * first element or harness matching the given search criteria. Matches are ordered first by\n * order in the DOM, and second by order in the queries list. If no matches are found, the\n * `Promise` rejects. The type that the `Promise` resolves to is a union of all result types for\n * each query.\n */\n locatorFor<T extends (HarnessQuery<any> | string)[]>(\n ...queries: T\n ): () => Promise<LocatorFnResult<T>>;\n\n /**\n * Creates an asynchronous locator function that can be used to find a `ComponentHarness` instance\n * or element under the root element of this `LocatorFactory`.\n *\n * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'`\n *\n * ```html\n * <div id=\"d1\"></div><div id=\"d2\"></div>\n * ```\n *\n * then we expect:\n *\n * ```ts\n * await lf.locatorForOptional(DivHarness, 'div')() // Gets a `DivHarness` instance for #d1\n * await lf.locatorForOptional('div', DivHarness)() // Gets a `TestElement` instance for #d1\n * await lf.locatorForOptional('span')() // Gets `null`\n * ```\n *\n * @param queries A list of queries specifying which harnesses and elements to search for:\n * - A `string` searches for elements matching the CSS selector specified by the string.\n * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the\n * given class.\n * - A `HarnessPredicate` searches for `ComponentHarness` instances matching the given\n * predicate.\n * @return An asynchronous locator function that searches for and returns a `Promise` for the\n * first element or harness matching the given search criteria. Matches are ordered first by\n * order in the DOM, and second by order in the queries list. If no matches are found, the\n * `Promise` is resolved with `null`. The type that the `Promise` resolves to is a union of all\n * result types for each query or null.\n */\n locatorForOptional<T extends (HarnessQuery<any> | string)[]>(\n ...queries: T\n ): () => Promise<LocatorFnResult<T> | null>;\n\n /**\n * Creates an asynchronous locator function that can be used to find `ComponentHarness` instances\n * or elements under the root element of this `LocatorFactory`.\n *\n * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'` and\n * `IdIsD1Harness.hostSelector` is `'#d1'`\n *\n * ```html\n * <div id=\"d1\"></div><div id=\"d2\"></div>\n * ```\n *\n * then we expect:\n *\n * ```ts\n * // Gets [DivHarness for #d1, TestElement for #d1, DivHarness for #d2, TestElement for #d2]\n * await lf.locatorForAll(DivHarness, 'div')()\n * // Gets [TestElement for #d1, TestElement for #d2]\n * await lf.locatorForAll('div', '#d1')()\n * // Gets [DivHarness for #d1, IdIsD1Harness for #d1, DivHarness for #d2]\n * await lf.locatorForAll(DivHarness, IdIsD1Harness)()\n * // Gets []\n * await lf.locatorForAll('span')()\n * ```\n *\n * @param queries A list of queries specifying which harnesses and elements to search for:\n * - A `string` searches for elements matching the CSS selector specified by the string.\n * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the\n * given class.\n * - A `HarnessPredicate` searches for `ComponentHarness` instances matching the given\n * predicate.\n * @return An asynchronous locator function that searches for and returns a `Promise` for all\n * elements and harnesses matching the given search criteria. Matches are ordered first by\n * order in the DOM, and second by order in the queries list. If an element matches more than\n * one `ComponentHarness` class, the locator gets an instance of each for the same element. If\n * an element matches multiple `string` selectors, only one `TestElement` instance is returned\n * for that element. The type that the `Promise` resolves to is an array where each element is\n * the union of all result types for each query.\n */\n locatorForAll<T extends (HarnessQuery<any> | string)[]>(\n ...queries: T\n ): () => Promise<LocatorFnResult<T>[]>;\n\n /** @return A `HarnessLoader` rooted at the root element of this `LocatorFactory`. */\n rootHarnessLoader(): Promise<HarnessLoader>;\n\n /**\n * Gets a `HarnessLoader` instance for an element under the root of this `LocatorFactory`.\n * @param selector The selector for the root element.\n * @return A `HarnessLoader` rooted at the first element matching the given selector.\n * @throws If no matching element is found for the given selector.\n */\n harnessLoaderFor(selector: string): Promise<HarnessLoader>;\n\n /**\n * Gets a `HarnessLoader` instance for an element under the root of this `LocatorFactory`\n * @param selector The selector for the root element.\n * @return A `HarnessLoader` rooted at the first element matching the given selector, or null if\n * no matching element is found.\n */\n harnessLoaderForOptional(selector: string): Promise<HarnessLoader | null>;\n\n /**\n * Gets a list of `HarnessLoader` instances, one for each matching element.\n * @param selector The selector for the root element.\n * @return A list of `HarnessLoader`, one rooted at each element matching the given selector.\n */\n harnessLoaderForAll(selector: string): Promise<HarnessLoader[]>;\n\n /**\n * Flushes change detection and async tasks captured in the Angular zone.\n * In most cases it should not be necessary to call this manually. However, there may be some edge\n * cases where it is needed to fully flush animation events.\n */\n forceStabilize(): Promise<void>;\n\n /**\n * Waits for all scheduled or running async tasks to complete. This allows harness\n * authors to wait for async tasks outside of the Angular zone.\n */\n waitForTasksOutsideAngular(): Promise<void>;\n}\n\n/**\n * Base class for component test harnesses that all component harness authors should extend. This\n * base component harness provides the basic ability to locate element and sub-component harnesses.\n */\nexport abstract class ComponentHarness {\n constructor(protected readonly locatorFactory: LocatorFactory) {}\n\n /** Gets a `Promise` for the `TestElement` representing the host element of the component. */\n async host(): Promise<TestElement> {\n return this.locatorFactory.rootElement;\n }\n\n /**\n * Gets a `LocatorFactory` for the document root element. This factory can be used to create\n * locators for elements that a component creates outside of its own root element. (e.g. by\n * appending to document.body).\n */\n protected documentRootLocatorFactory(): LocatorFactory {\n return this.locatorFactory.documentRootLocatorFactory();\n }\n\n /**\n * Creates an asynchronous locator function that can be used to find a `ComponentHarness` instance\n * or element under the host element of this `ComponentHarness`.\n *\n * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'`\n *\n * ```html\n * <div id=\"d1\"></div><div id=\"d2\"></div>\n * ```\n *\n * then we expect:\n *\n * ```ts\n * await ch.locatorFor(DivHarness, 'div')() // Gets a `DivHarness` instance for #d1\n * await ch.locatorFor('div', DivHarness)() // Gets a `TestElement` instance for #d1\n * await ch.locatorFor('span')() // Throws because the `Promise` rejects\n * ```\n *\n * @param queries A list of queries specifying which harnesses and elements to search for:\n * - A `string` searches for elements matching the CSS selector specified by the string.\n * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the\n * given class.\n * - A `HarnessPredicate` searches for `ComponentHarness` instances matching the given\n * predicate.\n * @return An asynchronous locator function that searches for and returns a `Promise` for the\n * first element or harness matching the given search criteria. Matches are ordered first by\n * order in the DOM, and second by order in the queries list. If no matches are found, the\n * `Promise` rejects. The type that the `Promise` resolves to is a union of all result types for\n * each query.\n */\n protected locatorFor<T extends (HarnessQuery<any> | string)[]>(\n ...queries: T\n ): () => Promise<LocatorFnResult<T>> {\n return this.locatorFactory.locatorFor(...queries);\n }\n\n /**\n * Creates an asynchronous locator function that can be used to find a `ComponentHarness` instance\n * or element under the host element of this `ComponentHarness`.\n *\n * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'`\n *\n * ```html\n * <div id=\"d1\"></div><div id=\"d2\"></div>\n * ```\n *\n * then we expect:\n *\n * ```ts\n * await ch.locatorForOptional(DivHarness, 'div')() // Gets a `DivHarness` instance for #d1\n * await ch.locatorForOptional('div', DivHarness)() // Gets a `TestElement` instance for #d1\n * await ch.locatorForOptional('span')() // Gets `null`\n * ```\n *\n * @param queries A list of queries specifying which harnesses and elements to search for:\n * - A `string` searches for elements matching the CSS selector specified by the string.\n * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the\n * given class.\n * - A `HarnessPredicate` searches for `ComponentHarness` instances matching the given\n * predicate.\n * @return An asynchronous locator function that searches for and returns a `Promise` for the\n * first element or harness matching the given search criteria. Matches are ordered first by\n * order in the DOM, and second by order in the queries list. If no matches are found, the\n * `Promise` is resolved with `null`. The type that the `Promise` resolves to is a union of all\n * result types for each query or null.\n */\n protected locatorForOptional<T extends (HarnessQuery<any> | string)[]>(\n ...queries: T\n ): () => Promise<LocatorFnResult<T> | null> {\n return this.locatorFactory.locatorForOptional(...queries);\n }\n\n /**\n * Creates an asynchronous locator function that can be used to find `ComponentHarness` instances\n * or elements under the host element of this `ComponentHarness`.\n *\n * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'` and\n * `IdIsD1Harness.hostSelector` is `'#d1'`\n *\n * ```html\n * <div id=\"d1\"></div><div id=\"d2\"></div>\n * ```\n *\n * then we expect:\n *\n * ```ts\n * // Gets [DivHarness for #d1, TestElement for #d1, DivHarness for #d2, TestElement for #d2]\n * await ch.locatorForAll(DivHarness, 'div')()\n * // Gets [TestElement for #d1, TestElement for #d2]\n * await ch.locatorForAll('div', '#d1')()\n * // Gets [DivHarness for #d1, IdIsD1Harness for #d1, DivHarness for #d2]\n * await ch.locatorForAll(DivHarness, IdIsD1Harness)()\n * // Gets []\n * await ch.locatorForAll('span')()\n * ```\n *\n * @param queries A list of queries specifying which harnesses and elements to search for:\n * - A `string` searches for elements matching the CSS selector specified by the string.\n * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the\n * given class.\n * - A `HarnessPredicate` searches for `ComponentHarness` instances matching the given\n * predicate.\n * @return An asynchronous locator function that searches for and returns a `Promise` for all\n * elements and harnesses matching the given search criteria. Matches are ordered first by\n * order in the DOM, and second by order in the queries list. If an element matches more than\n * one `ComponentHarness` class, the locator gets an instance of each for the same element. If\n * an element matches multiple `string` selectors, only one `TestElement` instance is returned\n * for that element. The type that the `Promise` resolves to is an array where each element is\n * the union of all result types for each query.\n */\n protected locatorForAll<T extends (HarnessQuery<any> | string)[]>(\n ...queries: T\n ): () => Promise<LocatorFnResult<T>[]> {\n return this.locatorFactory.locatorForAll(...queries);\n }\n\n /**\n * Flushes change detection and async tasks in the Angular zone.\n * In most cases it should not be necessary to call this manually. However, there may be some edge\n * cases where it is needed to fully flush animation events.\n */\n protected async forceStabilize() {\n return this.locatorFactory.forceStabilize();\n }\n\n /**\n * Waits for all scheduled or running async tasks to complete. This allows harness\n * authors to wait for async tasks outside of the Angular zone.\n */\n protected async waitForTasksOutsideAngular() {\n return this.locatorFactory.waitForTasksOutsideAngular();\n }\n}\n\n/**\n * Base class for component harnesses that authors should extend if they anticipate that consumers\n * of the harness may want to access other harnesses within the `<ng-content>` of the component.\n */\nexport abstract class ContentContainerComponentHarness<S extends string = string>\n extends ComponentHarness\n implements HarnessLoader\n{\n /**\n * Gets a `HarnessLoader` that searches for harnesses under the first element matching the given\n * selector within the current harness's content.\n * @param selector The selector for an element in the component's content.\n * @returns A `HarnessLoader` that searches for harnesses under the given selector.\n */\n async getChildLoader(selector: S): Promise<HarnessLoader> {\n return (await this.getRootHarnessLoader()).getChildLoader(selector);\n }\n\n /**\n * Gets a list of `HarnessLoader` for each element matching the given selector under the current\n * harness's cotnent that searches for harnesses under that element.\n * @param selector The selector for elements in the component's content.\n * @returns A list of `HarnessLoader` for each element matching the given selector.\n */\n async getAllChildLoaders(selector: S): Promise<HarnessLoader[]> {\n return (await this.getRootHarnessLoader()).getAllChildLoaders(selector);\n }\n\n /**\n * Gets the first matching harness for the given query within the current harness's content.\n * @param query The harness query to search for.\n * @returns The first harness matching the given query.\n * @throws If no matching harness is found.\n */\n async getHarness<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<T> {\n return (await this.getRootHarnessLoader()).getHarness(query);\n }\n\n /**\n * Gets the first matching harness for the given query within the current harness's content.\n * @param query The harness query to search for.\n * @returns The first harness matching the given query, or null if none is found.\n */\n async getHarnessOrNull<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<T | null> {\n return (await this.getRootHarnessLoader()).getHarnessOrNull(query);\n }\n\n /**\n * Gets all matching harnesses for the given query within the current harness's content.\n * @param query The harness query to search for.\n * @returns The list of harness matching the given query.\n */\n async getAllHarnesses<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<T[]> {\n return (await this.getRootHarnessLoader()).getAllHarnesses(query);\n }\n\n /**\n * Checks whether there is a matching harnesses for the given query within the current harness's\n * content.\n *\n * @param query The harness query to search for.\n * @returns Whetehr there is matching harnesses for the given query.\n */\n async hasHarness<T extends ComponentHarness>(query: HarnessQuery<T>): Promise<boolean> {\n return (await this.getRootHarnessLoader()).hasHarness(query);\n }\n\n /**\n * Gets the root harness loader from which to start\n * searching for content contained by this harness.\n */\n protected async getRootHarnessLoader(): Promise<HarnessLoader> {\n return this.locatorFactory.rootHarnessLoader();\n }\n}\n\n/**\n * Constructor for a ComponentHarness subclass. To be a valid ComponentHarnessConstructor, the\n * class must also have a static `hostSelector` property.\n */\nexport interface ComponentHarnessConstructor<T extends ComponentHarness> {\n new (locatorFactory: LocatorFactory): T;\n\n /**\n * `ComponentHarness` subclasses must specify a static `hostSelector` property that is used to\n * find the host element for the corresponding component. This property should match the selector\n * for the Angular component.\n */\n hostSelector: string;\n}\n\n/** A set of criteria that can be used to filter a list of `ComponentHarness` instances. */\nexport interface BaseHarnessFilters {\n /** Only find instances whose host element matches the given selector. */\n selector?: string;\n /** Only find instances that are nested under an element with the given selector. */\n ancestor?: string;\n}\n\n/**\n * A class used to associate a ComponentHarness class with predicate functions that can be used to\n * filter instances of the class to be matched.\n */\nexport class HarnessPredicate<T extends ComponentHarness> {\n private _predicates: AsyncPredicate<T>[] = [];\n private _descriptions: string[] = [];\n private _ancestor: string;\n\n constructor(\n public harnessType: ComponentHarnessConstructor<T>,\n options: BaseHarnessFilters,\n ) {\n this._addBaseOptions(options);\n }\n\n /**\n * Checks if the specified nullable string value matches the given pattern.\n * @param value The nullable string value to check, or a Promise resolving to the\n * nullable string value.\n * @param pattern The pattern the value is expected to match. If `pattern` is a string,\n * `value` is expected to match exactly. If `pattern` is a regex, a partial match is\n * allowed. If `pattern` is `null`, the value is expected to be `null`.\n * @return Whether the value matches the pattern.\n */\n static async stringMatches(\n value: string | null | Promise<string | null>,\n pattern: string | RegExp | null,\n ): Promise<boolean> {\n value = await value;\n if (pattern === null) {\n return value === null;\n } else if (value === null) {\n return false;\n }\n return typeof pattern === 'string' ? value === pattern : pattern.test(value);\n }\n\n /**\n * Adds a predicate function to be run against candidate harnesses.\n * @param description A description of this predicate that may be used in error messages.\n * @param predicate An async predicate function.\n * @return this (for method chaining).\n */\n add(description: string, predicate: AsyncPredicate<T>) {\n this._descriptions.push(description);\n this._predicates.push(predicate);\n return this;\n }\n\n /**\n * Adds a predicate function that depends on an option value to be run against candidate\n * harnesses. If the option value is undefined, the predicate will be ignored.\n * @param name The name of the option (may be used in error messages).\n * @param option The option value.\n * @param predicate The predicate function to run if the option value is not undefined.\n * @return this (for method chaining).\n */\n addOption<O>(name: string, option: O | undefined, predicate: AsyncOptionPredicate<T, O>) {\n if (option !== undefined) {\n this.add(`${name} = ${_valueAsString(option)}`, item => predicate(item, option));\n }\n return this;\n }\n\n /**\n * Filters a list of harnesses on this predicate.\n * @param harnesses The list of harnesses to filter.\n * @return A list of harnesses that satisfy this predicate.\n */\n async filter(harnesses: T[]): Promise<T[]> {\n if (harnesses.length === 0) {\n return [];\n }\n const results = await parallel(() => harnesses.map(h => this.evaluate(h)));\n return harnesses.filter((_, i) => results[i]);\n }\n\n /**\n * Evaluates whether the given harness satisfies this predicate.\n * @param harness The harness to check\n * @return A promise that resolves to true if the harness satisfies this predicate,\n * and resolves to false otherwise.\n */\n async evaluate(harness: T): Promise<boolean> {\n const results = await parallel(() => this._predicates.map(p => p(harness)));\n return results.reduce((combined, current) => combined && current, true);\n }\n\n /** Gets a description of this predicate for use in error messages. */\n getDescription() {\n return this._descriptions.join(', ');\n }\n\n /** Gets the selector used to find candidate elements. */\n getSelector() {\n // We don't have to go through the extra trouble if there are no ancestors.\n if (!this._ancestor) {\n return (this.harnessType.hostSelector || '').trim();\n }\n\n const [ancestors, ancestorPlaceholders] = _splitAndEscapeSelector(this._ancestor);\n const [selectors, selectorPlaceholders] = _splitAndEscapeSelector(\n this.harnessType.hostSelector || '',\n );\n const result: string[] = [];\n\n // We have to add the ancestor to each part of the host compound selector, otherwise we can get\n // incorrect results. E.g. `.ancestor .a, .ancestor .b` vs `.ancestor .a, .b`.\n ancestors.forEach(escapedAncestor => {\n const ancestor = _restoreSelector(escapedAncestor, ancestorPlaceholders);\n return selectors.forEach(escapedSelector =>\n result.push(`${ancestor} ${_restoreSelector(escapedSelector, selectorPlaceholders)}`),\n );\n });\n\n return result.join(', ');\n }\n\n /** Adds base options common to all harness types. */\n private _addBaseOptions(options: BaseHarnessFilters) {\n this._ancestor = options.ancestor || '';\n if (this._ancestor) {\n this._descriptions.push(`has ancestor matching selector \"${this._ancestor}\"`);\n }\n const selector = options.selector;\n if (selector !== undefined) {\n this.add(`host matches selector \"${selector}\"`, async item => {\n return (await item.host()).matchesSelector(selector);\n });\n }\n }\n}\n\n/** Represent a value as a string for the purpose of logging. */\nfunction _valueAsString(value: unknown) {\n if (value === undefined) {\n return 'undefined';\n }\n try {\n // `JSON.stringify` doesn't handle RegExp properly, so we need a custom replacer.\n // Use a character that is unlikely to appear in real strings to denote the start and end of\n // the regex. This allows us to strip out the extra quotes around the value added by\n // `JSON.stringify`. Also do custom escaping on `\"` characters to prevent `JSON.stringify`\n // from escaping them as if they were part of a string.\n const stringifiedValue = JSON.stringify(value, (_, v) =>\n v instanceof RegExp\n ? `◬MAT_RE_ESCAPE◬${v.toString().replace(/\"/g, '◬MAT_RE_ESCAPE◬')}◬MAT_RE_ESCAPE◬`\n : v,\n );\n // Strip out the extra quotes around regexes and put back the manually escaped `\"` characters.\n return stringifiedValue\n .replace(/\"◬MAT_RE_ESCAPE◬|◬MAT_RE_ESCAPE◬\"/g, '')\n .replace(/◬MAT_RE_ESCAPE◬/g, '\"');\n } catch {\n // `JSON.stringify` will throw if the object is cyclical,\n // in this case the best we can do is report the value as `{...}`.\n return '{...}';\n }\n}\n\n/**\n * Splits up a compound selector into its parts and escapes any quoted content. The quoted content\n * has to be escaped, because it can contain commas which will throw throw us off when trying to\n * split it.\n * @param selector Selector to be split.\n * @returns The escaped string where any quoted content is replaced with a placeholder. E.g.\n * `[foo=\"bar\"]` turns into `[foo=__cdkPlaceholder-0__]`. Use `_restoreSelector` to restore\n * the placeholders.\n */\nfunction _splitAndEscapeSelector(selector: string): [parts: string[], placeholders: string[]] {\n const placeholders: string[] = [];\n\n // Note that the regex doesn't account for nested quotes so something like `\"ab'cd'e\"` will be\n // considered as two blocks. It's a bit of an edge case, but if we find that it's a problem,\n // we can make it a bit smarter using a loop. Use this for now since it's more readable and\n // compact. More complete implementation:\n // https://github.com/angular/angular/blob/bd34bc9e89f18a/packages/compiler/src/shadow_css.ts#L655\n const result = selector.replace(/([\"'][^[\"']*[\"'])/g, (_, keep) => {\n const replaceBy = `__cdkPlaceholder-${placeholders.length}__`;\n placeholders.push(keep);\n return replaceBy;\n });\n\n return [result.split(',').map(part => part.trim()), placeholders];\n}\n\n/** Restores a selector whose content was escaped in `_splitAndEscapeSelector`. */\nfunction _restoreSelector(selector: string, placeholders: string[]): string {\n return selector.replace(/__cdkPlaceholder-(\\d+)__/g, (_, index) => placeholders[+index]);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {parallel} from './change-detection';\nimport {\n ComponentHarness,\n ComponentHarnessConstructor,\n HarnessLoader,\n HarnessPredicate,\n HarnessQuery,\n LocatorFactory,\n LocatorFnResult,\n} from './component-harness';\nimport {TestElement} from './test-element';\n\n/** Parsed form of the queries passed to the `locatorFor*` methods. */\ntype ParsedQueries<T extends ComponentHarness> = {\n /** The full list of queries, in their original order. */\n allQueries: (string | HarnessPredicate<T>)[];\n /**\n * A filtered view of `allQueries` containing only the queries that are looking for a\n * `ComponentHarness`\n */\n harnessQueries: HarnessPredicate<T>[];\n /**\n * A filtered view of `allQueries` containing only the queries that are looking for a\n * `TestElement`\n */\n elementQueries: string[];\n /** The set of all `ComponentHarness` subclasses represented in the original query list. */\n harnessTypes: Set<ComponentHarnessConstructor<T>>;\n};\n\n/**\n * Base harness environment class that can be extended to allow `ComponentHarness`es to be used in\n * different test environments (e.g. testbed, protractor, etc.). This class implements the\n * functionality of both a `HarnessLoader` and `LocatorFactory`. This class is generic on the raw\n * element type, `E`, used by the particular test environment.\n */\nexport abstract class HarnessEnvironment<E> implements HarnessLoader, LocatorFactory {\n /** The root element of this `HarnessEnvironment` as a `TestElement`. */\n get rootElement(): TestElement {\n this._rootElement = this._rootElement || this.createTestElement(this.rawRootElement);\n return this._rootElement;\n }\n set rootElement(element: TestElement) {\n this._rootElement = element;\n }\n private _rootElement: TestElement | undefined;\n\n protected constructor(\n /** The native root element of this `HarnessEnvironment`. */\n protected rawRootElement: E,\n ) {}\n\n /** Gets a locator factory rooted at the document root. */\n documentRootLocatorFactory(): LocatorFactory {\n return this.createEnvironment(this.getDocumentRoot());\n }\n\n /**\n * Creates an asynchronous locator function that can be used to find a `ComponentHarness` instance\n * or element under the root element of this `HarnessEnvironment`.\n *\n * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'`\n *\n * ```html\n * <div id=\"d1\"></div><div id=\"d2\"></div>\n * ```\n *\n * then we expect:\n *\n * ```ts\n * await lf.locatorFor(DivHarness, 'div')() // Gets a `DivHarness` instance for #d1\n * await lf.locatorFor('div', DivHarness)() // Gets a `TestElement` instance for #d1\n * await lf.locatorFor('span')() // Throws because the `Promise` rejects\n * ```\n *\n * @param queries A list of queries specifying which harnesses and elements to search for:\n * - A `string` searches for elements matching the CSS selector specified by the string.\n * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the\n * given class.\n * - A `HarnessPredicate` searches for `ComponentHarness` instances matching the given\n * predicate.\n * @return An asynchronous locator function that searches for and returns a `Promise` for the\n * first element or harness matching the given search criteria. Matches are ordered first by\n * order in the DOM, and second by order in the queries list. If no matches are found, the\n * `Promise` rejects. The type that the `Promise` resolves to is a union of all result types for\n * each query.\n */\n locatorFor<T extends (HarnessQuery<any> | string)[]>(\n ...queries: T\n ): () => Promise<LocatorFnResult<T>> {\n return () =>\n _assertResultFound(\n this._getAllHarnessesAndTestElements(queries),\n _getDescriptionForLocatorForQueries(queries),\n );\n }\n\n /**\n * Creates an asynchronous locator function that can be used to find a `ComponentHarness` instance\n * or element under the root element of this `HarnessEnvironmnet`.\n *\n * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'`\n *\n * ```html\n * <div id=\"d1\"></div><div id=\"d2\"></div>\n * ```\n *\n * then we expect:\n *\n * ```ts\n * await lf.locatorForOptional(DivHarness, 'div')() // Gets a `DivHarness` instance for #d1\n * await lf.locatorForOptional('div', DivHarness)() // Gets a `TestElement` instance for #d1\n * await lf.locatorForOptional('span')() // Gets `null`\n * ```\n *\n * @param queries A list of queries specifying which harnesses and elements to search for:\n * - A `string` searches for elements matching the CSS selector specified by the string.\n * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the\n * given class.\n * - A `HarnessPredicate` searches for `ComponentHarness` instances matching the given\n * predicate.\n * @return An asynchronous locator function that searches for and returns a `Promise` for the\n * first element or harness matching the given search criteria. Matches are ordered first by\n * order in the DOM, and second by order in the queries list. If no matches are found, the\n * `Promise` is resolved with `null`. The type that the `Promise` resolves to is a union of all\n * result types for each query or null.\n */\n locatorForOptional<T extends (HarnessQuery<any> | string)[]>(\n ...queries: T\n ): () => Promise<LocatorFnResult<T> | null> {\n return async () => (await this._getAllHarnessesAndTestElements(queries))[0] || null;\n }\n\n /**\n * Creates an asynchronous locator function that can be used to find `ComponentHarness` instances\n * or elements under the root element of this `HarnessEnvironment`.\n *\n * For example, given the following DOM and assuming `DivHarness.hostSelector` is `'div'` and\n * `IdIsD1Harness.hostSelector` is `'#d1'`\n *\n * ```html\n * <div id=\"d1\"></div><div id=\"d2\"></div>\n * ```\n *\n * then we expect:\n *\n * ```ts\n * // Gets [DivHarness for #d1, TestElement for #d1, DivHarness for #d2, TestElement for #d2]\n * await lf.locatorForAll(DivHarness, 'div')()\n * // Gets [TestElement for #d1, TestElement for #d2]\n * await lf.locatorForAll('div', '#d1')()\n * // Gets [DivHarness for #d1, IdIsD1Harness for #d1, DivHarness for #d2]\n * await lf.locatorForAll(DivHarness, IdIsD1Harness)()\n * // Gets []\n * await lf.locatorForAll('span')()\n * ```\n *\n * @param queries A list of queries specifying which harnesses and elements to search for:\n * - A `string` searches for elements matching the CSS selector specified by the string.\n * - A `ComponentHarness` constructor searches for `ComponentHarness` instances matching the\n * given class.\n * - A `HarnessPredicate` searches for `ComponentHarness` instances matching the given\n * predicate.\n * @return An asynchronous locator function that searches for and returns a `Promise` for all\n * elements and harnesses matching the given search criteria. Matches are ordered first by\n * order in the DOM, and second by order in the queries list. If an element matches more than\n * one `ComponentHarness` class, the locator gets an instance of each for the same element. If\n * an element matches multiple `string` selectors, only one `TestElement` instance is returned\n * for that element. The type that the `Promise` resolves to is an array where each element is\n * the union of all result types for each query.\n */\n locatorForAll<T extends (HarnessQuery<any> | string)[]>(\n ...queries: T\n ): () => Promise<LocatorFnResult<T>[]> {\n return () => this._getAllHarnessesAndTestElements(queries);\n }\n\n /** @return A `HarnessLoader` rooted at the root element of this `HarnessEnvironment`. */\n async rootHarnessLoader(): Promise<HarnessLoader> {\n return this;\n }\n\n /**\n * Gets a `HarnessLoader` instance for an element under the root of this `HarnessEnvironment`.\n * @param selector The selector for the root element.\n * @return A `HarnessLoader` rooted at the first element matching the given selector.\n * @throws If no matching element is found for the given selector.\n */\n async harnessLoaderFor(selector: string): Promise<HarnessLoader> {\n return this.createEnvironment(\n await