@tft/crispr-forms-testing 21.0.1

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.
Files changed (41) hide show
  1. package/CLAUDE.md +105 -0
  2. package/field-tests/autocomplete-chiplist.tests.d.ts +10 -0
  3. package/field-tests/autocomplete-chiplist.tests.js +51 -0
  4. package/field-tests/autocomplete-chiplist.tests.js.map +1 -0
  5. package/field-tests/autocomplete.tests.d.ts +11 -0
  6. package/field-tests/autocomplete.tests.js +42 -0
  7. package/field-tests/autocomplete.tests.js.map +1 -0
  8. package/field-tests/checkbox.tests.d.ts +6 -0
  9. package/field-tests/checkbox.tests.js +27 -0
  10. package/field-tests/checkbox.tests.js.map +1 -0
  11. package/field-tests/datepicker.tests.d.ts +6 -0
  12. package/field-tests/datepicker.tests.js +30 -0
  13. package/field-tests/datepicker.tests.js.map +1 -0
  14. package/field-tests/field-test-factory.d.ts +21 -0
  15. package/field-tests/field-test-factory.js +69 -0
  16. package/field-tests/field-test-factory.js.map +1 -0
  17. package/field-tests/file-upload.tests.d.ts +6 -0
  18. package/field-tests/file-upload.tests.js +27 -0
  19. package/field-tests/file-upload.tests.js.map +1 -0
  20. package/field-tests/input.tests.d.ts +15 -0
  21. package/field-tests/input.tests.js +73 -0
  22. package/field-tests/input.tests.js.map +1 -0
  23. package/field-tests/select.tests.d.ts +6 -0
  24. package/field-tests/select.tests.js +39 -0
  25. package/field-tests/select.tests.js.map +1 -0
  26. package/field-tests/slider.tests.d.ts +13 -0
  27. package/field-tests/slider.tests.js +37 -0
  28. package/field-tests/slider.tests.js.map +1 -0
  29. package/field-tests/textarea.tests.d.ts +6 -0
  30. package/field-tests/textarea.tests.js +28 -0
  31. package/field-tests/textarea.tests.js.map +1 -0
  32. package/field-tests/unit-conversion.tests.d.ts +11 -0
  33. package/field-tests/unit-conversion.tests.js +29 -0
  34. package/field-tests/unit-conversion.tests.js.map +1 -0
  35. package/helpers/form.helpers.d.ts +80 -0
  36. package/helpers/form.helpers.js +123 -0
  37. package/helpers/form.helpers.js.map +1 -0
  38. package/index.d.ts +14 -0
  39. package/index.js +18 -0
  40. package/index.js.map +1 -0
  41. package/package.json +18 -0
