@tft/crispr-forms-testing 21.0.13 → 21.0.15
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/README.md +163 -0
- package/field-tests/autocomplete-chiplist.tests.d.ts +1 -1
- package/field-tests/autocomplete-chiplist.tests.js +25 -22
- package/field-tests/autocomplete-chiplist.tests.js.map +1 -1
- package/field-tests/autocomplete.tests.d.ts +1 -1
- package/field-tests/autocomplete.tests.js +22 -19
- package/field-tests/autocomplete.tests.js.map +1 -1
- package/field-tests/checkbox.tests.d.ts +1 -1
- package/field-tests/checkbox.tests.js +18 -15
- package/field-tests/checkbox.tests.js.map +1 -1
- package/field-tests/datepicker.tests.d.ts +1 -1
- package/field-tests/datepicker.tests.js +18 -15
- package/field-tests/datepicker.tests.js.map +1 -1
- package/field-tests/field-test-factory.d.ts +1 -1
- package/field-tests/field-test-factory.js +28 -24
- package/field-tests/field-test-factory.js.map +1 -1
- package/field-tests/file-upload.tests.d.ts +1 -1
- package/field-tests/file-upload.tests.js +15 -12
- package/field-tests/file-upload.tests.js.map +1 -1
- package/field-tests/input.tests.d.ts +1 -1
- package/field-tests/input.tests.js +39 -36
- package/field-tests/input.tests.js.map +1 -1
- package/field-tests/select.tests.d.ts +1 -1
- package/field-tests/select.tests.js +21 -18
- package/field-tests/select.tests.js.map +1 -1
- package/field-tests/slider.tests.d.ts +1 -1
- package/field-tests/slider.tests.js +13 -10
- package/field-tests/slider.tests.js.map +1 -1
- package/field-tests/textarea.tests.d.ts +1 -1
- package/field-tests/textarea.tests.js +15 -12
- package/field-tests/textarea.tests.js.map +1 -1
- package/field-tests/unit-conversion.tests.d.ts +1 -1
- package/field-tests/unit-conversion.tests.js +13 -10
- package/field-tests/unit-conversion.tests.js.map +1 -1
- package/helpers/form.helpers.js +34 -16
- package/helpers/form.helpers.js.map +1 -1
- package/index.js +30 -14
- package/index.js.map +1 -1
- package/package.json +6 -1
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# @tft/crispr-forms-testing
|
|
2
|
+
|
|
3
|
+
Playwright helpers and test factories for apps built with [`@tft/crispr-forms`](../crispr-forms/README.md).
|
|
4
|
+
|
|
5
|
+
Point it at the same `FormConfig` your component renders and it generates a standard set of e2e tests for every field, covering labels, hints, options and validation errors. When you need to write your own assertions, it also provides locators and actions that already handle Angular Material's quirks.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install --save-dev @tft/crispr-forms-testing
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Peer dependencies: `@playwright/test` (>= 1.40), `@angular/compiler` and `@angular/forms` (^21), and `@tft/crispr-forms`.
|
|
14
|
+
|
|
15
|
+
The package re-exports everything from `@playwright/test`, so a spec file needs only one import:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { test, expect, testForm, getInput } from '@tft/crispr-forms-testing';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Importing the package also loads `@angular/compiler`. crispr-forms ships in partial-compilation (Ivy linker) format, and its configs need the JIT compiler to load in Node. If a Playwright file imports a crispr-forms config **without** importing this package, add `import '@angular/compiler';` at the top of it.
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
Keep your form config in its own file so the component and the spec can share it:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
// profile.config.ts
|
|
29
|
+
export const profileFormConfig: FormConfig = {
|
|
30
|
+
fields: [
|
|
31
|
+
{ controlType: ControlType.INPUT, controlName: 'name', label: 'Name', validators: [Validators.required] },
|
|
32
|
+
{ controlType: ControlType.SELECT, controlName: 'role', label: 'Role', options: [/* ... */] },
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Then generate the tests from it:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// profile.spec.ts
|
|
41
|
+
import { test, testForm } from '@tft/crispr-forms-testing';
|
|
42
|
+
import { profileFormConfig } from '../app/profile/profile.config';
|
|
43
|
+
|
|
44
|
+
test.describe('Profile form', () => {
|
|
45
|
+
test.beforeEach(async ({ page }) => {
|
|
46
|
+
await page.goto('/profile');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
testForm(profileFormConfig);
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Because the tests are derived from the config, a validator added there is tested automatically.
|
|
54
|
+
|
|
55
|
+
To cover just some fields, call `testField` per field instead:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
for (const field of profileFormConfig.fields) {
|
|
59
|
+
testField(field);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Generated tests
|
|
64
|
+
|
|
65
|
+
`testForm(config)` calls `testField` for every top-level field. `testField(config)` opens a `test.describe` named after the `controlName` and picks the factory for its `controlType`. Each factory only registers tests for what the config defines; a field without a `hint`, for example, gets no hint test.
|
|
66
|
+
|
|
67
|
+
| Control type | Factory | Tests |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `INPUT` | `testInputField` | label, input type, placeholder, hint, required, min, max, maxLength |
|
|
70
|
+
| `TEXTAREA` | `testTextareaField` | label, hint, required |
|
|
71
|
+
| `CHECKBOX` | `testCheckboxField` | label, `requiredTrue` error |
|
|
72
|
+
| `SELECT` | `testSelectField` | label, hint, option count, selecting an option, required |
|
|
73
|
+
| `DATEPICKER` | `testDatepickerField` | label, hint, toggle button, required |
|
|
74
|
+
| `SLIDER` | `testSliderField` | renders, label, min, max |
|
|
75
|
+
| `AUTOCOMPLETE` | `testAutocompleteField` | label, hint, dropdown, selecting an option, required |
|
|
76
|
+
| `AUTOCOMPLETE_CHIPLIST` | `testAutocompleteChiplistField` | label, hint, typing shows options, add chip, remove chip, `minArrayLength` error |
|
|
77
|
+
| `FILE_UPLOAD` | `testFileUploadField` | label, drop zone text, select button |
|
|
78
|
+
| `UNIT_CONVERSION` | `testUnitConversionField` | label, number input, unit select |
|
|
79
|
+
|
|
80
|
+
`testForm` and `testField` do **not** cover:
|
|
81
|
+
|
|
82
|
+
- Fields without a `controlName` (headings, dividers, buttons). These are skipped.
|
|
83
|
+
- `SUB_GROUP` and `GROUP_LIST` children. Nested fields aren't walked; test them with the helpers below.
|
|
84
|
+
- `IMAGE_UPLOAD` and `MAP`. These have no factory yet.
|
|
85
|
+
|
|
86
|
+
The individual factories are exported too, if you want one suite for one field.
|
|
87
|
+
|
|
88
|
+
## Test ids
|
|
89
|
+
|
|
90
|
+
Every crispr-forms control renders a `data-testid` set to its control path. All helpers take this path as their `testId` argument.
|
|
91
|
+
|
|
92
|
+
| Field | `data-testid` |
|
|
93
|
+
|---|---|
|
|
94
|
+
| Top-level `firstName` | `firstName` |
|
|
95
|
+
| `firstName` in sub group `address` | `address.firstName` |
|
|
96
|
+
| `firstName` in group list `contacts`, item 2 | `contacts[2].firstName` |
|
|
97
|
+
| Nested in both | `contacts[2].address.city` |
|
|
98
|
+
|
|
99
|
+
Fields without a `controlName`, such as buttons, have no test id by default. Set `testId` on the field config to add one. Any parent prefix still applies, so `testId: 'save'` inside sub group `actions` renders `actions.save`.
|
|
100
|
+
|
|
101
|
+
## Helpers
|
|
102
|
+
|
|
103
|
+
### Locators
|
|
104
|
+
|
|
105
|
+
| Helper | Returns |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `getMatFormField(page, testId)` | The element carrying the `data-testid`. Usually a `mat-form-field`, a `mat-checkbox` or a `mat-slider`. |
|
|
108
|
+
| `getInput(page, testId)` | The `input` inside the field |
|
|
109
|
+
| `getLabel(page, testId)` | The `mat-label` inside the field |
|
|
110
|
+
| `getHint(page, testId)` | The `mat-hint` inside the field |
|
|
111
|
+
| `getMatSelect(page, testId)` | The `mat-select` inside a select field |
|
|
112
|
+
| `getCheckbox(page, testId)` | The `mat-checkbox` element |
|
|
113
|
+
| `getAutocompleteInput(page, testId)` | The visible text input of an autocomplete or chiplist |
|
|
114
|
+
| `getError(page, testId, fieldType = 'input')` | The displayed error message. Pass the field's kebab-case type for non-input fields: `'select'`, `'textarea'`, `'checkbox'`, `'datepicker'`, `'autocomplete'` or `'autocomplete-chiplist'`. |
|
|
115
|
+
|
|
116
|
+
### Actions
|
|
117
|
+
|
|
118
|
+
| Helper | Does |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `touchInput(page, testId)` | Clicks the input, then Tabs away to blur it |
|
|
121
|
+
| `touchSelect(page, testId)` | Opens the select, presses Escape, then Tabs away |
|
|
122
|
+
| `touchCheckbox(page, testId)` | Clicks twice, so the box ends unchecked, then Tabs away |
|
|
123
|
+
| `touchAutocomplete(page, testId)` | Clicks the visible input, then Tabs away |
|
|
124
|
+
| `touchDatepicker(page, testId)` | Clicks the input, then Tabs away |
|
|
125
|
+
| `openSelect(page, testId)` | Opens the select panel |
|
|
126
|
+
| `selectOption(page, testId, optionLabel)` | Opens the select and picks the option with that label |
|
|
127
|
+
| `submitForm(page, nth = 0)` | Submits the `nth` crispr form on the page, which shows every control's errors |
|
|
128
|
+
|
|
129
|
+
### Example
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { test, expect, getInput, getError, touchInput, submitForm } from '@tft/crispr-forms-testing';
|
|
133
|
+
|
|
134
|
+
test('name is required', async ({ page }) => {
|
|
135
|
+
await page.goto('/profile');
|
|
136
|
+
|
|
137
|
+
await touchInput(page, 'name');
|
|
138
|
+
await expect(getError(page, 'name')).toHaveText('This field is required');
|
|
139
|
+
|
|
140
|
+
await getInput(page, 'name').fill('Ada');
|
|
141
|
+
await expect(getError(page, 'name')).toHaveCount(0);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('nested list item shows its errors on submit', async ({ page }) => {
|
|
145
|
+
await page.goto('/profile');
|
|
146
|
+
await submitForm(page);
|
|
147
|
+
await expect(getError(page, 'contacts[0].email')).toBeVisible();
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Gotchas
|
|
152
|
+
|
|
153
|
+
**Showing errors on autocomplete and chiplist fields.** Their validators are on a hidden control, not on the visible input, so blurring the input never touches the validated control. Call `submitForm(page)` instead: it forces every control in the form to display its errors.
|
|
154
|
+
|
|
155
|
+
**Why `submitForm` doesn't click.** crispr-forms submits through a hidden `display: none` button, which Playwright can't click. `submitForm` clicks it from inside the page with `page.evaluate`.
|
|
156
|
+
|
|
157
|
+
**Select fields need a Tab after Escape.** Escape closes the panel but leaves the select focused. Errors appear on blur, so `touchSelect` also presses Tab.
|
|
158
|
+
|
|
159
|
+
**`getError` targets the message, not the host.** The `tft-control-error` element is always in the DOM, even with no error, and autocomplete/chiplist fields render two of them. `getError` matches the inner `p.tft-control-error`, which only exists while a message is shown. That keeps Playwright's strict mode happy, and `toHaveCount(0)` reliably asserts "no error".
|
|
160
|
+
|
|
161
|
+
**Slider labels and bounds.** Sliders aren't inside a `mat-form-field`, so their label is a plain `<label>` and `getLabel` won't find it; use `getMatFormField(page, id).locator('label')`. Read min and max from the native `min`/`max` attributes on `input[matSliderThumb]`, not from `aria-valuemin`/`aria-valuemax`.
|
|
162
|
+
|
|
163
|
+
**Unit conversion `required` can't be tested.** The visible input is a separate control from the one that holds the validators, so a required error never shows on the field.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AutocompleteChiplistFieldConfig } from '@tft/crispr-forms
|
|
1
|
+
import type { AutocompleteChiplistFieldConfig } from '@tft/crispr-forms';
|
|
2
2
|
/**
|
|
3
3
|
* Registers Playwright tests for an AUTOCOMPLETE_CHIPLIST field config.
|
|
4
4
|
* Call inside a `test.describe` block that has already navigated to the page.
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.testAutocompleteChiplistField = testAutocompleteChiplistField;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
const form_helpers_1 = require("../helpers/form.helpers");
|
|
3
6
|
/**
|
|
4
7
|
* Registers Playwright tests for an AUTOCOMPLETE_CHIPLIST field config.
|
|
5
8
|
* Call inside a `test.describe` block that has already navigated to the page.
|
|
@@ -8,44 +11,44 @@ import { getLabel, getHint, getError, getMatFormField, submitForm } from '../hel
|
|
|
8
11
|
* the hidden `[formControlName]` anchor is what holds validators. Blurring the
|
|
9
12
|
* visible search input does not touch the hidden control.
|
|
10
13
|
*/
|
|
11
|
-
|
|
14
|
+
function testAutocompleteChiplistField(config) {
|
|
12
15
|
if (config.label) {
|
|
13
|
-
test('renders label', async ({ page }) => {
|
|
14
|
-
await expect(getLabel(page, config.controlName)).toHaveText(config.label);
|
|
16
|
+
(0, test_1.test)('renders label', async ({ page }) => {
|
|
17
|
+
await (0, test_1.expect)((0, form_helpers_1.getLabel)(page, config.controlName)).toHaveText(config.label);
|
|
15
18
|
});
|
|
16
19
|
}
|
|
17
20
|
if (config.hint) {
|
|
18
|
-
test('shows hint when untouched', async ({ page }) => {
|
|
19
|
-
await expect(getHint(page, config.controlName)).toBeVisible();
|
|
21
|
+
(0, test_1.test)('shows hint when untouched', async ({ page }) => {
|
|
22
|
+
await (0, test_1.expect)((0, form_helpers_1.getHint)(page, config.controlName)).toBeVisible();
|
|
20
23
|
});
|
|
21
24
|
}
|
|
22
|
-
test('can type and see options', async ({ page }) => {
|
|
23
|
-
const searchInput = getMatFormField(page, config.controlName).locator('input[matInput]');
|
|
25
|
+
(0, test_1.test)('can type and see options', async ({ page }) => {
|
|
26
|
+
const searchInput = (0, form_helpers_1.getMatFormField)(page, config.controlName).locator('input[matInput]');
|
|
24
27
|
await searchInput.fill('Option');
|
|
25
28
|
const options = page.locator('mat-option');
|
|
26
|
-
await expect(options.first()).toBeVisible();
|
|
29
|
+
await (0, test_1.expect)(options.first()).toBeVisible();
|
|
27
30
|
});
|
|
28
|
-
test('can add a chip by selecting an option', async ({ page }) => {
|
|
29
|
-
const searchInput = getMatFormField(page, config.controlName).locator('input[matInput]');
|
|
31
|
+
(0, test_1.test)('can add a chip by selecting an option', async ({ page }) => {
|
|
32
|
+
const searchInput = (0, form_helpers_1.getMatFormField)(page, config.controlName).locator('input[matInput]');
|
|
30
33
|
await searchInput.fill('Option A');
|
|
31
34
|
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');
|
|
35
|
+
const chip = (0, form_helpers_1.getMatFormField)(page, config.controlName).locator('mat-chip-row').first();
|
|
36
|
+
await (0, test_1.expect)(chip).toContainText('Option A');
|
|
34
37
|
});
|
|
35
|
-
test('can remove a chip', async ({ page }) => {
|
|
36
|
-
const searchInput = getMatFormField(page, config.controlName).locator('input[matInput]');
|
|
38
|
+
(0, test_1.test)('can remove a chip', async ({ page }) => {
|
|
39
|
+
const searchInput = (0, form_helpers_1.getMatFormField)(page, config.controlName).locator('input[matInput]');
|
|
37
40
|
await searchInput.fill('Option A');
|
|
38
41
|
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();
|
|
42
|
+
const removeBtn = (0, form_helpers_1.getMatFormField)(page, config.controlName).locator('mat-chip-row mat-icon[matChipRemove]').first();
|
|
40
43
|
if (await removeBtn.isVisible()) {
|
|
41
44
|
await removeBtn.click();
|
|
42
|
-
const chips = getMatFormField(page, config.controlName).locator('mat-chip-row');
|
|
43
|
-
await expect(chips).toHaveCount(0);
|
|
45
|
+
const chips = (0, form_helpers_1.getMatFormField)(page, config.controlName).locator('mat-chip-row');
|
|
46
|
+
await (0, test_1.expect)(chips).toHaveCount(0);
|
|
44
47
|
}
|
|
45
48
|
});
|
|
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
|
+
(0, test_1.test)('shows error when not enough chips selected', async ({ page }) => {
|
|
50
|
+
await (0, form_helpers_1.submitForm)(page);
|
|
51
|
+
await (0, test_1.expect)((0, form_helpers_1.getError)(page, config.controlName, 'autocomplete-chiplist')).toBeVisible();
|
|
49
52
|
});
|
|
50
53
|
}
|
|
51
54
|
//# sourceMappingURL=autocomplete-chiplist.tests.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autocomplete-chiplist.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/autocomplete-chiplist.tests.ts"],"names":[],"mappings":"
|
|
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';\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"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AutocompleteFieldConfig } from '@tft/crispr-forms
|
|
1
|
+
import type { AutocompleteFieldConfig } from '@tft/crispr-forms';
|
|
2
2
|
/**
|
|
3
3
|
* Registers Playwright tests for an AUTOCOMPLETE field config.
|
|
4
4
|
* Call inside a `test.describe` block that has already navigated to the page.
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.testAutocompleteField = testAutocompleteField;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
const forms_1 = require("@angular/forms");
|
|
6
|
+
const form_helpers_1 = require("../helpers/form.helpers");
|
|
4
7
|
/**
|
|
5
8
|
* Registers Playwright tests for an AUTOCOMPLETE field config.
|
|
6
9
|
* Call inside a `test.describe` block that has already navigated to the page.
|
|
@@ -10,32 +13,32 @@ import { getLabel, getHint, getError, getAutocompleteInput, submitForm } from '.
|
|
|
10
13
|
* the visible input does not touch the hidden control. `submitForm()` fires
|
|
11
14
|
* `ControlErrorsFormDirective.submit$` which forces all controls to show errors.
|
|
12
15
|
*/
|
|
13
|
-
|
|
16
|
+
function testAutocompleteField(config) {
|
|
14
17
|
if (config.label) {
|
|
15
|
-
test('renders label', async ({ page }) => {
|
|
16
|
-
await expect(getLabel(page, config.controlName)).toHaveText(config.label);
|
|
18
|
+
(0, test_1.test)('renders label', async ({ page }) => {
|
|
19
|
+
await (0, test_1.expect)((0, form_helpers_1.getLabel)(page, config.controlName)).toHaveText(config.label);
|
|
17
20
|
});
|
|
18
21
|
}
|
|
19
22
|
if (config.hint) {
|
|
20
|
-
test('shows hint when untouched', async ({ page }) => {
|
|
21
|
-
await expect(getHint(page, config.controlName)).toBeVisible();
|
|
23
|
+
(0, test_1.test)('shows hint when untouched', async ({ page }) => {
|
|
24
|
+
await (0, test_1.expect)((0, form_helpers_1.getHint)(page, config.controlName)).toBeVisible();
|
|
22
25
|
});
|
|
23
26
|
}
|
|
24
|
-
test('shows dropdown options when typing', async ({ page }) => {
|
|
25
|
-
await getAutocompleteInput(page, config.controlName).fill('Option');
|
|
27
|
+
(0, test_1.test)('shows dropdown options when typing', async ({ page }) => {
|
|
28
|
+
await (0, form_helpers_1.getAutocompleteInput)(page, config.controlName).fill('Option');
|
|
26
29
|
const options = page.locator('mat-option');
|
|
27
|
-
await expect(options.first()).toBeVisible();
|
|
30
|
+
await (0, test_1.expect)(options.first()).toBeVisible();
|
|
28
31
|
});
|
|
29
|
-
test('can select an autocomplete option', async ({ page }) => {
|
|
30
|
-
await getAutocompleteInput(page, config.controlName).fill('Option A');
|
|
32
|
+
(0, test_1.test)('can select an autocomplete option', async ({ page }) => {
|
|
33
|
+
await (0, form_helpers_1.getAutocompleteInput)(page, config.controlName).fill('Option A');
|
|
31
34
|
await page.locator('mat-option').filter({ hasText: 'Option A' }).click();
|
|
32
|
-
await expect(getAutocompleteInput(page, config.controlName)).toHaveValue('Option A');
|
|
35
|
+
await (0, test_1.expect)((0, form_helpers_1.getAutocompleteInput)(page, config.controlName)).toHaveValue('Option A');
|
|
33
36
|
});
|
|
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();
|
|
37
|
+
if (config.validators?.includes(forms_1.Validators.required)) {
|
|
38
|
+
(0, test_1.test)('shows required error when form submitted empty', async ({ page }) => {
|
|
39
|
+
await (0, form_helpers_1.getAutocompleteInput)(page, config.controlName).fill('');
|
|
40
|
+
await (0, form_helpers_1.submitForm)(page);
|
|
41
|
+
await (0, test_1.expect)((0, form_helpers_1.getError)(page, config.controlName, 'autocomplete')).toBeVisible();
|
|
39
42
|
});
|
|
40
43
|
}
|
|
41
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autocomplete.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/autocomplete.tests.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"autocomplete.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/autocomplete.tests.ts"],"names":[],"mappings":";;AAcA,sDAgCC;AA9CD,2CAAgD;AAEhD,0CAA4C;AAC5C,0DAAwG;AAExG;;;;;;;;GAQG;AACH,SAAgB,qBAAqB,CAAC,MAA+B;IACnE,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,IAAA,WAAI,EAAC,eAAe,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACvC,MAAM,IAAA,aAAM,EAAC,IAAA,uBAAQ,EAAC,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,IAAA,WAAI,EAAC,2BAA2B,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACnD,MAAM,IAAA,aAAM,EAAC,IAAA,sBAAO,EAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAChE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAA,WAAI,EAAC,oCAAoC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QAC5D,MAAM,IAAA,mCAAoB,EAAC,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,IAAA,aAAM,EAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAA,WAAI,EAAC,mCAAmC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QAC3D,MAAM,IAAA,mCAAoB,EAAC,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,IAAA,aAAM,EAAC,IAAA,mCAAoB,EAAC,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,kBAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrD,IAAA,WAAI,EAAC,gDAAgD,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACxE,MAAM,IAAA,mCAAoB,EAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC9D,MAAM,IAAA,yBAAU,EAAC,IAAI,CAAC,CAAC;YACvB,MAAM,IAAA,aAAM,EAAC,IAAA,uBAAQ,EAAC,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';\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"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CheckboxFieldConfig } from '@tft/crispr-forms
|
|
1
|
+
import type { CheckboxFieldConfig } from '@tft/crispr-forms';
|
|
2
2
|
/**
|
|
3
3
|
* Registers Playwright tests for a CHECKBOX field config.
|
|
4
4
|
* Call inside a `test.describe` block that has already navigated to the page.
|
|
@@ -1,26 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.testCheckboxField = testCheckboxField;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
const forms_1 = require("@angular/forms");
|
|
6
|
+
const form_helpers_1 = require("../helpers/form.helpers");
|
|
4
7
|
/**
|
|
5
8
|
* Registers Playwright tests for a CHECKBOX field config.
|
|
6
9
|
* Call inside a `test.describe` block that has already navigated to the page.
|
|
7
10
|
*/
|
|
8
|
-
|
|
9
|
-
test('renders label', async ({ page }) => {
|
|
10
|
-
const label = getCheckbox(page, config.controlName).locator('.mdc-label');
|
|
11
|
-
await expect(label).toContainText(config.label);
|
|
11
|
+
function testCheckboxField(config) {
|
|
12
|
+
(0, test_1.test)('renders label', async ({ page }) => {
|
|
13
|
+
const label = (0, form_helpers_1.getCheckbox)(page, config.controlName).locator('.mdc-label');
|
|
14
|
+
await (0, test_1.expect)(label).toContainText(config.label);
|
|
12
15
|
});
|
|
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();
|
|
16
|
+
if (config.validators?.includes(forms_1.Validators.requiredTrue)) {
|
|
17
|
+
(0, test_1.test)('shows error when unchecked and blurred', async ({ page }) => {
|
|
18
|
+
await (0, form_helpers_1.getCheckbox)(page, config.controlName).click();
|
|
19
|
+
await (0, form_helpers_1.getCheckbox)(page, config.controlName).click();
|
|
17
20
|
await page.keyboard.press('Tab');
|
|
18
|
-
await expect(getError(page, config.controlName, 'checkbox')).toBeVisible();
|
|
21
|
+
await (0, test_1.expect)((0, form_helpers_1.getError)(page, config.controlName, 'checkbox')).toBeVisible();
|
|
19
22
|
});
|
|
20
|
-
test('no error when checked', async ({ page }) => {
|
|
21
|
-
await getCheckbox(page, config.controlName).click();
|
|
23
|
+
(0, test_1.test)('no error when checked', async ({ page }) => {
|
|
24
|
+
await (0, form_helpers_1.getCheckbox)(page, config.controlName).click();
|
|
22
25
|
await page.keyboard.press('Tab');
|
|
23
|
-
await expect(getError(page, config.controlName, 'checkbox')).not.toBeVisible();
|
|
26
|
+
await (0, test_1.expect)((0, form_helpers_1.getError)(page, config.controlName, 'checkbox')).not.toBeVisible();
|
|
24
27
|
});
|
|
25
28
|
}
|
|
26
29
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkbox.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/checkbox.tests.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"checkbox.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/checkbox.tests.ts"],"names":[],"mappings":";;AASA,8CAoBC;AA7BD,2CAAgD;AAEhD,0CAA4C;AAC5C,0DAAgE;AAEhE;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,MAA2B;IAC3D,IAAA,WAAI,EAAC,eAAe,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACvC,MAAM,KAAK,GAAG,IAAA,0BAAW,EAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC1E,MAAM,IAAA,aAAM,EAAC,KAAK,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,kBAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACzD,IAAA,WAAI,EAAC,wCAAwC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YAChE,MAAM,IAAA,0BAAW,EAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,CAAC;YACpD,MAAM,IAAA,0BAAW,EAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,CAAC;YACpD,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACjC,MAAM,IAAA,aAAM,EAAC,IAAA,uBAAQ,EAAC,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7E,CAAC,CAAC,CAAC;QAEH,IAAA,WAAI,EAAC,uBAAuB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YAC/C,MAAM,IAAA,0BAAW,EAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,CAAC;YACpD,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACjC,MAAM,IAAA,aAAM,EAAC,IAAA,uBAAQ,EAAC,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';\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"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DatepickerFieldConfig } from '@tft/crispr-forms
|
|
1
|
+
import type { DatepickerFieldConfig } from '@tft/crispr-forms';
|
|
2
2
|
/**
|
|
3
3
|
* Registers Playwright tests for a DATEPICKER field config.
|
|
4
4
|
* Call inside a `test.describe` block that has already navigated to the page.
|
|
@@ -1,29 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.testDatepickerField = testDatepickerField;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
const forms_1 = require("@angular/forms");
|
|
6
|
+
const form_helpers_1 = require("../helpers/form.helpers");
|
|
4
7
|
/**
|
|
5
8
|
* Registers Playwright tests for a DATEPICKER field config.
|
|
6
9
|
* Call inside a `test.describe` block that has already navigated to the page.
|
|
7
10
|
*/
|
|
8
|
-
|
|
11
|
+
function testDatepickerField(config) {
|
|
9
12
|
if (config.label) {
|
|
10
|
-
test('renders label', async ({ page }) => {
|
|
11
|
-
await expect(getLabel(page, config.controlName)).toHaveText(config.label);
|
|
13
|
+
(0, test_1.test)('renders label', async ({ page }) => {
|
|
14
|
+
await (0, test_1.expect)((0, form_helpers_1.getLabel)(page, config.controlName)).toHaveText(config.label);
|
|
12
15
|
});
|
|
13
16
|
}
|
|
14
17
|
if (config.hint) {
|
|
15
|
-
test('shows hint when untouched', async ({ page }) => {
|
|
16
|
-
await expect(getHint(page, config.controlName)).toBeVisible();
|
|
18
|
+
(0, test_1.test)('shows hint when untouched', async ({ page }) => {
|
|
19
|
+
await (0, test_1.expect)((0, form_helpers_1.getHint)(page, config.controlName)).toBeVisible();
|
|
17
20
|
});
|
|
18
21
|
}
|
|
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
|
+
(0, test_1.test)('renders datepicker toggle button', async ({ page }) => {
|
|
23
|
+
const toggle = (0, form_helpers_1.getMatFormField)(page, config.controlName).locator('mat-datepicker-toggle');
|
|
24
|
+
await (0, test_1.expect)(toggle).toBeVisible();
|
|
22
25
|
});
|
|
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();
|
|
26
|
+
if (config.validators?.includes(forms_1.Validators.required)) {
|
|
27
|
+
(0, test_1.test)('shows required error when blurred without date', async ({ page }) => {
|
|
28
|
+
await (0, form_helpers_1.touchDatepicker)(page, config.controlName);
|
|
29
|
+
await (0, test_1.expect)((0, form_helpers_1.getError)(page, config.controlName, 'datepicker')).toBeVisible();
|
|
27
30
|
});
|
|
28
31
|
}
|
|
29
32
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datepicker.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/datepicker.tests.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"datepicker.tests.js","sourceRoot":"","sources":["../../../../libs/crispr-forms-testing/src/field-tests/datepicker.tests.ts"],"names":[],"mappings":";;AASA,kDAwBC;AAjCD,2CAAgD;AAEhD,0CAA4C;AAC5C,0DAAwG;AAExG;;;GAGG;AACH,SAAgB,mBAAmB,CAAC,MAA6B;IAC/D,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,IAAA,WAAI,EAAC,eAAe,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACvC,MAAM,IAAA,aAAM,EAAC,IAAA,uBAAQ,EAAC,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,IAAA,WAAI,EAAC,2BAA2B,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACnD,MAAM,IAAA,aAAM,EAAC,IAAA,sBAAO,EAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAChE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAA,WAAI,EAAC,kCAAkC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QAC1D,MAAM,MAAM,GAAG,IAAA,8BAAe,EAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;QAC1F,MAAM,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,kBAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrD,IAAA,WAAI,EAAC,gDAAgD,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YACxE,MAAM,IAAA,8BAAe,EAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;YAChD,MAAM,IAAA,aAAM,EAAC,IAAA,uBAAQ,EAAC,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';\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"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FormConfig, AnyFieldConfig } from '@tft/crispr-forms
|
|
1
|
+
import type { FormConfig, AnyFieldConfig } from '@tft/crispr-forms';
|
|
2
2
|
/**
|
|
3
3
|
* Registers a `test.describe` block for a field config, automatically selecting
|
|
4
4
|
* the correct test suite based on `controlType`.
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.testField = testField;
|
|
4
|
+
exports.testForm = testForm;
|
|
5
|
+
const test_1 = require("@playwright/test");
|
|
6
|
+
const input_tests_1 = require("./input.tests");
|
|
7
|
+
const textarea_tests_1 = require("./textarea.tests");
|
|
8
|
+
const checkbox_tests_1 = require("./checkbox.tests");
|
|
9
|
+
const select_tests_1 = require("./select.tests");
|
|
10
|
+
const slider_tests_1 = require("./slider.tests");
|
|
11
|
+
const datepicker_tests_1 = require("./datepicker.tests");
|
|
12
|
+
const autocomplete_tests_1 = require("./autocomplete.tests");
|
|
13
|
+
const autocomplete_chiplist_tests_1 = require("./autocomplete-chiplist.tests");
|
|
14
|
+
const file_upload_tests_1 = require("./file-upload.tests");
|
|
15
|
+
const unit_conversion_tests_1 = require("./unit-conversion.tests");
|
|
12
16
|
/**
|
|
13
17
|
* Registers a `test.describe` block for a field config, automatically selecting
|
|
14
18
|
* the correct test suite based on `controlType`.
|
|
@@ -28,40 +32,40 @@ import { testUnitConversionField } from './unit-conversion.tests';
|
|
|
28
32
|
* });
|
|
29
33
|
* ```
|
|
30
34
|
*/
|
|
31
|
-
|
|
35
|
+
function testField(config) {
|
|
32
36
|
if (!('controlName' in config))
|
|
33
37
|
return;
|
|
34
|
-
test.describe(config.controlName, () => {
|
|
38
|
+
test_1.test.describe(config.controlName, () => {
|
|
35
39
|
switch (config.controlType) {
|
|
36
40
|
case 'input':
|
|
37
|
-
testInputField(config);
|
|
41
|
+
(0, input_tests_1.testInputField)(config);
|
|
38
42
|
break;
|
|
39
43
|
case 'textarea':
|
|
40
|
-
testTextareaField(config);
|
|
44
|
+
(0, textarea_tests_1.testTextareaField)(config);
|
|
41
45
|
break;
|
|
42
46
|
case 'checkbox':
|
|
43
|
-
testCheckboxField(config);
|
|
47
|
+
(0, checkbox_tests_1.testCheckboxField)(config);
|
|
44
48
|
break;
|
|
45
49
|
case 'select':
|
|
46
|
-
testSelectField(config);
|
|
50
|
+
(0, select_tests_1.testSelectField)(config);
|
|
47
51
|
break;
|
|
48
52
|
case 'slider':
|
|
49
|
-
testSliderField(config);
|
|
53
|
+
(0, slider_tests_1.testSliderField)(config);
|
|
50
54
|
break;
|
|
51
55
|
case 'datepicker':
|
|
52
|
-
testDatepickerField(config);
|
|
56
|
+
(0, datepicker_tests_1.testDatepickerField)(config);
|
|
53
57
|
break;
|
|
54
58
|
case 'autocomplete':
|
|
55
|
-
testAutocompleteField(config);
|
|
59
|
+
(0, autocomplete_tests_1.testAutocompleteField)(config);
|
|
56
60
|
break;
|
|
57
61
|
case 'autocompleteChiplist':
|
|
58
|
-
testAutocompleteChiplistField(config);
|
|
62
|
+
(0, autocomplete_chiplist_tests_1.testAutocompleteChiplistField)(config);
|
|
59
63
|
break;
|
|
60
64
|
case 'fileUpload':
|
|
61
|
-
testFileUploadField(config);
|
|
65
|
+
(0, file_upload_tests_1.testFileUploadField)(config);
|
|
62
66
|
break;
|
|
63
67
|
case 'unitConversion':
|
|
64
|
-
testUnitConversionField(config);
|
|
68
|
+
(0, unit_conversion_tests_1.testUnitConversionField)(config);
|
|
65
69
|
break;
|
|
66
70
|
}
|
|
67
71
|
});
|
|
@@ -78,7 +82,7 @@ export function testField(config) {
|
|
|
78
82
|
* });
|
|
79
83
|
* ```
|
|
80
84
|
*/
|
|
81
|
-
|
|
85
|
+
function testForm(config) {
|
|
82
86
|
for (const field of config.fields) {
|
|
83
87
|
testField(field);
|
|
84
88
|
}
|