@bitforgehq/ui 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +132 -0
- package/README.md +4 -4
- package/ai/MANIFEST.index.json +36 -0
- package/ai/MANIFEST.json +436 -24
- package/ai/REFERENCE.index.json +35 -0
- package/ai/REFERENCE.json +202 -10
- package/ai/UI_GUIDE.md +25 -4
- package/ai/components/app-frame.md +14 -7
- package/ai/components/badge.md +39 -2
- package/ai/components/header.md +9 -0
- package/ai/components/list-item.md +105 -9
- package/ai/components/step.md +112 -0
- package/ai/components/steps.md +124 -0
- package/ai/components/table.md +119 -5
- package/ai/components/timeline-event.md +144 -0
- package/ai/components/timeline.md +201 -0
- package/ai/components/toggle.md +11 -1
- package/ai/reference/bf-action-directive.md +3 -1
- package/ai/reference/bf-badge-emphasis.md +27 -0
- package/ai/reference/bf-labels.md +2 -0
- package/ai/reference/bf-select-harness.md +4 -0
- package/ai/reference/bf-step-heading-level.md +28 -0
- package/ai/reference/bf-table-align.md +1 -1
- package/ai/reference/bf-table-column-align.md +33 -0
- package/ai/reference/bf-table-column.md +7 -7
- package/ai/reference/bf-table-harness.md +18 -0
- package/ai/reference/bf-timeline-tone.md +31 -0
- package/ai/reference/bf-toggle-harness.md +55 -0
- package/ai/reference/provide-bf-labels.md +2 -2
- package/dark-ink.css +124 -0
- package/fesm2022/bitforgehq-ui-mobile.mjs +13 -3
- package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-testing.mjs +55 -2
- package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui.mjs +714 -46
- package/fesm2022/bitforgehq-ui.mjs.map +1 -1
- package/llms-full.txt +1128 -26
- package/llms.txt +9 -0
- package/package.json +2 -1
- package/theme.css +37 -0
- package/types/bitforgehq-ui-testing.d.ts +90 -1
- package/types/bitforgehq-ui.d.ts +306 -18
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bitforgehq-ui-testing.mjs","names":[],"sources":["../../../projects/ui/testing/jsdom-shims.ts","../../../projects/ui/testing/provide-bf-testing.ts","../../../projects/ui/testing/harnesses/select.harness.ts","../../../projects/ui/testing/harnesses/radio-group.harness.ts","../../../projects/ui/testing/harnesses/segmented.harness.ts","../../../projects/ui/testing/harnesses/dialog.harness.ts","../../../projects/ui/testing/harnesses/menu.harness.ts","../../../projects/ui/testing/harnesses/menu-button.harness.ts","../../../projects/ui/testing/harnesses/desktop-toast.harness.ts","../../../projects/ui/testing/harnesses/table.harness.ts","../../../projects/ui/testing/harnesses/pagination.harness.ts","../../../projects/ui/testing/harnesses/tab-group.harness.ts","../../../projects/ui/testing/harnesses/side-panel.harness.ts","../../../projects/ui/testing/harnesses/calendar.harness.ts","../../../projects/ui/testing/harnesses/tooltip.harness.ts","../../../projects/ui/testing/harnesses/popover.harness.ts","../../../projects/ui/testing/harnesses/combobox.harness.ts","../../../projects/ui/testing/harnesses/date-time-field.ts","../../../projects/ui/testing/harnesses/date-input.harness.ts","../../../projects/ui/testing/harnesses/time-input.harness.ts","../../../projects/ui/testing/harnesses/datetime-input.harness.ts","../../../projects/ui/testing/harnesses/chart.harness.ts","../../../projects/ui/testing/harnesses/sidebar-toggle.harness.ts"],"sourcesContent":["/**\n * Options of `installBfJsdomShims()`.\n *\n * @summary Options of the jsdom shims: the matchMedia stub and its reduced-motion answer\n * @since 0.3.0\n */\nexport interface BfJsdomShimOptions {\n /**\n * Install a `matchMedia` stub where missing (all queries false).\n * @default true\n */\n matchMedia?: boolean;\n /**\n * With the stub: report `(prefers-reduced-motion: reduce)` as matching, so overlays use their\n * short fade. Has no effect where the environment already has `matchMedia`.\n * @default false\n */\n reducedMotion?: boolean;\n}\n\nconst REDUCED_MOTION_QUERY = /\\(\\s*prefers-reduced-motion\\s*:\\s*reduce\\s*\\)/;\n\n/**\n * A running `Animation` stand-in for jsdom's `Element.prototype.animate`: `finished` resolves at\n * once, and the controls set `playState`. Implements the interface in full (no cast), the same\n * way `BfFinishedAnimation` in `overlay/core/overlay-animations.ts` does for its own, always-done\n * stand-in.\n */\nclass BfShimAnimation extends EventTarget implements Animation {\n currentTime: CSSNumberish | null = 0;\n effect: AnimationEffect | null = null;\n id = '';\n oncancel: ((this: Animation, ev: AnimationPlaybackEvent) => unknown) | null = null;\n onfinish: ((this: Animation, ev: AnimationPlaybackEvent) => unknown) | null = null;\n onremove: ((this: Animation, ev: Event) => unknown) | null = null;\n readonly overallProgress: number | null = 1;\n readonly pending = false;\n playState: AnimationPlayState = 'running';\n playbackRate = 1;\n readonly finished: Promise<Animation> = Promise.resolve(this);\n readonly ready: Promise<Animation> = Promise.resolve(this);\n readonly replaceState: AnimationReplaceState = 'active';\n startTime: CSSNumberish | null = null;\n timeline: AnimationTimeline | null = null;\n\n cancel(): void {\n this.playState = 'idle';\n }\n commitStyles(): void {\n // The end state is already on the element.\n }\n finish(): void {\n this.playState = 'finished';\n }\n pause(): void {\n this.playState = 'paused';\n }\n persist(): void {\n // Nothing to persist.\n }\n play(): void {\n this.playState = 'running';\n }\n reverse(): void {\n // Nothing to reverse.\n }\n updatePlaybackRate(rate: number): void {\n this.playbackRate = rate;\n }\n}\n\n/** A `MouseEvent` carrying the fields of `PointerEventInit`, with the specification's defaults. */\nclass BfPointerEventShim extends MouseEvent {\n readonly pointerId: number;\n readonly isPrimary: boolean;\n readonly pointerType: string;\n readonly width: number;\n readonly height: number;\n readonly pressure: number;\n\n constructor(type: string, init: PointerEventInit = {}) {\n super(type, init);\n this.pointerId = init.pointerId ?? 0;\n this.isPrimary = init.isPrimary ?? false;\n this.pointerType = init.pointerType ?? '';\n this.width = init.width ?? 1;\n this.height = init.height ?? 1;\n this.pressure = init.pressure ?? 0;\n }\n}\n\n/** A `MediaQueryList` stand-in that never changes. */\nfunction staticMediaQueryList(media: string, matches: boolean): MediaQueryList {\n return {\n matches,\n media,\n onchange: null,\n addListener: (): void => undefined,\n removeListener: (): void => undefined,\n addEventListener: (): void => undefined,\n removeEventListener: (): void => undefined,\n dispatchEvent: (): boolean => false,\n };\n}\n\n/**\n * Sets `target[key]` and returns the function that puts back what was there, or `null` when the\n * property cannot be set (then nothing changed). A new property is writable and configurable, so\n * it can be deleted again; a configurable existing one is restored from its descriptor, getters\n * included. An existing non-configurable one (a runner's own `matchMedia` mock, assigned\n * `undefined` to test without it) is assigned through its setter or value and restored the same\n * way; a non-writable one without a setter is left alone. Internal: not exported from the entry.\n */\nexport function installGlobal(target: object, key: string, value: unknown): (() => void) | null {\n const previous = Object.getOwnPropertyDescriptor(target, key);\n if (previous && !previous.configurable) {\n const before: unknown = previous.get ? previous.get.call(target) : previous.value;\n if (!Reflect.set(target, key, value)) {\n return null;\n }\n return () => {\n Reflect.set(target, key, before);\n };\n }\n Object.defineProperty(target, key, { value, writable: true, configurable: true });\n return () => {\n if (previous) {\n Object.defineProperty(target, key, previous);\n } else {\n Reflect.deleteProperty(target, key);\n }\n };\n}\n\n/**\n * Installs, only where missing: `Element.prototype.animate` (an Animation whose `finished`\n * resolves at once), `PointerEvent` (a MouseEvent subclass with pointerId, isPrimary, pointerType,\n * width, height, pressure) and optionally `matchMedia`. Idempotent; returns a function that removes\n * exactly what it installed. Call it once in the test runner's setup file (Vitest `setupFiles`,\n * Jest `setupFilesAfterEnv`).\n *\n * @example\n * // src/test-setup.ts\n * import { installBfJsdomShims } from '@bitforgehq/ui/testing';\n * installBfJsdomShims();\n * @param options Whether to stub `matchMedia`, and whether the stub reports reduced motion.\n * @returns A function that removes the shims this call installed.\n * @summary Installs the browser APIs jsdom lacks and the library needs, only where missing\n * @since 0.3.0\n */\nexport function installBfJsdomShims(options: BfJsdomShimOptions = {}): () => void {\n const removers: (() => void)[] = [];\n const add = (remove: (() => void) | null): void => {\n if (remove) {\n removers.push(remove);\n }\n };\n\n if (typeof Element !== 'undefined' && typeof Element.prototype.animate !== 'function') {\n add(\n installGlobal(\n Element.prototype,\n 'animate',\n function animate(\n _keyframes: Keyframe[] | PropertyIndexedKeyframes | null,\n _timing?: number | KeyframeAnimationOptions,\n ): Animation {\n return new BfShimAnimation();\n },\n ),\n );\n }\n\n if (typeof globalThis.PointerEvent !== 'function' && typeof MouseEvent === 'function') {\n add(installGlobal(globalThis, 'PointerEvent', BfPointerEventShim));\n }\n\n if (\n (options.matchMedia ?? true) &&\n typeof window !== 'undefined' &&\n typeof window.matchMedia !== 'function'\n ) {\n const reducedMotion = options.reducedMotion ?? false;\n const media = (query: string): MediaQueryList =>\n staticMediaQueryList(query, reducedMotion && REDUCED_MOTION_QUERY.test(query));\n add(installGlobal(window, 'matchMedia', media));\n }\n\n return () => {\n for (const remove of removers.splice(0).reverse()) {\n remove();\n }\n };\n}\n","import { type EnvironmentProviders, makeEnvironmentProviders, type Provider } from '@angular/core';\nimport { provideBfDefaults, provideBfLabels, ɵprovideBfDocumentReset } from '@bitforgehq/ui';\n\n/**\n * Options of `provideBfTesting()`.\n *\n * @summary Options of the test providers: the size defaults and labels a spec runs with\n * @since 0.3.0\n */\nexport interface BfTestingOptions {\n /** Passed to `provideBfDefaults()`, e.g. `{ fieldSize: 'sm' }`; left out, the library defaults apply. */\n defaults?: Parameters<typeof provideBfDefaults>[0];\n /** Passed to `provideBfLabels()`, e.g. German validation messages; left out, the English defaults apply. */\n labels?: Parameters<typeof provideBfLabels>[0];\n}\n\n/**\n * Test providers for apps using the root and overlay entries: passes `defaults`/`labels` to 3b's\n * providers, and on environment teardown removes what overlays and themes leave on the document —\n * `dark` and `cdk-global-scrollblock` on `<html>`, `inert` on body children, stray\n * `.cdk-overlay-container` children. The cleanup runs when TestBed destroys the test environment,\n * so it needs TestBed's default `teardown: { destroyAfterEach: true }`; with `destroyAfterEach:\n * false` nothing is cleaned between tests. Mobile shell apps use `provideBfShellTesting()` from\n * `@bitforgehq/ui/mobile` instead.\n *\n * @example\n * TestBed.configureTestingModule({\n * providers: [provideBfTesting({ defaults: { fieldSize: 'sm' } })],\n * });\n * @param options The size defaults and labels to provide.\n * @returns The environment providers for `TestBed.configureTestingModule()`.\n * @summary Test providers for the widgets and overlays, with document cleanup after each test\n * @since 0.3.0\n */\nexport function provideBfTesting(options: BfTestingOptions = {}): EnvironmentProviders {\n const providers: Provider[] = [];\n if (options.defaults) {\n providers.push(provideBfDefaults(options.defaults));\n }\n if (options.labels) {\n providers.push(provideBfLabels(options.labels));\n }\n return makeEnvironmentProviders([...providers, ɵprovideBfDocumentReset()]);\n}\n","import { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/** A rendered native option and whether it is one of the select's `options` (not the placeholder). */\ninterface NativeOption {\n element: TestElement;\n label: string;\n choice: boolean;\n}\n\n/**\n * Harness for `bf-select`. It works by option label, so a test never needs the native option\n * values (an object option's native value is its index).\n *\n * @example\n * const select = await loader.getHarness(BfSelectHarness.with({ label: 'Project' }));\n * await select.selectOption('Two');\n * @summary Test harness for the select: reads its label and options and picks one\n * @since 0.3.0\n */\nexport class BfSelectHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-select';\n\n private readonly select = this.locatorFor('select');\n private readonly label = this.locatorForOptional('label');\n private readonly error = this.locatorForOptional('p[id$=\"-error\"]');\n private readonly help = this.locatorForOptional('p[id$=\"-help\"]');\n private readonly nativeOptions = this.locatorForAll('option');\n\n /**\n * A predicate for a select with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfSelectHarness> {\n return new HarnessPredicate(BfSelectHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The visible label without the required mark, else the `aria-label`.\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n const label = await this.label();\n if (label) {\n return (await label.text({ exclude: '[aria-hidden=\"true\"]' })).trim();\n }\n return (await (await this.select()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The selected option's label, `''` while nothing is selected.\n * @returns The label text.\n */\n async getValueLabel(): Promise<string> {\n for (const option of await this.options()) {\n if (option.choice && (await option.element.getProperty<boolean>('selected'))) {\n return option.label;\n }\n }\n return '';\n }\n\n /**\n * The labels of the options, in order, without the placeholder.\n * @returns The option labels.\n */\n async getOptionLabels(): Promise<string[]> {\n return (await this.options()).filter((option) => option.choice).map((option) => option.label);\n }\n\n /**\n * Chooses the option with this label, as a user would: the select's `value` becomes that\n * option's value. Throws, leaving the value as it is, for an unknown or disabled option and\n * while the select itself is disabled.\n * @param label The option's label.\n */\n async selectOption(label: string): Promise<void> {\n if (await this.isDisabled()) {\n throw new Error(`BfSelectHarness: the select is disabled, so \"${label}\" cannot be chosen`);\n }\n const options = await this.options();\n const option = options.find((o) => o.choice && o.label === label);\n if (!option) {\n const known = options.filter((o) => o.choice).map((o) => o.label);\n throw new Error(`BfSelectHarness: no option \"${label}\" (options: ${known.join(', ')})`);\n }\n if (await option.element.getProperty<boolean>('disabled')) {\n throw new Error(`BfSelectHarness: the option \"${label}\" is disabled`);\n }\n // Not `TestElement.selectOptions()`: it deselects the current option first and fires `change`\n // for that too, so the select would first see the browser's fallback option. A user's pick is\n // one `change` with the new option selected.\n const select = await this.select();\n await select.setInputValue(await option.element.getProperty<string>('value'));\n await select.dispatchEvent('change');\n }\n\n /**\n * Whether the select is disabled.\n * @returns True when disabled.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.select()).getProperty<boolean>('disabled');\n }\n\n /**\n * Whether the select is read-only (`aria-readonly`).\n * @returns True when read-only.\n */\n async isReadonly(): Promise<boolean> {\n return (await (await this.select()).getAttribute('aria-readonly')) === 'true';\n }\n\n /**\n * Whether the select is required.\n * @returns True when required.\n */\n async isRequired(): Promise<boolean> {\n return (await this.select()).getProperty<boolean>('required');\n }\n\n /**\n * The error text under the select (an explicit message, or the first form error once touched).\n * @returns The error text, `''` when there is none.\n */\n async getErrorText(): Promise<string> {\n return (await (await this.error())?.text())?.trim() ?? '';\n }\n\n /**\n * The help text under the select; it is hidden while an error shows.\n * @returns The help text, `''` when there is none.\n */\n async getHelpText(): Promise<string> {\n return (await (await this.help())?.text())?.trim() ?? '';\n }\n\n /** Focuses the native select. */\n async focus(): Promise<void> {\n await (await this.select()).focus();\n }\n\n /** Blurs the native select, which marks a bound form field touched. */\n async blur(): Promise<void> {\n await (await this.select()).blur();\n }\n\n /** Every native option; the placeholder or hidden empty option comes first, empty and disabled. */\n private async options(): Promise<NativeOption[]> {\n return Promise.all(\n (await this.nativeOptions()).map(async (element, index) => {\n const [label, value, hidden, disabled] = await Promise.all([\n element.text(),\n element.getAttribute('value'),\n element.getProperty<boolean>('hidden'),\n element.getProperty<boolean>('disabled'),\n ]);\n const placeholder = index === 0 && (hidden || (value === '' && disabled));\n return { element, label: label.trim(), choice: !placeholder };\n }),\n );\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-radio-group`. It works by option label, so a test never needs the native radio\n * values (an object option's native value is its index).\n *\n * @example\n * const plan = await loader.getHarness(BfRadioGroupHarness.with({ label: 'Plan' }));\n * await plan.select('Team');\n * @summary Test harness for the radio group: reads its options and selects one by label\n * @since 0.3.0\n */\nexport class BfRadioGroupHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-radio-group';\n\n private readonly fieldset = this.locatorFor('fieldset');\n private readonly legend = this.locatorForOptional('legend');\n private readonly error = this.locatorForOptional('p[id$=\"-error\"]');\n private readonly radios = this.locatorForAll('[data-options] > label input[type=\"radio\"]');\n private readonly labels = this.locatorForAll(\n '[data-options] > label > span:last-child > span:first-child',\n );\n\n /**\n * A predicate for a radio group with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfRadioGroupHarness> {\n return new HarnessPredicate(BfRadioGroupHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The legend, else the group's `aria-label`.\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n const legend = await this.legend();\n if (legend) {\n return (await legend.text()).trim();\n }\n return (await (await this.fieldset()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The labels of the options, in order.\n * @returns The option labels.\n */\n async getOptionLabels(): Promise<string[]> {\n return Promise.all((await this.labels()).map(async (label) => (await label.text()).trim()));\n }\n\n /**\n * The selected option's label, `null` while nothing is selected.\n * @returns The label text or `null`.\n */\n async getSelectedLabel(): Promise<string | null> {\n const [radios, labels] = await Promise.all([this.radios(), this.getOptionLabels()]);\n for (const [index, radio] of radios.entries()) {\n if (await radio.getProperty<boolean>('checked')) {\n return labels[index] ?? null;\n }\n }\n return null;\n }\n\n /**\n * Clicks the option with this label, as a user would. Throws for an unknown label, and for a\n * disabled option or group (a user's click would do nothing).\n * @param label The option's label.\n */\n async select(label: string): Promise<void> {\n const [radios, labels] = await Promise.all([this.radios(), this.getOptionLabels()]);\n const radio = radios[labels.indexOf(label)];\n if (!radio) {\n throw new Error(`BfRadioGroupHarness: no option \"${label}\" (options: ${labels.join(', ')})`);\n }\n // `:disabled` also covers a radio inside the disabled fieldset of a disabled group.\n if (await radio.matchesSelector(':disabled')) {\n throw new Error(`BfRadioGroupHarness: the option \"${label}\" is disabled`);\n }\n await radio.click();\n }\n\n /**\n * Whether the whole group is disabled.\n * @returns True when disabled.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.fieldset()).getProperty<boolean>('disabled');\n }\n\n /**\n * The error text under the options.\n * @returns The error text, `''` when there is none.\n */\n async getErrorText(): Promise<string> {\n return (await (await this.error())?.text())?.trim() ?? '';\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-segmented`. It works by segment label, so a test never needs the option values.\n *\n * @example\n * const range = await loader.getHarness(BfSegmentedHarness.with({ label: 'Range' }));\n * await range.select('Week');\n * @summary Test harness for the segmented control: reads its segments and selects one\n * @since 0.3.0\n */\nexport class BfSegmentedHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-segmented';\n\n private readonly group = this.locatorFor('[role=\"radiogroup\"]');\n private readonly error = this.locatorForOptional('p[id$=\"-error\"]');\n private readonly segments = this.locatorForAll('[role=\"radio\"]');\n\n /**\n * A predicate for a segmented control with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfSegmentedHarness> {\n return new HarnessPredicate(BfSegmentedHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The group's accessible name (`label`, else `ariaLabel`); it is not rendered visibly.\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n return (await (await this.group()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The labels of the segments, in order.\n * @returns The segment labels.\n */\n async getOptionLabels(): Promise<string[]> {\n return Promise.all(\n (await this.segments()).map(async (segment) => (await segment.text()).trim()),\n );\n }\n\n /**\n * The selected segment's label, `null` while nothing is selected.\n * @returns The label text or `null`.\n */\n async getSelectedLabel(): Promise<string | null> {\n for (const segment of await this.segments()) {\n if ((await segment.getAttribute('aria-checked')) === 'true') {\n return (await segment.text()).trim();\n }\n }\n return null;\n }\n\n /**\n * Clicks the segment with this label, as a user would. Throws for an unknown label, and for a\n * disabled segment or control (a user's click would do nothing).\n * @param label The segment's label.\n */\n async select(label: string): Promise<void> {\n const [segments, labels] = await Promise.all([this.segments(), this.getOptionLabels()]);\n const segment = segments[labels.indexOf(label)];\n if (!segment) {\n throw new Error(`BfSegmentedHarness: no segment \"${label}\" (segments: ${labels.join(', ')})`);\n }\n if (await segment.matchesSelector(':disabled')) {\n throw new Error(`BfSegmentedHarness: the segment \"${label}\" is disabled`);\n }\n await segment.click();\n }\n\n /**\n * Whether the control is disabled: every segment is.\n * @returns True when disabled.\n */\n async isDisabled(): Promise<boolean> {\n const segments = await this.segments();\n const disabled = await Promise.all(\n segments.map((segment) => segment.getProperty<boolean>('disabled')),\n );\n return segments.length > 0 && disabled.every(Boolean);\n }\n\n /**\n * The error text under the track.\n * @returns The error text, `''` when there is none.\n */\n async getErrorText(): Promise<string> {\n return (await (await this.error())?.text())?.trim() ?? '';\n }\n}\n","import { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-dialog`, the card `BfDialogService` opens (a `confirm()` alert dialog is built\n * from it too). Dialogs render in the CDK overlay container outside the tested component, so get\n * it through `TestbedHarnessEnvironment.documentRootLoader(fixture)`.\n *\n * @example\n * const dialog = await TestbedHarnessEnvironment.documentRootLoader(fixture).getHarness(\n * BfDialogHarness.with({ label: 'Delete project?' }),\n * );\n * await dialog.clickButton('Delete');\n * @summary Test harness for a dialog: its title, text and role, its buttons and Escape\n * @since 0.3.0\n */\nexport class BfDialogHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-dialog';\n\n private readonly title = this.locatorForOptional('[data-dialog-header] h2');\n private readonly body = this.locatorFor('[data-dialog-body]');\n private readonly closeButton = this.locatorForOptional('[data-dialog-close] button');\n private readonly buttons = this.locatorForAll('button');\n\n /**\n * A predicate for a dialog with the given title.\n * @param filters The title (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfDialogHarness> {\n return new HarnessPredicate(BfDialogHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getTitle(), label),\n );\n }\n\n /**\n * The title heading.\n * @returns The title, `''` without one.\n */\n async getTitle(): Promise<string> {\n return (await (await this.title())?.text())?.trim() ?? '';\n }\n\n /**\n * The text of the dialog's body (its content, without the title and footer).\n * @returns The body text.\n */\n async getText(): Promise<string> {\n return (await (await this.body()).text()).trim();\n }\n\n /**\n * The role of the overlay panel this dialog sits in: `dialog`, or `alertdialog` for\n * `confirm()` and `role: 'alertdialog'`. The panel is found by the dialog's own\n * `data-bf-dialog-id`, so a dialog under another one (a confirm opened from a dialog) reads its\n * own panel.\n * @returns The role, `null` outside `BfDialogService` (a `bf-dialog` rendered in the page).\n */\n async getRole(): Promise<string | null> {\n const id = await (await this.host()).getAttribute('data-bf-dialog-id');\n if (!id) {\n return null;\n }\n const root = this.documentRootLocatorFactory();\n for (const panel of await root.locatorForAll('.bf-overlay-panel[id]')()) {\n const paneId = await panel.getAttribute('id');\n const own = await root.locatorForOptional(\n `[id=\"${paneId}\"] bf-dialog[data-bf-dialog-id=\"${id}\"]`,\n )();\n if (own) {\n return panel.getAttribute('role');\n }\n }\n return null;\n }\n\n /**\n * Clicks the button with this text (a footer or content button). Throws for an unknown label.\n * @param label The button's text.\n */\n async clickButton(label: string): Promise<void> {\n const buttons = await this.buttons();\n const texts = await Promise.all(buttons.map(async (button) => (await button.text()).trim()));\n const button = buttons[texts.indexOf(label)];\n if (!button) {\n const known = texts.filter(Boolean);\n throw new Error(`BfDialogHarness: no button \"${label}\" (buttons: ${known.join(', ')})`);\n }\n await button.click();\n }\n\n /** Clicks the close button (×), which closes the dialog with no result. Throws without one. */\n async close(): Promise<void> {\n const button = await this.closeButton();\n if (!button) {\n throw new Error('BfDialogHarness: the dialog has no close button');\n }\n await button.click();\n }\n\n /** Presses `Escape` inside the dialog: it closes, or a `confirm()` resolves its cancel answer. */\n async pressEscape(): Promise<void> {\n await (await this.body()).sendKeys(TestKey.ESCAPE);\n }\n}\n","import { ComponentHarness, HarnessPredicate, type LocatorFactory } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for one row of a `bf-menu`: a `bfMenuItem`, `bfMenuItemCheckbox` or `bfMenuItemRadio`\n * button (or `a[bfMenuItem]` link). Its `label` filter matches the row's text.\n *\n * @example\n * const [first] = await menu.getItems({ label: 'Duplicate' });\n * expect(await first.isDisabled()).toBe(false);\n * @summary Test harness for a menu row: its text, disabled, checked and submenu state\n * @since 0.3.0\n */\nexport class BfMenuItemHarness extends ComponentHarness {\n /** The host element's selector: the menu item directives' selectors. */\n static hostSelector =\n 'button[bfMenuItem], a[bfMenuItem], button[bfMenuItemCheckbox], button[bfMenuItemRadio]';\n\n /**\n * A predicate for a menu item with the given text.\n * @param filters The text (exact or a pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfMenuItemHarness> {\n return new HarnessPredicate(BfMenuItemHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getText(), label),\n );\n }\n\n /**\n * The row's text.\n * @returns The text.\n */\n async getText(): Promise<string> {\n return (await (await this.host()).text()).trim();\n }\n\n /**\n * Whether the row is disabled (`aria-disabled`; a disabled row stays focusable).\n * @returns True when disabled.\n */\n async isDisabled(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-disabled')) === 'true';\n }\n\n /**\n * Whether a checkbox or radio row is checked.\n * @returns True when checked.\n */\n async isChecked(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-checked')) === 'true';\n }\n\n /**\n * Whether the row opens a submenu.\n * @returns True when it has a submenu.\n */\n async hasSubmenu(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-haspopup')) === 'menu';\n }\n\n /** Clicks the row: runs its action (closing the menu), toggles it, or opens its submenu. */\n async click(): Promise<void> {\n await (await this.host()).click();\n }\n\n /**\n * The submenu this row opened, or `null` while it is closed or when it has none.\n * @returns The submenu's harness or `null`.\n */\n async getSubmenu(): Promise<BfMenuHarness | null> {\n const id = await (await this.host()).getAttribute('aria-controls');\n return id ? menuById(this.documentRootLocatorFactory(), id) : null;\n }\n}\n\n/**\n * Harness for `bf-menu`, the card a `bfMenuTriggerFor` trigger or `bf-menu-button` opens. It\n * renders in the CDK overlay container, so get it from `BfMenuButtonHarness.getMenu()` or through\n * `TestbedHarnessEnvironment.documentRootLoader(fixture)`. Its `label` filter matches the menu's\n * accessible name (its `ariaLabel`, else its trigger's text).\n *\n * @example\n * const menu = await (await loader.getHarness(BfMenuButtonHarness)).getMenu();\n * await menu.clickItem('Move to', 'Archive');\n * @summary Test harness for a menu: lists its items and clicks one, through submenus\n * @since 0.3.0\n */\nexport class BfMenuHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-menu';\n\n /**\n * A predicate for a menu with the given accessible name.\n * @param filters The name (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfMenuHarness> {\n return new HarnessPredicate(BfMenuHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The menu's accessible name: its `aria-label`, else the text of the element it is labelled\n * by (its trigger).\n * @returns The name, `''` without one.\n */\n async getLabel(): Promise<string> {\n const host = await this.host();\n const label = await host.getAttribute('aria-label');\n if (label) {\n return label;\n }\n const by = await host.getAttribute('aria-labelledby');\n const named = by\n ? await this.documentRootLocatorFactory().locatorForOptional(`[id=\"${by}\"]`)()\n : null;\n return (await named?.text())?.trim() ?? '';\n }\n\n /**\n * The menu's rows, in order (a submenu's rows belong to the submenu).\n * @param filter Only the rows matching it, e.g. `{ label: 'Archive' }`.\n * @returns The row harnesses.\n */\n async getItems(filter: BfHarnessFilters = {}): Promise<BfMenuItemHarness[]> {\n return this.locatorForAll(BfMenuItemHarness.with(filter))();\n }\n\n /**\n * Clicks the row with this text; with a `submenuPath`, the row opens a submenu and the path's\n * labels are clicked in the submenus that open, in order. Throws for an unknown label.\n * @param label The row's text.\n * @param submenuPath The labels to click in the submenus that open, in order.\n */\n async clickItem(label: string, ...submenuPath: string[]): Promise<void> {\n const [item] = await this.getItems({ label });\n if (!item) {\n const known = await Promise.all((await this.getItems()).map((i) => i.getText()));\n throw new Error(`BfMenuHarness: no item \"${label}\" (items: ${known.join(', ')})`);\n }\n await item.click();\n const [next, ...rest] = submenuPath;\n if (next === undefined) {\n return;\n }\n const submenu = await item.getSubmenu();\n if (!submenu) {\n throw new Error(`BfMenuHarness: the item \"${label}\" opened no submenu`);\n }\n await submenu.clickItem(next, ...rest);\n }\n}\n\n/** The `bf-menu` with this id in the document, or `null`. Internal. */\nexport async function menuById(root: LocatorFactory, id: string): Promise<BfMenuHarness | null> {\n return root.locatorForOptional(BfMenuHarness.with({ selector: `[id=\"${id}\"]` }))();\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\nimport { type BfMenuHarness, menuById } from './menu.harness';\n\n/**\n * Harness for `bf-menu-button`, a button that opens a `bf-menu`. Its `label` filter matches the\n * button's accessible name (its `label`, else its text).\n *\n * @example\n * const button = await loader.getHarness(BfMenuButtonHarness.with({ label: 'Actions' }));\n * await (await button.getMenu()).clickItem('Archive');\n * @summary Test harness for a menu button: opens it and hands out the menu it opened\n * @since 0.3.0\n */\nexport class BfMenuButtonHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-menu-button';\n\n private readonly button = this.locatorFor('button');\n\n /**\n * A predicate for a menu button with the given accessible name.\n * @param filters The name (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfMenuButtonHarness> {\n return new HarnessPredicate(BfMenuButtonHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The button's accessible name: its `aria-label` (the `label` input), else its text.\n * @returns The name.\n */\n async getLabel(): Promise<string> {\n const button = await this.button();\n return (await button.getAttribute('aria-label')) ?? (await button.text()).trim();\n }\n\n /** Opens the menu with a click; does nothing while it is open. */\n async open(): Promise<void> {\n if (!(await this.isOpen())) {\n await (await this.button()).click();\n }\n }\n\n /**\n * Whether the menu is open (`aria-expanded`).\n * @returns True while open.\n */\n async isOpen(): Promise<boolean> {\n return (await (await this.button()).getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * The open menu, opening it first when it is closed.\n * @returns The menu's harness.\n */\n async getMenu(): Promise<BfMenuHarness> {\n await this.open();\n const id = await (await this.button()).getAttribute('aria-controls');\n const menu = id ? await menuById(this.documentRootLocatorFactory(), id) : null;\n if (!menu) {\n throw new Error('BfMenuButtonHarness: the menu did not open');\n }\n return menu;\n }\n}\n","import { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-desktop-toast`, the card `BfDesktopToastService.show()` renders. Toasts render\n * in the CDK overlay container, so get it through\n * `TestbedHarnessEnvironment.documentRootLoader(fixture)`.\n *\n * @example\n * const toast = await TestbedHarnessEnvironment.documentRootLoader(fixture).getHarness(\n * BfDesktopToastHarness,\n * );\n * await toast.clickAction();\n * @summary Test harness for a desktop toast: its message, its action and Escape\n * @since 0.3.0\n */\nexport class BfDesktopToastHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-desktop-toast';\n\n private readonly message = this.locatorFor('span');\n private readonly action = this.locatorForOptional('button');\n\n /**\n * A predicate for a toast with the given message.\n * @param filters The message (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfDesktopToastHarness> {\n return new HarnessPredicate(BfDesktopToastHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getMessage(), label),\n );\n }\n\n /**\n * The toast's message.\n * @returns The message text.\n */\n async getMessage(): Promise<string> {\n return (await (await this.message()).text()).trim();\n }\n\n /** Clicks the action button, which dismisses the toast with `'action'`. Throws without one. */\n async clickAction(): Promise<void> {\n const action = await this.action();\n if (!action) {\n throw new Error('BfDesktopToastHarness: the toast has no action');\n }\n await action.click();\n }\n\n /** Presses `Escape` inside the toast, which dismisses it with `'dismiss'`. */\n async pressEscape(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.ESCAPE);\n }\n}\n","import { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\nimport { type BfTableSort } from '@bitforgehq/ui';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-table`. It works by column header text and row position, so a test never needs\n * the column keys or the row keys (except `getSort()`, which returns the column's `key`, as the\n * `sort` model holds it). It reads and drives a stacked table (`stack`) the same way.\n *\n * @example\n * const tasks = await loader.getHarness(BfTableHarness.with({ label: 'Tasks' }));\n * await tasks.sortBy('Title');\n * expect(await tasks.getCellText(0, 'Title')).toBe('Atlas rebrand');\n * @summary Test harness for the data table: reads rows and cells, sorts and selects rows\n * @since 0.3.1\n */\nexport class BfTableHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-table';\n\n private readonly caption = this.locatorForOptional('caption');\n private readonly headers = this.locatorForAll('thead th[data-bf-column]');\n private readonly rows = this.locatorForAll('tbody tr[data-bf-table-row]');\n private readonly selectAllBox = this.locatorForOptional('thead input[type=\"checkbox\"]');\n private readonly rowBoxes = this.locatorForAll(\n 'tbody tr[data-bf-table-row] input[type=\"checkbox\"]',\n );\n\n /**\n * A predicate for a table with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfTableHarness> {\n return new HarnessPredicate(BfTableHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The table's name, its visually hidden caption.\n * @returns The label text, `''` without one.\n */\n async getLabel(): Promise<string> {\n return (await (await this.caption())?.text())?.trim() ?? '';\n }\n\n /**\n * The number of body rows; `0` while the empty slot shows.\n * @returns The row count.\n */\n async getRowCount(): Promise<number> {\n return (await this.rows()).length;\n }\n\n /**\n * The column headers' text, in order, without the selection column.\n * @returns The header labels.\n */\n async getHeaderLabels(): Promise<string[]> {\n // A stacked table repeats a sortable header's label outside its hidden button; read it once.\n return Promise.all(\n (await this.headers()).map(async (th) =>\n (await th.text({ exclude: '[data-bf-stacked-label]' })).trim(),\n ),\n );\n }\n\n /**\n * The text of one cell.\n * @param row The row's position, from 0.\n * @param column The column's header text.\n * @returns The cell's text.\n */\n async getCellText(row: number, column: string): Promise<string> {\n const index = await this.columnIndex(column);\n if (row < 0 || row >= (await this.getRowCount())) {\n throw new Error(`BfTableHarness: no row ${row}`);\n }\n const cells = await this.locatorForAll(\n `tbody tr[data-bf-table-row]:nth-of-type(${row + 1}) [data-bf-cell]`,\n )();\n return (await cells[index]?.text())?.trim() ?? '';\n }\n\n /**\n * Clicks a sortable column's header, as a user would: ascending first, then descending.\n * Throws for an unknown or unsortable column.\n * @param column The column's header text.\n */\n async sortBy(column: string): Promise<void> {\n const key = await (\n await this.headers()\n )[await this.columnIndex(column)]?.getAttribute('data-bf-column');\n const button = key\n ? await this.locatorForOptional(`thead th[data-bf-column=\"${key}\"] button`)()\n : null;\n if (!button) {\n throw new Error(`BfTableHarness: the column \"${column}\" is not sortable`);\n }\n await button.click();\n }\n\n /**\n * The sorted column (its `key`) and direction, from `aria-sort`.\n * @returns The sort, `null` while no column is sorted.\n */\n async getSort(): Promise<BfTableSort | null> {\n for (const header of await this.headers()) {\n const direction = await header.getAttribute('aria-sort');\n const column = await header.getAttribute('data-bf-column');\n if (column && (direction === 'ascending' || direction === 'descending')) {\n return { column, direction };\n }\n }\n return null;\n }\n\n /**\n * Checks one row's checkbox, as a user would; a selected row stays selected. Throws when the\n * table is not selectable or the row does not exist.\n * @param index The row's position, from 0.\n */\n async selectRow(index: number): Promise<void> {\n const box = (await this.rowBoxes())[index];\n if (!box) {\n throw new Error(`BfTableHarness: no selectable row ${index}`);\n }\n await this.check(box);\n }\n\n /**\n * Checks the header checkbox, as a user would, so every row is selected. Throws when the table\n * is not selectable.\n */\n async selectAll(): Promise<void> {\n const box = await this.selectAllBox();\n if (!box) {\n throw new Error('BfTableHarness: the table is not selectable');\n }\n await this.check(box);\n }\n\n /**\n * The number of selected rows (checked row checkboxes).\n * @returns The count.\n */\n async getSelectedCount(): Promise<number> {\n const checked = await Promise.all(\n (await this.rowBoxes()).map((box) => box.getProperty<boolean>('checked')),\n );\n return checked.filter(Boolean).length;\n }\n\n private async check(box: TestElement): Promise<void> {\n if (!(await box.getProperty<boolean>('checked'))) {\n await box.click();\n }\n }\n\n private async columnIndex(column: string): Promise<number> {\n const labels = await this.getHeaderLabels();\n const index = labels.indexOf(column);\n if (index === -1) {\n throw new Error(`BfTableHarness: no column \"${column}\" (columns: ${labels.join(', ')})`);\n }\n return index;\n }\n}\n","import { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-pagination`. It reads the current page and the page count as numbers, so a test\n * never parses the \"Page 3 of 12\" text, and clicks the buttons as a user would.\n *\n * @example\n * const pages = await loader.getHarness(BfPaginationHarness.with({ label: 'Invoice pages' }));\n * await pages.next();\n * expect(await pages.getCurrentPage()).toBe(2);\n * @summary Test harness for pagination: reads the current page and moves between pages\n * @since 0.3.1\n */\nexport class BfPaginationHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-pagination';\n\n private readonly nav = this.locatorFor('nav');\n private readonly previousButton = this.locatorFor('[data-bf-pagination-previous]');\n private readonly nextButton = this.locatorFor('[data-bf-pagination-next]');\n private readonly pages = this.locatorForAll('[data-bf-pagination-page]');\n\n /**\n * A predicate for a pagination with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfPaginationHarness> {\n return new HarnessPredicate(BfPaginationHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The navigation's accessible name (`label`, else \"Pagination\").\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n return (await (await this.nav()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The page shown as current, 1-based.\n * @returns The current page.\n */\n async getCurrentPage(): Promise<number> {\n return Number(await (await this.nav()).getAttribute('data-bf-page'));\n }\n\n /**\n * The number of pages in effect (`pageCount`, else `total` over `pageSize`, at least 1).\n * @returns The page count.\n */\n async getPageCount(): Promise<number> {\n return Number(await (await this.nav()).getAttribute('data-bf-page-count'));\n }\n\n /**\n * The page numbers the row shows right now, in order, without the gaps; empty when compact.\n * @returns The visible page numbers.\n */\n async getVisiblePages(): Promise<number[]> {\n return Promise.all(\n (await this.pages()).map(async (page) => Number((await page.text()).trim())),\n );\n }\n\n /**\n * Clicks the button of this page, as a user would. Throws when the row does not show it (behind\n * a gap, or in a compact pagination).\n * @param page The 1-based page to go to.\n */\n async goTo(page: number): Promise<void> {\n const [buttons, visible] = await Promise.all([this.pages(), this.getVisiblePages()]);\n const button = buttons[visible.indexOf(page)];\n if (!button) {\n const shown = visible.length > 0 ? visible.join(', ') : 'none';\n throw new Error(`BfPaginationHarness: page ${page} has no button (visible: ${shown})`);\n }\n await button.click();\n }\n\n /** Clicks Next. Throws on the last page, where the button is disabled. */\n async next(): Promise<void> {\n await this.press(await this.nextButton(), 'next');\n }\n\n /** Clicks Previous. Throws on the first page, where the button is disabled. */\n async previous(): Promise<void> {\n await this.press(await this.previousButton(), 'previous');\n }\n\n /**\n * Whether Previous is disabled (on the first page).\n * @returns True on the first page.\n */\n async isPreviousDisabled(): Promise<boolean> {\n return (await (await this.previousButton()).getAttribute('aria-disabled')) === 'true';\n }\n\n /**\n * Whether Next is disabled (on the last page).\n * @returns True on the last page.\n */\n async isNextDisabled(): Promise<boolean> {\n return (await (await this.nextButton()).getAttribute('aria-disabled')) === 'true';\n }\n\n /** Clicks an arrow button, unless it is disabled. */\n private async press(button: TestElement, name: string): Promise<void> {\n if ((await button.getAttribute('aria-disabled')) === 'true') {\n throw new Error(`BfPaginationHarness: the ${name} button is disabled`);\n }\n await button.click();\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/* The group's own tabs and panels only, never those of a tab group nested in a panel. */\nconst TAB = ':scope > [data-bf-tab-scroller] > [role=\"tablist\"] > [role=\"tab\"]';\n\n/**\n * Harness for `bf-tab-group`. It works by tab label, so a test never needs the panel values.\n *\n * @example\n * const tabs = await loader.getHarness(BfTabGroupHarness.with({ label: 'Project' }));\n * await tabs.selectTab('Activity');\n * expect(await tabs.getPanelText()).toContain('No activity yet');\n * @summary Test harness for the tab group: reads its tabs and panel and selects a tab\n * @since 0.3.1\n */\nexport class BfTabGroupHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-tab-group';\n\n private readonly list = this.locatorFor(':scope > [data-bf-tab-scroller] > [role=\"tablist\"]');\n private readonly tabs = this.locatorForAll(TAB);\n private readonly tabLabels = this.locatorForAll(`${TAB} > [data-label]`);\n private readonly panel = this.locatorForOptional(':scope > [role=\"tabpanel\"]:not([hidden])');\n\n /**\n * A predicate for a tab group with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfTabGroupHarness> {\n return new HarnessPredicate(BfTabGroupHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The tablist's accessible name (`label`, else the `tabGroup` label).\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n return (await (await this.list()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The tabs' labels, in order (without their badges).\n * @returns The tab labels.\n */\n async getTabLabels(): Promise<string[]> {\n return Promise.all((await this.tabLabels()).map(async (label) => (await label.text()).trim()));\n }\n\n /**\n * The selected tab's label, `null` when the group has no tabs.\n * @returns The label text or `null`.\n */\n async getSelectedLabel(): Promise<string | null> {\n const [tabs, labels] = await Promise.all([this.tabs(), this.getTabLabels()]);\n for (const [index, tab] of tabs.entries()) {\n if ((await tab.getAttribute('aria-selected')) === 'true') {\n return labels[index] ?? null;\n }\n }\n return null;\n }\n\n /**\n * Clicks the tab with this label, as a user would. Throws for an unknown label, and for a\n * disabled tab (a user's click would do nothing).\n * @param label The tab's label.\n */\n async selectTab(label: string): Promise<void> {\n const [tabs, labels] = await Promise.all([this.tabs(), this.getTabLabels()]);\n const tab = tabs[labels.indexOf(label)];\n if (!tab) {\n throw new Error(`BfTabGroupHarness: no tab \"${label}\" (tabs: ${labels.join(', ')})`);\n }\n if (await tab.matchesSelector(':disabled')) {\n throw new Error(`BfTabGroupHarness: the tab \"${label}\" is disabled`);\n }\n await tab.click();\n }\n\n /**\n * The text of the panel shown.\n * @returns The panel's text, `''` when no panel shows.\n */\n async getPanelText(): Promise<string> {\n return (await (await this.panel())?.text())?.trim() ?? '';\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-side-panel`, the panel `BfSidePanelService` opens. Side panels render in the\n * CDK overlay container outside the tested component, so get it through\n * `TestbedHarnessEnvironment.documentRootLoader(fixture)`.\n *\n * @example\n * const panel = await TestbedHarnessEnvironment.documentRootLoader(fixture).getHarness(\n * BfSidePanelHarness.with({ label: 'INV-204' }),\n * );\n * await panel.close();\n * expect(await panel.isOpen()).toBe(false);\n * @summary Test harness for a side panel: its title, its close button and its state\n * @since 0.3.1\n */\nexport class BfSidePanelHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-side-panel';\n\n private readonly title = this.locatorForOptional('[data-side-panel-header] h2');\n private readonly closeButton = this.locatorFor('[data-side-panel-close] button');\n\n /**\n * A predicate for a side panel with the given title.\n * @param filters The title (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfSidePanelHarness> {\n return new HarnessPredicate(BfSidePanelHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getTitle(), label),\n );\n }\n\n /**\n * The title heading.\n * @returns The title, `''` without one.\n */\n async getTitle(): Promise<string> {\n return (await (await this.title())?.text())?.trim() ?? '';\n }\n\n /** Clicks the close button (×), which closes the side panel with no result. */\n async close(): Promise<void> {\n await (await this.closeButton()).click();\n }\n\n /**\n * Whether the side panel is open: its overlay pane is in the page and not sliding out. The pane\n * is found by the panel's own `data-bf-side-panel-id`.\n * @returns True while the panel is open, false once closing or closed, or outside the service.\n */\n async isOpen(): Promise<boolean> {\n const id = await (await this.host()).getAttribute('data-bf-side-panel-id');\n if (!id) {\n return false;\n }\n const open = await this.documentRootLocatorFactory().locatorForOptional(\n `.bf-overlay-panel:not([data-bf-closing]) bf-side-panel[data-bf-side-panel-id=\"${id}\"]`,\n )();\n return open !== null;\n }\n}\n","import { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-calendar`. It works with ISO dates ('YYYY-MM-DD'), as the calendar's value\n * does, and drives it as a user would: `selectDate()` jumps to another month through the month\n * and year views, then clicks the day.\n *\n * @example\n * const due = await loader.getHarness(BfCalendarHarness.with({ label: 'Due date' }));\n * await due.selectDate('2026-11-03');\n * expect(await due.getSelected()).toBe('2026-11-03');\n * @summary Test harness for the calendar: reads the month and picks a date by ISO string\n * @since 0.3.1\n */\nexport class BfCalendarHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-calendar';\n\n private readonly group = this.locatorFor('[role=\"group\"]');\n private readonly liveTitle = this.locatorFor('[aria-live=\"polite\"]');\n private readonly titleButton = this.locatorForOptional('[data-bf-calendar-title]');\n private readonly previousButton = this.locatorForOptional('[data-bf-calendar-previous]');\n private readonly nextButton = this.locatorForOptional('[data-bf-calendar-next]');\n private readonly selectedDay = this.locatorForOptional('td[data-date][aria-selected=\"true\"]');\n private readonly dayGrid = this.locatorForOptional('table[data-bf-calendar-grid=\"day\"]');\n\n /**\n * A predicate for a calendar with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfCalendarHarness> {\n return new HarnessPredicate(BfCalendarHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The calendar's accessible name (`label`, else `ariaLabel`); it is not rendered visibly.\n * @returns The label text, `''` without one.\n */\n async getLabel(): Promise<string> {\n return (await (await this.group()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The shown month and year as the header writes them, in the calendar's locale.\n * @returns The month label, such as \"October 2026\".\n */\n async getMonthLabel(): Promise<string> {\n return (await (await this.liveTitle()).text()).trim();\n }\n\n /**\n * The picked date while it is in the shown month.\n * @returns The ISO date, or `null` when none is picked or it is in another month.\n */\n async getSelected(): Promise<string | null> {\n return (await (await this.selectedDay())?.getAttribute('data-date')) ?? null;\n }\n\n /** Clicks the next-month button. Throws when it is disabled (the month is past `max`). */\n async nextMonth(): Promise<void> {\n await this.clickNav(await this.nextButton(), 'next');\n }\n\n /** Clicks the previous-month button. Throws when it is disabled (the month is before `min`). */\n async previousMonth(): Promise<void> {\n await this.clickNav(await this.previousButton(), 'previous');\n }\n\n /**\n * Picks a date as a user would: when it is in another month, opens the month view, then the\n * year view, clicks its year and month, then clicks the day. Throws for an invalid date, one\n * the views do not list, and a disabled date or calendar.\n * @param iso The ISO date to pick, such as '2026-11-03'.\n */\n async selectDate(iso: string): Promise<void> {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(iso)) {\n throw new Error(`BfCalendarHarness: \"${iso}\" is not an ISO date (YYYY-MM-DD)`);\n }\n if (!(await this.dayGrid())) {\n throw new Error('BfCalendarHarness: the calendar is not showing its days');\n }\n let day = await this.locatorForOptional(`td[data-date=\"${iso}\"]`)();\n if (!day) {\n await this.jumpTo(iso.slice(0, 7));\n day = await this.locatorForOptional(`td[data-date=\"${iso}\"]`)();\n }\n if (!day) {\n throw new Error(`BfCalendarHarness: no day ${iso}`);\n }\n if ((await day.getAttribute('aria-disabled')) === 'true') {\n throw new Error(`BfCalendarHarness: the day ${iso} is disabled`);\n }\n if ((await (await this.dayGrid())?.getAttribute('aria-disabled')) === 'true') {\n throw new Error('BfCalendarHarness: the calendar is disabled');\n }\n await day.click();\n }\n\n private async jumpTo(month: string): Promise<void> {\n const title = await this.titleButton();\n if (!title || (await title.getProperty<boolean>('disabled'))) {\n throw new Error('BfCalendarHarness: the calendar is disabled');\n }\n await title.click();\n await (await this.locatorFor('[data-bf-calendar-title]')()).click();\n const year = await this.locatorForOptional(`td[data-year=\"${Number(month.slice(0, 4))}\"]`)();\n if (!year) {\n throw new Error(`BfCalendarHarness: the year view does not list ${month.slice(0, 4)}`);\n }\n await year.click();\n const cell = await this.locatorFor(`td[data-month=\"${month}\"]`)();\n if ((await cell.getAttribute('aria-disabled')) === 'true') {\n throw new Error(`BfCalendarHarness: the month ${month} is outside min and max`);\n }\n await cell.click();\n }\n\n private async clickNav(button: TestElement | null, which: string): Promise<void> {\n if (!button) {\n throw new Error('BfCalendarHarness: the calendar is not showing its days');\n }\n if (\n (await button.getProperty<boolean>('disabled')) ||\n (await button.getAttribute('aria-disabled')) === 'true'\n ) {\n throw new Error(`BfCalendarHarness: the ${which} month button is disabled`);\n }\n await button.click();\n }\n}\n","import { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/** The elements that take keyboard focus, as the overlay core counts them. Internal. */\nexport const FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]):not([type=\"hidden\"]), ' +\n 'select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Harness for an element with a `[bfTooltip]`. The tooltip renders in the CDK overlay container\n * only while it shows, so `show()` focuses the element (the tooltip shows at once on keyboard\n * focus; a 500 ms hover would need real time) and `getText()` reads the `bf-tooltip` its control\n * is described by. Its `label` filter matches the element's accessible name (`aria-label`, else\n * its text). Under a coarse pointer no tooltip shows.\n *\n * @example\n * const copy = await loader.getHarness(BfTooltipHarness.with({ label: 'Copy link' }));\n * expect(await copy.getText()).toBe('Copy link');\n * @summary Test harness for a tooltip: shows it, reads its text and whether it shows\n * @since 0.3.1\n */\nexport class BfTooltipHarness extends ComponentHarness {\n /** The host element's selector: the attribute the tooltip directive puts on its host. */\n static hostSelector = '[data-bf-tooltip]';\n\n private readonly inner = this.locatorForOptional(FOCUSABLE);\n\n /**\n * A predicate for an element with a tooltip and the given accessible name.\n * @param filters The name (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfTooltipHarness> {\n return new HarnessPredicate(BfTooltipHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The element's accessible name: its control's `aria-label`, else its text.\n * @returns The name.\n */\n async getLabel(): Promise<string> {\n const control = await this.control();\n return (await control.getAttribute('aria-label')) ?? (await control.text()).trim();\n }\n\n /** Shows the tooltip by focusing the element (or the first control inside it). */\n async show(): Promise<void> {\n await (await this.control()).focus();\n }\n\n /** Hides the tooltip by moving focus off the element. */\n async hide(): Promise<void> {\n await (await this.control()).blur();\n }\n\n /**\n * Whether the tooltip shows.\n * @returns True while it shows.\n */\n async isOpen(): Promise<boolean> {\n return (await this.tooltip()) !== null;\n }\n\n /**\n * The tooltip's text, showing it first when it is hidden.\n * @returns The text; throws when no tooltip shows (an empty text or a coarse pointer).\n */\n async getText(): Promise<string> {\n if (!(await this.isOpen())) {\n await this.show();\n }\n const tooltip = await this.tooltip();\n if (!tooltip) {\n throw new Error('BfTooltipHarness: the tooltip did not show');\n }\n return (await tooltip.text()).trim();\n }\n\n /** The element the tooltip describes: the host when it takes focus, else its first control. */\n private async control(): Promise<TestElement> {\n const host = await this.host();\n return (await host.matchesSelector(FOCUSABLE)) ? host : ((await this.inner()) ?? host);\n }\n\n /** The shown `bf-tooltip` among the ids the control is described by, or `null`. */\n private async tooltip(): Promise<TestElement | null> {\n const ids = ((await (await this.control()).getAttribute('aria-describedby')) ?? '')\n .split(/\\s+/)\n .filter(Boolean);\n const root = this.documentRootLocatorFactory();\n for (const id of ids) {\n const found = await root.locatorForOptional(`bf-tooltip[id=\"${id}\"]`)();\n if (found) {\n return found;\n }\n }\n return null;\n }\n}\n","import {\n ComponentHarness,\n HarnessPredicate,\n TestKey,\n type TestElement,\n} from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\nimport { FOCUSABLE } from './tooltip.harness';\n\n/**\n * Harness for an element with a `[bfPopoverTriggerFor]` and the `bf-popover` it opens, which\n * renders in the CDK overlay container while open (found through the trigger's\n * `aria-controls`). Its `label` filter matches the trigger's accessible name (`aria-label`, else\n * its text).\n *\n * @example\n * const filters = await loader.getHarness(BfPopoverHarness.with({ label: 'Filters' }));\n * await filters.open();\n * expect(await filters.getPanelText()).toContain('Status');\n * @summary Test harness for a popover: opens and closes it and reads its panel\n * @since 0.3.1\n */\nexport class BfPopoverHarness extends ComponentHarness {\n /** The host element's selector: the attribute the popover trigger directive puts on its host. */\n static hostSelector = '[data-bf-popover-trigger]';\n\n private readonly inner = this.locatorForOptional(FOCUSABLE);\n\n /**\n * A predicate for a popover trigger with the given accessible name.\n * @param filters The name (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfPopoverHarness> {\n return new HarnessPredicate(BfPopoverHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The trigger's accessible name: its control's `aria-label`, else its text.\n * @returns The name.\n */\n async getLabel(): Promise<string> {\n const control = await this.control();\n return (await control.getAttribute('aria-label')) ?? (await control.text()).trim();\n }\n\n /** Opens the popover with a click; does nothing while it is open. */\n async open(): Promise<void> {\n if (!(await this.isOpen())) {\n await (await this.control()).click();\n }\n }\n\n /** Closes the popover with `Escape`, as a user would; does nothing while it is closed. */\n async close(): Promise<void> {\n await (await this.panel())?.sendKeys(TestKey.ESCAPE);\n }\n\n /**\n * Whether the popover is open (`aria-expanded`).\n * @returns True while open.\n */\n async isOpen(): Promise<boolean> {\n return (await (await this.control()).getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * The open panel's text, opening it first when it is closed.\n * @returns The text; throws when the panel cannot open (a disabled trigger).\n */\n async getPanelText(): Promise<string> {\n await this.open();\n const panel = await this.panel();\n if (!panel) {\n throw new Error('BfPopoverHarness: the popover did not open');\n }\n return (await panel.text()).trim();\n }\n\n /** The element that carries the ARIA state: the host, or its first control. */\n private async control(): Promise<TestElement> {\n const host = await this.host();\n return (await host.matchesSelector(FOCUSABLE)) ? host : ((await this.inner()) ?? host);\n }\n\n /** The open panel, or `null`. */\n private async panel(): Promise<TestElement | null> {\n const id = await (await this.control()).getAttribute('aria-controls');\n return id ? this.documentRootLocatorFactory().locatorForOptional(`[id=\"${id}\"]`)() : null;\n }\n}\n","import {\n ComponentHarness,\n HarnessPredicate,\n TestKey,\n type TestElement,\n} from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-combobox` and `bf-multi-combobox`. Its option list renders in the CDK overlay\n * container while open (found through the input's `aria-controls`); it works by option label, so a\n * test never needs the option values. Its `label` filter matches the visible label, else the\n * `aria-label`.\n *\n * @example\n * const assignee = await loader.getHarness(BfComboboxHarness.with({ label: 'Assignee' }));\n * await assignee.type('gra');\n * await assignee.selectOption('Grace Hopper');\n * expect(await assignee.getValueText()).toBe('Grace Hopper');\n * @summary Test harness for the comboboxes: types, reads and picks options and tags\n * @since 0.3.1\n */\nexport class BfComboboxHarness extends ComponentHarness {\n /** The host element's selector: either combobox. */\n static hostSelector = 'bf-combobox, bf-multi-combobox';\n\n private readonly input = this.locatorFor('input[role=\"combobox\"]');\n private readonly label = this.locatorForOptional('label');\n private readonly chips = this.locatorForAll('[data-chip]');\n private readonly removeButtons = this.locatorForAll('[data-chip] button');\n\n /**\n * A predicate for a combobox with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfComboboxHarness> {\n return new HarnessPredicate(BfComboboxHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The visible label without the required mark, else the `aria-label`.\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n const label = await this.label();\n if (label) {\n return (await label.text({ exclude: '[aria-hidden=\"true\"]' })).trim();\n }\n return (await (await this.input()).getAttribute('aria-label')) ?? '';\n }\n\n /** Opens the option list with a click into the field; does nothing while it is open. */\n async open(): Promise<void> {\n if (!(await this.isOpen())) {\n await (await this.input()).click();\n }\n }\n\n /** Closes the option list with `Escape`, as a user would; does nothing while it is closed. */\n async close(): Promise<void> {\n if (await this.isOpen()) {\n await (await this.input()).sendKeys(TestKey.ESCAPE);\n }\n }\n\n /**\n * Whether the option list is open (`aria-expanded`).\n * @returns True while open.\n */\n async isOpen(): Promise<boolean> {\n return (await (await this.input()).getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * Replaces the field's text with `query`, key by key as a user types; the list opens filtered.\n * @param query The text to type.\n */\n async type(query: string): Promise<void> {\n const input = await this.input();\n await input.focus();\n await input.clear();\n if (query) {\n await input.sendKeys(query);\n }\n }\n\n /**\n * The labels of the listed options, in order, opening the list first when it is closed.\n * @returns The option labels; empty when nothing matches.\n */\n async getOptionLabels(): Promise<string[]> {\n await this.open();\n return Promise.all((await this.options()).map(async (o) => (await o.label.text()).trim()));\n }\n\n /**\n * Picks the listed option with this label with a click, opening the list first when it is\n * closed. Throws for an unknown or disabled option.\n * @param label The option's label.\n */\n async selectOption(label: string): Promise<void> {\n await this.open();\n const options = await this.options();\n const labels = await Promise.all(options.map(async (o) => (await o.label.text()).trim()));\n const option = options[labels.indexOf(label)];\n if (!option) {\n throw new Error(`BfComboboxHarness: no option \"${label}\" (options: ${labels.join(', ')})`);\n }\n if ((await option.element.getAttribute('aria-disabled')) === 'true') {\n throw new Error(`BfComboboxHarness: the option \"${label}\" is disabled`);\n }\n await option.element.click();\n }\n\n /**\n * The field's text: the picked option's label on a `bf-combobox` (or the text being typed), the\n * typed text on a `bf-multi-combobox` (its picks are tags, see `getChipLabels()`).\n * @returns The input's value.\n */\n async getValueText(): Promise<string> {\n return (await this.input()).getProperty<string>('value');\n }\n\n /**\n * The labels of a `bf-multi-combobox`'s tags, in order; empty for a `bf-combobox`.\n * @returns The tag labels.\n */\n async getChipLabels(): Promise<string[]> {\n return Promise.all((await this.chips()).map(async (chip) => this.chipLabel(chip)));\n }\n\n /**\n * Removes the tag with this label with its remove button. Throws for an unknown tag or one\n * without a remove button (a disabled or read-only field).\n * @param label The tag's label.\n */\n async removeChip(label: string): Promise<void> {\n const chips = await this.chips();\n const labels = await Promise.all(chips.map(async (chip) => this.chipLabel(chip)));\n const index = labels.indexOf(label);\n if (index < 0) {\n throw new Error(`BfComboboxHarness: no tag \"${label}\" (tags: ${labels.join(', ')})`);\n }\n // Every tag has a remove button, or none has (a disabled or read-only field).\n const button = (await this.removeButtons())[index];\n if (!button) {\n throw new Error(`BfComboboxHarness: the tag \"${label}\" cannot be removed`);\n }\n await button.click();\n }\n\n /**\n * Whether the field is disabled.\n * @returns True when disabled.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.input()).getProperty<boolean>('disabled');\n }\n\n /** Focuses the input. */\n async focus(): Promise<void> {\n await (await this.input()).focus();\n }\n\n /** Blurs the input, which closes the list and marks a bound form field touched. */\n async blur(): Promise<void> {\n await (await this.input()).blur();\n }\n\n /** The text of a tag's label. */\n private async chipLabel(chip: TestElement): Promise<string> {\n return (await chip.text()).trim();\n }\n\n /** The listed options and their label elements, from the listbox the input controls. */\n private async options(): Promise<{ element: TestElement; label: TestElement }[]> {\n const id = await (await this.input()).getAttribute('aria-controls');\n if (!id) {\n return [];\n }\n const root = this.documentRootLocatorFactory();\n const [elements, labels] = await Promise.all([\n root.locatorForAll(`[id=\"${id}\"] [role=\"option\"]`)(),\n root.locatorForAll(`[id=\"${id}\"] [role=\"option\"] [data-label]`)(),\n ]);\n return elements.map((element, i) => ({ element, label: labels[i] ?? element }));\n }\n}\n","import { type LocatorFactory, TestKey, type TestElement } from '@angular/cdk/testing';\nimport { BfCalendarHarness } from './calendar.harness';\n\n/*\n * What the date, time and datetime input harnesses share: they drive the same segmented field\n * (spinbutton parts, a picker button, a panel in the overlay container, or the platform input\n * under a coarse pointer with `native`). Plain functions over the harnesses' own locators, so\n * each harness stays a flat class. Internal to the testing entry.\n */\n\n/** The parts of one rendered field, as a harness locates them. */\nexport interface FieldElements {\n /** The group of parts (segmented field), or `null` in native mode. */\n readonly group: TestElement | null;\n /** The platform input (native mode), or `null`. */\n readonly native: TestElement | null;\n /** The spinbutton parts in the order they are laid out. */\n readonly segments: readonly TestElement[];\n /** The visible label, or `null`. */\n readonly label: TestElement | null;\n}\n\n/** The visible label without the required mark, else the field's `aria-label`. */\nexport async function fieldLabel(field: FieldElements): Promise<string> {\n if (field.label) {\n return (await field.label.text({ exclude: '[aria-hidden=\"true\"]' })).trim();\n }\n const named = field.group ?? field.native;\n return (await named?.getAttribute('aria-label')) ?? '';\n}\n\n/** The field's text as shown (\"14/10/2026\"), `''` while every part is empty. */\nexport async function fieldText(field: FieldElements): Promise<string> {\n if (field.native) {\n return field.native.getProperty<string>('value');\n }\n const filled = await Promise.all(\n field.segments.map(async (segment) => (await segment.getAttribute('aria-valuenow')) !== null),\n );\n if (!filled.some(Boolean) || !field.group) {\n return '';\n }\n return (await field.group.text()).replace(/\\s+/g, ' ').trim();\n}\n\n/** Clears every part with `Backspace`, as a user would (or empties the platform input). */\nexport async function clearField(field: FieldElements): Promise<void> {\n if (field.native) {\n await field.native.clear();\n await field.native.dispatchEvent('change');\n return;\n }\n for (const segment of field.segments) {\n await segment.sendKeys(TestKey.BACKSPACE);\n }\n}\n\n/**\n * Types `text` as a user would: cleared first, then each run of digits or letters goes into the\n * next part in the order they are laid out (\"14/10/2026\" types 14, 10 and 2026; \"2:30 PM\" types\n * 2, 30 and P). In native mode the platform input takes the ISO value instead.\n */\nexport async function typeField(field: FieldElements, text: string): Promise<void> {\n if (field.native) {\n await field.native.setInputValue(text);\n await field.native.dispatchEvent('change');\n return;\n }\n await clearField(field);\n const runs = text.match(/\\d+|\\p{L}+/gu) ?? [];\n for (const [index, run] of runs.entries()) {\n const segment = field.segments[index];\n if (!segment) {\n throw new Error(`BfDateTimeHarness: \"${text}\" has more parts than the field`);\n }\n await segment.focus();\n await segment.sendKeys(...run.split(''));\n await segment.blur();\n }\n}\n\n/** Opens the picker panel with a click on its button; does nothing while it is open. */\nexport async function openPanel(trigger: TestElement | null): Promise<string> {\n if (!trigger) {\n throw new Error('BfDateTimeHarness: the field has no picker');\n }\n if ((await trigger.getAttribute('aria-expanded')) !== 'true') {\n if (await trigger.getProperty<boolean>('disabled')) {\n throw new Error('BfDateTimeHarness: the picker is disabled');\n }\n await trigger.click();\n }\n const id = await trigger.getAttribute('aria-controls');\n if (!id) {\n throw new Error('BfDateTimeHarness: the picker did not open');\n }\n return id;\n}\n\n/** Picks a date in the open panel's calendar. */\nexport async function pickPanelDate(\n root: LocatorFactory,\n panelId: string,\n iso: string,\n): Promise<void> {\n const calendar = await root.locatorFor(\n BfCalendarHarness.with({ ancestor: `[id=\"${panelId}\"]` }),\n )();\n await calendar.selectDate(iso);\n}\n\n/** Clicks a time in the open panel's list. */\nexport async function pickPanelTime(\n root: LocatorFactory,\n panelId: string,\n time: string,\n): Promise<void> {\n const option = await root.locatorForOptional(\n `[id=\"${panelId}\"] [role=\"option\"][data-time=\"${time}\"]`,\n )();\n if (!option) {\n throw new Error(`BfDateTimeHarness: the list has no time ${time}`);\n }\n if ((await option.getAttribute('aria-disabled')) === 'true') {\n throw new Error(`BfDateTimeHarness: the time ${time} is disabled`);\n }\n await option.click();\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport {\n type FieldElements,\n clearField,\n fieldLabel,\n fieldText,\n openPanel,\n pickPanelDate,\n typeField,\n} from './date-time-field';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-date-input`. It reads and types the field as a user sees it (in its locale's\n * order) and picks a date through the calendar popover with `BfCalendarHarness`.\n *\n * @example\n * const due = await loader.getHarness(BfDateInputHarness.with({ label: 'Due date' }));\n * await due.setValue('14/10/2026');\n * await due.pickDate('2026-11-03');\n * expect(await due.getValue()).toBe('03/11/2026');\n * @summary Test harness for the date input: types a date or picks one in its calendar\n * @since 0.3.1\n */\nexport class BfDateInputHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-date-input';\n\n private readonly group = this.locatorForOptional('[role=\"group\"]');\n private readonly nativeInput = this.locatorForOptional('input');\n private readonly segments = this.locatorForAll('[role=\"spinbutton\"]');\n private readonly label = this.locatorForOptional('label');\n private readonly trigger = this.locatorForOptional('button[aria-haspopup=\"dialog\"]');\n\n /**\n * A predicate for a date input with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfDateInputHarness> {\n return new HarnessPredicate(BfDateInputHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The visible label without the required mark, else the field's `aria-label`.\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n return fieldLabel(await this.elements());\n }\n\n /**\n * The field's text as the locale shows it (\"14/10/2026\"); the ISO value in native mode.\n * @returns The text, `''` while every part is empty.\n */\n async getValue(): Promise<string> {\n return fieldText(await this.elements());\n }\n\n /**\n * Types a date as a user would: clears the field, then types each run of digits (or letters)\n * into the next part in the field's order, such as \"14/10/2026\" in an en-GB field. In native mode it sets the\n * platform input to the ISO value.\n * @param text The date as the field shows it.\n */\n async setValue(text: string): Promise<void> {\n await typeField(await this.elements(), text);\n }\n\n /** Clears every part with `Backspace`, as a user would. */\n async clear(): Promise<void> {\n await clearField(await this.elements());\n }\n\n /**\n * Whether the field shows its error state (`aria-invalid` on its parts or native input).\n * @returns True while invalid.\n */\n async isInvalid(): Promise<boolean> {\n const control = (await this.nativeInput()) ?? (await this.segments())[0];\n return (await control?.getAttribute('aria-invalid')) === 'true';\n }\n\n /**\n * Whether the field is disabled.\n * @returns True while disabled.\n */\n async isDisabled(): Promise<boolean> {\n const native = await this.nativeInput();\n if (native) {\n return native.getProperty<boolean>('disabled');\n }\n return (await (await this.group())?.getAttribute('aria-disabled')) === 'true';\n }\n\n /** Opens the calendar popover with a click on its button; throws when the field has none or it is disabled. */\n async openPicker(): Promise<void> {\n await openPanel(await this.trigger());\n }\n\n /**\n * Whether the calendar popover is open (`aria-expanded` on its button).\n * @returns True while open.\n */\n async isPickerOpen(): Promise<boolean> {\n return (await (await this.trigger())?.getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * Picks a date in the calendar as a user would: opens the popover (when closed), then picks the\n * day through the calendar's harness, which closes it.\n * @param iso The ISO date to pick, such as '2026-11-03'.\n */\n async pickDate(iso: string): Promise<void> {\n const panel = await openPanel(await this.trigger());\n await pickPanelDate(this.documentRootLocatorFactory(), panel, iso);\n }\n\n private async elements(): Promise<FieldElements> {\n return {\n group: await this.group(),\n native: await this.nativeInput(),\n segments: await this.segments(),\n label: await this.label(),\n };\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport {\n type FieldElements,\n clearField,\n fieldLabel,\n fieldText,\n openPanel,\n pickPanelTime,\n typeField,\n} from './date-time-field';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-time-input`. It reads and types the field as a user sees it and picks a time\n * from the popover list.\n *\n * @example\n * const start = await loader.getHarness(BfTimeInputHarness.with({ label: 'Start' }));\n * await start.setValue('09:30');\n * await start.pickTime('14:00');\n * expect(await start.getValue()).toBe('14:00');\n * @summary Test harness for the time input: types a time or picks one from its list\n * @since 0.3.1\n */\nexport class BfTimeInputHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-time-input';\n\n private readonly group = this.locatorForOptional('[role=\"group\"]');\n private readonly nativeInput = this.locatorForOptional('input');\n private readonly segments = this.locatorForAll('[role=\"spinbutton\"]');\n private readonly label = this.locatorForOptional('label');\n private readonly trigger = this.locatorForOptional('button[aria-haspopup=\"dialog\"]');\n\n /**\n * A predicate for a time input with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfTimeInputHarness> {\n return new HarnessPredicate(BfTimeInputHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The visible label without the required mark, else the field's `aria-label`.\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n return fieldLabel(await this.elements());\n }\n\n /**\n * The field's text as the locale shows it (\"14:30\" or \"02:30 PM\"); the ISO value in native mode.\n * @returns The text, `''` while every part is empty.\n */\n async getValue(): Promise<string> {\n return fieldText(await this.elements());\n }\n\n /**\n * Types a time as a user would: clears the field, then types each run of digits (or letters)\n * into the next part in the field's order, such as \"2:30 PM\" in a 12-hour field. In native mode it sets the\n * platform input to the ISO value.\n * @param text The time as the field shows it.\n */\n async setValue(text: string): Promise<void> {\n await typeField(await this.elements(), text);\n }\n\n /** Clears every part with `Backspace`, as a user would. */\n async clear(): Promise<void> {\n await clearField(await this.elements());\n }\n\n /**\n * Whether the field shows its error state (`aria-invalid` on its parts or native input).\n * @returns True while invalid.\n */\n async isInvalid(): Promise<boolean> {\n const control = (await this.nativeInput()) ?? (await this.segments())[0];\n return (await control?.getAttribute('aria-invalid')) === 'true';\n }\n\n /**\n * Whether the field is disabled.\n * @returns True while disabled.\n */\n async isDisabled(): Promise<boolean> {\n const native = await this.nativeInput();\n if (native) {\n return native.getProperty<boolean>('disabled');\n }\n return (await (await this.group())?.getAttribute('aria-disabled')) === 'true';\n }\n\n /** Opens the list of times with a click on its button; throws when the field has none or it is disabled. */\n async openPicker(): Promise<void> {\n await openPanel(await this.trigger());\n }\n\n /**\n * Whether the list of times is open (`aria-expanded` on its button).\n * @returns True while open.\n */\n async isPickerOpen(): Promise<boolean> {\n return (await (await this.trigger())?.getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * Picks a time in the list as a user would: opens the list (when closed), then clicks the\n * time, which closes it. Throws for a time the list does not hold (off the `step`) or disables.\n * @param time The ISO time to pick, such as '14:30'.\n */\n async pickTime(time: string): Promise<void> {\n const panel = await openPanel(await this.trigger());\n await pickPanelTime(this.documentRootLocatorFactory(), panel, time);\n }\n\n private async elements(): Promise<FieldElements> {\n return {\n group: await this.group(),\n native: await this.nativeInput(),\n segments: await this.segments(),\n label: await this.label(),\n };\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport {\n type FieldElements,\n clearField,\n fieldLabel,\n fieldText,\n openPanel,\n pickPanelDate,\n pickPanelTime,\n typeField,\n} from './date-time-field';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-datetime-input`. It reads and types the field as a user sees it and picks the\n * date in the popover's calendar and the time in its list.\n *\n * @example\n * const meeting = await loader.getHarness(BfDateTimeInputHarness.with({ label: 'Meeting' }));\n * await meeting.pickDate('2026-10-14');\n * await meeting.pickTime('09:30');\n * expect(await meeting.getValue()).toBe('14/10/2026, 09:30');\n * @summary Test harness for the date and time input: types or picks a date and time\n * @since 0.3.1\n */\nexport class BfDateTimeInputHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-datetime-input';\n\n private readonly group = this.locatorForOptional('[role=\"group\"]');\n private readonly nativeInput = this.locatorForOptional('input');\n private readonly segments = this.locatorForAll('[role=\"spinbutton\"]');\n private readonly label = this.locatorForOptional('label');\n private readonly trigger = this.locatorForOptional('button[aria-haspopup=\"dialog\"]');\n\n /**\n * A predicate for a date and time input with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfDateTimeInputHarness> {\n return new HarnessPredicate(BfDateTimeInputHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The visible label without the required mark, else the field's `aria-label`.\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n return fieldLabel(await this.elements());\n }\n\n /**\n * The field's text as the locale shows it (\"14/10/2026, 09:30\"); the ISO value in native mode.\n * @returns The text, `''` while every part is empty.\n */\n async getValue(): Promise<string> {\n return fieldText(await this.elements());\n }\n\n /**\n * Types a date and time as a user would: clears the field, then types each run of digits (or letters)\n * into the next part in the field's order, such as \"14/10/2026, 09:30\" in an en-GB field. In native mode it sets the\n * platform input to the ISO value.\n * @param text The date and time as the field shows it.\n */\n async setValue(text: string): Promise<void> {\n await typeField(await this.elements(), text);\n }\n\n /** Clears every part with `Backspace`, as a user would. */\n async clear(): Promise<void> {\n await clearField(await this.elements());\n }\n\n /**\n * Whether the field shows its error state (`aria-invalid` on its parts or native input).\n * @returns True while invalid.\n */\n async isInvalid(): Promise<boolean> {\n const control = (await this.nativeInput()) ?? (await this.segments())[0];\n return (await control?.getAttribute('aria-invalid')) === 'true';\n }\n\n /**\n * Whether the field is disabled.\n * @returns True while disabled.\n */\n async isDisabled(): Promise<boolean> {\n const native = await this.nativeInput();\n if (native) {\n return native.getProperty<boolean>('disabled');\n }\n return (await (await this.group())?.getAttribute('aria-disabled')) === 'true';\n }\n\n /** Opens the popover with a click on its button; throws when the field has none or it is disabled. */\n async openPicker(): Promise<void> {\n await openPanel(await this.trigger());\n }\n\n /**\n * Whether the popover is open (`aria-expanded` on its button).\n * @returns True while open.\n */\n async isPickerOpen(): Promise<boolean> {\n return (await (await this.trigger())?.getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * Picks a date in the calendar as a user would: opens the popover (when closed), then picks the\n * day through the calendar's harness. The popover stays open, with focus on its time list.\n * @param iso The ISO date to pick, such as '2026-11-03'.\n */\n async pickDate(iso: string): Promise<void> {\n const panel = await openPanel(await this.trigger());\n await pickPanelDate(this.documentRootLocatorFactory(), panel, iso);\n }\n\n /**\n * Picks a time in the list as a user would: opens the popover (when closed), then clicks the\n * time, which closes it. Throws for a time the list does not hold (off the `step`) or disables.\n * @param time The ISO time to pick, such as '14:30'.\n */\n async pickTime(time: string): Promise<void> {\n const panel = await openPanel(await this.trigger());\n await pickPanelTime(this.documentRootLocatorFactory(), panel, time);\n }\n\n private async elements(): Promise<FieldElements> {\n return {\n group: await this.group(),\n native: await this.nativeInput(),\n segments: await this.segments(),\n label: await this.label(),\n };\n }\n}\n","import { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/* The chart's own figure parts (a chart is never nested in another). */\nconst LEGEND_BUTTON = '[data-bf-chart-legend] button';\n\n/**\n * Harness for the `@bitforgehq/ui/charts` charts (`bf-line-chart`, `bf-bar-chart`,\n * `bf-donut-chart`). It works through what a user sees: the legend's series names, its toggle\n * buttons and the Show data table, so a test never reads the SVG.\n *\n * @example\n * const chart = await loader.getHarness(BfChartHarness.with({ label: 'Revenue' }));\n * await chart.toggleSeries('Store');\n * expect(await chart.showData()).toContainEqual(['Mar', '15', '11']);\n * @summary Test harness for the charts: reads the series, toggles one and reads the data table\n * @since 0.3.1\n */\nexport class BfChartHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-line-chart, bf-bar-chart, bf-donut-chart';\n\n private readonly figure = this.locatorFor('figure');\n private readonly legendButtons = this.locatorForAll(LEGEND_BUTTON);\n private readonly dataToggle = this.locatorFor('[data-bf-chart-data-toggle]');\n private readonly headCells = this.locatorForAll('[data-bf-chart-table] thead th');\n private readonly bodyCells = this.locatorForAll(\n '[data-bf-chart-table] tbody th, [data-bf-chart-table] tbody td',\n );\n\n /**\n * A predicate for a chart with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfChartHarness> {\n return new HarnessPredicate(BfChartHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The figure's accessible name (`label`, else the chart card's title, else the `chart` label).\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n return (await (await this.figure()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The series (or slice) names in the legend, in order; empty for a chart of one series, which\n * draws no legend.\n * @returns The names.\n */\n async getSeriesNames(): Promise<string[]> {\n return Promise.all(\n (await this.legendButtons()).map(async (button) => (await button.text()).trim()),\n );\n }\n\n /**\n * Whether the legend shows the series (or slice) with this name.\n * @param name The series name.\n * @returns `true` while it is drawn.\n */\n async isSeriesShown(name: string): Promise<boolean> {\n return (await (await this.legendButton(name)).getAttribute('aria-pressed')) === 'true';\n }\n\n /**\n * Clicks the legend button of the series (or slice) with this name, as a user would, to hide\n * or show it. Throws for an unknown name.\n * @param name The series name.\n */\n async toggleSeries(name: string): Promise<void> {\n await (await this.legendButton(name)).click();\n }\n\n /**\n * Opens the Show data table (if it is closed) and reads it.\n * @returns One array per category (or slice): its name, then its formatted values.\n */\n async showData(): Promise<string[][]> {\n const toggle = await this.dataToggle();\n if ((await toggle.getAttribute('aria-expanded')) !== 'true') {\n await toggle.click();\n }\n const [head, cells] = await Promise.all([this.headCells(), this.bodyCells()]);\n const texts = await Promise.all(cells.map(async (cell) => (await cell.text()).trim()));\n const columns = Math.max(1, head.length);\n const rows: string[][] = [];\n for (let i = 0; i < texts.length; i += columns) {\n rows.push(texts.slice(i, i + columns));\n }\n return rows;\n }\n\n private async legendButton(name: string): Promise<TestElement> {\n const [buttons, names] = await Promise.all([this.legendButtons(), this.getSeriesNames()]);\n const button = buttons[names.indexOf(name)];\n if (!button) {\n throw new Error(`BfChartHarness: no series \"${name}\" (series: ${names.join(', ')})`);\n }\n return button;\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-sidebar-toggle`, the button that collapses a `bf-sidebar` to an icon rail. Its\n * `label` filter matches the button's name (\"Collapse sidebar\" or \"Expand sidebar\").\n *\n * @example\n * const toggle = await loader.getHarness(BfSidebarToggleHarness);\n * await toggle.toggle();\n * expect(await toggle.isExpanded()).toBe(false);\n * @summary Test harness for a sidebar toggle: reads and flips the collapsed sidebar\n * @since 0.3.2\n */\nexport class BfSidebarToggleHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-sidebar-toggle';\n\n private readonly button = this.locatorFor('button');\n\n /**\n * A predicate for a sidebar toggle with the given name.\n * @param filters The name (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfSidebarToggleHarness> {\n return new HarnessPredicate(BfSidebarToggleHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The button's name, which says what the next press does.\n * @returns The `aria-label`.\n */\n async getLabel(): Promise<string> {\n return (await (await this.button()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * Whether the sidebar is at full width (`aria-expanded`).\n * @returns True while expanded, false while it is an icon rail.\n */\n async isExpanded(): Promise<boolean> {\n return (await (await this.button()).getAttribute('aria-expanded')) === 'true';\n }\n\n /** Flips the sidebar with a click. */\n async toggle(): Promise<void> {\n await (await this.button()).click();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bitforgehq-ui-testing.mjs","names":[],"sources":["../../../projects/ui/testing/jsdom-shims.ts","../../../projects/ui/testing/provide-bf-testing.ts","../../../projects/ui/testing/harnesses/select.harness.ts","../../../projects/ui/testing/harnesses/radio-group.harness.ts","../../../projects/ui/testing/harnesses/segmented.harness.ts","../../../projects/ui/testing/harnesses/dialog.harness.ts","../../../projects/ui/testing/harnesses/menu.harness.ts","../../../projects/ui/testing/harnesses/menu-button.harness.ts","../../../projects/ui/testing/harnesses/desktop-toast.harness.ts","../../../projects/ui/testing/harnesses/table.harness.ts","../../../projects/ui/testing/harnesses/pagination.harness.ts","../../../projects/ui/testing/harnesses/tab-group.harness.ts","../../../projects/ui/testing/harnesses/side-panel.harness.ts","../../../projects/ui/testing/harnesses/calendar.harness.ts","../../../projects/ui/testing/harnesses/tooltip.harness.ts","../../../projects/ui/testing/harnesses/popover.harness.ts","../../../projects/ui/testing/harnesses/combobox.harness.ts","../../../projects/ui/testing/harnesses/date-time-field.ts","../../../projects/ui/testing/harnesses/date-input.harness.ts","../../../projects/ui/testing/harnesses/time-input.harness.ts","../../../projects/ui/testing/harnesses/datetime-input.harness.ts","../../../projects/ui/testing/harnesses/chart.harness.ts","../../../projects/ui/testing/harnesses/sidebar-toggle.harness.ts","../../../projects/ui/testing/harnesses/toggle.harness.ts"],"sourcesContent":["/**\n * Options of `installBfJsdomShims()`.\n *\n * @summary Options of the jsdom shims: the matchMedia stub and its reduced-motion answer\n * @since 0.3.0\n */\nexport interface BfJsdomShimOptions {\n /**\n * Install a `matchMedia` stub where missing (all queries false).\n * @default true\n */\n matchMedia?: boolean;\n /**\n * With the stub: report `(prefers-reduced-motion: reduce)` as matching, so overlays use their\n * short fade. Has no effect where the environment already has `matchMedia`.\n * @default false\n */\n reducedMotion?: boolean;\n}\n\nconst REDUCED_MOTION_QUERY = /\\(\\s*prefers-reduced-motion\\s*:\\s*reduce\\s*\\)/;\n\n/**\n * A running `Animation` stand-in for jsdom's `Element.prototype.animate`: `finished` resolves at\n * once, and the controls set `playState`. Implements the interface in full (no cast), the same\n * way `BfFinishedAnimation` in `overlay/core/overlay-animations.ts` does for its own, always-done\n * stand-in.\n */\nclass BfShimAnimation extends EventTarget implements Animation {\n currentTime: CSSNumberish | null = 0;\n effect: AnimationEffect | null = null;\n id = '';\n oncancel: ((this: Animation, ev: AnimationPlaybackEvent) => unknown) | null = null;\n onfinish: ((this: Animation, ev: AnimationPlaybackEvent) => unknown) | null = null;\n onremove: ((this: Animation, ev: Event) => unknown) | null = null;\n readonly overallProgress: number | null = 1;\n readonly pending = false;\n playState: AnimationPlayState = 'running';\n playbackRate = 1;\n readonly finished: Promise<Animation> = Promise.resolve(this);\n readonly ready: Promise<Animation> = Promise.resolve(this);\n readonly replaceState: AnimationReplaceState = 'active';\n startTime: CSSNumberish | null = null;\n timeline: AnimationTimeline | null = null;\n\n cancel(): void {\n this.playState = 'idle';\n }\n commitStyles(): void {\n // The end state is already on the element.\n }\n finish(): void {\n this.playState = 'finished';\n }\n pause(): void {\n this.playState = 'paused';\n }\n persist(): void {\n // Nothing to persist.\n }\n play(): void {\n this.playState = 'running';\n }\n reverse(): void {\n // Nothing to reverse.\n }\n updatePlaybackRate(rate: number): void {\n this.playbackRate = rate;\n }\n}\n\n/** A `MouseEvent` carrying the fields of `PointerEventInit`, with the specification's defaults. */\nclass BfPointerEventShim extends MouseEvent {\n readonly pointerId: number;\n readonly isPrimary: boolean;\n readonly pointerType: string;\n readonly width: number;\n readonly height: number;\n readonly pressure: number;\n\n constructor(type: string, init: PointerEventInit = {}) {\n super(type, init);\n this.pointerId = init.pointerId ?? 0;\n this.isPrimary = init.isPrimary ?? false;\n this.pointerType = init.pointerType ?? '';\n this.width = init.width ?? 1;\n this.height = init.height ?? 1;\n this.pressure = init.pressure ?? 0;\n }\n}\n\n/** A `MediaQueryList` stand-in that never changes. */\nfunction staticMediaQueryList(media: string, matches: boolean): MediaQueryList {\n return {\n matches,\n media,\n onchange: null,\n addListener: (): void => undefined,\n removeListener: (): void => undefined,\n addEventListener: (): void => undefined,\n removeEventListener: (): void => undefined,\n dispatchEvent: (): boolean => false,\n };\n}\n\n/**\n * Sets `target[key]` and returns the function that puts back what was there, or `null` when the\n * property cannot be set (then nothing changed). A new property is writable and configurable, so\n * it can be deleted again; a configurable existing one is restored from its descriptor, getters\n * included. An existing non-configurable one (a runner's own `matchMedia` mock, assigned\n * `undefined` to test without it) is assigned through its setter or value and restored the same\n * way; a non-writable one without a setter is left alone. Internal: not exported from the entry.\n */\nexport function installGlobal(target: object, key: string, value: unknown): (() => void) | null {\n const previous = Object.getOwnPropertyDescriptor(target, key);\n if (previous && !previous.configurable) {\n const before: unknown = previous.get ? previous.get.call(target) : previous.value;\n if (!Reflect.set(target, key, value)) {\n return null;\n }\n return () => {\n Reflect.set(target, key, before);\n };\n }\n Object.defineProperty(target, key, { value, writable: true, configurable: true });\n return () => {\n if (previous) {\n Object.defineProperty(target, key, previous);\n } else {\n Reflect.deleteProperty(target, key);\n }\n };\n}\n\n/**\n * Installs, only where missing: `Element.prototype.animate` (an Animation whose `finished`\n * resolves at once), `PointerEvent` (a MouseEvent subclass with pointerId, isPrimary, pointerType,\n * width, height, pressure) and optionally `matchMedia`. Idempotent; returns a function that removes\n * exactly what it installed. Call it once in the test runner's setup file (Vitest `setupFiles`,\n * Jest `setupFilesAfterEnv`).\n *\n * @example\n * // src/test-setup.ts\n * import { installBfJsdomShims } from '@bitforgehq/ui/testing';\n * installBfJsdomShims();\n * @param options Whether to stub `matchMedia`, and whether the stub reports reduced motion.\n * @returns A function that removes the shims this call installed.\n * @summary Installs the browser APIs jsdom lacks and the library needs, only where missing\n * @since 0.3.0\n */\nexport function installBfJsdomShims(options: BfJsdomShimOptions = {}): () => void {\n const removers: (() => void)[] = [];\n const add = (remove: (() => void) | null): void => {\n if (remove) {\n removers.push(remove);\n }\n };\n\n if (typeof Element !== 'undefined' && typeof Element.prototype.animate !== 'function') {\n add(\n installGlobal(\n Element.prototype,\n 'animate',\n function animate(\n _keyframes: Keyframe[] | PropertyIndexedKeyframes | null,\n _timing?: number | KeyframeAnimationOptions,\n ): Animation {\n return new BfShimAnimation();\n },\n ),\n );\n }\n\n if (typeof globalThis.PointerEvent !== 'function' && typeof MouseEvent === 'function') {\n add(installGlobal(globalThis, 'PointerEvent', BfPointerEventShim));\n }\n\n if (\n (options.matchMedia ?? true) &&\n typeof window !== 'undefined' &&\n typeof window.matchMedia !== 'function'\n ) {\n const reducedMotion = options.reducedMotion ?? false;\n const media = (query: string): MediaQueryList =>\n staticMediaQueryList(query, reducedMotion && REDUCED_MOTION_QUERY.test(query));\n add(installGlobal(window, 'matchMedia', media));\n }\n\n return () => {\n for (const remove of removers.splice(0).reverse()) {\n remove();\n }\n };\n}\n","import { type EnvironmentProviders, makeEnvironmentProviders, type Provider } from '@angular/core';\nimport { provideBfDefaults, provideBfLabels, ɵprovideBfDocumentReset } from '@bitforgehq/ui';\n\n/**\n * Options of `provideBfTesting()`.\n *\n * @summary Options of the test providers: the size defaults and labels a spec runs with\n * @since 0.3.0\n */\nexport interface BfTestingOptions {\n /** Passed to `provideBfDefaults()`, e.g. `{ fieldSize: 'sm' }`; left out, the library defaults apply. */\n defaults?: Parameters<typeof provideBfDefaults>[0];\n /** Passed to `provideBfLabels()`, e.g. German validation messages; left out, the English defaults apply. */\n labels?: Parameters<typeof provideBfLabels>[0];\n}\n\n/**\n * Test providers for apps using the root and overlay entries: passes `defaults`/`labels` to 3b's\n * providers, and on environment teardown removes what overlays and themes leave on the document —\n * `dark` and `cdk-global-scrollblock` on `<html>`, `inert` on body children, stray\n * `.cdk-overlay-container` children. The cleanup runs when TestBed destroys the test environment,\n * so it needs TestBed's default `teardown: { destroyAfterEach: true }`; with `destroyAfterEach:\n * false` nothing is cleaned between tests. Mobile shell apps use `provideBfShellTesting()` from\n * `@bitforgehq/ui/mobile` instead.\n *\n * @example\n * TestBed.configureTestingModule({\n * providers: [provideBfTesting({ defaults: { fieldSize: 'sm' } })],\n * });\n * @param options The size defaults and labels to provide.\n * @returns The environment providers for `TestBed.configureTestingModule()`.\n * @summary Test providers for the widgets and overlays, with document cleanup after each test\n * @since 0.3.0\n */\nexport function provideBfTesting(options: BfTestingOptions = {}): EnvironmentProviders {\n const providers: Provider[] = [];\n if (options.defaults) {\n providers.push(provideBfDefaults(options.defaults));\n }\n if (options.labels) {\n providers.push(provideBfLabels(options.labels));\n }\n return makeEnvironmentProviders([...providers, ɵprovideBfDocumentReset()]);\n}\n","import { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/** A rendered native option and whether it is one of the select's `options` (not the placeholder). */\ninterface NativeOption {\n element: TestElement;\n label: string;\n choice: boolean;\n}\n\n/**\n * Harness for `bf-select`. It works by option label, so a test never needs the native option\n * values (an object option's native value is its index).\n *\n * @example\n * const select = await loader.getHarness(BfSelectHarness.with({ label: 'Project' }));\n * await select.selectOption('Two');\n * @summary Test harness for the select: reads its label and options and picks one\n * @since 0.3.0\n */\nexport class BfSelectHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-select';\n\n private readonly select = this.locatorFor('select');\n private readonly label = this.locatorForOptional('label');\n private readonly error = this.locatorForOptional('p[id$=\"-error\"]');\n private readonly help = this.locatorForOptional('p[id$=\"-help\"]');\n private readonly nativeOptions = this.locatorForAll('option');\n\n /**\n * A predicate for a select with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfSelectHarness> {\n return new HarnessPredicate(BfSelectHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The visible label without the required mark, else the `aria-label`.\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n const label = await this.label();\n if (label) {\n return (await label.text({ exclude: '[aria-hidden=\"true\"]' })).trim();\n }\n return (await (await this.select()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The selected option's label, `''` while nothing is selected.\n * @returns The label text.\n */\n async getValueLabel(): Promise<string> {\n for (const option of await this.options()) {\n if (option.choice && (await option.element.getProperty<boolean>('selected'))) {\n return option.label;\n }\n }\n return '';\n }\n\n /**\n * The labels of the options, in order, without the placeholder.\n * @returns The option labels.\n */\n async getOptionLabels(): Promise<string[]> {\n return (await this.options()).filter((option) => option.choice).map((option) => option.label);\n }\n\n /**\n * Chooses the option with this label, as a user would: the select's `value` becomes that\n * option's value. Throws, leaving the value as it is, for an unknown or disabled option and\n * while the select itself is disabled.\n * @param label The option's label.\n */\n async selectOption(label: string): Promise<void> {\n if (await this.isDisabled()) {\n throw new Error(`BfSelectHarness: the select is disabled, so \"${label}\" cannot be chosen`);\n }\n const options = await this.options();\n const option = options.find((o) => o.choice && o.label === label);\n if (!option) {\n const known = options.filter((o) => o.choice).map((o) => o.label);\n throw new Error(`BfSelectHarness: no option \"${label}\" (options: ${known.join(', ')})`);\n }\n if (await option.element.getProperty<boolean>('disabled')) {\n throw new Error(`BfSelectHarness: the option \"${label}\" is disabled`);\n }\n // Not `TestElement.selectOptions()`: it deselects the current option first and fires `change`\n // for that too, so the select would first see the browser's fallback option. A user's pick is\n // one `change` with the new option selected.\n const select = await this.select();\n await select.setInputValue(await option.element.getProperty<string>('value'));\n await select.dispatchEvent('change');\n }\n\n /**\n * Whether the select is disabled.\n * @returns True when disabled.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.select()).getProperty<boolean>('disabled');\n }\n\n /**\n * Whether the select is read-only (`aria-readonly`).\n * @returns True when read-only.\n */\n async isReadonly(): Promise<boolean> {\n return (await (await this.select()).getAttribute('aria-readonly')) === 'true';\n }\n\n /**\n * Whether the select is required.\n * @returns True when required.\n */\n async isRequired(): Promise<boolean> {\n return (await this.select()).getProperty<boolean>('required');\n }\n\n /**\n * The error text under the select (an explicit message, or the first form error once touched).\n * @returns The error text, `''` when there is none.\n */\n async getErrorText(): Promise<string> {\n return (await (await this.error())?.text())?.trim() ?? '';\n }\n\n /**\n * The help text under the select; it is hidden while an error shows.\n * @returns The help text, `''` when there is none.\n */\n async getHelpText(): Promise<string> {\n return (await (await this.help())?.text())?.trim() ?? '';\n }\n\n /** Focuses the native select. */\n async focus(): Promise<void> {\n await (await this.select()).focus();\n }\n\n /** Blurs the native select, which marks a bound form field touched. */\n async blur(): Promise<void> {\n await (await this.select()).blur();\n }\n\n /** Every native option; the placeholder or hidden empty option comes first, empty and disabled. */\n private async options(): Promise<NativeOption[]> {\n return Promise.all(\n (await this.nativeOptions()).map(async (element, index) => {\n const [label, value, hidden, disabled] = await Promise.all([\n element.text(),\n element.getAttribute('value'),\n element.getProperty<boolean>('hidden'),\n element.getProperty<boolean>('disabled'),\n ]);\n const placeholder = index === 0 && (hidden || (value === '' && disabled));\n return { element, label: label.trim(), choice: !placeholder };\n }),\n );\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-radio-group`. It works by option label, so a test never needs the native radio\n * values (an object option's native value is its index).\n *\n * @example\n * const plan = await loader.getHarness(BfRadioGroupHarness.with({ label: 'Plan' }));\n * await plan.select('Team');\n * @summary Test harness for the radio group: reads its options and selects one by label\n * @since 0.3.0\n */\nexport class BfRadioGroupHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-radio-group';\n\n private readonly fieldset = this.locatorFor('fieldset');\n private readonly legend = this.locatorForOptional('legend');\n private readonly error = this.locatorForOptional('p[id$=\"-error\"]');\n private readonly radios = this.locatorForAll('[data-options] > label input[type=\"radio\"]');\n private readonly labels = this.locatorForAll(\n '[data-options] > label > span:last-child > span:first-child',\n );\n\n /**\n * A predicate for a radio group with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfRadioGroupHarness> {\n return new HarnessPredicate(BfRadioGroupHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The legend, else the group's `aria-label`.\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n const legend = await this.legend();\n if (legend) {\n return (await legend.text()).trim();\n }\n return (await (await this.fieldset()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The labels of the options, in order.\n * @returns The option labels.\n */\n async getOptionLabels(): Promise<string[]> {\n return Promise.all((await this.labels()).map(async (label) => (await label.text()).trim()));\n }\n\n /**\n * The selected option's label, `null` while nothing is selected.\n * @returns The label text or `null`.\n */\n async getSelectedLabel(): Promise<string | null> {\n const [radios, labels] = await Promise.all([this.radios(), this.getOptionLabels()]);\n for (const [index, radio] of radios.entries()) {\n if (await radio.getProperty<boolean>('checked')) {\n return labels[index] ?? null;\n }\n }\n return null;\n }\n\n /**\n * Clicks the option with this label, as a user would. Throws for an unknown label, and for a\n * disabled option or group (a user's click would do nothing).\n * @param label The option's label.\n */\n async select(label: string): Promise<void> {\n const [radios, labels] = await Promise.all([this.radios(), this.getOptionLabels()]);\n const radio = radios[labels.indexOf(label)];\n if (!radio) {\n throw new Error(`BfRadioGroupHarness: no option \"${label}\" (options: ${labels.join(', ')})`);\n }\n // `:disabled` also covers a radio inside the disabled fieldset of a disabled group.\n if (await radio.matchesSelector(':disabled')) {\n throw new Error(`BfRadioGroupHarness: the option \"${label}\" is disabled`);\n }\n await radio.click();\n }\n\n /**\n * Whether the whole group is disabled.\n * @returns True when disabled.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.fieldset()).getProperty<boolean>('disabled');\n }\n\n /**\n * The error text under the options.\n * @returns The error text, `''` when there is none.\n */\n async getErrorText(): Promise<string> {\n return (await (await this.error())?.text())?.trim() ?? '';\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-segmented`. It works by segment label, so a test never needs the option values.\n *\n * @example\n * const range = await loader.getHarness(BfSegmentedHarness.with({ label: 'Range' }));\n * await range.select('Week');\n * @summary Test harness for the segmented control: reads its segments and selects one\n * @since 0.3.0\n */\nexport class BfSegmentedHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-segmented';\n\n private readonly group = this.locatorFor('[role=\"radiogroup\"]');\n private readonly error = this.locatorForOptional('p[id$=\"-error\"]');\n private readonly segments = this.locatorForAll('[role=\"radio\"]');\n\n /**\n * A predicate for a segmented control with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfSegmentedHarness> {\n return new HarnessPredicate(BfSegmentedHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The group's accessible name (`label`, else `ariaLabel`); it is not rendered visibly.\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n return (await (await this.group()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The labels of the segments, in order.\n * @returns The segment labels.\n */\n async getOptionLabels(): Promise<string[]> {\n return Promise.all(\n (await this.segments()).map(async (segment) => (await segment.text()).trim()),\n );\n }\n\n /**\n * The selected segment's label, `null` while nothing is selected.\n * @returns The label text or `null`.\n */\n async getSelectedLabel(): Promise<string | null> {\n for (const segment of await this.segments()) {\n if ((await segment.getAttribute('aria-checked')) === 'true') {\n return (await segment.text()).trim();\n }\n }\n return null;\n }\n\n /**\n * Clicks the segment with this label, as a user would. Throws for an unknown label, and for a\n * disabled segment or control (a user's click would do nothing).\n * @param label The segment's label.\n */\n async select(label: string): Promise<void> {\n const [segments, labels] = await Promise.all([this.segments(), this.getOptionLabels()]);\n const segment = segments[labels.indexOf(label)];\n if (!segment) {\n throw new Error(`BfSegmentedHarness: no segment \"${label}\" (segments: ${labels.join(', ')})`);\n }\n if (await segment.matchesSelector(':disabled')) {\n throw new Error(`BfSegmentedHarness: the segment \"${label}\" is disabled`);\n }\n await segment.click();\n }\n\n /**\n * Whether the control is disabled: every segment is.\n * @returns True when disabled.\n */\n async isDisabled(): Promise<boolean> {\n const segments = await this.segments();\n const disabled = await Promise.all(\n segments.map((segment) => segment.getProperty<boolean>('disabled')),\n );\n return segments.length > 0 && disabled.every(Boolean);\n }\n\n /**\n * The error text under the track.\n * @returns The error text, `''` when there is none.\n */\n async getErrorText(): Promise<string> {\n return (await (await this.error())?.text())?.trim() ?? '';\n }\n}\n","import { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-dialog`, the card `BfDialogService` opens (a `confirm()` alert dialog is built\n * from it too). Dialogs render in the CDK overlay container outside the tested component, so get\n * it through `TestbedHarnessEnvironment.documentRootLoader(fixture)`.\n *\n * @example\n * const dialog = await TestbedHarnessEnvironment.documentRootLoader(fixture).getHarness(\n * BfDialogHarness.with({ label: 'Delete project?' }),\n * );\n * await dialog.clickButton('Delete');\n * @summary Test harness for a dialog: its title, text and role, its buttons and Escape\n * @since 0.3.0\n */\nexport class BfDialogHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-dialog';\n\n private readonly title = this.locatorForOptional('[data-dialog-header] h2');\n private readonly body = this.locatorFor('[data-dialog-body]');\n private readonly closeButton = this.locatorForOptional('[data-dialog-close] button');\n private readonly buttons = this.locatorForAll('button');\n\n /**\n * A predicate for a dialog with the given title.\n * @param filters The title (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfDialogHarness> {\n return new HarnessPredicate(BfDialogHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getTitle(), label),\n );\n }\n\n /**\n * The title heading.\n * @returns The title, `''` without one.\n */\n async getTitle(): Promise<string> {\n return (await (await this.title())?.text())?.trim() ?? '';\n }\n\n /**\n * The text of the dialog's body (its content, without the title and footer).\n * @returns The body text.\n */\n async getText(): Promise<string> {\n return (await (await this.body()).text()).trim();\n }\n\n /**\n * The role of the overlay panel this dialog sits in: `dialog`, or `alertdialog` for\n * `confirm()` and `role: 'alertdialog'`. The panel is found by the dialog's own\n * `data-bf-dialog-id`, so a dialog under another one (a confirm opened from a dialog) reads its\n * own panel.\n * @returns The role, `null` outside `BfDialogService` (a `bf-dialog` rendered in the page).\n */\n async getRole(): Promise<string | null> {\n const id = await (await this.host()).getAttribute('data-bf-dialog-id');\n if (!id) {\n return null;\n }\n const root = this.documentRootLocatorFactory();\n for (const panel of await root.locatorForAll('.bf-overlay-panel[id]')()) {\n const paneId = await panel.getAttribute('id');\n const own = await root.locatorForOptional(\n `[id=\"${paneId}\"] bf-dialog[data-bf-dialog-id=\"${id}\"]`,\n )();\n if (own) {\n return panel.getAttribute('role');\n }\n }\n return null;\n }\n\n /**\n * Clicks the button with this text (a footer or content button). Throws for an unknown label.\n * @param label The button's text.\n */\n async clickButton(label: string): Promise<void> {\n const buttons = await this.buttons();\n const texts = await Promise.all(buttons.map(async (button) => (await button.text()).trim()));\n const button = buttons[texts.indexOf(label)];\n if (!button) {\n const known = texts.filter(Boolean);\n throw new Error(`BfDialogHarness: no button \"${label}\" (buttons: ${known.join(', ')})`);\n }\n await button.click();\n }\n\n /** Clicks the close button (×), which closes the dialog with no result. Throws without one. */\n async close(): Promise<void> {\n const button = await this.closeButton();\n if (!button) {\n throw new Error('BfDialogHarness: the dialog has no close button');\n }\n await button.click();\n }\n\n /** Presses `Escape` inside the dialog: it closes, or a `confirm()` resolves its cancel answer. */\n async pressEscape(): Promise<void> {\n await (await this.body()).sendKeys(TestKey.ESCAPE);\n }\n}\n","import { ComponentHarness, HarnessPredicate, type LocatorFactory } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for one row of a `bf-menu`: a `bfMenuItem`, `bfMenuItemCheckbox` or `bfMenuItemRadio`\n * button (or `a[bfMenuItem]` link). Its `label` filter matches the row's text.\n *\n * @example\n * const [first] = await menu.getItems({ label: 'Duplicate' });\n * expect(await first.isDisabled()).toBe(false);\n * @summary Test harness for a menu row: its text, disabled, checked and submenu state\n * @since 0.3.0\n */\nexport class BfMenuItemHarness extends ComponentHarness {\n /** The host element's selector: the menu item directives' selectors. */\n static hostSelector =\n 'button[bfMenuItem], a[bfMenuItem], button[bfMenuItemCheckbox], button[bfMenuItemRadio]';\n\n /**\n * A predicate for a menu item with the given text.\n * @param filters The text (exact or a pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfMenuItemHarness> {\n return new HarnessPredicate(BfMenuItemHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getText(), label),\n );\n }\n\n /**\n * The row's text.\n * @returns The text.\n */\n async getText(): Promise<string> {\n return (await (await this.host()).text()).trim();\n }\n\n /**\n * Whether the row is disabled (`aria-disabled`; a disabled row stays focusable).\n * @returns True when disabled.\n */\n async isDisabled(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-disabled')) === 'true';\n }\n\n /**\n * Whether a checkbox or radio row is checked.\n * @returns True when checked.\n */\n async isChecked(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-checked')) === 'true';\n }\n\n /**\n * Whether the row opens a submenu.\n * @returns True when it has a submenu.\n */\n async hasSubmenu(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-haspopup')) === 'menu';\n }\n\n /** Clicks the row: runs its action (closing the menu), toggles it, or opens its submenu. */\n async click(): Promise<void> {\n await (await this.host()).click();\n }\n\n /**\n * The submenu this row opened, or `null` while it is closed or when it has none.\n * @returns The submenu's harness or `null`.\n */\n async getSubmenu(): Promise<BfMenuHarness | null> {\n const id = await (await this.host()).getAttribute('aria-controls');\n return id ? menuById(this.documentRootLocatorFactory(), id) : null;\n }\n}\n\n/**\n * Harness for `bf-menu`, the card a `bfMenuTriggerFor` trigger or `bf-menu-button` opens. It\n * renders in the CDK overlay container, so get it from `BfMenuButtonHarness.getMenu()` or through\n * `TestbedHarnessEnvironment.documentRootLoader(fixture)`. Its `label` filter matches the menu's\n * accessible name (its `ariaLabel`, else its trigger's text).\n *\n * @example\n * const menu = await (await loader.getHarness(BfMenuButtonHarness)).getMenu();\n * await menu.clickItem('Move to', 'Archive');\n * @summary Test harness for a menu: lists its items and clicks one, through submenus\n * @since 0.3.0\n */\nexport class BfMenuHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-menu';\n\n /**\n * A predicate for a menu with the given accessible name.\n * @param filters The name (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfMenuHarness> {\n return new HarnessPredicate(BfMenuHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The menu's accessible name: its `aria-label`, else the text of the element it is labelled\n * by (its trigger).\n * @returns The name, `''` without one.\n */\n async getLabel(): Promise<string> {\n const host = await this.host();\n const label = await host.getAttribute('aria-label');\n if (label) {\n return label;\n }\n const by = await host.getAttribute('aria-labelledby');\n const named = by\n ? await this.documentRootLocatorFactory().locatorForOptional(`[id=\"${by}\"]`)()\n : null;\n return (await named?.text())?.trim() ?? '';\n }\n\n /**\n * The menu's rows, in order (a submenu's rows belong to the submenu).\n * @param filter Only the rows matching it, e.g. `{ label: 'Archive' }`.\n * @returns The row harnesses.\n */\n async getItems(filter: BfHarnessFilters = {}): Promise<BfMenuItemHarness[]> {\n return this.locatorForAll(BfMenuItemHarness.with(filter))();\n }\n\n /**\n * Clicks the row with this text; with a `submenuPath`, the row opens a submenu and the path's\n * labels are clicked in the submenus that open, in order. Throws for an unknown label.\n * @param label The row's text.\n * @param submenuPath The labels to click in the submenus that open, in order.\n */\n async clickItem(label: string, ...submenuPath: string[]): Promise<void> {\n const [item] = await this.getItems({ label });\n if (!item) {\n const known = await Promise.all((await this.getItems()).map((i) => i.getText()));\n throw new Error(`BfMenuHarness: no item \"${label}\" (items: ${known.join(', ')})`);\n }\n await item.click();\n const [next, ...rest] = submenuPath;\n if (next === undefined) {\n return;\n }\n const submenu = await item.getSubmenu();\n if (!submenu) {\n throw new Error(`BfMenuHarness: the item \"${label}\" opened no submenu`);\n }\n await submenu.clickItem(next, ...rest);\n }\n}\n\n/** The `bf-menu` with this id in the document, or `null`. Internal. */\nexport async function menuById(root: LocatorFactory, id: string): Promise<BfMenuHarness | null> {\n return root.locatorForOptional(BfMenuHarness.with({ selector: `[id=\"${id}\"]` }))();\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\nimport { type BfMenuHarness, menuById } from './menu.harness';\n\n/**\n * Harness for `bf-menu-button`, a button that opens a `bf-menu`. Its `label` filter matches the\n * button's accessible name (its `label`, else its text).\n *\n * @example\n * const button = await loader.getHarness(BfMenuButtonHarness.with({ label: 'Actions' }));\n * await (await button.getMenu()).clickItem('Archive');\n * @summary Test harness for a menu button: opens it and hands out the menu it opened\n * @since 0.3.0\n */\nexport class BfMenuButtonHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-menu-button';\n\n private readonly button = this.locatorFor('button');\n\n /**\n * A predicate for a menu button with the given accessible name.\n * @param filters The name (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfMenuButtonHarness> {\n return new HarnessPredicate(BfMenuButtonHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The button's accessible name: its `aria-label` (the `label` input), else its text.\n * @returns The name.\n */\n async getLabel(): Promise<string> {\n const button = await this.button();\n return (await button.getAttribute('aria-label')) ?? (await button.text()).trim();\n }\n\n /** Opens the menu with a click; does nothing while it is open. */\n async open(): Promise<void> {\n if (!(await this.isOpen())) {\n await (await this.button()).click();\n }\n }\n\n /**\n * Whether the menu is open (`aria-expanded`).\n * @returns True while open.\n */\n async isOpen(): Promise<boolean> {\n return (await (await this.button()).getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * The open menu, opening it first when it is closed.\n * @returns The menu's harness.\n */\n async getMenu(): Promise<BfMenuHarness> {\n await this.open();\n const id = await (await this.button()).getAttribute('aria-controls');\n const menu = id ? await menuById(this.documentRootLocatorFactory(), id) : null;\n if (!menu) {\n throw new Error('BfMenuButtonHarness: the menu did not open');\n }\n return menu;\n }\n}\n","import { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-desktop-toast`, the card `BfDesktopToastService.show()` renders. Toasts render\n * in the CDK overlay container, so get it through\n * `TestbedHarnessEnvironment.documentRootLoader(fixture)`.\n *\n * @example\n * const toast = await TestbedHarnessEnvironment.documentRootLoader(fixture).getHarness(\n * BfDesktopToastHarness,\n * );\n * await toast.clickAction();\n * @summary Test harness for a desktop toast: its message, its action and Escape\n * @since 0.3.0\n */\nexport class BfDesktopToastHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-desktop-toast';\n\n private readonly message = this.locatorFor('span');\n private readonly action = this.locatorForOptional('button');\n\n /**\n * A predicate for a toast with the given message.\n * @param filters The message (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfDesktopToastHarness> {\n return new HarnessPredicate(BfDesktopToastHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getMessage(), label),\n );\n }\n\n /**\n * The toast's message.\n * @returns The message text.\n */\n async getMessage(): Promise<string> {\n return (await (await this.message()).text()).trim();\n }\n\n /** Clicks the action button, which dismisses the toast with `'action'`. Throws without one. */\n async clickAction(): Promise<void> {\n const action = await this.action();\n if (!action) {\n throw new Error('BfDesktopToastHarness: the toast has no action');\n }\n await action.click();\n }\n\n /** Presses `Escape` inside the toast, which dismisses it with `'dismiss'`. */\n async pressEscape(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.ESCAPE);\n }\n}\n","import {\n ComponentHarness,\n type HarnessQuery,\n HarnessPredicate,\n type TestElement,\n} from '@angular/cdk/testing';\nimport { type BfTableSort } from '@bitforgehq/ui';\nimport { type BfHarnessFilters } from './harness-filters';\nimport { BfSelectHarness } from './select.harness';\n\n/**\n * Harness for `bf-table`. It works by column header text and row position, so a test never needs\n * the column keys or the row keys (except `getSort()`, which returns the column's `key`, as the\n * `sort` model holds it). It reads and drives a stacked table (`stack`) the same way.\n *\n * @example\n * const tasks = await loader.getHarness(BfTableHarness.with({ label: 'Tasks' }));\n * await tasks.sortBy('Title');\n * expect(await tasks.getCellText(0, 'Title')).toBe('Atlas rebrand');\n * @summary Test harness for the data table: reads rows and cells, sorts and selects rows\n * @since 0.3.1\n */\nexport class BfTableHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-table';\n\n private readonly caption = this.locatorForOptional('caption');\n private readonly headers = this.locatorForAll('thead th[data-bf-column]');\n private readonly rows = this.locatorForAll('tbody tr[data-bf-table-row]');\n private readonly selectAllBox = this.locatorForOptional('thead input[type=\"checkbox\"]');\n /* The selection column's checkboxes only (its cells carry no data-bf-cell), never a checkbox or\n toggle in a custom cell. */\n private readonly rowBoxes = this.locatorForAll(\n 'tbody tr[data-bf-table-row] > td:not([data-bf-cell]) input[type=\"checkbox\"]',\n );\n private readonly sortSelect = this.locatorForOptional(\n BfSelectHarness.with({ ancestor: '[data-bf-table-stack-bar]' }),\n );\n\n /**\n * A predicate for a table with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfTableHarness> {\n return new HarnessPredicate(BfTableHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The table's name, its visually hidden caption.\n * @returns The label text, `''` without one.\n */\n async getLabel(): Promise<string> {\n return (await (await this.caption())?.text())?.trim() ?? '';\n }\n\n /**\n * The number of body rows; `0` while the empty slot shows.\n * @returns The row count.\n */\n async getRowCount(): Promise<number> {\n return (await this.rows()).length;\n }\n\n /**\n * The column headers' text, in order, without the selection column.\n * @returns The header labels.\n */\n async getHeaderLabels(): Promise<string[]> {\n // A stacked table repeats a sortable header's label outside its hidden button; read it once.\n return Promise.all(\n (await this.headers()).map(async (th) =>\n (await th.text({ exclude: '[data-bf-stacked-label]' })).trim(),\n ),\n );\n }\n\n /**\n * The text of one cell.\n * @param row The row's position, from 0.\n * @param column The column's header text.\n * @returns The cell's text.\n */\n async getCellText(row: number, column: string): Promise<string> {\n const index = await this.columnIndex(column);\n if (row < 0 || row >= (await this.getRowCount())) {\n throw new Error(`BfTableHarness: no row ${row}`);\n }\n const cells = await this.locatorForAll(\n `tbody tr[data-bf-table-row]:nth-of-type(${row + 1}) [data-bf-cell]`,\n )();\n return (await cells[index]?.text())?.trim() ?? '';\n }\n\n /**\n * A harness for a control in one cell, such as a `bf-toggle` of a settings matrix or a\n * `bf-select` in a custom cell. Throws for an unknown row or column, or when the cell holds no\n * matching control.\n *\n * @example\n * const push = await alerts.getCellHarness(0, 'Push', BfToggleHarness);\n * await push.check();\n * @param row The row's position, from 0.\n * @param column The column's header text.\n * @param query The harness type, or a predicate from its `with()`.\n * @returns The first matching harness in the cell.\n * @since 0.4.0\n */\n async getCellHarness<T extends ComponentHarness>(\n row: number,\n column: string,\n query: HarnessQuery<T>,\n ): Promise<T> {\n const index = await this.columnIndex(column);\n if (row < 0 || row >= (await this.getRowCount())) {\n throw new Error(`BfTableHarness: no row ${row}`);\n }\n // The selection column, when there is one, comes before the data cells.\n const offset = (await this.locatorForOptional(\n 'tbody tr[data-bf-table-row] > td:not([data-bf-cell])',\n )())\n ? 1\n : 0;\n const cell = `tbody tr[data-bf-table-row]:nth-of-type(${row + 1}) > td:nth-child(${index + offset + 1})`;\n return (await this.locatorFactory.harnessLoaderFor(cell)).getHarness(query);\n }\n\n /**\n * Clicks a sortable column's header, as a user would: ascending first, then descending.\n * Throws for an unknown or unsortable column.\n * @param column The column's header text.\n */\n async sortBy(column: string): Promise<void> {\n const key = await (\n await this.headers()\n )[await this.columnIndex(column)]?.getAttribute('data-bf-column');\n const button = key\n ? await this.locatorForOptional(`thead th[data-bf-column=\"${key}\"] button`)()\n : null;\n if (!button) {\n throw new Error(`BfTableHarness: the column \"${column}\" is not sortable`);\n }\n await button.click();\n }\n\n /**\n * The sorted column (its `key`) and direction, from `aria-sort`.\n * @returns The sort, `null` while no column is sorted.\n */\n async getSort(): Promise<BfTableSort | null> {\n for (const header of await this.headers()) {\n const direction = await header.getAttribute('aria-sort');\n const column = await header.getAttribute('data-bf-column');\n if (column && (direction === 'ascending' || direction === 'descending')) {\n return { column, direction };\n }\n }\n return null;\n }\n\n /**\n * The Sort by select in a stacked table's bar: its first option, Not sorted (`tableSortNone`),\n * sets `sort` to null and shows while it is; then each sortable column ascending and descending.\n *\n * @example\n * const sortBy = await tasks.getSortSelect();\n * await sortBy?.selectOption('Hours, descending');\n * expect(await tasks.getSort()).toEqual({ column: 'hours', direction: 'descending' });\n * @returns The select's harness; `null` when the table cannot stack (`stack=\"never\"`) or has no\n * sortable column.\n * @since 0.3.3\n */\n async getSortSelect(): Promise<BfSelectHarness | null> {\n return this.sortSelect();\n }\n\n /**\n * Checks one row's checkbox, as a user would; a selected row stays selected. Throws when the\n * table is not selectable or the row does not exist.\n * @param index The row's position, from 0.\n */\n async selectRow(index: number): Promise<void> {\n const box = (await this.rowBoxes())[index];\n if (!box) {\n throw new Error(`BfTableHarness: no selectable row ${index}`);\n }\n await this.check(box);\n }\n\n /**\n * Checks the header checkbox, as a user would, so every row is selected. Throws when the table\n * is not selectable.\n */\n async selectAll(): Promise<void> {\n const box = await this.selectAllBox();\n if (!box) {\n throw new Error('BfTableHarness: the table is not selectable');\n }\n await this.check(box);\n }\n\n /**\n * The number of selected rows (checked row checkboxes).\n * @returns The count.\n */\n async getSelectedCount(): Promise<number> {\n const checked = await Promise.all(\n (await this.rowBoxes()).map((box) => box.getProperty<boolean>('checked')),\n );\n return checked.filter(Boolean).length;\n }\n\n private async check(box: TestElement): Promise<void> {\n if (!(await box.getProperty<boolean>('checked'))) {\n await box.click();\n }\n }\n\n private async columnIndex(column: string): Promise<number> {\n const labels = await this.getHeaderLabels();\n const index = labels.indexOf(column);\n if (index === -1) {\n throw new Error(`BfTableHarness: no column \"${column}\" (columns: ${labels.join(', ')})`);\n }\n return index;\n }\n}\n","import { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-pagination`. It reads the current page and the page count as numbers, so a test\n * never parses the \"Page 3 of 12\" text, and clicks the buttons as a user would.\n *\n * @example\n * const pages = await loader.getHarness(BfPaginationHarness.with({ label: 'Invoice pages' }));\n * await pages.next();\n * expect(await pages.getCurrentPage()).toBe(2);\n * @summary Test harness for pagination: reads the current page and moves between pages\n * @since 0.3.1\n */\nexport class BfPaginationHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-pagination';\n\n private readonly nav = this.locatorFor('nav');\n private readonly previousButton = this.locatorFor('[data-bf-pagination-previous]');\n private readonly nextButton = this.locatorFor('[data-bf-pagination-next]');\n private readonly pages = this.locatorForAll('[data-bf-pagination-page]');\n\n /**\n * A predicate for a pagination with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfPaginationHarness> {\n return new HarnessPredicate(BfPaginationHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The navigation's accessible name (`label`, else \"Pagination\").\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n return (await (await this.nav()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The page shown as current, 1-based.\n * @returns The current page.\n */\n async getCurrentPage(): Promise<number> {\n return Number(await (await this.nav()).getAttribute('data-bf-page'));\n }\n\n /**\n * The number of pages in effect (`pageCount`, else `total` over `pageSize`, at least 1).\n * @returns The page count.\n */\n async getPageCount(): Promise<number> {\n return Number(await (await this.nav()).getAttribute('data-bf-page-count'));\n }\n\n /**\n * The page numbers the row shows right now, in order, without the gaps; empty when compact.\n * @returns The visible page numbers.\n */\n async getVisiblePages(): Promise<number[]> {\n return Promise.all(\n (await this.pages()).map(async (page) => Number((await page.text()).trim())),\n );\n }\n\n /**\n * Clicks the button of this page, as a user would. Throws when the row does not show it (behind\n * a gap, or in a compact pagination).\n * @param page The 1-based page to go to.\n */\n async goTo(page: number): Promise<void> {\n const [buttons, visible] = await Promise.all([this.pages(), this.getVisiblePages()]);\n const button = buttons[visible.indexOf(page)];\n if (!button) {\n const shown = visible.length > 0 ? visible.join(', ') : 'none';\n throw new Error(`BfPaginationHarness: page ${page} has no button (visible: ${shown})`);\n }\n await button.click();\n }\n\n /** Clicks Next. Throws on the last page, where the button is disabled. */\n async next(): Promise<void> {\n await this.press(await this.nextButton(), 'next');\n }\n\n /** Clicks Previous. Throws on the first page, where the button is disabled. */\n async previous(): Promise<void> {\n await this.press(await this.previousButton(), 'previous');\n }\n\n /**\n * Whether Previous is disabled (on the first page).\n * @returns True on the first page.\n */\n async isPreviousDisabled(): Promise<boolean> {\n return (await (await this.previousButton()).getAttribute('aria-disabled')) === 'true';\n }\n\n /**\n * Whether Next is disabled (on the last page).\n * @returns True on the last page.\n */\n async isNextDisabled(): Promise<boolean> {\n return (await (await this.nextButton()).getAttribute('aria-disabled')) === 'true';\n }\n\n /** Clicks an arrow button, unless it is disabled. */\n private async press(button: TestElement, name: string): Promise<void> {\n if ((await button.getAttribute('aria-disabled')) === 'true') {\n throw new Error(`BfPaginationHarness: the ${name} button is disabled`);\n }\n await button.click();\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/* The group's own tabs and panels only, never those of a tab group nested in a panel. */\nconst TAB = ':scope > [data-bf-tab-scroller] > [role=\"tablist\"] > [role=\"tab\"]';\n\n/**\n * Harness for `bf-tab-group`. It works by tab label, so a test never needs the panel values.\n *\n * @example\n * const tabs = await loader.getHarness(BfTabGroupHarness.with({ label: 'Project' }));\n * await tabs.selectTab('Activity');\n * expect(await tabs.getPanelText()).toContain('No activity yet');\n * @summary Test harness for the tab group: reads its tabs and panel and selects a tab\n * @since 0.3.1\n */\nexport class BfTabGroupHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-tab-group';\n\n private readonly list = this.locatorFor(':scope > [data-bf-tab-scroller] > [role=\"tablist\"]');\n private readonly tabs = this.locatorForAll(TAB);\n private readonly tabLabels = this.locatorForAll(`${TAB} > [data-label]`);\n private readonly panel = this.locatorForOptional(':scope > [role=\"tabpanel\"]:not([hidden])');\n\n /**\n * A predicate for a tab group with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfTabGroupHarness> {\n return new HarnessPredicate(BfTabGroupHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The tablist's accessible name (`label`, else the `tabGroup` label).\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n return (await (await this.list()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The tabs' labels, in order (without their badges).\n * @returns The tab labels.\n */\n async getTabLabels(): Promise<string[]> {\n return Promise.all((await this.tabLabels()).map(async (label) => (await label.text()).trim()));\n }\n\n /**\n * The selected tab's label, `null` when the group has no tabs.\n * @returns The label text or `null`.\n */\n async getSelectedLabel(): Promise<string | null> {\n const [tabs, labels] = await Promise.all([this.tabs(), this.getTabLabels()]);\n for (const [index, tab] of tabs.entries()) {\n if ((await tab.getAttribute('aria-selected')) === 'true') {\n return labels[index] ?? null;\n }\n }\n return null;\n }\n\n /**\n * Clicks the tab with this label, as a user would. Throws for an unknown label, and for a\n * disabled tab (a user's click would do nothing).\n * @param label The tab's label.\n */\n async selectTab(label: string): Promise<void> {\n const [tabs, labels] = await Promise.all([this.tabs(), this.getTabLabels()]);\n const tab = tabs[labels.indexOf(label)];\n if (!tab) {\n throw new Error(`BfTabGroupHarness: no tab \"${label}\" (tabs: ${labels.join(', ')})`);\n }\n if (await tab.matchesSelector(':disabled')) {\n throw new Error(`BfTabGroupHarness: the tab \"${label}\" is disabled`);\n }\n await tab.click();\n }\n\n /**\n * The text of the panel shown.\n * @returns The panel's text, `''` when no panel shows.\n */\n async getPanelText(): Promise<string> {\n return (await (await this.panel())?.text())?.trim() ?? '';\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-side-panel`, the panel `BfSidePanelService` opens. Side panels render in the\n * CDK overlay container outside the tested component, so get it through\n * `TestbedHarnessEnvironment.documentRootLoader(fixture)`.\n *\n * @example\n * const panel = await TestbedHarnessEnvironment.documentRootLoader(fixture).getHarness(\n * BfSidePanelHarness.with({ label: 'INV-204' }),\n * );\n * await panel.close();\n * expect(await panel.isOpen()).toBe(false);\n * @summary Test harness for a side panel: its title, its close button and its state\n * @since 0.3.1\n */\nexport class BfSidePanelHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-side-panel';\n\n private readonly title = this.locatorForOptional('[data-side-panel-header] h2');\n private readonly closeButton = this.locatorFor('[data-side-panel-close] button');\n\n /**\n * A predicate for a side panel with the given title.\n * @param filters The title (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfSidePanelHarness> {\n return new HarnessPredicate(BfSidePanelHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getTitle(), label),\n );\n }\n\n /**\n * The title heading.\n * @returns The title, `''` without one.\n */\n async getTitle(): Promise<string> {\n return (await (await this.title())?.text())?.trim() ?? '';\n }\n\n /** Clicks the close button (×), which closes the side panel with no result. */\n async close(): Promise<void> {\n await (await this.closeButton()).click();\n }\n\n /**\n * Whether the side panel is open: its overlay pane is in the page and not sliding out. The pane\n * is found by the panel's own `data-bf-side-panel-id`.\n * @returns True while the panel is open, false once closing or closed, or outside the service.\n */\n async isOpen(): Promise<boolean> {\n const id = await (await this.host()).getAttribute('data-bf-side-panel-id');\n if (!id) {\n return false;\n }\n const open = await this.documentRootLocatorFactory().locatorForOptional(\n `.bf-overlay-panel:not([data-bf-closing]) bf-side-panel[data-bf-side-panel-id=\"${id}\"]`,\n )();\n return open !== null;\n }\n}\n","import { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-calendar`. It works with ISO dates ('YYYY-MM-DD'), as the calendar's value\n * does, and drives it as a user would: `selectDate()` jumps to another month through the month\n * and year views, then clicks the day.\n *\n * @example\n * const due = await loader.getHarness(BfCalendarHarness.with({ label: 'Due date' }));\n * await due.selectDate('2026-11-03');\n * expect(await due.getSelected()).toBe('2026-11-03');\n * @summary Test harness for the calendar: reads the month and picks a date by ISO string\n * @since 0.3.1\n */\nexport class BfCalendarHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-calendar';\n\n private readonly group = this.locatorFor('[role=\"group\"]');\n private readonly liveTitle = this.locatorFor('[aria-live=\"polite\"]');\n private readonly titleButton = this.locatorForOptional('[data-bf-calendar-title]');\n private readonly previousButton = this.locatorForOptional('[data-bf-calendar-previous]');\n private readonly nextButton = this.locatorForOptional('[data-bf-calendar-next]');\n private readonly selectedDay = this.locatorForOptional('td[data-date][aria-selected=\"true\"]');\n private readonly dayGrid = this.locatorForOptional('table[data-bf-calendar-grid=\"day\"]');\n\n /**\n * A predicate for a calendar with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfCalendarHarness> {\n return new HarnessPredicate(BfCalendarHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The calendar's accessible name (`label`, else `ariaLabel`); it is not rendered visibly.\n * @returns The label text, `''` without one.\n */\n async getLabel(): Promise<string> {\n return (await (await this.group()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The shown month and year as the header writes them, in the calendar's locale.\n * @returns The month label, such as \"October 2026\".\n */\n async getMonthLabel(): Promise<string> {\n return (await (await this.liveTitle()).text()).trim();\n }\n\n /**\n * The picked date while it is in the shown month.\n * @returns The ISO date, or `null` when none is picked or it is in another month.\n */\n async getSelected(): Promise<string | null> {\n return (await (await this.selectedDay())?.getAttribute('data-date')) ?? null;\n }\n\n /** Clicks the next-month button. Throws when it is disabled (the month is past `max`). */\n async nextMonth(): Promise<void> {\n await this.clickNav(await this.nextButton(), 'next');\n }\n\n /** Clicks the previous-month button. Throws when it is disabled (the month is before `min`). */\n async previousMonth(): Promise<void> {\n await this.clickNav(await this.previousButton(), 'previous');\n }\n\n /**\n * Picks a date as a user would: when it is in another month, opens the month view, then the\n * year view, clicks its year and month, then clicks the day. Throws for an invalid date, one\n * the views do not list, and a disabled date or calendar.\n * @param iso The ISO date to pick, such as '2026-11-03'.\n */\n async selectDate(iso: string): Promise<void> {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(iso)) {\n throw new Error(`BfCalendarHarness: \"${iso}\" is not an ISO date (YYYY-MM-DD)`);\n }\n if (!(await this.dayGrid())) {\n throw new Error('BfCalendarHarness: the calendar is not showing its days');\n }\n let day = await this.locatorForOptional(`td[data-date=\"${iso}\"]`)();\n if (!day) {\n await this.jumpTo(iso.slice(0, 7));\n day = await this.locatorForOptional(`td[data-date=\"${iso}\"]`)();\n }\n if (!day) {\n throw new Error(`BfCalendarHarness: no day ${iso}`);\n }\n if ((await day.getAttribute('aria-disabled')) === 'true') {\n throw new Error(`BfCalendarHarness: the day ${iso} is disabled`);\n }\n if ((await (await this.dayGrid())?.getAttribute('aria-disabled')) === 'true') {\n throw new Error('BfCalendarHarness: the calendar is disabled');\n }\n await day.click();\n }\n\n private async jumpTo(month: string): Promise<void> {\n const title = await this.titleButton();\n if (!title || (await title.getProperty<boolean>('disabled'))) {\n throw new Error('BfCalendarHarness: the calendar is disabled');\n }\n await title.click();\n await (await this.locatorFor('[data-bf-calendar-title]')()).click();\n const year = await this.locatorForOptional(`td[data-year=\"${Number(month.slice(0, 4))}\"]`)();\n if (!year) {\n throw new Error(`BfCalendarHarness: the year view does not list ${month.slice(0, 4)}`);\n }\n await year.click();\n const cell = await this.locatorFor(`td[data-month=\"${month}\"]`)();\n if ((await cell.getAttribute('aria-disabled')) === 'true') {\n throw new Error(`BfCalendarHarness: the month ${month} is outside min and max`);\n }\n await cell.click();\n }\n\n private async clickNav(button: TestElement | null, which: string): Promise<void> {\n if (!button) {\n throw new Error('BfCalendarHarness: the calendar is not showing its days');\n }\n if (\n (await button.getProperty<boolean>('disabled')) ||\n (await button.getAttribute('aria-disabled')) === 'true'\n ) {\n throw new Error(`BfCalendarHarness: the ${which} month button is disabled`);\n }\n await button.click();\n }\n}\n","import { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/** The elements that take keyboard focus, as the overlay core counts them. Internal. */\nexport const FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]):not([type=\"hidden\"]), ' +\n 'select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Harness for an element with a `[bfTooltip]`. The tooltip renders in the CDK overlay container\n * only while it shows, so `show()` focuses the element (the tooltip shows at once on keyboard\n * focus; a 500 ms hover would need real time) and `getText()` reads the `bf-tooltip` its control\n * is described by. Its `label` filter matches the element's accessible name (`aria-label`, else\n * its text). Under a coarse pointer no tooltip shows.\n *\n * @example\n * const copy = await loader.getHarness(BfTooltipHarness.with({ label: 'Copy link' }));\n * expect(await copy.getText()).toBe('Copy link');\n * @summary Test harness for a tooltip: shows it, reads its text and whether it shows\n * @since 0.3.1\n */\nexport class BfTooltipHarness extends ComponentHarness {\n /** The host element's selector: the attribute the tooltip directive puts on its host. */\n static hostSelector = '[data-bf-tooltip]';\n\n private readonly inner = this.locatorForOptional(FOCUSABLE);\n\n /**\n * A predicate for an element with a tooltip and the given accessible name.\n * @param filters The name (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfTooltipHarness> {\n return new HarnessPredicate(BfTooltipHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The element's accessible name: its control's `aria-label`, else its text.\n * @returns The name.\n */\n async getLabel(): Promise<string> {\n const control = await this.control();\n return (await control.getAttribute('aria-label')) ?? (await control.text()).trim();\n }\n\n /** Shows the tooltip by focusing the element (or the first control inside it). */\n async show(): Promise<void> {\n await (await this.control()).focus();\n }\n\n /** Hides the tooltip by moving focus off the element. */\n async hide(): Promise<void> {\n await (await this.control()).blur();\n }\n\n /**\n * Whether the tooltip shows.\n * @returns True while it shows.\n */\n async isOpen(): Promise<boolean> {\n return (await this.tooltip()) !== null;\n }\n\n /**\n * The tooltip's text, showing it first when it is hidden.\n * @returns The text; throws when no tooltip shows (an empty text or a coarse pointer).\n */\n async getText(): Promise<string> {\n if (!(await this.isOpen())) {\n await this.show();\n }\n const tooltip = await this.tooltip();\n if (!tooltip) {\n throw new Error('BfTooltipHarness: the tooltip did not show');\n }\n return (await tooltip.text()).trim();\n }\n\n /** The element the tooltip describes: the host when it takes focus, else its first control. */\n private async control(): Promise<TestElement> {\n const host = await this.host();\n return (await host.matchesSelector(FOCUSABLE)) ? host : ((await this.inner()) ?? host);\n }\n\n /** The shown `bf-tooltip` among the ids the control is described by, or `null`. */\n private async tooltip(): Promise<TestElement | null> {\n const ids = ((await (await this.control()).getAttribute('aria-describedby')) ?? '')\n .split(/\\s+/)\n .filter(Boolean);\n const root = this.documentRootLocatorFactory();\n for (const id of ids) {\n const found = await root.locatorForOptional(`bf-tooltip[id=\"${id}\"]`)();\n if (found) {\n return found;\n }\n }\n return null;\n }\n}\n","import {\n ComponentHarness,\n HarnessPredicate,\n TestKey,\n type TestElement,\n} from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\nimport { FOCUSABLE } from './tooltip.harness';\n\n/**\n * Harness for an element with a `[bfPopoverTriggerFor]` and the `bf-popover` it opens, which\n * renders in the CDK overlay container while open (found through the trigger's\n * `aria-controls`). Its `label` filter matches the trigger's accessible name (`aria-label`, else\n * its text).\n *\n * @example\n * const filters = await loader.getHarness(BfPopoverHarness.with({ label: 'Filters' }));\n * await filters.open();\n * expect(await filters.getPanelText()).toContain('Status');\n * @summary Test harness for a popover: opens and closes it and reads its panel\n * @since 0.3.1\n */\nexport class BfPopoverHarness extends ComponentHarness {\n /** The host element's selector: the attribute the popover trigger directive puts on its host. */\n static hostSelector = '[data-bf-popover-trigger]';\n\n private readonly inner = this.locatorForOptional(FOCUSABLE);\n\n /**\n * A predicate for a popover trigger with the given accessible name.\n * @param filters The name (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfPopoverHarness> {\n return new HarnessPredicate(BfPopoverHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The trigger's accessible name: its control's `aria-label`, else its text.\n * @returns The name.\n */\n async getLabel(): Promise<string> {\n const control = await this.control();\n return (await control.getAttribute('aria-label')) ?? (await control.text()).trim();\n }\n\n /** Opens the popover with a click; does nothing while it is open. */\n async open(): Promise<void> {\n if (!(await this.isOpen())) {\n await (await this.control()).click();\n }\n }\n\n /** Closes the popover with `Escape`, as a user would; does nothing while it is closed. */\n async close(): Promise<void> {\n await (await this.panel())?.sendKeys(TestKey.ESCAPE);\n }\n\n /**\n * Whether the popover is open (`aria-expanded`).\n * @returns True while open.\n */\n async isOpen(): Promise<boolean> {\n return (await (await this.control()).getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * The open panel's text, opening it first when it is closed.\n * @returns The text; throws when the panel cannot open (a disabled trigger).\n */\n async getPanelText(): Promise<string> {\n await this.open();\n const panel = await this.panel();\n if (!panel) {\n throw new Error('BfPopoverHarness: the popover did not open');\n }\n return (await panel.text()).trim();\n }\n\n /** The element that carries the ARIA state: the host, or its first control. */\n private async control(): Promise<TestElement> {\n const host = await this.host();\n return (await host.matchesSelector(FOCUSABLE)) ? host : ((await this.inner()) ?? host);\n }\n\n /** The open panel, or `null`. */\n private async panel(): Promise<TestElement | null> {\n const id = await (await this.control()).getAttribute('aria-controls');\n return id ? this.documentRootLocatorFactory().locatorForOptional(`[id=\"${id}\"]`)() : null;\n }\n}\n","import {\n ComponentHarness,\n HarnessPredicate,\n TestKey,\n type TestElement,\n} from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-combobox` and `bf-multi-combobox`. Its option list renders in the CDK overlay\n * container while open (found through the input's `aria-controls`); it works by option label, so a\n * test never needs the option values. Its `label` filter matches the visible label, else the\n * `aria-label`.\n *\n * @example\n * const assignee = await loader.getHarness(BfComboboxHarness.with({ label: 'Assignee' }));\n * await assignee.type('gra');\n * await assignee.selectOption('Grace Hopper');\n * expect(await assignee.getValueText()).toBe('Grace Hopper');\n * @summary Test harness for the comboboxes: types, reads and picks options and tags\n * @since 0.3.1\n */\nexport class BfComboboxHarness extends ComponentHarness {\n /** The host element's selector: either combobox. */\n static hostSelector = 'bf-combobox, bf-multi-combobox';\n\n private readonly input = this.locatorFor('input[role=\"combobox\"]');\n private readonly label = this.locatorForOptional('label');\n private readonly chips = this.locatorForAll('[data-chip]');\n private readonly removeButtons = this.locatorForAll('[data-chip] button');\n\n /**\n * A predicate for a combobox with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfComboboxHarness> {\n return new HarnessPredicate(BfComboboxHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The visible label without the required mark, else the `aria-label`.\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n const label = await this.label();\n if (label) {\n return (await label.text({ exclude: '[aria-hidden=\"true\"]' })).trim();\n }\n return (await (await this.input()).getAttribute('aria-label')) ?? '';\n }\n\n /** Opens the option list with a click into the field; does nothing while it is open. */\n async open(): Promise<void> {\n if (!(await this.isOpen())) {\n await (await this.input()).click();\n }\n }\n\n /** Closes the option list with `Escape`, as a user would; does nothing while it is closed. */\n async close(): Promise<void> {\n if (await this.isOpen()) {\n await (await this.input()).sendKeys(TestKey.ESCAPE);\n }\n }\n\n /**\n * Whether the option list is open (`aria-expanded`).\n * @returns True while open.\n */\n async isOpen(): Promise<boolean> {\n return (await (await this.input()).getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * Replaces the field's text with `query`, key by key as a user types; the list opens filtered.\n * @param query The text to type.\n */\n async type(query: string): Promise<void> {\n const input = await this.input();\n await input.focus();\n await input.clear();\n if (query) {\n await input.sendKeys(query);\n }\n }\n\n /**\n * The labels of the listed options, in order, opening the list first when it is closed.\n * @returns The option labels; empty when nothing matches.\n */\n async getOptionLabels(): Promise<string[]> {\n await this.open();\n return Promise.all((await this.options()).map(async (o) => (await o.label.text()).trim()));\n }\n\n /**\n * Picks the listed option with this label with a click, opening the list first when it is\n * closed. Throws for an unknown or disabled option.\n * @param label The option's label.\n */\n async selectOption(label: string): Promise<void> {\n await this.open();\n const options = await this.options();\n const labels = await Promise.all(options.map(async (o) => (await o.label.text()).trim()));\n const option = options[labels.indexOf(label)];\n if (!option) {\n throw new Error(`BfComboboxHarness: no option \"${label}\" (options: ${labels.join(', ')})`);\n }\n if ((await option.element.getAttribute('aria-disabled')) === 'true') {\n throw new Error(`BfComboboxHarness: the option \"${label}\" is disabled`);\n }\n await option.element.click();\n }\n\n /**\n * The field's text: the picked option's label on a `bf-combobox` (or the text being typed), the\n * typed text on a `bf-multi-combobox` (its picks are tags, see `getChipLabels()`).\n * @returns The input's value.\n */\n async getValueText(): Promise<string> {\n return (await this.input()).getProperty<string>('value');\n }\n\n /**\n * The labels of a `bf-multi-combobox`'s tags, in order; empty for a `bf-combobox`.\n * @returns The tag labels.\n */\n async getChipLabels(): Promise<string[]> {\n return Promise.all((await this.chips()).map(async (chip) => this.chipLabel(chip)));\n }\n\n /**\n * Removes the tag with this label with its remove button. Throws for an unknown tag or one\n * without a remove button (a disabled or read-only field).\n * @param label The tag's label.\n */\n async removeChip(label: string): Promise<void> {\n const chips = await this.chips();\n const labels = await Promise.all(chips.map(async (chip) => this.chipLabel(chip)));\n const index = labels.indexOf(label);\n if (index < 0) {\n throw new Error(`BfComboboxHarness: no tag \"${label}\" (tags: ${labels.join(', ')})`);\n }\n // Every tag has a remove button, or none has (a disabled or read-only field).\n const button = (await this.removeButtons())[index];\n if (!button) {\n throw new Error(`BfComboboxHarness: the tag \"${label}\" cannot be removed`);\n }\n await button.click();\n }\n\n /**\n * Whether the field is disabled.\n * @returns True when disabled.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.input()).getProperty<boolean>('disabled');\n }\n\n /** Focuses the input. */\n async focus(): Promise<void> {\n await (await this.input()).focus();\n }\n\n /** Blurs the input, which closes the list and marks a bound form field touched. */\n async blur(): Promise<void> {\n await (await this.input()).blur();\n }\n\n /** The text of a tag's label. */\n private async chipLabel(chip: TestElement): Promise<string> {\n return (await chip.text()).trim();\n }\n\n /** The listed options and their label elements, from the listbox the input controls. */\n private async options(): Promise<{ element: TestElement; label: TestElement }[]> {\n const id = await (await this.input()).getAttribute('aria-controls');\n if (!id) {\n return [];\n }\n const root = this.documentRootLocatorFactory();\n const [elements, labels] = await Promise.all([\n root.locatorForAll(`[id=\"${id}\"] [role=\"option\"]`)(),\n root.locatorForAll(`[id=\"${id}\"] [role=\"option\"] [data-label]`)(),\n ]);\n return elements.map((element, i) => ({ element, label: labels[i] ?? element }));\n }\n}\n","import { type LocatorFactory, TestKey, type TestElement } from '@angular/cdk/testing';\nimport { BfCalendarHarness } from './calendar.harness';\n\n/*\n * What the date, time and datetime input harnesses share: they drive the same segmented field\n * (spinbutton parts, a picker button, a panel in the overlay container, or the platform input\n * under a coarse pointer with `native`). Plain functions over the harnesses' own locators, so\n * each harness stays a flat class. Internal to the testing entry.\n */\n\n/** The parts of one rendered field, as a harness locates them. */\nexport interface FieldElements {\n /** The group of parts (segmented field), or `null` in native mode. */\n readonly group: TestElement | null;\n /** The platform input (native mode), or `null`. */\n readonly native: TestElement | null;\n /** The spinbutton parts in the order they are laid out. */\n readonly segments: readonly TestElement[];\n /** The visible label, or `null`. */\n readonly label: TestElement | null;\n}\n\n/** The visible label without the required mark, else the field's `aria-label`. */\nexport async function fieldLabel(field: FieldElements): Promise<string> {\n if (field.label) {\n return (await field.label.text({ exclude: '[aria-hidden=\"true\"]' })).trim();\n }\n const named = field.group ?? field.native;\n return (await named?.getAttribute('aria-label')) ?? '';\n}\n\n/** The field's text as shown (\"14/10/2026\"), `''` while every part is empty. */\nexport async function fieldText(field: FieldElements): Promise<string> {\n if (field.native) {\n return field.native.getProperty<string>('value');\n }\n const filled = await Promise.all(\n field.segments.map(async (segment) => (await segment.getAttribute('aria-valuenow')) !== null),\n );\n if (!filled.some(Boolean) || !field.group) {\n return '';\n }\n return (await field.group.text()).replace(/\\s+/g, ' ').trim();\n}\n\n/** Clears every part with `Backspace`, as a user would (or empties the platform input). */\nexport async function clearField(field: FieldElements): Promise<void> {\n if (field.native) {\n await field.native.clear();\n await field.native.dispatchEvent('change');\n return;\n }\n for (const segment of field.segments) {\n await segment.sendKeys(TestKey.BACKSPACE);\n }\n}\n\n/**\n * Types `text` as a user would: cleared first, then each run of digits or letters goes into the\n * next part in the order they are laid out (\"14/10/2026\" types 14, 10 and 2026; \"2:30 PM\" types\n * 2, 30 and P). In native mode the platform input takes the ISO value instead.\n */\nexport async function typeField(field: FieldElements, text: string): Promise<void> {\n if (field.native) {\n await field.native.setInputValue(text);\n await field.native.dispatchEvent('change');\n return;\n }\n await clearField(field);\n const runs = text.match(/\\d+|\\p{L}+/gu) ?? [];\n for (const [index, run] of runs.entries()) {\n const segment = field.segments[index];\n if (!segment) {\n throw new Error(`BfDateTimeHarness: \"${text}\" has more parts than the field`);\n }\n await segment.focus();\n await segment.sendKeys(...run.split(''));\n await segment.blur();\n }\n}\n\n/** Opens the picker panel with a click on its button; does nothing while it is open. */\nexport async function openPanel(trigger: TestElement | null): Promise<string> {\n if (!trigger) {\n throw new Error('BfDateTimeHarness: the field has no picker');\n }\n if ((await trigger.getAttribute('aria-expanded')) !== 'true') {\n if (await trigger.getProperty<boolean>('disabled')) {\n throw new Error('BfDateTimeHarness: the picker is disabled');\n }\n await trigger.click();\n }\n const id = await trigger.getAttribute('aria-controls');\n if (!id) {\n throw new Error('BfDateTimeHarness: the picker did not open');\n }\n return id;\n}\n\n/** Picks a date in the open panel's calendar. */\nexport async function pickPanelDate(\n root: LocatorFactory,\n panelId: string,\n iso: string,\n): Promise<void> {\n const calendar = await root.locatorFor(\n BfCalendarHarness.with({ ancestor: `[id=\"${panelId}\"]` }),\n )();\n await calendar.selectDate(iso);\n}\n\n/** Clicks a time in the open panel's list. */\nexport async function pickPanelTime(\n root: LocatorFactory,\n panelId: string,\n time: string,\n): Promise<void> {\n const option = await root.locatorForOptional(\n `[id=\"${panelId}\"] [role=\"option\"][data-time=\"${time}\"]`,\n )();\n if (!option) {\n throw new Error(`BfDateTimeHarness: the list has no time ${time}`);\n }\n if ((await option.getAttribute('aria-disabled')) === 'true') {\n throw new Error(`BfDateTimeHarness: the time ${time} is disabled`);\n }\n await option.click();\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport {\n type FieldElements,\n clearField,\n fieldLabel,\n fieldText,\n openPanel,\n pickPanelDate,\n typeField,\n} from './date-time-field';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-date-input`. It reads and types the field as a user sees it (in its locale's\n * order) and picks a date through the calendar popover with `BfCalendarHarness`.\n *\n * @example\n * const due = await loader.getHarness(BfDateInputHarness.with({ label: 'Due date' }));\n * await due.setValue('14/10/2026');\n * await due.pickDate('2026-11-03');\n * expect(await due.getValue()).toBe('03/11/2026');\n * @summary Test harness for the date input: types a date or picks one in its calendar\n * @since 0.3.1\n */\nexport class BfDateInputHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-date-input';\n\n private readonly group = this.locatorForOptional('[role=\"group\"]');\n private readonly nativeInput = this.locatorForOptional('input');\n private readonly segments = this.locatorForAll('[role=\"spinbutton\"]');\n private readonly label = this.locatorForOptional('label');\n private readonly trigger = this.locatorForOptional('button[aria-haspopup=\"dialog\"]');\n\n /**\n * A predicate for a date input with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfDateInputHarness> {\n return new HarnessPredicate(BfDateInputHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The visible label without the required mark, else the field's `aria-label`.\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n return fieldLabel(await this.elements());\n }\n\n /**\n * The field's text as the locale shows it (\"14/10/2026\"); the ISO value in native mode.\n * @returns The text, `''` while every part is empty.\n */\n async getValue(): Promise<string> {\n return fieldText(await this.elements());\n }\n\n /**\n * Types a date as a user would: clears the field, then types each run of digits (or letters)\n * into the next part in the field's order, such as \"14/10/2026\" in an en-GB field. In native mode it sets the\n * platform input to the ISO value.\n * @param text The date as the field shows it.\n */\n async setValue(text: string): Promise<void> {\n await typeField(await this.elements(), text);\n }\n\n /** Clears every part with `Backspace`, as a user would. */\n async clear(): Promise<void> {\n await clearField(await this.elements());\n }\n\n /**\n * Whether the field shows its error state (`aria-invalid` on its parts or native input).\n * @returns True while invalid.\n */\n async isInvalid(): Promise<boolean> {\n const control = (await this.nativeInput()) ?? (await this.segments())[0];\n return (await control?.getAttribute('aria-invalid')) === 'true';\n }\n\n /**\n * Whether the field is disabled.\n * @returns True while disabled.\n */\n async isDisabled(): Promise<boolean> {\n const native = await this.nativeInput();\n if (native) {\n return native.getProperty<boolean>('disabled');\n }\n return (await (await this.group())?.getAttribute('aria-disabled')) === 'true';\n }\n\n /** Opens the calendar popover with a click on its button; throws when the field has none or it is disabled. */\n async openPicker(): Promise<void> {\n await openPanel(await this.trigger());\n }\n\n /**\n * Whether the calendar popover is open (`aria-expanded` on its button).\n * @returns True while open.\n */\n async isPickerOpen(): Promise<boolean> {\n return (await (await this.trigger())?.getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * Picks a date in the calendar as a user would: opens the popover (when closed), then picks the\n * day through the calendar's harness, which closes it.\n * @param iso The ISO date to pick, such as '2026-11-03'.\n */\n async pickDate(iso: string): Promise<void> {\n const panel = await openPanel(await this.trigger());\n await pickPanelDate(this.documentRootLocatorFactory(), panel, iso);\n }\n\n private async elements(): Promise<FieldElements> {\n return {\n group: await this.group(),\n native: await this.nativeInput(),\n segments: await this.segments(),\n label: await this.label(),\n };\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport {\n type FieldElements,\n clearField,\n fieldLabel,\n fieldText,\n openPanel,\n pickPanelTime,\n typeField,\n} from './date-time-field';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-time-input`. It reads and types the field as a user sees it and picks a time\n * from the popover list.\n *\n * @example\n * const start = await loader.getHarness(BfTimeInputHarness.with({ label: 'Start' }));\n * await start.setValue('09:30');\n * await start.pickTime('14:00');\n * expect(await start.getValue()).toBe('14:00');\n * @summary Test harness for the time input: types a time or picks one from its list\n * @since 0.3.1\n */\nexport class BfTimeInputHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-time-input';\n\n private readonly group = this.locatorForOptional('[role=\"group\"]');\n private readonly nativeInput = this.locatorForOptional('input');\n private readonly segments = this.locatorForAll('[role=\"spinbutton\"]');\n private readonly label = this.locatorForOptional('label');\n private readonly trigger = this.locatorForOptional('button[aria-haspopup=\"dialog\"]');\n\n /**\n * A predicate for a time input with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfTimeInputHarness> {\n return new HarnessPredicate(BfTimeInputHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The visible label without the required mark, else the field's `aria-label`.\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n return fieldLabel(await this.elements());\n }\n\n /**\n * The field's text as the locale shows it (\"14:30\" or \"02:30 PM\"); the ISO value in native mode.\n * @returns The text, `''` while every part is empty.\n */\n async getValue(): Promise<string> {\n return fieldText(await this.elements());\n }\n\n /**\n * Types a time as a user would: clears the field, then types each run of digits (or letters)\n * into the next part in the field's order, such as \"2:30 PM\" in a 12-hour field. In native mode it sets the\n * platform input to the ISO value.\n * @param text The time as the field shows it.\n */\n async setValue(text: string): Promise<void> {\n await typeField(await this.elements(), text);\n }\n\n /** Clears every part with `Backspace`, as a user would. */\n async clear(): Promise<void> {\n await clearField(await this.elements());\n }\n\n /**\n * Whether the field shows its error state (`aria-invalid` on its parts or native input).\n * @returns True while invalid.\n */\n async isInvalid(): Promise<boolean> {\n const control = (await this.nativeInput()) ?? (await this.segments())[0];\n return (await control?.getAttribute('aria-invalid')) === 'true';\n }\n\n /**\n * Whether the field is disabled.\n * @returns True while disabled.\n */\n async isDisabled(): Promise<boolean> {\n const native = await this.nativeInput();\n if (native) {\n return native.getProperty<boolean>('disabled');\n }\n return (await (await this.group())?.getAttribute('aria-disabled')) === 'true';\n }\n\n /** Opens the list of times with a click on its button; throws when the field has none or it is disabled. */\n async openPicker(): Promise<void> {\n await openPanel(await this.trigger());\n }\n\n /**\n * Whether the list of times is open (`aria-expanded` on its button).\n * @returns True while open.\n */\n async isPickerOpen(): Promise<boolean> {\n return (await (await this.trigger())?.getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * Picks a time in the list as a user would: opens the list (when closed), then clicks the\n * time, which closes it. Throws for a time the list does not hold (off the `step`) or disables.\n * @param time The ISO time to pick, such as '14:30'.\n */\n async pickTime(time: string): Promise<void> {\n const panel = await openPanel(await this.trigger());\n await pickPanelTime(this.documentRootLocatorFactory(), panel, time);\n }\n\n private async elements(): Promise<FieldElements> {\n return {\n group: await this.group(),\n native: await this.nativeInput(),\n segments: await this.segments(),\n label: await this.label(),\n };\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport {\n type FieldElements,\n clearField,\n fieldLabel,\n fieldText,\n openPanel,\n pickPanelDate,\n pickPanelTime,\n typeField,\n} from './date-time-field';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-datetime-input`. It reads and types the field as a user sees it and picks the\n * date in the popover's calendar and the time in its list.\n *\n * @example\n * const meeting = await loader.getHarness(BfDateTimeInputHarness.with({ label: 'Meeting' }));\n * await meeting.pickDate('2026-10-14');\n * await meeting.pickTime('09:30');\n * expect(await meeting.getValue()).toBe('14/10/2026, 09:30');\n * @summary Test harness for the date and time input: types or picks a date and time\n * @since 0.3.1\n */\nexport class BfDateTimeInputHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-datetime-input';\n\n private readonly group = this.locatorForOptional('[role=\"group\"]');\n private readonly nativeInput = this.locatorForOptional('input');\n private readonly segments = this.locatorForAll('[role=\"spinbutton\"]');\n private readonly label = this.locatorForOptional('label');\n private readonly trigger = this.locatorForOptional('button[aria-haspopup=\"dialog\"]');\n\n /**\n * A predicate for a date and time input with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfDateTimeInputHarness> {\n return new HarnessPredicate(BfDateTimeInputHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The visible label without the required mark, else the field's `aria-label`.\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n return fieldLabel(await this.elements());\n }\n\n /**\n * The field's text as the locale shows it (\"14/10/2026, 09:30\"); the ISO value in native mode.\n * @returns The text, `''` while every part is empty.\n */\n async getValue(): Promise<string> {\n return fieldText(await this.elements());\n }\n\n /**\n * Types a date and time as a user would: clears the field, then types each run of digits (or letters)\n * into the next part in the field's order, such as \"14/10/2026, 09:30\" in an en-GB field. In native mode it sets the\n * platform input to the ISO value.\n * @param text The date and time as the field shows it.\n */\n async setValue(text: string): Promise<void> {\n await typeField(await this.elements(), text);\n }\n\n /** Clears every part with `Backspace`, as a user would. */\n async clear(): Promise<void> {\n await clearField(await this.elements());\n }\n\n /**\n * Whether the field shows its error state (`aria-invalid` on its parts or native input).\n * @returns True while invalid.\n */\n async isInvalid(): Promise<boolean> {\n const control = (await this.nativeInput()) ?? (await this.segments())[0];\n return (await control?.getAttribute('aria-invalid')) === 'true';\n }\n\n /**\n * Whether the field is disabled.\n * @returns True while disabled.\n */\n async isDisabled(): Promise<boolean> {\n const native = await this.nativeInput();\n if (native) {\n return native.getProperty<boolean>('disabled');\n }\n return (await (await this.group())?.getAttribute('aria-disabled')) === 'true';\n }\n\n /** Opens the popover with a click on its button; throws when the field has none or it is disabled. */\n async openPicker(): Promise<void> {\n await openPanel(await this.trigger());\n }\n\n /**\n * Whether the popover is open (`aria-expanded` on its button).\n * @returns True while open.\n */\n async isPickerOpen(): Promise<boolean> {\n return (await (await this.trigger())?.getAttribute('aria-expanded')) === 'true';\n }\n\n /**\n * Picks a date in the calendar as a user would: opens the popover (when closed), then picks the\n * day through the calendar's harness. The popover stays open, with focus on its time list.\n * @param iso The ISO date to pick, such as '2026-11-03'.\n */\n async pickDate(iso: string): Promise<void> {\n const panel = await openPanel(await this.trigger());\n await pickPanelDate(this.documentRootLocatorFactory(), panel, iso);\n }\n\n /**\n * Picks a time in the list as a user would: opens the popover (when closed), then clicks the\n * time, which closes it. Throws for a time the list does not hold (off the `step`) or disables.\n * @param time The ISO time to pick, such as '14:30'.\n */\n async pickTime(time: string): Promise<void> {\n const panel = await openPanel(await this.trigger());\n await pickPanelTime(this.documentRootLocatorFactory(), panel, time);\n }\n\n private async elements(): Promise<FieldElements> {\n return {\n group: await this.group(),\n native: await this.nativeInput(),\n segments: await this.segments(),\n label: await this.label(),\n };\n }\n}\n","import { ComponentHarness, HarnessPredicate, type TestElement } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/* The chart's own figure parts (a chart is never nested in another). */\nconst LEGEND_BUTTON = '[data-bf-chart-legend] button';\n\n/**\n * Harness for the `@bitforgehq/ui/charts` charts (`bf-line-chart`, `bf-bar-chart`,\n * `bf-donut-chart`). It works through what a user sees: the legend's series names, its toggle\n * buttons and the Show data table, so a test never reads the SVG.\n *\n * @example\n * const chart = await loader.getHarness(BfChartHarness.with({ label: 'Revenue' }));\n * await chart.toggleSeries('Store');\n * expect(await chart.showData()).toContainEqual(['Mar', '15', '11']);\n * @summary Test harness for the charts: reads the series, toggles one and reads the data table\n * @since 0.3.1\n */\nexport class BfChartHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-line-chart, bf-bar-chart, bf-donut-chart';\n\n private readonly figure = this.locatorFor('figure');\n private readonly legendButtons = this.locatorForAll(LEGEND_BUTTON);\n private readonly dataToggle = this.locatorFor('[data-bf-chart-data-toggle]');\n private readonly headCells = this.locatorForAll('[data-bf-chart-table] thead th');\n private readonly bodyCells = this.locatorForAll(\n '[data-bf-chart-table] tbody th, [data-bf-chart-table] tbody td',\n );\n\n /**\n * A predicate for a chart with the given label.\n * @param filters The label (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfChartHarness> {\n return new HarnessPredicate(BfChartHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The figure's accessible name (`label`, else the chart card's title, else the `chart` label).\n * @returns The label text.\n */\n async getLabel(): Promise<string> {\n return (await (await this.figure()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * The series (or slice) names in the legend, in order; empty for a chart of one series, which\n * draws no legend.\n * @returns The names.\n */\n async getSeriesNames(): Promise<string[]> {\n return Promise.all(\n (await this.legendButtons()).map(async (button) => (await button.text()).trim()),\n );\n }\n\n /**\n * Whether the legend shows the series (or slice) with this name.\n * @param name The series name.\n * @returns `true` while it is drawn.\n */\n async isSeriesShown(name: string): Promise<boolean> {\n return (await (await this.legendButton(name)).getAttribute('aria-pressed')) === 'true';\n }\n\n /**\n * Clicks the legend button of the series (or slice) with this name, as a user would, to hide\n * or show it. Throws for an unknown name.\n * @param name The series name.\n */\n async toggleSeries(name: string): Promise<void> {\n await (await this.legendButton(name)).click();\n }\n\n /**\n * Opens the Show data table (if it is closed) and reads it.\n * @returns One array per category (or slice): its name, then its formatted values.\n */\n async showData(): Promise<string[][]> {\n const toggle = await this.dataToggle();\n if ((await toggle.getAttribute('aria-expanded')) !== 'true') {\n await toggle.click();\n }\n const [head, cells] = await Promise.all([this.headCells(), this.bodyCells()]);\n const texts = await Promise.all(cells.map(async (cell) => (await cell.text()).trim()));\n const columns = Math.max(1, head.length);\n const rows: string[][] = [];\n for (let i = 0; i < texts.length; i += columns) {\n rows.push(texts.slice(i, i + columns));\n }\n return rows;\n }\n\n private async legendButton(name: string): Promise<TestElement> {\n const [buttons, names] = await Promise.all([this.legendButtons(), this.getSeriesNames()]);\n const button = buttons[names.indexOf(name)];\n if (!button) {\n throw new Error(`BfChartHarness: no series \"${name}\" (series: ${names.join(', ')})`);\n }\n return button;\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-sidebar-toggle`, the button that collapses a `bf-sidebar` to an icon rail. Its\n * `label` filter matches the button's name (\"Collapse sidebar\" or \"Expand sidebar\").\n *\n * @example\n * const toggle = await loader.getHarness(BfSidebarToggleHarness);\n * await toggle.toggle();\n * expect(await toggle.isExpanded()).toBe(false);\n * @summary Test harness for a sidebar toggle: reads and flips the collapsed sidebar\n * @since 0.3.2\n */\nexport class BfSidebarToggleHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-sidebar-toggle';\n\n private readonly button = this.locatorFor('button');\n\n /**\n * A predicate for a sidebar toggle with the given name.\n * @param filters The name (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfSidebarToggleHarness> {\n return new HarnessPredicate(BfSidebarToggleHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The button's name, which says what the next press does.\n * @returns The `aria-label`.\n */\n async getLabel(): Promise<string> {\n return (await (await this.button()).getAttribute('aria-label')) ?? '';\n }\n\n /**\n * Whether the sidebar is at full width (`aria-expanded`).\n * @returns True while expanded, false while it is an icon rail.\n */\n async isExpanded(): Promise<boolean> {\n return (await (await this.button()).getAttribute('aria-expanded')) === 'true';\n }\n\n /** Flips the sidebar with a click. */\n async toggle(): Promise<void> {\n await (await this.button()).click();\n }\n}\n","import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\nimport { type BfHarnessFilters } from './harness-filters';\n\n/**\n * Harness for `bf-toggle`. Its `label` filter matches the switch's name: the text of the elements\n * `ariaLabelledby` names, else `ariaLabel`, else the visible label, so a label-less switch in a\n * table cell or a list row is found the same way.\n *\n * @example\n * const email = await loader.getHarness(BfToggleHarness.with({ label: 'E-Mail, Kritisch' }));\n * await email.check();\n * expect(await email.isChecked()).toBe(true);\n * @summary Test harness for the toggle: reads its name and state and switches it\n * @since 0.4.0\n */\nexport class BfToggleHarness extends ComponentHarness {\n /** The host element's selector. */\n static hostSelector = 'bf-toggle';\n\n private readonly input = this.locatorFor('input[role=\"switch\"]');\n private readonly label = this.locatorFor('label');\n private readonly error = this.locatorForOptional('p[id$=\"-error\"]');\n\n /**\n * A predicate for a toggle with the given name.\n * @param filters The name (text or pattern) and the CDK's selector and ancestor filters.\n * @returns The predicate for `getHarness()` and friends.\n */\n static with(filters: BfHarnessFilters = {}): HarnessPredicate<BfToggleHarness> {\n return new HarnessPredicate(BfToggleHarness, filters).addOption(\n 'label',\n filters.label,\n async (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label),\n );\n }\n\n /**\n * The switch's name, as the browser computes it: the text of the elements `ariaLabelledby`\n * names, else `ariaLabel`, else the visible label (the `label` text and the default slot).\n * @returns The name, `''` without one.\n */\n async getLabel(): Promise<string> {\n const input = await this.input();\n const ids = (await input.getAttribute('aria-labelledby'))?.split(/\\s+/).filter(Boolean) ?? [];\n if (ids.length > 0) {\n const root = this.documentRootLocatorFactory();\n const texts = await Promise.all(\n ids.map(\n async (id) => (await (await root.locatorForOptional(`[id=\"${id}\"]`)())?.text()) ?? '',\n ),\n );\n return texts\n .map((text) => text.trim())\n .filter(Boolean)\n .join(' ');\n }\n const ariaLabel = await input.getAttribute('aria-label');\n if (ariaLabel) {\n return ariaLabel;\n }\n return (await (await this.label()).text()).trim();\n }\n\n /**\n * Whether the switch is on.\n * @returns True when on.\n */\n async isChecked(): Promise<boolean> {\n return (await this.input()).getProperty<boolean>('checked');\n }\n\n /**\n * Whether the switch is disabled.\n * @returns True when disabled.\n */\n async isDisabled(): Promise<boolean> {\n return (await this.input()).getProperty<boolean>('disabled');\n }\n\n /**\n * Whether the switch is read-only (`aria-readonly`): it shows its state but a click does nothing.\n * @returns True when read-only.\n */\n async isReadonly(): Promise<boolean> {\n return (await (await this.input()).getAttribute('aria-readonly')) === 'true';\n }\n\n /**\n * Clicks the switch, as a user would, so it turns on or off. Throws while it is disabled (a\n * user's click would do nothing); a read-only switch takes the click and stays as it is.\n */\n async toggle(): Promise<void> {\n if (await this.isDisabled()) {\n throw new Error(`BfToggleHarness: the toggle \"${await this.getLabel()}\" is disabled`);\n }\n await (await this.input()).click();\n }\n\n /** Turns the switch on with a click; an on switch stays on. Throws while it is disabled. */\n async check(): Promise<void> {\n if (!(await this.isChecked())) {\n await this.toggle();\n }\n }\n\n /** Turns the switch off with a click; an off switch stays off. Throws while it is disabled. */\n async uncheck(): Promise<void> {\n if (await this.isChecked()) {\n await this.toggle();\n }\n }\n\n /**\n * The error text under the switch.\n * @returns The error text, `''` when there is none.\n */\n async getErrorText(): Promise<string> {\n return (await (await this.error())?.text())?.trim() ?? '';\n }\n}\n"],"mappings":"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"}
|