package/CLAUDE.md ADDED
@@ -0,0 +1,105 @@
1
+ # crispr-forms-testing
2
+
3
+ Playwright helpers and field test factories for testing forms built with `@tft/crispr-forms`.
4
+
5
+ ## Setup
6
+
7
+ ### 1. Load the Angular JIT compiler
8
+ The lib re-exports `@angular/compiler` automatically when you import from it. You do NOT need a separate fixture file — just import directly:
9
+
10
+ ```ts
11
+ import { testField, getMatFormField } from '@tft/crispr-forms-testing';
12
+ ```
13
+
14
+ If you are NOT importing from this lib in a given file, add this at the top of your Playwright setup or fixture:
15
+ ```ts
16
+ import '@angular/compiler';
17
+ ```
18
+
19
+ ### 2. Use `testField` to generate standard assertions from a config
20
+ ```ts
21
+ import { test } from '@playwright/test';
22
+ import { testField } from '@tft/crispr-forms-testing';
23
+ import { myFormConfig } from './my-form.config';
24
+
25
+ test.describe('My Form', () => {
26
+ test.beforeEach(async ({ page }) => page.goto('/my-form'));
27
+ for (const field of myFormConfig.fields) {
28
+ testField(field);
29
+ }
30
+ });
31
+ ```
32
+
33
+ ## testId attribute pattern
34
+
35
+ Every crispr-forms field has `data-testid` set automatically. The value is the full control path:
36
+
37
+ | Context | testId value |
38
+ |---------|-------------|
39
+ | Top-level field `firstName` | `firstName` |
40
+ | In a subGroup `address` | `address.firstName` |
41
+ | In a formGroupList `contacts` at index 2 | `contacts[2].firstName` |
42
+ | Doubly-nested | `contacts[2].address.city` |
43
+
44
+ ## Helper functions
45
+
46
+ ### Locators
47
+ - `getMatFormField(page, testId)` — the root element with `data-testid` (usually `mat-form-field`, `mat-checkbox`, or `mat-slider`)
48
+ - `getInput(page, testId)` — `input` inside the field
49
+ - `getLabel(page, testId)` — `mat-label` inside the field
50
+ - `getHint(page, testId)` — `mat-hint` inside the field
51
+ - `getMatSelect(page, testId)` — `mat-select` inside a select field
52
+ - `getCheckbox(page, testId)` — `mat-checkbox` element
53
+ - `getAutocompleteInput(page, testId)` — the visible `role="textbox"` input in autocomplete (NOT the hidden formControlName anchor)
54
+ - `getError(page, testId, fieldType?)` — the `<p class="tft-control-error">` paragraph. `fieldType` defaults to `'input'`; pass the field's kebab-case type for other fields (e.g. `'select'`, `'autocomplete'`, `'autocomplete-chiplist'`, `'checkbox'`, `'datepicker'`, `'textarea'`)
55
+
56
+ ### Actions
57
+ - `touchInput(page, testId)` — click + Tab to blur
58
+ - `touchSelect(page, testId)` — open + Escape + Tab (Tab is required; Escape alone does not fire blur)
59
+ - `touchCheckbox(page, testId)` — click + click + Tab (leaves unchecked, triggers requiredTrue)
60
+ - `touchAutocomplete(page, testId)` — click visible input + Tab
61
+ - `touchDatepicker(page, testId)` — click input + Tab
62
+ - `openSelect(page, testId)` — clicks mat-select to open panel
63
+ - `selectOption(page, testId, optionLabel)` — opens select and clicks the option
64
+ - `submitForm(page, nth?)` — programmatically clicks crispr-form's hidden submit button (index `nth`, defaults to 0)
65
+
66
+ ## Known quirks
67
+
68
+ ### Autocomplete and chiplist: use submitForm() to trigger errors
69
+ Both fields use a hidden `display:none` `[formControlName]` anchor for validation. The visible input has no validators. Blurring the visible input does NOT touch the hidden control. Use `submitForm()` instead — it fires `ControlErrorsFormDirective.submit$` which forces all controls to display errors regardless of touched state.
70
+
71
+ ### submitForm() must use page.evaluate
72
+ The hidden submit button is `display:none`, so Playwright pointer events can't reach it. `submitForm()` calls `document.querySelector('[data-testid="submit-trigger"]').click()` via `page.evaluate`.
73
+
74
+ ### touchSelect() requires Tab after Escape
75
+ Pressing Escape dismisses the mat-select panel but leaves the trigger element focused. `ControlErrorsDirective` listens for `blur` events. Without the Tab press, no blur fires and the error never appears.
76
+
77
+ ### getError() targets the inner <p>, not the host element
78
+ `tft-control-error` host elements are always in the DOM (even when empty). Autocomplete/chiplist render two of them (one per internal input). Targeting `p.tft-control-error` (the inner paragraph, only present when there is error text) avoids Playwright strict-mode failures.
79
+
80
+ ### Slider: plain <label>, not mat-label
81
+ Slider is not inside a `mat-form-field`, so it uses a plain `<label>`. `getLabel()` (which queries `mat-label`) won't work — query `.locator('label')` on the slider host instead.
82
+
83
+ ### Slider: min/max are native HTML attributes
84
+ Read `min`/`max` from `input[matSliderThumb]` native HTML attributes, not `aria-valuemin`/`aria-valuemax`.
85
+
86
+ ### Unit conversion: required errors are not testable
87
+ The `displayValueControl` (visible input) is separate from the `[formControlName]` that holds validators. Validation errors are never wired to the visible input, so required error tests are not possible for this field type.
88
+
89
+ ## Field test factories
90
+
91
+ Each factory registers Playwright `test()` calls conditioned on the config:
92
+
93
+ | Factory | Config type | Tests generated |
94
+ |---------|-------------|-----------------|
95
+ | `testInputField` | `InputFieldConfig` | label, input type, placeholder, hint, required, min, max, maxLength |
96
+ | `testTextareaField` | `TextareaFieldConfig` | label, hint, required |
97
+ | `testCheckboxField` | `CheckboxFieldConfig` | label, requiredTrue error |
98
+ | `testSelectField` | `SelectFieldConfig` | label, hint, options count, select option, required |
99
+ | `testDatepickerField` | `DatepickerFieldConfig` | label, hint, toggle button, required |
100
+ | `testSliderField` | `SliderFieldConfig` | renders, label, min, max |
101
+ | `testAutocompleteField` | `AutocompleteFieldConfig` | label, hint, dropdown, select option, required (via submitForm) |
102
+ | `testAutocompleteChiplistField` | `AutocompleteChiplistFieldConfig` | label, hint, type+see options, add chip, remove chip, minArrayLength error |
103
+ | `testFileUploadField` | `FileUploadFieldConfig` | label, drop zone text, select button |
104
+ | `testUnitConversionField` | `UnitConversionFieldConfig` | label, number input, unit select |
105
+ | `testField` | `AnyFieldConfig` | dispatches to the correct factory above; skips non-control fields |
@@ -0,0 +1,10 @@
1
+ import type { AutocompleteChiplistFieldConfig } from '@tft/crispr-forms/utils';
2
+ /**
3
+ * Registers Playwright tests for an AUTOCOMPLETE_CHIPLIST field config.
4
+ * Call inside a `test.describe` block that has already navigated to the page.
5
+ *
6
+ * Note: like autocomplete, errors require `submitForm()` rather than blur because
7
+ * the hidden `[formControlName]` anchor is what holds validators. Blurring the
8
+ * visible search input does not touch the hidden control.
9
+ */
10
+ export declare function testAutocompleteChiplistField(config: AutocompleteChiplistFieldConfig): void;
@@ -0,0 +1,51 @@
1
+ import { test, expect } from '@playwright/test';
2
+ import { getLabel, getHint, getError, getMatFormField, submitForm } from '../helpers/form.helpers';
3
+ /**
4
+ * Registers Playwright tests for an AUTOCOMPLETE_CHIPLIST field config.
5
+ * Call inside a `test.describe` block that has already navigated to the page.
6
+ *
7
+ * Note: like autocomplete, errors require `submitForm()` rather than blur because
8
+ * the hidden `[formControlName]` anchor is what holds validators. Blurring the
9
+ * visible search input does not touch the hidden control.
10
+ */
11
+ export function testAutocompleteChiplistField(config) {
12
+ if (config.label) {
13
+ test('renders label', async ({ page }) => {
14
+ await expect(getLabel(page, config.controlName)).toHaveText(config.label);
15
+ });
16
+ }
17
+ if (config.hint) {
18
+ test('shows hint when untouched', async ({ page }) => {
19
+ await expect(getHint(page, config.controlName)).toBeVisible();
20
+ });
21
+ }
22
+ test('can type and see options', async ({ page }) => {
23
+ const searchInput = getMatFormField(page, config.controlName).locator('input[matInput]');
24
+ await searchInput.fill('Option');
25
+ const options = page.locator('mat-option');
26
+ await expect(options.first()).toBeVisible();
27
+ });
28
+ test('can add a chip by selecting an option', async ({ page }) => {
29
+ const searchInput = getMatFormField(page, config.controlName).locator('input[matInput]');
30
+ await searchInput.fill('Option A');
31
+ await page.locator('mat-option').filter({ hasText: 'Option A' }).click();
32
+ const chip = getMatFormField(page, config.controlName).locator('mat-chip-row').first();
33
+ await expect(chip).toContainText('Option A');
34
+ });
35
+ test('can remove a chip', async ({ page }) => {
36
+ const searchInput = getMatFormField(page, config.controlName).locator('input[matInput]');
37
+ await searchInput.fill('Option A');
38
+ await page.locator('mat-option').filter({ hasText: 'Option A' }).click();
39
+ const removeBtn = getMatFormField(page, config.controlName).locator('mat-chip-row mat-icon[matChipRemove]').first();
40
+ if (await removeBtn.isVisible()) {
41
+ await removeBtn.click();
42
+ const chips = getMatFormField(page, config.controlName).locator('mat-chip-row');
43
+ await expect(chips).toHaveCount(0);
44
+ }
45
+ });
46
+ test('shows error when not enough chips selected', async ({ page }) => {
47
+ await submitForm(page);
48
+ await expect(getError(page, config.controlName, 'autocomplete-chiplist')).toBeVisible();
49
+ });
50
+ }
51
+ //# sourceMappingURL=autocomplete-chiplist.tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autocomplete-chiplist.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/autocomplete-chiplist.tests.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from '@playwright/test';\nimport type { AutocompleteChiplistFieldConfig } from '@tft/crispr-forms/utils';\nimport { getLabel, getHint, getError, getMatFormField, submitForm } from '../helpers/form.helpers';\n\n/**\n * Registers Playwright tests for an AUTOCOMPLETE_CHIPLIST field config.\n * Call inside a `test.describe` block that has already navigated to the page.\n *\n * Note: like autocomplete, errors require `submitForm()` rather than blur because\n * the hidden `[formControlName]` anchor is what holds validators. Blurring the\n * visible search input does not touch the hidden control.\n */\nexport function testAutocompleteChiplistField(config: AutocompleteChiplistFieldConfig) {\n if (config.label) {\n test('renders label', async ({ page }) => {\n await expect(getLabel(page, config.controlName)).toHaveText(config.label);\n });\n }\n\n if (config.hint) {\n test('shows hint when untouched', async ({ page }) => {\n await expect(getHint(page, config.controlName)).toBeVisible();\n });\n }\n\n test('can type and see options', async ({ page }) => {\n const searchInput = getMatFormField(page, config.controlName).locator('input[matInput]');\n await searchInput.fill('Option');\n const options = page.locator('mat-option');\n await expect(options.first()).toBeVisible();\n });\n\n test('can add a chip by selecting an option', async ({ page }) => {\n const searchInput = getMatFormField(page, config.controlName).locator('input[matInput]');\n await searchInput.fill('Option A');\n await page.locator('mat-option').filter({ hasText: 'Option A' }).click();\n const chip = getMatFormField(page, config.controlName).locator('mat-chip-row').first();\n await expect(chip).toContainText('Option A');\n });\n\n test('can remove a chip', async ({ page }) => {\n const searchInput = getMatFormField(page, config.controlName).locator('input[matInput]');\n await searchInput.fill('Option A');\n await page.locator('mat-option').filter({ hasText: 'Option A' }).click();\n const removeBtn = getMatFormField(page, config.controlName).locator('mat-chip-row mat-icon[matChipRemove]').first();\n if (await removeBtn.isVisible()) {\n await removeBtn.click();\n const chips = getMatFormField(page, config.controlName).locator('mat-chip-row');\n await expect(chips).toHaveCount(0);\n }\n });\n\n test('shows error when not enough chips selected', async ({ page }) => {\n await submitForm(page);\n await expect(getError(page, config.controlName, 'autocomplete-chiplist')).toBeVisible();\n });\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { AutocompleteFieldConfig } from '@tft/crispr-forms/utils';
2
+ /**
3
+ * Registers Playwright tests for an AUTOCOMPLETE field config.
4
+ * Call inside a `test.describe` block that has already navigated to the page.
5
+ *
6
+ * Note: errors are triggered via `submitForm()` rather than blur because the
7
+ * autocomplete uses a hidden `[formControlName]` anchor for validation — blurring
8
+ * the visible input does not touch the hidden control. `submitForm()` fires
9
+ * `ControlErrorsFormDirective.submit$` which forces all controls to show errors.
10
+ */
11
+ export declare function testAutocompleteField(config: AutocompleteFieldConfig): void;
@@ -0,0 +1,42 @@
1
+ import { test, expect } from '@playwright/test';
2
+ import { Validators } from '@angular/forms';
3
+ import { getLabel, getHint, getError, getAutocompleteInput, submitForm } from '../helpers/form.helpers';
4
+ /**
5
+ * Registers Playwright tests for an AUTOCOMPLETE field config.
6
+ * Call inside a `test.describe` block that has already navigated to the page.
7
+ *
8
+ * Note: errors are triggered via `submitForm()` rather than blur because the
9
+ * autocomplete uses a hidden `[formControlName]` anchor for validation — blurring
10
+ * the visible input does not touch the hidden control. `submitForm()` fires
11
+ * `ControlErrorsFormDirective.submit$` which forces all controls to show errors.
12
+ */
13
+ export function testAutocompleteField(config) {
14
+ if (config.label) {
15
+ test('renders label', async ({ page }) => {
16
+ await expect(getLabel(page, config.controlName)).toHaveText(config.label);
17
+ });
18
+ }
19
+ if (config.hint) {
20
+ test('shows hint when untouched', async ({ page }) => {
21
+ await expect(getHint(page, config.controlName)).toBeVisible();
22
+ });
23
+ }
24
+ test('shows dropdown options when typing', async ({ page }) => {
25
+ await getAutocompleteInput(page, config.controlName).fill('Option');
26
+ const options = page.locator('mat-option');
27
+ await expect(options.first()).toBeVisible();
28
+ });
29
+ test('can select an autocomplete option', async ({ page }) => {
30
+ await getAutocompleteInput(page, config.controlName).fill('Option A');
31
+ await page.locator('mat-option').filter({ hasText: 'Option A' }).click();
32
+ await expect(getAutocompleteInput(page, config.controlName)).toHaveValue('Option A');
33
+ });
34
+ if (config.validators?.includes(Validators.required)) {
35
+ test('shows required error when form submitted empty', async ({ page }) => {
36
+ await getAutocompleteInput(page, config.controlName).fill('');
37
+ await submitForm(page);
38
+ await expect(getError(page, config.controlName, 'autocomplete')).toBeVisible();
39
+ });
40
+ }
41
+ }
42
+ //# sourceMappingURL=autocomplete.tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autocomplete.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/autocomplete.tests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAExG;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAA+B;IACnE,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACvC,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,IAAI,CAAC,2BAA2B,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACnD,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAChE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,oCAAoC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QAC5D,MAAM,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC3C,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,mCAAmC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QAC3D,MAAM,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACtE,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;QACzE,MAAM,MAAM,CAAC,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,gDAAgD,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACxE,MAAM,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC9D,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;YACvB,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACjF,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC","sourcesContent":["import { test, expect } from '@playwright/test';\nimport type { AutocompleteFieldConfig } from '@tft/crispr-forms/utils';\nimport { Validators } from '@angular/forms';\nimport { getLabel, getHint, getError, getAutocompleteInput, submitForm } from '../helpers/form.helpers';\n\n/**\n * Registers Playwright tests for an AUTOCOMPLETE field config.\n * Call inside a `test.describe` block that has already navigated to the page.\n *\n * Note: errors are triggered via `submitForm()` rather than blur because the\n * autocomplete uses a hidden `[formControlName]` anchor for validation — blurring\n * the visible input does not touch the hidden control. `submitForm()` fires\n * `ControlErrorsFormDirective.submit$` which forces all controls to show errors.\n */\nexport function testAutocompleteField(config: AutocompleteFieldConfig) {\n if (config.label) {\n test('renders label', async ({ page }) => {\n await expect(getLabel(page, config.controlName)).toHaveText(config.label);\n });\n }\n\n if (config.hint) {\n test('shows hint when untouched', async ({ page }) => {\n await expect(getHint(page, config.controlName)).toBeVisible();\n });\n }\n\n test('shows dropdown options when typing', async ({ page }) => {\n await getAutocompleteInput(page, config.controlName).fill('Option');\n const options = page.locator('mat-option');\n await expect(options.first()).toBeVisible();\n });\n\n test('can select an autocomplete option', async ({ page }) => {\n await getAutocompleteInput(page, config.controlName).fill('Option A');\n await page.locator('mat-option').filter({ hasText: 'Option A' }).click();\n await expect(getAutocompleteInput(page, config.controlName)).toHaveValue('Option A');\n });\n\n if (config.validators?.includes(Validators.required)) {\n test('shows required error when form submitted empty', async ({ page }) => {\n await getAutocompleteInput(page, config.controlName).fill('');\n await submitForm(page);\n await expect(getError(page, config.controlName, 'autocomplete')).toBeVisible();\n });\n }\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { CheckboxFieldConfig } from '@tft/crispr-forms/utils';
2
+ /**
3
+ * Registers Playwright tests for a CHECKBOX field config.
4
+ * Call inside a `test.describe` block that has already navigated to the page.
5
+ */
6
+ export declare function testCheckboxField(config: CheckboxFieldConfig): void;
@@ -0,0 +1,27 @@
1
+ import { test, expect } from '@playwright/test';
2
+ import { Validators } from '@angular/forms';
3
+ import { getCheckbox, getError } from '../helpers/form.helpers';
4
+ /**
5
+ * Registers Playwright tests for a CHECKBOX field config.
6
+ * Call inside a `test.describe` block that has already navigated to the page.
7
+ */
8
+ export function testCheckboxField(config) {
9
+ test('renders label', async ({ page }) => {
10
+ const label = getCheckbox(page, config.controlName).locator('.mdc-label');
11
+ await expect(label).toContainText(config.label);
12
+ });
13
+ if (config.validators?.includes(Validators.requiredTrue)) {
14
+ test('shows error when unchecked and blurred', async ({ page }) => {
15
+ await getCheckbox(page, config.controlName).click();
16
+ await getCheckbox(page, config.controlName).click();
17
+ await page.keyboard.press('Tab');
18
+ await expect(getError(page, config.controlName, 'checkbox')).toBeVisible();
19
+ });
20
+ test('no error when checked', async ({ page }) => {
21
+ await getCheckbox(page, config.controlName).click();
22
+ await page.keyboard.press('Tab');
23
+ await expect(getError(page, config.controlName, 'checkbox')).not.toBeVisible();
24
+ });
25
+ }
26
+ }
27
+ //# sourceMappingURL=checkbox.tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkbox.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/checkbox.tests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEhE;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAA2B;IAC3D,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACvC,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC1E,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACzD,IAAI,CAAC,wCAAwC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YAChE,MAAM,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,CAAC;YACpD,MAAM,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,CAAC;YACpD,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACjC,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7E,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,uBAAuB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YAC/C,MAAM,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,CAAC;YACpD,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACjC,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;QACjF,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC","sourcesContent":["import { test, expect } from '@playwright/test';\nimport type { CheckboxFieldConfig } from '@tft/crispr-forms/utils';\nimport { Validators } from '@angular/forms';\nimport { getCheckbox, getError } from '../helpers/form.helpers';\n\n/**\n * Registers Playwright tests for a CHECKBOX field config.\n * Call inside a `test.describe` block that has already navigated to the page.\n */\nexport function testCheckboxField(config: CheckboxFieldConfig) {\n test('renders label', async ({ page }) => {\n const label = getCheckbox(page, config.controlName).locator('.mdc-label');\n await expect(label).toContainText(config.label);\n });\n\n if (config.validators?.includes(Validators.requiredTrue)) {\n test('shows error when unchecked and blurred', async ({ page }) => {\n await getCheckbox(page, config.controlName).click();\n await getCheckbox(page, config.controlName).click();\n await page.keyboard.press('Tab');\n await expect(getError(page, config.controlName, 'checkbox')).toBeVisible();\n });\n\n test('no error when checked', async ({ page }) => {\n await getCheckbox(page, config.controlName).click();\n await page.keyboard.press('Tab');\n await expect(getError(page, config.controlName, 'checkbox')).not.toBeVisible();\n });\n }\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { DatepickerFieldConfig } from '@tft/crispr-forms/utils';
2
+ /**
3
+ * Registers Playwright tests for a DATEPICKER field config.
4
+ * Call inside a `test.describe` block that has already navigated to the page.
5
+ */
6
+ export declare function testDatepickerField(config: DatepickerFieldConfig): void;
@@ -0,0 +1,30 @@
1
+ import { test, expect } from '@playwright/test';
2
+ import { Validators } from '@angular/forms';
3
+ import { getLabel, getHint, getError, getMatFormField, touchDatepicker } from '../helpers/form.helpers';
4
+ /**
5
+ * Registers Playwright tests for a DATEPICKER field config.
6
+ * Call inside a `test.describe` block that has already navigated to the page.
7
+ */
8
+ export function testDatepickerField(config) {
9
+ if (config.label) {
10
+ test('renders label', async ({ page }) => {
11
+ await expect(getLabel(page, config.controlName)).toHaveText(config.label);
12
+ });
13
+ }
14
+ if (config.hint) {
15
+ test('shows hint when untouched', async ({ page }) => {
16
+ await expect(getHint(page, config.controlName)).toBeVisible();
17
+ });
18
+ }
19
+ test('renders datepicker toggle button', async ({ page }) => {
20
+ const toggle = getMatFormField(page, config.controlName).locator('mat-datepicker-toggle');
21
+ await expect(toggle).toBeVisible();
22
+ });
23
+ if (config.validators?.includes(Validators.required)) {
24
+ test('shows required error when blurred without date', async ({ page }) => {
25
+ await touchDatepicker(page, config.controlName);
26
+ await expect(getError(page, config.controlName, 'datepicker')).toBeVisible();
27
+ });
28
+ }
29
+ }
30
+ //# sourceMappingURL=datepicker.tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"datepicker.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/datepicker.tests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAExG;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAA6B;IAC/D,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACvC,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,IAAI,CAAC,2BAA2B,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACnD,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAChE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,kCAAkC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QAC1D,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;QAC1F,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,gDAAgD,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACxE,MAAM,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;YAChD,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAC/E,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC","sourcesContent":["import { test, expect } from '@playwright/test';\nimport type { DatepickerFieldConfig } from '@tft/crispr-forms/utils';\nimport { Validators } from '@angular/forms';\nimport { getLabel, getHint, getError, getMatFormField, touchDatepicker } from '../helpers/form.helpers';\n\n/**\n * Registers Playwright tests for a DATEPICKER field config.\n * Call inside a `test.describe` block that has already navigated to the page.\n */\nexport function testDatepickerField(config: DatepickerFieldConfig) {\n if (config.label) {\n test('renders label', async ({ page }) => {\n await expect(getLabel(page, config.controlName)).toHaveText(config.label);\n });\n }\n\n if (config.hint) {\n test('shows hint when untouched', async ({ page }) => {\n await expect(getHint(page, config.controlName)).toBeVisible();\n });\n }\n\n test('renders datepicker toggle button', async ({ page }) => {\n const toggle = getMatFormField(page, config.controlName).locator('mat-datepicker-toggle');\n await expect(toggle).toBeVisible();\n });\n\n if (config.validators?.includes(Validators.required)) {\n test('shows required error when blurred without date', async ({ page }) => {\n await touchDatepicker(page, config.controlName);\n await expect(getError(page, config.controlName, 'datepicker')).toBeVisible();\n });\n }\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import type { AnyFieldConfig } from '@tft/crispr-forms/utils';
2
+ /**
3
+ * Registers a `test.describe` block for a field config, automatically selecting
4
+ * the correct test suite based on `controlType`.
5
+ *
6
+ * Non-control fields (heading, divider, button) are skipped silently.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { testField } from '@tft/crispr-forms-testing';
11
+ * import { myFormConfig } from './my-form.config';
12
+ *
13
+ * test.describe('My Form', () => {
14
+ * test.beforeEach(async ({ page }) => page.goto('/my-form'));
15
+ * for (const field of myFormConfig.fields) {
16
+ * testField(field);
17
+ * }
18
+ * });
19
+ * ```
20
+ */
21
+ export declare function testField(config: AnyFieldConfig): void;
@@ -0,0 +1,69 @@
1
+ import { test } from '@playwright/test';
2
+ import { testInputField } from './input.tests';
3
+ import { testTextareaField } from './textarea.tests';
4
+ import { testCheckboxField } from './checkbox.tests';
5
+ import { testSelectField } from './select.tests';
6
+ import { testSliderField } from './slider.tests';
7
+ import { testDatepickerField } from './datepicker.tests';
8
+ import { testAutocompleteField } from './autocomplete.tests';
9
+ import { testAutocompleteChiplistField } from './autocomplete-chiplist.tests';
10
+ import { testFileUploadField } from './file-upload.tests';
11
+ import { testUnitConversionField } from './unit-conversion.tests';
12
+ /**
13
+ * Registers a `test.describe` block for a field config, automatically selecting
14
+ * the correct test suite based on `controlType`.
15
+ *
16
+ * Non-control fields (heading, divider, button) are skipped silently.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { testField } from '@tft/crispr-forms-testing';
21
+ * import { myFormConfig } from './my-form.config';
22
+ *
23
+ * test.describe('My Form', () => {
24
+ * test.beforeEach(async ({ page }) => page.goto('/my-form'));
25
+ * for (const field of myFormConfig.fields) {
26
+ * testField(field);
27
+ * }
28
+ * });
29
+ * ```
30
+ */
31
+ export function testField(config) {
32
+ if (!('controlName' in config))
33
+ return;
34
+ test.describe(config.controlName, () => {
35
+ switch (config.controlType) {
36
+ case 'input':
37
+ testInputField(config);
38
+ break;
39
+ case 'textarea':
40
+ testTextareaField(config);
41
+ break;
42
+ case 'checkbox':
43
+ testCheckboxField(config);
44
+ break;
45
+ case 'select':
46
+ testSelectField(config);
47
+ break;
48
+ case 'slider':
49
+ testSliderField(config);
50
+ break;
51
+ case 'datepicker':
52
+ testDatepickerField(config);
53
+ break;
54
+ case 'autocomplete':
55
+ testAutocompleteField(config);
56
+ break;
57
+ case 'autocompleteChiplist':
58
+ testAutocompleteChiplistField(config);
59
+ break;
60
+ case 'fileUpload':
61
+ testFileUploadField(config);
62
+ break;
63
+ case 'unitConversion':
64
+ testUnitConversionField(config);
65
+ break;
66
+ }
67
+ });
68
+ }
69
+ //# sourceMappingURL=field-test-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-test-factory.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/field-test-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAcxC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAElE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,SAAS,CAAC,MAAsB;IAC9C,IAAI,CAAC,CAAC,aAAa,IAAI,MAAM,CAAC;QAAE,OAAO;IAEvC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG,EAAE;QACrC,QAAQ,MAAM,CAAC,WAAW,EAAE,CAAC;YAC3B,KAAK,OAAO;gBACV,cAAc,CAAC,MAA0B,CAAC,CAAC;gBAC3C,MAAM;YACR,KAAK,UAAU;gBACb,iBAAiB,CAAC,MAA6B,CAAC,CAAC;gBACjD,MAAM;YACR,KAAK,UAAU;gBACb,iBAAiB,CAAC,MAA6B,CAAC,CAAC;gBACjD,MAAM;YACR,KAAK,QAAQ;gBACX,eAAe,CAAC,MAA2B,CAAC,CAAC;gBAC7C,MAAM;YACR,KAAK,QAAQ;gBACX,eAAe,CAAC,MAA2B,CAAC,CAAC;gBAC7C,MAAM;YACR,KAAK,YAAY;gBACf,mBAAmB,CAAC,MAA+B,CAAC,CAAC;gBACrD,MAAM;YACR,KAAK,cAAc;gBACjB,qBAAqB,CAAC,MAAiC,CAAC,CAAC;gBACzD,MAAM;YACR,KAAK,sBAAsB;gBACzB,6BAA6B,CAAC,MAAyC,CAAC,CAAC;gBACzE,MAAM;YACR,KAAK,YAAY;gBACf,mBAAmB,CAAC,MAA+B,CAAC,CAAC;gBACrD,MAAM;YACR,KAAK,gBAAgB;gBACnB,uBAAuB,CAAC,MAAmC,CAAC,CAAC;gBAC7D,MAAM;QACV,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { test } from '@playwright/test';\nimport type {\n AnyFieldConfig,\n InputFieldConfig,\n TextareaFieldConfig,\n CheckboxFieldConfig,\n SelectFieldConfig,\n SliderFieldConfig,\n DatepickerFieldConfig,\n AutocompleteFieldConfig,\n AutocompleteChiplistFieldConfig,\n FileUploadFieldConfig,\n UnitConversionFieldConfig,\n} from '@tft/crispr-forms/utils';\nimport { testInputField } from './input.tests';\nimport { testTextareaField } from './textarea.tests';\nimport { testCheckboxField } from './checkbox.tests';\nimport { testSelectField } from './select.tests';\nimport { testSliderField } from './slider.tests';\nimport { testDatepickerField } from './datepicker.tests';\nimport { testAutocompleteField } from './autocomplete.tests';\nimport { testAutocompleteChiplistField } from './autocomplete-chiplist.tests';\nimport { testFileUploadField } from './file-upload.tests';\nimport { testUnitConversionField } from './unit-conversion.tests';\n\n/**\n * Registers a `test.describe` block for a field config, automatically selecting\n * the correct test suite based on `controlType`.\n *\n * Non-control fields (heading, divider, button) are skipped silently.\n *\n * @example\n * ```ts\n * import { testField } from '@tft/crispr-forms-testing';\n * import { myFormConfig } from './my-form.config';\n *\n * test.describe('My Form', () => {\n * test.beforeEach(async ({ page }) => page.goto('/my-form'));\n * for (const field of myFormConfig.fields) {\n * testField(field);\n * }\n * });\n * ```\n */\nexport function testField(config: AnyFieldConfig) {\n if (!('controlName' in config)) return;\n\n test.describe(config.controlName, () => {\n switch (config.controlType) {\n case 'input':\n testInputField(config as InputFieldConfig);\n break;\n case 'textarea':\n testTextareaField(config as TextareaFieldConfig);\n break;\n case 'checkbox':\n testCheckboxField(config as CheckboxFieldConfig);\n break;\n case 'select':\n testSelectField(config as SelectFieldConfig);\n break;\n case 'slider':\n testSliderField(config as SliderFieldConfig);\n break;\n case 'datepicker':\n testDatepickerField(config as DatepickerFieldConfig);\n break;\n case 'autocomplete':\n testAutocompleteField(config as AutocompleteFieldConfig);\n break;\n case 'autocompleteChiplist':\n testAutocompleteChiplistField(config as AutocompleteChiplistFieldConfig);\n break;\n case 'fileUpload':\n testFileUploadField(config as FileUploadFieldConfig);\n break;\n case 'unitConversion':\n testUnitConversionField(config as UnitConversionFieldConfig);\n break;\n }\n });\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { FileUploadFieldConfig } from '@tft/crispr-forms/utils';
2
+ /**
3
+ * Registers Playwright tests for a FILE_UPLOAD field config.
4
+ * Call inside a `test.describe` block that has already navigated to the page.
5
+ */
6
+ export declare function testFileUploadField(config: FileUploadFieldConfig): void;
@@ -0,0 +1,27 @@
1
+ import { test, expect } from '@playwright/test';
2
+ import { getLabel, getMatFormField } from '../helpers/form.helpers';
3
+ /**
4
+ * Registers Playwright tests for a FILE_UPLOAD field config.
5
+ * Call inside a `test.describe` block that has already navigated to the page.
6
+ */
7
+ export function testFileUploadField(config) {
8
+ if (config.label) {
9
+ test('renders label', async ({ page }) => {
10
+ await expect(getLabel(page, config.controlName)).toHaveText(config.label);
11
+ });
12
+ }
13
+ test('shows drop zone text', async ({ page }) => {
14
+ if (config.dropZoneText) {
15
+ const dropZone = getMatFormField(page, config.controlName).locator('.drop-info');
16
+ await expect(dropZone).toContainText(config.dropZoneText);
17
+ }
18
+ });
19
+ test('shows select files button', async ({ page }) => {
20
+ const button = getMatFormField(page, config.controlName).locator('button');
21
+ await expect(button).toBeVisible();
22
+ if (config.selectButtonText) {
23
+ await expect(button).toContainText(config.selectButtonText);
24
+ }
25
+ });
26
+ }
27
+ //# sourceMappingURL=file-upload.tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-upload.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/file-upload.tests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEpE;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAA6B;IAC/D,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACvC,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,sBAAsB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QAC9C,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YACjF,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,2BAA2B,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACnD,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC3E,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;QACnC,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC5B,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { test, expect } from '@playwright/test';\nimport type { FileUploadFieldConfig } from '@tft/crispr-forms/utils';\nimport { getLabel, getMatFormField } from '../helpers/form.helpers';\n\n/**\n * Registers Playwright tests for a FILE_UPLOAD field config.\n * Call inside a `test.describe` block that has already navigated to the page.\n */\nexport function testFileUploadField(config: FileUploadFieldConfig) {\n if (config.label) {\n test('renders label', async ({ page }) => {\n await expect(getLabel(page, config.controlName)).toHaveText(config.label);\n });\n }\n\n test('shows drop zone text', async ({ page }) => {\n if (config.dropZoneText) {\n const dropZone = getMatFormField(page, config.controlName).locator('.drop-info');\n await expect(dropZone).toContainText(config.dropZoneText);\n }\n });\n\n test('shows select files button', async ({ page }) => {\n const button = getMatFormField(page, config.controlName).locator('button');\n await expect(button).toBeVisible();\n if (config.selectButtonText) {\n await expect(button).toContainText(config.selectButtonText);\n }\n });\n}\n"]}
@@ -0,0 +1,15 @@
1
+ import type { InputFieldConfig } from '@tft/crispr-forms/utils';
2
+ /**
3
+ * Registers Playwright tests for an INPUT field config.
4
+ * Call inside a `test.describe` block that has already navigated to the page.
5
+ *
6
+ * Tests generated based on config:
7
+ * - Always: renders label, has correct input type
8
+ * - If `placeholder`: renders placeholder attribute
9
+ * - If `hint`: shows hint when untouched
10
+ * - If `Validators.required`: shows required error on blur
11
+ * - If `min`: validates min boundary
12
+ * - If `max`: validates max boundary
13
+ * - If `maxLength`: enforces maxLength attribute
14
+ */
15
+ export declare function testInputField(config: InputFieldConfig): void;
@@ -0,0 +1,73 @@
1
+ import { test, expect } from '@playwright/test';
2
+ import { Validators } from '@angular/forms';
3
+ import { getInput, getError, getLabel, getHint, touchInput } from '../helpers/form.helpers';
4
+ /**
5
+ * Registers Playwright tests for an INPUT field config.
6
+ * Call inside a `test.describe` block that has already navigated to the page.
7
+ *
8
+ * Tests generated based on config:
9
+ * - Always: renders label, has correct input type
10
+ * - If `placeholder`: renders placeholder attribute
11
+ * - If `hint`: shows hint when untouched
12
+ * - If `Validators.required`: shows required error on blur
13
+ * - If `min`: validates min boundary
14
+ * - If `max`: validates max boundary
15
+ * - If `maxLength`: enforces maxLength attribute
16
+ */
17
+ export function testInputField(config) {
18
+ test('renders label', async ({ page }) => {
19
+ await expect(getLabel(page, config.controlName)).toHaveText(config.label);
20
+ });
21
+ test(`has input type "${config.inputType ?? 'text'}"`, async ({ page }) => {
22
+ await expect(getInput(page, config.controlName)).toHaveAttribute('type', config.inputType ?? 'text');
23
+ });
24
+ if (config.placeholder) {
25
+ test('renders placeholder', async ({ page }) => {
26
+ await expect(getInput(page, config.controlName)).toHaveAttribute('placeholder', config.placeholder);
27
+ });
28
+ }
29
+ if (config.hint) {
30
+ test('shows hint when untouched', async ({ page }) => {
31
+ await expect(getHint(page, config.controlName)).toBeVisible();
32
+ });
33
+ }
34
+ if (config.validators?.includes(Validators.required)) {
35
+ test('shows required error when blurred empty', async ({ page }) => {
36
+ await touchInput(page, config.controlName);
37
+ await expect(getError(page, config.controlName)).toBeVisible();
38
+ });
39
+ }
40
+ if (config.min !== undefined) {
41
+ test(`shows error when value is below min (${config.min})`, async ({ page }) => {
42
+ await getInput(page, config.controlName).fill(String(config.min - 1));
43
+ await touchInput(page, config.controlName);
44
+ await expect(getError(page, config.controlName)).toBeVisible();
45
+ });
46
+ test(`accepts value at min boundary (${config.min})`, async ({ page }) => {
47
+ await getInput(page, config.controlName).fill(String(config.min));
48
+ await touchInput(page, config.controlName);
49
+ await expect(getError(page, config.controlName)).not.toBeVisible();
50
+ });
51
+ }
52
+ if (config.max !== undefined) {
53
+ test(`shows error when value exceeds max (${config.max})`, async ({ page }) => {
54
+ await getInput(page, config.controlName).fill(String(config.max + 1));
55
+ await touchInput(page, config.controlName);
56
+ await expect(getError(page, config.controlName)).toBeVisible();
57
+ });
58
+ test(`accepts value at max boundary (${config.max})`, async ({ page }) => {
59
+ await getInput(page, config.controlName).fill(String(config.max));
60
+ await touchInput(page, config.controlName);
61
+ await expect(getError(page, config.controlName)).not.toBeVisible();
62
+ });
63
+ }
64
+ if (config.maxLength !== undefined) {
65
+ test(`enforces maxLength of ${config.maxLength}`, async ({ page }) => {
66
+ const overLimit = 'a'.repeat(config.maxLength + 1);
67
+ await getInput(page, config.controlName).fill(overLimit);
68
+ const value = await getInput(page, config.controlName).inputValue();
69
+ expect(value.length).toBeLessThanOrEqual(config.maxLength);
70
+ });
71
+ }
72
+ }
73
+ //# sourceMappingURL=input.tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/input.tests.ts"],"names":[],"mappings":"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","sourcesContent":["import { test, expect } from '@playwright/test';\nimport type { InputFieldConfig } from '@tft/crispr-forms/utils';\nimport { Validators } from '@angular/forms';\nimport { getInput, getError, getLabel, getHint, touchInput } from '../helpers/form.helpers';\n\n/**\n * Registers Playwright tests for an INPUT field config.\n * Call inside a `test.describe` block that has already navigated to the page.\n *\n * Tests generated based on config:\n * - Always: renders label, has correct input type\n * - If `placeholder`: renders placeholder attribute\n * - If `hint`: shows hint when untouched\n * - If `Validators.required`: shows required error on blur\n * - If `min`: validates min boundary\n * - If `max`: validates max boundary\n * - If `maxLength`: enforces maxLength attribute\n */\nexport function testInputField(config: InputFieldConfig) {\n test('renders label', async ({ page }) => {\n await expect(getLabel(page, config.controlName)).toHaveText(config.label);\n });\n\n test(`has input type \"${config.inputType ?? 'text'}\"`, async ({ page }) => {\n await expect(getInput(page, config.controlName)).toHaveAttribute('type', config.inputType ?? 'text');\n });\n\n if (config.placeholder) {\n test('renders placeholder', async ({ page }) => {\n await expect(getInput(page, config.controlName)).toHaveAttribute('placeholder', config.placeholder);\n });\n }\n\n if (config.hint) {\n test('shows hint when untouched', async ({ page }) => {\n await expect(getHint(page, config.controlName)).toBeVisible();\n });\n }\n\n if (config.validators?.includes(Validators.required)) {\n test('shows required error when blurred empty', async ({ page }) => {\n await touchInput(page, config.controlName);\n await expect(getError(page, config.controlName)).toBeVisible();\n });\n }\n\n if (config.min !== undefined) {\n test(`shows error when value is below min (${config.min})`, async ({ page }) => {\n await getInput(page, config.controlName).fill(String(config.min - 1));\n await touchInput(page, config.controlName);\n await expect(getError(page, config.controlName)).toBeVisible();\n });\n\n test(`accepts value at min boundary (${config.min})`, async ({ page }) => {\n await getInput(page, config.controlName).fill(String(config.min));\n await touchInput(page, config.controlName);\n await expect(getError(page, config.controlName)).not.toBeVisible();\n });\n }\n\n if (config.max !== undefined) {\n test(`shows error when value exceeds max (${config.max})`, async ({ page }) => {\n await getInput(page, config.controlName).fill(String(config.max + 1));\n await touchInput(page, config.controlName);\n await expect(getError(page, config.controlName)).toBeVisible();\n });\n\n test(`accepts value at max boundary (${config.max})`, async ({ page }) => {\n await getInput(page, config.controlName).fill(String(config.max));\n await touchInput(page, config.controlName);\n await expect(getError(page, config.controlName)).not.toBeVisible();\n });\n }\n\n if (config.maxLength !== undefined) {\n test(`enforces maxLength of ${config.maxLength}`, async ({ page }) => {\n const overLimit = 'a'.repeat(config.maxLength + 1);\n await getInput(page, config.controlName).fill(overLimit);\n const value = await getInput(page, config.controlName).inputValue();\n expect(value.length).toBeLessThanOrEqual(config.maxLength);\n });\n }\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { SelectFieldConfig } from '@tft/crispr-forms/utils';
2
+ /**
3
+ * Registers Playwright tests for a SELECT field config.
4
+ * Call inside a `test.describe` block that has already navigated to the page.
5
+ */
6
+ export declare function testSelectField(config: SelectFieldConfig): void;
@@ -0,0 +1,39 @@
1
+ import { test, expect } from '@playwright/test';
2
+ import { Validators } from '@angular/forms';
3
+ import { getLabel, getHint, getError, getMatSelect, openSelect, selectOption, touchSelect } from '../helpers/form.helpers';
4
+ /**
5
+ * Registers Playwright tests for a SELECT field config.
6
+ * Call inside a `test.describe` block that has already navigated to the page.
7
+ */
8
+ export function testSelectField(config) {
9
+ if (config.label) {
10
+ test('renders label', async ({ page }) => {
11
+ await expect(getLabel(page, config.controlName)).toHaveText(config.label);
12
+ });
13
+ }
14
+ if (config.hint) {
15
+ test('shows hint when untouched', async ({ page }) => {
16
+ await expect(getHint(page, config.controlName)).toBeVisible();
17
+ });
18
+ }
19
+ if (Array.isArray(config.options)) {
20
+ test('shows all options when opened', async ({ page }) => {
21
+ await openSelect(page, config.controlName);
22
+ const options = page.locator('mat-option');
23
+ await expect(options).toHaveCount(config.options.length);
24
+ await page.keyboard.press('Escape');
25
+ });
26
+ test('can select an option', async ({ page }) => {
27
+ const firstOption = config.options[0];
28
+ await selectOption(page, config.controlName, firstOption.label);
29
+ await expect(getMatSelect(page, config.controlName)).toContainText(firstOption.label);
30
+ });
31
+ }
32
+ if (config.validators?.includes(Validators.required)) {
33
+ test('shows required error when blurred without selection', async ({ page }) => {
34
+ await touchSelect(page, config.controlName);
35
+ await expect(getError(page, config.controlName, 'select')).toBeVisible();
36
+ });
37
+ }
38
+ }
39
+ //# sourceMappingURL=select.tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/select.tests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3H;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAAyB;IACvD,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACvC,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,IAAI,CAAC,2BAA2B,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACnD,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAChE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAClC,IAAI,CAAC,+BAA+B,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACvD,MAAM,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;YAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAC3C,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,CAAE,MAAM,CAAC,OAA0B,CAAC,MAAM,CAAC,CAAC;YAC7E,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,sBAAsB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YAC9C,MAAM,WAAW,GAAI,MAAM,CAAC,OAAoD,CAAC,CAAC,CAAC,CAAC;YACpF,MAAM,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;YAChE,MAAM,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QACxF,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,qDAAqD,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YAC7E,MAAM,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;YAC5C,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAC3E,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC","sourcesContent":["import { test, expect } from '@playwright/test';\nimport type { SelectFieldConfig, SelectOption } from '@tft/crispr-forms/utils';\nimport { Validators } from '@angular/forms';\nimport { getLabel, getHint, getError, getMatSelect, openSelect, selectOption, touchSelect } from '../helpers/form.helpers';\n\n/**\n * Registers Playwright tests for a SELECT field config.\n * Call inside a `test.describe` block that has already navigated to the page.\n */\nexport function testSelectField(config: SelectFieldConfig) {\n if (config.label) {\n test('renders label', async ({ page }) => {\n await expect(getLabel(page, config.controlName)).toHaveText(config.label);\n });\n }\n\n if (config.hint) {\n test('shows hint when untouched', async ({ page }) => {\n await expect(getHint(page, config.controlName)).toBeVisible();\n });\n }\n\n if (Array.isArray(config.options)) {\n test('shows all options when opened', async ({ page }) => {\n await openSelect(page, config.controlName);\n const options = page.locator('mat-option');\n await expect(options).toHaveCount((config.options as SelectOption[]).length);\n await page.keyboard.press('Escape');\n });\n\n test('can select an option', async ({ page }) => {\n const firstOption = (config.options as Array<{ label: string; value: unknown }>)[0];\n await selectOption(page, config.controlName, firstOption.label);\n await expect(getMatSelect(page, config.controlName)).toContainText(firstOption.label);\n });\n }\n\n if (config.validators?.includes(Validators.required)) {\n test('shows required error when blurred without selection', async ({ page }) => {\n await touchSelect(page, config.controlName);\n await expect(getError(page, config.controlName, 'select')).toBeVisible();\n });\n }\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import type { SliderFieldConfig } from '@tft/crispr-forms/utils';
2
+ /**
3
+ * Registers Playwright tests for a SLIDER field config.
4
+ * Call inside a `test.describe` block that has already navigated to the page.
5
+ *
6
+ * Note: slider uses a plain `<label>` (not `<mat-label>`) because it is not
7
+ * rendered inside a `mat-form-field`. The `data-testid` is on the `<mat-slider>`
8
+ * element itself.
9
+ *
10
+ * Note: `min`/`max` are read from the native HTML attributes on `input[matSliderThumb]`,
11
+ * not from `aria-valuemin`/`aria-valuemax`.
12
+ */
13
+ export declare function testSliderField(config: SliderFieldConfig): void;
@@ -0,0 +1,37 @@
1
+ import { test, expect } from '@playwright/test';
2
+ /**
3
+ * Registers Playwright tests for a SLIDER field config.
4
+ * Call inside a `test.describe` block that has already navigated to the page.
5
+ *
6
+ * Note: slider uses a plain `<label>` (not `<mat-label>`) because it is not
7
+ * rendered inside a `mat-form-field`. The `data-testid` is on the `<mat-slider>`
8
+ * element itself.
9
+ *
10
+ * Note: `min`/`max` are read from the native HTML attributes on `input[matSliderThumb]`,
11
+ * not from `aria-valuemin`/`aria-valuemax`.
12
+ */
13
+ export function testSliderField(config) {
14
+ test('renders slider', async ({ page }) => {
15
+ const slider = page.locator(`[data-testid="${config.controlName}"]`);
16
+ await expect(slider).toBeVisible();
17
+ });
18
+ if (config.label) {
19
+ test('renders label', async ({ page }) => {
20
+ const sliderHost = page.locator(`crispr-slider-field:has([data-testid="${config.controlName}"])`);
21
+ await expect(sliderHost.locator('label')).toContainText(config.label);
22
+ });
23
+ }
24
+ if (config.min !== undefined) {
25
+ test(`has correct min value (${config.min})`, async ({ page }) => {
26
+ const thumb = page.locator(`[data-testid="${config.controlName}"] input`);
27
+ await expect(thumb).toHaveAttribute('min', String(config.min));
28
+ });
29
+ }
30
+ if (config.max !== undefined) {
31
+ test(`has correct max value (${config.max})`, async ({ page }) => {
32
+ const thumb = page.locator(`[data-testid="${config.controlName}"] input`);
33
+ await expect(thumb).toHaveAttribute('max', String(config.max));
34
+ });
35
+ }
36
+ }
37
+ //# sourceMappingURL=slider.tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slider.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/slider.tests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAGhD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,MAAyB;IACvD,IAAI,CAAC,gBAAgB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,MAAM,CAAC,WAAW,IAAI,CAAC,CAAC;QACrE,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACvC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,yCAAyC,MAAM,CAAC,WAAW,KAAK,CAAC,CAAC;YAClG,MAAM,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC7B,IAAI,CAAC,0BAA0B,MAAM,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,MAAM,CAAC,WAAW,UAAU,CAAC,CAAC;YAC1E,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACjE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC7B,IAAI,CAAC,0BAA0B,MAAM,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,MAAM,CAAC,WAAW,UAAU,CAAC,CAAC;YAC1E,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACjE,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC","sourcesContent":["import { test, expect } from '@playwright/test';\nimport type { SliderFieldConfig } from '@tft/crispr-forms/utils';\n\n/**\n * Registers Playwright tests for a SLIDER field config.\n * Call inside a `test.describe` block that has already navigated to the page.\n *\n * Note: slider uses a plain `<label>` (not `<mat-label>`) because it is not\n * rendered inside a `mat-form-field`. The `data-testid` is on the `<mat-slider>`\n * element itself.\n *\n * Note: `min`/`max` are read from the native HTML attributes on `input[matSliderThumb]`,\n * not from `aria-valuemin`/`aria-valuemax`.\n */\nexport function testSliderField(config: SliderFieldConfig) {\n test('renders slider', async ({ page }) => {\n const slider = page.locator(`[data-testid=\"${config.controlName}\"]`);\n await expect(slider).toBeVisible();\n });\n\n if (config.label) {\n test('renders label', async ({ page }) => {\n const sliderHost = page.locator(`crispr-slider-field:has([data-testid=\"${config.controlName}\"])`);\n await expect(sliderHost.locator('label')).toContainText(config.label);\n });\n }\n\n if (config.min !== undefined) {\n test(`has correct min value (${config.min})`, async ({ page }) => {\n const thumb = page.locator(`[data-testid=\"${config.controlName}\"] input`);\n await expect(thumb).toHaveAttribute('min', String(config.min));\n });\n }\n\n if (config.max !== undefined) {\n test(`has correct max value (${config.max})`, async ({ page }) => {\n const thumb = page.locator(`[data-testid=\"${config.controlName}\"] input`);\n await expect(thumb).toHaveAttribute('max', String(config.max));\n });\n }\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { TextareaFieldConfig } from '@tft/crispr-forms/utils';
2
+ /**
3
+ * Registers Playwright tests for a TEXTAREA field config.
4
+ * Call inside a `test.describe` block that has already navigated to the page.
5
+ */
6
+ export declare function testTextareaField(config: TextareaFieldConfig): void;
@@ -0,0 +1,28 @@
1
+ import { test, expect } from '@playwright/test';
2
+ import { Validators } from '@angular/forms';
3
+ import { getMatFormField, getError, getLabel, getHint } from '../helpers/form.helpers';
4
+ /**
5
+ * Registers Playwright tests for a TEXTAREA field config.
6
+ * Call inside a `test.describe` block that has already navigated to the page.
7
+ */
8
+ export function testTextareaField(config) {
9
+ if (config.label) {
10
+ test('renders label', async ({ page }) => {
11
+ await expect(getLabel(page, config.controlName)).toHaveText(config.label);
12
+ });
13
+ }
14
+ if (config.hint) {
15
+ test('shows hint when untouched', async ({ page }) => {
16
+ await expect(getHint(page, config.controlName)).toBeVisible();
17
+ });
18
+ }
19
+ if (config.validators?.includes(Validators.required)) {
20
+ test('shows required error when blurred empty', async ({ page }) => {
21
+ const textarea = getMatFormField(page, config.controlName).locator('textarea');
22
+ await textarea.click();
23
+ await page.keyboard.press('Tab');
24
+ await expect(getError(page, config.controlName, 'textarea')).toBeVisible();
25
+ });
26
+ }
27
+ }
28
+ //# sourceMappingURL=textarea.tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textarea.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/textarea.tests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAEvF;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAA2B;IAC3D,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACvC,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,IAAI,CAAC,2BAA2B,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACnD,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAChE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,yCAAyC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACjE,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC/E,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC;YACvB,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACjC,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7E,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC","sourcesContent":["import { test, expect } from '@playwright/test';\nimport type { TextareaFieldConfig } from '@tft/crispr-forms/utils';\nimport { Validators } from '@angular/forms';\nimport { getMatFormField, getError, getLabel, getHint } from '../helpers/form.helpers';\n\n/**\n * Registers Playwright tests for a TEXTAREA field config.\n * Call inside a `test.describe` block that has already navigated to the page.\n */\nexport function testTextareaField(config: TextareaFieldConfig) {\n if (config.label) {\n test('renders label', async ({ page }) => {\n await expect(getLabel(page, config.controlName)).toHaveText(config.label);\n });\n }\n\n if (config.hint) {\n test('shows hint when untouched', async ({ page }) => {\n await expect(getHint(page, config.controlName)).toBeVisible();\n });\n }\n\n if (config.validators?.includes(Validators.required)) {\n test('shows required error when blurred empty', async ({ page }) => {\n const textarea = getMatFormField(page, config.controlName).locator('textarea');\n await textarea.click();\n await page.keyboard.press('Tab');\n await expect(getError(page, config.controlName, 'textarea')).toBeVisible();\n });\n }\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { UnitConversionFieldConfig } from '@tft/crispr-forms/utils';
2
+ /**
3
+ * Registers Playwright tests for a UNIT_CONVERSION field config.
4
+ * Call inside a `test.describe` block that has already navigated to the page.
5
+ *
6
+ * Note: required error display is not testable for this field type. The
7
+ * `displayValueControl` (what the user types into) is separate from the
8
+ * `[formControlName]` that holds validators, so validation errors are never
9
+ * wired to the visible input.
10
+ */
11
+ export declare function testUnitConversionField(config: UnitConversionFieldConfig): void;
@@ -0,0 +1,29 @@
1
+ import { test, expect } from '@playwright/test';
2
+ import { getLabel, getMatFormField } from '../helpers/form.helpers';
3
+ /**
4
+ * Registers Playwright tests for a UNIT_CONVERSION field config.
5
+ * Call inside a `test.describe` block that has already navigated to the page.
6
+ *
7
+ * Note: required error display is not testable for this field type. The
8
+ * `displayValueControl` (what the user types into) is separate from the
9
+ * `[formControlName]` that holds validators, so validation errors are never
10
+ * wired to the visible input.
11
+ */
12
+ export function testUnitConversionField(config) {
13
+ if (config.label) {
14
+ test('renders label', async ({ page }) => {
15
+ await expect(getLabel(page, config.controlName)).toHaveText(config.label);
16
+ });
17
+ }
18
+ test('renders number input', async ({ page }) => {
19
+ const numberInput = getMatFormField(page, config.controlName).locator('input[type="number"]');
20
+ await expect(numberInput).toBeVisible();
21
+ });
22
+ if (config.showUnitSelect) {
23
+ test('renders unit select when showUnitSelect is true', async ({ page }) => {
24
+ const unitSelect = page.locator(`crispr-unit-conversion-field:has([data-testid="${config.controlName}"]) .unit-field mat-select`);
25
+ await expect(unitSelect).toBeVisible();
26
+ });
27
+ }
28
+ }
29
+ //# sourceMappingURL=unit-conversion.tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unit-conversion.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/unit-conversion.tests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEpE;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAiC;IACvE,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACvC,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,sBAAsB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QAC9C,MAAM,WAAW,GAAG,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;QAC9F,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QAC1B,IAAI,CAAC,iDAAiD,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACzE,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,kDAAkD,MAAM,CAAC,WAAW,4BAA4B,CAAC,CAAC;YAClI,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;QACzC,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC","sourcesContent":["import { test, expect } from '@playwright/test';\nimport type { UnitConversionFieldConfig } from '@tft/crispr-forms/utils';\nimport { getLabel, getMatFormField } from '../helpers/form.helpers';\n\n/**\n * Registers Playwright tests for a UNIT_CONVERSION field config.\n * Call inside a `test.describe` block that has already navigated to the page.\n *\n * Note: required error display is not testable for this field type. The\n * `displayValueControl` (what the user types into) is separate from the\n * `[formControlName]` that holds validators, so validation errors are never\n * wired to the visible input.\n */\nexport function testUnitConversionField(config: UnitConversionFieldConfig) {\n if (config.label) {\n test('renders label', async ({ page }) => {\n await expect(getLabel(page, config.controlName)).toHaveText(config.label);\n });\n }\n\n test('renders number input', async ({ page }) => {\n const numberInput = getMatFormField(page, config.controlName).locator('input[type=\"number\"]');\n await expect(numberInput).toBeVisible();\n });\n\n if (config.showUnitSelect) {\n test('renders unit select when showUnitSelect is true', async ({ page }) => {\n const unitSelect = page.locator(`crispr-unit-conversion-field:has([data-testid=\"${config.controlName}\"]) .unit-field mat-select`);\n await expect(unitSelect).toBeVisible();\n });\n }\n}\n"]}
@@ -0,0 +1,80 @@
1
+ import { Page, Locator } from '@playwright/test';
2
+ /**
3
+ * Returns the locator for a field's root element (the element with `data-testid`).
4
+ * For most fields this is a `mat-form-field`; for checkbox/slider it is the control element.
5
+ *
6
+ * For nested fields, pass the full hierarchical testId path:
7
+ * - subGroup child: `'address.city'`
8
+ * - formGroupList item child: `'contacts[0].email'`
9
+ */
10
+ export declare function getMatFormField(page: Page, testId: string): Locator;
11
+ /**
12
+ * Returns the `<input>` inside a field. Works for INPUT, DATEPICKER, and other fields
13
+ * that render a single native input.
14
+ */
15
+ export declare function getInput(page: Page, testId: string): Locator;
16
+ /**
17
+ * Returns the validation error paragraph for a field.
18
+ *
19
+ * Targets the inner `<p class="tft-control-error">` rather than the host element to
20
+ * avoid Playwright strict-mode failures on fields like autocomplete/chiplist that
21
+ * render two `tft-control-error` host elements (one for each internal input).
22
+ *
23
+ * @param fieldType - The kebab-case field type used in the component selector,
24
+ * e.g. `'input'`, `'select'`, `'autocomplete'`, `'autocomplete-chiplist'`,
25
+ * `'checkbox'`, `'datepicker'`, `'textarea'`, `'slider'`. Defaults to `'input'`.
26
+ */
27
+ export declare function getError(page: Page, testId: string, fieldType?: string): Locator;
28
+ /**
29
+ * Returns the `<mat-label>` element inside a field's form field.
30
+ */
31
+ export declare function getLabel(page: Page, testId: string): Locator;
32
+ /**
33
+ * Returns the `<mat-hint>` element inside a field's form field.
34
+ */
35
+ export declare function getHint(page: Page, testId: string): Locator;
36
+ /**
37
+ * Clicks an input and presses Tab to trigger a real blur event.
38
+ * Angular's touched state is driven by blur, not just focus-out.
39
+ */
40
+ export declare function touchInput(page: Page, testId: string): Promise<void>;
41
+ /** Returns the `<mat-select>` element inside a select field. */
42
+ export declare function getMatSelect(page: Page, testId: string): Locator;
43
+ /** Opens a `mat-select` dropdown. */
44
+ export declare function openSelect(page: Page, testId: string): Promise<void>;
45
+ /** Opens a `mat-select`, clicks the option with the given label, and waits for it to close. */
46
+ export declare function selectOption(page: Page, testId: string, optionLabel: string): Promise<void>;
47
+ /**
48
+ * Opens a `mat-select`, presses Escape, then Tab to blur it.
49
+ * The extra Tab is required — Escape alone dismisses the panel but does not blur
50
+ * the trigger element, so `ControlErrorsDirective` never sees a blur event.
51
+ */
52
+ export declare function touchSelect(page: Page, testId: string): Promise<void>;
53
+ /** Returns the `<mat-checkbox>` element for a checkbox field. */
54
+ export declare function getCheckbox(page: Page, testId: string): Locator;
55
+ /**
56
+ * Clicks a checkbox twice (check then uncheck) and presses Tab to blur,
57
+ * leaving it unchecked and touched — useful for triggering `requiredTrue` errors.
58
+ */
59
+ export declare function touchCheckbox(page: Page, testId: string): Promise<void>;
60
+ /**
61
+ * Returns the visible search input for an autocomplete field.
62
+ * Note: this is the `role="textbox"` input, NOT the hidden `[formControlName]` anchor.
63
+ */
64
+ export declare function getAutocompleteInput(page: Page, testId: string): Locator;
65
+ /** Clicks the autocomplete input and presses Tab to blur. */
66
+ export declare function touchAutocomplete(page: Page, testId: string): Promise<void>;
67
+ /**
68
+ * Programmatically clicks the hidden crispr-form submit button.
69
+ *
70
+ * This fires both the native `submit` event (required by `ControlErrorsFormDirective`
71
+ * to show errors on untouched controls) and Angular's `ngSubmit` event.
72
+ *
73
+ * Must use `evaluate` + native `.click()` because `display:none` elements
74
+ * have no layout position for Playwright pointer events.
75
+ *
76
+ * @param nth - Zero-based index of the form on the page. Defaults to `0`.
77
+ */
78
+ export declare function submitForm(page: Page, nth?: number): Promise<void>;
79
+ /** Clicks the datepicker input and presses Tab to blur. */
80
+ export declare function touchDatepicker(page: Page, testId: string): Promise<void>;
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Returns the locator for a field's root element (the element with `data-testid`).
3
+ * For most fields this is a `mat-form-field`; for checkbox/slider it is the control element.
4
+ *
5
+ * For nested fields, pass the full hierarchical testId path:
6
+ * - subGroup child: `'address.city'`
7
+ * - formGroupList item child: `'contacts[0].email'`
8
+ */
9
+ export function getMatFormField(page, testId) {
10
+ return page.locator(`[data-testid="${testId}"]`);
11
+ }
12
+ /**
13
+ * Returns the `<input>` inside a field. Works for INPUT, DATEPICKER, and other fields
14
+ * that render a single native input.
15
+ */
16
+ export function getInput(page, testId) {
17
+ return getMatFormField(page, testId).locator('input');
18
+ }
19
+ /**
20
+ * Returns the validation error paragraph for a field.
21
+ *
22
+ * Targets the inner `<p class="tft-control-error">` rather than the host element to
23
+ * avoid Playwright strict-mode failures on fields like autocomplete/chiplist that
24
+ * render two `tft-control-error` host elements (one for each internal input).
25
+ *
26
+ * @param fieldType - The kebab-case field type used in the component selector,
27
+ * e.g. `'input'`, `'select'`, `'autocomplete'`, `'autocomplete-chiplist'`,
28
+ * `'checkbox'`, `'datepicker'`, `'textarea'`, `'slider'`. Defaults to `'input'`.
29
+ */
30
+ export function getError(page, testId, fieldType = 'input') {
31
+ return page.locator(`crispr-${fieldType}-field:has([data-testid="${testId}"]) p.tft-control-error`);
32
+ }
33
+ /**
34
+ * Returns the `<mat-label>` element inside a field's form field.
35
+ */
36
+ export function getLabel(page, testId) {
37
+ return getMatFormField(page, testId).locator('mat-label');
38
+ }
39
+ /**
40
+ * Returns the `<mat-hint>` element inside a field's form field.
41
+ */
42
+ export function getHint(page, testId) {
43
+ return getMatFormField(page, testId).locator('mat-hint');
44
+ }
45
+ /**
46
+ * Clicks an input and presses Tab to trigger a real blur event.
47
+ * Angular's touched state is driven by blur, not just focus-out.
48
+ */
49
+ export async function touchInput(page, testId) {
50
+ await getInput(page, testId).click();
51
+ await page.keyboard.press('Tab');
52
+ }
53
+ /** Returns the `<mat-select>` element inside a select field. */
54
+ export function getMatSelect(page, testId) {
55
+ return getMatFormField(page, testId).locator('mat-select');
56
+ }
57
+ /** Opens a `mat-select` dropdown. */
58
+ export async function openSelect(page, testId) {
59
+ await getMatSelect(page, testId).click();
60
+ }
61
+ /** Opens a `mat-select`, clicks the option with the given label, and waits for it to close. */
62
+ export async function selectOption(page, testId, optionLabel) {
63
+ await openSelect(page, testId);
64
+ await page.locator('mat-option').filter({ hasText: optionLabel }).click();
65
+ }
66
+ /**
67
+ * Opens a `mat-select`, presses Escape, then Tab to blur it.
68
+ * The extra Tab is required — Escape alone dismisses the panel but does not blur
69
+ * the trigger element, so `ControlErrorsDirective` never sees a blur event.
70
+ */
71
+ export async function touchSelect(page, testId) {
72
+ await openSelect(page, testId);
73
+ await page.keyboard.press('Escape');
74
+ await page.keyboard.press('Tab');
75
+ }
76
+ /** Returns the `<mat-checkbox>` element for a checkbox field. */
77
+ export function getCheckbox(page, testId) {
78
+ return page.locator(`[data-testid="${testId}"]`);
79
+ }
80
+ /**
81
+ * Clicks a checkbox twice (check then uncheck) and presses Tab to blur,
82
+ * leaving it unchecked and touched — useful for triggering `requiredTrue` errors.
83
+ */
84
+ export async function touchCheckbox(page, testId) {
85
+ await getCheckbox(page, testId).click();
86
+ await getCheckbox(page, testId).click();
87
+ await page.keyboard.press('Tab');
88
+ }
89
+ /**
90
+ * Returns the visible search input for an autocomplete field.
91
+ * Note: this is the `role="textbox"` input, NOT the hidden `[formControlName]` anchor.
92
+ */
93
+ export function getAutocompleteInput(page, testId) {
94
+ return getMatFormField(page, testId).locator('input[type="textbox"]');
95
+ }
96
+ /** Clicks the autocomplete input and presses Tab to blur. */
97
+ export async function touchAutocomplete(page, testId) {
98
+ await getAutocompleteInput(page, testId).click();
99
+ await page.keyboard.press('Tab');
100
+ }
101
+ /**
102
+ * Programmatically clicks the hidden crispr-form submit button.
103
+ *
104
+ * This fires both the native `submit` event (required by `ControlErrorsFormDirective`
105
+ * to show errors on untouched controls) and Angular's `ngSubmit` event.
106
+ *
107
+ * Must use `evaluate` + native `.click()` because `display:none` elements
108
+ * have no layout position for Playwright pointer events.
109
+ *
110
+ * @param nth - Zero-based index of the form on the page. Defaults to `0`.
111
+ */
112
+ export async function submitForm(page, nth = 0) {
113
+ await page.evaluate((n) => {
114
+ const triggers = document.querySelectorAll('[data-testid="submit-trigger"]');
115
+ triggers[n]?.click();
116
+ }, nth);
117
+ }
118
+ /** Clicks the datepicker input and presses Tab to blur. */
119
+ export async function touchDatepicker(page, testId) {
120
+ await getMatFormField(page, testId).locator('input').click();
121
+ await page.keyboard.press('Tab');
122
+ }
123
+ //# sourceMappingURL=form.helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.helpers.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/helpers/form.helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import { Page, Locator } from '@playwright/test';\n\n/**\n * Returns the locator for a field's root element (the element with `data-testid`).\n * For most fields this is a `mat-form-field`; for checkbox/slider it is the control element.\n *\n * For nested fields, pass the full hierarchical testId path:\n * - subGroup child: `'address.city'`\n * - formGroupList item child: `'contacts[0].email'`\n */\nexport function getMatFormField(page: Page, testId: string): Locator {\n return page.locator(`[data-testid=\"${testId}\"]`);\n}\n\n/**\n * Returns the `<input>` inside a field. Works for INPUT, DATEPICKER, and other fields\n * that render a single native input.\n */\nexport function getInput(page: Page, testId: string): Locator {\n return getMatFormField(page, testId).locator('input');\n}\n\n/**\n * Returns the validation error paragraph for a field.\n *\n * Targets the inner `<p class=\"tft-control-error\">` rather than the host element to\n * avoid Playwright strict-mode failures on fields like autocomplete/chiplist that\n * render two `tft-control-error` host elements (one for each internal input).\n *\n * @param fieldType - The kebab-case field type used in the component selector,\n * e.g. `'input'`, `'select'`, `'autocomplete'`, `'autocomplete-chiplist'`,\n * `'checkbox'`, `'datepicker'`, `'textarea'`, `'slider'`. Defaults to `'input'`.\n */\nexport function getError(page: Page, testId: string, fieldType = 'input'): Locator {\n return page.locator(`crispr-${fieldType}-field:has([data-testid=\"${testId}\"]) p.tft-control-error`);\n}\n\n/**\n * Returns the `<mat-label>` element inside a field's form field.\n */\nexport function getLabel(page: Page, testId: string): Locator {\n return getMatFormField(page, testId).locator('mat-label');\n}\n\n/**\n * Returns the `<mat-hint>` element inside a field's form field.\n */\nexport function getHint(page: Page, testId: string): Locator {\n return getMatFormField(page, testId).locator('mat-hint');\n}\n\n/**\n * Clicks an input and presses Tab to trigger a real blur event.\n * Angular's touched state is driven by blur, not just focus-out.\n */\nexport async function touchInput(page: Page, testId: string): Promise<void> {\n await getInput(page, testId).click();\n await page.keyboard.press('Tab');\n}\n\n/** Returns the `<mat-select>` element inside a select field. */\nexport function getMatSelect(page: Page, testId: string): Locator {\n return getMatFormField(page, testId).locator('mat-select');\n}\n\n/** Opens a `mat-select` dropdown. */\nexport async function openSelect(page: Page, testId: string): Promise<void> {\n await getMatSelect(page, testId).click();\n}\n\n/** Opens a `mat-select`, clicks the option with the given label, and waits for it to close. */\nexport async function selectOption(page: Page, testId: string, optionLabel: string): Promise<void> {\n await openSelect(page, testId);\n await page.locator('mat-option').filter({ hasText: optionLabel }).click();\n}\n\n/**\n * Opens a `mat-select`, presses Escape, then Tab to blur it.\n * The extra Tab is required — Escape alone dismisses the panel but does not blur\n * the trigger element, so `ControlErrorsDirective` never sees a blur event.\n */\nexport async function touchSelect(page: Page, testId: string): Promise<void> {\n await openSelect(page, testId);\n await page.keyboard.press('Escape');\n await page.keyboard.press('Tab');\n}\n\n/** Returns the `<mat-checkbox>` element for a checkbox field. */\nexport function getCheckbox(page: Page, testId: string): Locator {\n return page.locator(`[data-testid=\"${testId}\"]`);\n}\n\n/**\n * Clicks a checkbox twice (check then uncheck) and presses Tab to blur,\n * leaving it unchecked and touched — useful for triggering `requiredTrue` errors.\n */\nexport async function touchCheckbox(page: Page, testId: string): Promise<void> {\n await getCheckbox(page, testId).click();\n await getCheckbox(page, testId).click();\n await page.keyboard.press('Tab');\n}\n\n/**\n * Returns the visible search input for an autocomplete field.\n * Note: this is the `role=\"textbox\"` input, NOT the hidden `[formControlName]` anchor.\n */\nexport function getAutocompleteInput(page: Page, testId: string): Locator {\n return getMatFormField(page, testId).locator('input[type=\"textbox\"]');\n}\n\n/** Clicks the autocomplete input and presses Tab to blur. */\nexport async function touchAutocomplete(page: Page, testId: string): Promise<void> {\n await getAutocompleteInput(page, testId).click();\n await page.keyboard.press('Tab');\n}\n\n/**\n * Programmatically clicks the hidden crispr-form submit button.\n *\n * This fires both the native `submit` event (required by `ControlErrorsFormDirective`\n * to show errors on untouched controls) and Angular's `ngSubmit` event.\n *\n * Must use `evaluate` + native `.click()` because `display:none` elements\n * have no layout position for Playwright pointer events.\n *\n * @param nth - Zero-based index of the form on the page. Defaults to `0`.\n */\nexport async function submitForm(page: Page, nth = 0): Promise<void> {\n await page.evaluate((n) => {\n const triggers = document.querySelectorAll<HTMLButtonElement>('[data-testid=\"submit-trigger\"]');\n triggers[n]?.click();\n }, nth);\n}\n\n/** Clicks the datepicker input and presses Tab to blur. */\nexport async function touchDatepicker(page: Page, testId: string): Promise<void> {\n await getMatFormField(page, testId).locator('input').click();\n await page.keyboard.press('Tab');\n}\n"]}
package/index.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ import '@angular/compiler';
2
+ export * from '@playwright/test';
3
+ export * from './helpers/form.helpers';
4
+ export * from './field-tests/input.tests';
5
+ export * from './field-tests/textarea.tests';
6
+ export * from './field-tests/checkbox.tests';
7
+ export * from './field-tests/select.tests';
8
+ export * from './field-tests/slider.tests';
9
+ export * from './field-tests/datepicker.tests';
10
+ export * from './field-tests/autocomplete.tests';
11
+ export * from './field-tests/autocomplete-chiplist.tests';
12
+ export * from './field-tests/file-upload.tests';
13
+ export * from './field-tests/unit-conversion.tests';
14
+ export * from './field-tests/field-test-factory';
package/index.js ADDED
@@ -0,0 +1,18 @@
1
+ // Load Angular's JIT compiler. Required because crispr-forms uses partial compilation
2
+ // (Ivy linker format) which needs the JIT compiler as a fallback in Node/Playwright environments.
3
+ import '@angular/compiler';
4
+ // Re-export Playwright so consumers only need one import.
5
+ export * from '@playwright/test';
6
+ export * from './helpers/form.helpers';
7
+ export * from './field-tests/input.tests';
8
+ export * from './field-tests/textarea.tests';
9
+ export * from './field-tests/checkbox.tests';
10
+ export * from './field-tests/select.tests';
11
+ export * from './field-tests/slider.tests';
12
+ export * from './field-tests/datepicker.tests';
13
+ export * from './field-tests/autocomplete.tests';
14
+ export * from './field-tests/autocomplete-chiplist.tests';
15
+ export * from './field-tests/file-upload.tests';
16
+ export * from './field-tests/unit-conversion.tests';
17
+ export * from './field-tests/field-test-factory';
18
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../libs/crispr-forms-testing/src/index.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,kGAAkG;AAClG,OAAO,mBAAmB,CAAC;AAE3B,0DAA0D;AAC1D,cAAc,kBAAkB,CAAC;AAEjC,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,kCAAkC,CAAC;AACjD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,kCAAkC,CAAC","sourcesContent":["// Load Angular's JIT compiler. Required because crispr-forms uses partial compilation\n// (Ivy linker format) which needs the JIT compiler as a fallback in Node/Playwright environments.\nimport '@angular/compiler';\n\n// Re-export Playwright so consumers only need one import.\nexport * from '@playwright/test';\n\nexport * from './helpers/form.helpers';\nexport * from './field-tests/input.tests';\nexport * from './field-tests/textarea.tests';\nexport * from './field-tests/checkbox.tests';\nexport * from './field-tests/select.tests';\nexport * from './field-tests/slider.tests';\nexport * from './field-tests/datepicker.tests';\nexport * from './field-tests/autocomplete.tests';\nexport * from './field-tests/autocomplete-chiplist.tests';\nexport * from './field-tests/file-upload.tests';\nexport * from './field-tests/unit-conversion.tests';\nexport * from './field-tests/field-test-factory';\n"]}
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@tft/crispr-forms-testing",
3
+ "version": "21.0.1",
4
+ "main": "index.js",
5
+ "types": "index.d.ts",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./index.d.ts",
9
+ "default": "./index.js"
10
+ }
11
+ },
12
+ "peerDependencies": {
13
+ "@angular/compiler": "^21.0.0",
14
+ "@angular/forms": "^21.0.0",
15
+ "@playwright/test": ">=1.40.0",
16
+ "@tft/crispr-forms": "*"
17
+ }
18
+ }