ngwr 11.1.0 → 11.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/circular-text/styles/_index.scss +11 -0
- package/command-palette/styles/_index.scss +12 -0
- package/fesm2022/ngwr-action-sheet-testing.mjs +313 -0
- package/fesm2022/ngwr-action-sheet-testing.mjs.map +1 -0
- package/fesm2022/ngwr-bar-chart-testing.mjs +89 -0
- package/fesm2022/ngwr-bar-chart-testing.mjs.map +1 -0
- package/fesm2022/ngwr-blur-text-testing.mjs +203 -0
- package/fesm2022/ngwr-blur-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-blur-text.mjs +15 -1
- package/fesm2022/ngwr-blur-text.mjs.map +1 -1
- package/fesm2022/ngwr-border-glow-testing.mjs +325 -0
- package/fesm2022/ngwr-border-glow-testing.mjs.map +1 -0
- package/fesm2022/ngwr-calendar-heatmap-testing.mjs +95 -0
- package/fesm2022/ngwr-calendar-heatmap-testing.mjs.map +1 -0
- package/fesm2022/ngwr-calendar-testing.mjs +368 -0
- package/fesm2022/ngwr-calendar-testing.mjs.map +1 -0
- package/fesm2022/ngwr-calendar.mjs +43 -3
- package/fesm2022/ngwr-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-cascader.mjs +12 -0
- package/fesm2022/ngwr-cascader.mjs.map +1 -1
- package/fesm2022/ngwr-checkbox.mjs +8 -0
- package/fesm2022/ngwr-checkbox.mjs.map +1 -1
- package/fesm2022/ngwr-circular-text-testing.mjs +230 -0
- package/fesm2022/ngwr-circular-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-circular-text.mjs +2 -2
- package/fesm2022/ngwr-circular-text.mjs.map +1 -1
- package/fesm2022/ngwr-color-picker-testing.mjs +432 -0
- package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -0
- package/fesm2022/ngwr-color-picker.mjs +29 -3
- package/fesm2022/ngwr-color-picker.mjs.map +1 -1
- package/fesm2022/ngwr-context-menu.mjs +25 -5
- package/fesm2022/ngwr-context-menu.mjs.map +1 -1
- package/fesm2022/ngwr-date-picker-testing.mjs +10 -79
- package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
- package/fesm2022/ngwr-decrypt-text-testing.mjs +261 -0
- package/fesm2022/ngwr-decrypt-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-decrypt-text.mjs +6 -1
- package/fesm2022/ngwr-decrypt-text.mjs.map +1 -1
- package/fesm2022/ngwr-donut-chart-testing.mjs +93 -0
- package/fesm2022/ngwr-donut-chart-testing.mjs.map +1 -0
- package/fesm2022/ngwr-drawer.mjs +36 -7
- package/fesm2022/ngwr-drawer.mjs.map +1 -1
- package/fesm2022/ngwr-dropdown.mjs +32 -2
- package/fesm2022/ngwr-dropdown.mjs.map +1 -1
- package/fesm2022/ngwr-event-calendar-testing.mjs +446 -0
- package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -0
- package/fesm2022/ngwr-event-calendar.mjs +43 -18
- package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
- package/fesm2022/ngwr-falling-text-testing.mjs +218 -0
- package/fesm2022/ngwr-falling-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-fuzzy-text-testing.mjs +134 -0
- package/fesm2022/ngwr-fuzzy-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-gauge-testing.mjs +86 -0
- package/fesm2022/ngwr-gauge-testing.mjs.map +1 -0
- package/fesm2022/ngwr-glitch-text-testing.mjs +181 -0
- package/fesm2022/ngwr-glitch-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-gradient-text-testing.mjs +223 -0
- package/fesm2022/ngwr-gradient-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-i18n-en.mjs +14 -0
- package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
- package/fesm2022/ngwr-i18n-ru.mjs +14 -0
- package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
- package/fesm2022/ngwr-image-cropper-testing.mjs +156 -0
- package/fesm2022/ngwr-image-cropper-testing.mjs.map +1 -0
- package/fesm2022/ngwr-image-cropper.mjs +10 -3
- package/fesm2022/ngwr-image-cropper.mjs.map +1 -1
- package/fesm2022/ngwr-input-number.mjs +73 -6
- package/fesm2022/ngwr-input-number.mjs.map +1 -1
- package/fesm2022/ngwr-knob-testing.mjs +220 -0
- package/fesm2022/ngwr-knob-testing.mjs.map +1 -0
- package/fesm2022/ngwr-lightbox-testing.mjs +223 -0
- package/fesm2022/ngwr-lightbox-testing.mjs.map +1 -0
- package/fesm2022/ngwr-lightbox.mjs +9 -2
- package/fesm2022/ngwr-lightbox.mjs.map +1 -1
- package/fesm2022/ngwr-line-chart-testing.mjs +119 -0
- package/fesm2022/ngwr-line-chart-testing.mjs.map +1 -0
- package/fesm2022/ngwr-marquee-testing.mjs +336 -0
- package/fesm2022/ngwr-marquee-testing.mjs.map +1 -0
- package/fesm2022/ngwr-meter-group-testing.mjs +80 -0
- package/fesm2022/ngwr-meter-group-testing.mjs.map +1 -0
- package/fesm2022/ngwr-overlay.mjs +28 -1
- package/fesm2022/ngwr-overlay.mjs.map +1 -1
- package/fesm2022/ngwr-popover.mjs +14 -0
- package/fesm2022/ngwr-popover.mjs.map +1 -1
- package/fesm2022/ngwr-rotating-text-testing.mjs +220 -0
- package/fesm2022/ngwr-rotating-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-rotating-text.mjs +2 -2
- package/fesm2022/ngwr-rotating-text.mjs.map +1 -1
- package/fesm2022/ngwr-select.mjs +70 -11
- package/fesm2022/ngwr-select.mjs.map +1 -1
- package/fesm2022/ngwr-shiny-text-testing.mjs +237 -0
- package/fesm2022/ngwr-shiny-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-slider.mjs +44 -6
- package/fesm2022/ngwr-slider.mjs.map +1 -1
- package/fesm2022/ngwr-sparkline-testing.mjs +76 -0
- package/fesm2022/ngwr-sparkline-testing.mjs.map +1 -0
- package/fesm2022/ngwr-speed-dial-testing.mjs +279 -0
- package/fesm2022/ngwr-speed-dial-testing.mjs.map +1 -0
- package/fesm2022/ngwr-splash-cursor-testing.mjs +89 -0
- package/fesm2022/ngwr-splash-cursor-testing.mjs.map +1 -0
- package/fesm2022/ngwr-split-text-testing.mjs +224 -0
- package/fesm2022/ngwr-split-text-testing.mjs.map +1 -0
- package/fesm2022/ngwr-split-text.mjs +15 -1
- package/fesm2022/ngwr-split-text.mjs.map +1 -1
- package/fesm2022/ngwr-splitter-testing.mjs +245 -0
- package/fesm2022/ngwr-splitter-testing.mjs.map +1 -0
- package/fesm2022/ngwr-spotlight-card-testing.mjs +368 -0
- package/fesm2022/ngwr-spotlight-card-testing.mjs.map +1 -0
- package/fesm2022/ngwr-star-border-testing.mjs +172 -0
- package/fesm2022/ngwr-star-border-testing.mjs.map +1 -0
- package/fesm2022/ngwr-table.mjs +13 -3
- package/fesm2022/ngwr-table.mjs.map +1 -1
- package/fesm2022/ngwr-tabs.mjs +15 -1
- package/fesm2022/ngwr-tabs.mjs.map +1 -1
- package/fesm2022/ngwr-tilt-card-testing.mjs +190 -0
- package/fesm2022/ngwr-tilt-card-testing.mjs.map +1 -0
- package/fesm2022/ngwr-tilt-card.mjs +19 -1
- package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
- package/fesm2022/ngwr-toast.mjs +36 -7
- package/fesm2022/ngwr-toast.mjs.map +1 -1
- package/fesm2022/ngwr-tour-testing.mjs +173 -0
- package/fesm2022/ngwr-tour-testing.mjs.map +1 -0
- package/fesm2022/ngwr-tour.mjs +2 -2
- package/fesm2022/ngwr-tour.mjs.map +1 -1
- package/fesm2022/ngwr-tree.mjs +10 -3
- package/fesm2022/ngwr-tree.mjs.map +1 -1
- package/fesm2022/ngwr-typewriter-testing.mjs +228 -0
- package/fesm2022/ngwr-typewriter-testing.mjs.map +1 -0
- package/fesm2022/ngwr-version.mjs +1 -1
- package/fesm2022/ngwr-version.mjs.map +1 -1
- package/fesm2022/ngwr-waves-testing.mjs +128 -0
- package/fesm2022/ngwr-waves-testing.mjs.map +1 -0
- package/fesm2022/ngwr-window-testing.mjs +361 -0
- package/fesm2022/ngwr-window-testing.mjs.map +1 -0
- package/fesm2022/ngwr-window.mjs +84 -10
- package/fesm2022/ngwr-window.mjs.map +1 -1
- package/i18n/en.json +16 -2
- package/i18n/ru.json +16 -2
- package/llms-full.txt +147 -0
- package/llms.txt +27 -8
- package/mcp/providers.js +29 -0
- package/mcp/tools.js +1 -29
- package/package.json +145 -1
- package/skills/ngwr/SKILL.md +115 -0
- package/skills/ngwr/references/catalog.md +212 -0
- package/skills/ngwr/references/setup.md +73 -0
- package/types/ngwr-action-sheet-testing.d.ts +260 -0
- package/types/ngwr-bar-chart-testing.d.ts +77 -0
- package/types/ngwr-blur-text-testing.d.ts +180 -0
- package/types/ngwr-blur-text.d.ts +12 -0
- package/types/ngwr-border-glow-testing.d.ts +219 -0
- package/types/ngwr-calendar-heatmap-testing.d.ts +79 -0
- package/types/ngwr-calendar-testing.d.ts +277 -0
- package/types/ngwr-calendar.d.ts +10 -0
- package/types/ngwr-checkbox.d.ts +13 -0
- package/types/ngwr-circular-text-testing.d.ts +196 -0
- package/types/ngwr-color-picker-testing.d.ts +291 -0
- package/types/ngwr-color-picker.d.ts +13 -1
- package/types/ngwr-context-menu.d.ts +15 -1
- package/types/ngwr-date-picker-testing.d.ts +6 -66
- package/types/ngwr-decrypt-text-testing.d.ts +222 -0
- package/types/ngwr-donut-chart-testing.d.ts +79 -0
- package/types/ngwr-drawer.d.ts +12 -0
- package/types/ngwr-dropdown.d.ts +9 -0
- package/types/ngwr-event-calendar-testing.d.ts +301 -0
- package/types/ngwr-event-calendar.d.ts +9 -6
- package/types/ngwr-falling-text-testing.d.ts +186 -0
- package/types/ngwr-fuzzy-text-testing.d.ts +130 -0
- package/types/ngwr-gauge-testing.d.ts +71 -0
- package/types/ngwr-glitch-text-testing.d.ts +172 -0
- package/types/ngwr-gradient-text-testing.d.ts +173 -0
- package/types/ngwr-image-cropper-testing.d.ts +134 -0
- package/types/ngwr-image-cropper.d.ts +1 -0
- package/types/ngwr-input-number.d.ts +33 -1
- package/types/ngwr-knob-testing.d.ts +150 -0
- package/types/ngwr-lightbox-testing.d.ts +154 -0
- package/types/ngwr-lightbox.d.ts +6 -0
- package/types/ngwr-line-chart-testing.d.ts +93 -0
- package/types/ngwr-marquee-testing.d.ts +280 -0
- package/types/ngwr-meter-group-testing.d.ts +67 -0
- package/types/ngwr-overlay.d.ts +19 -1
- package/types/ngwr-rotating-text-testing.d.ts +191 -0
- package/types/ngwr-select.d.ts +32 -0
- package/types/ngwr-shiny-text-testing.d.ts +165 -0
- package/types/ngwr-slider.d.ts +15 -0
- package/types/ngwr-sparkline-testing.d.ts +69 -0
- package/types/ngwr-speed-dial-testing.d.ts +195 -0
- package/types/ngwr-splash-cursor-testing.d.ts +95 -0
- package/types/ngwr-split-text-testing.d.ts +209 -0
- package/types/ngwr-split-text.d.ts +12 -0
- package/types/ngwr-splitter-testing.d.ts +174 -0
- package/types/ngwr-spotlight-card-testing.d.ts +303 -0
- package/types/ngwr-star-border-testing.d.ts +135 -0
- package/types/ngwr-table.d.ts +10 -1
- package/types/ngwr-tilt-card-testing.d.ts +158 -0
- package/types/ngwr-tilt-card.d.ts +2 -0
- package/types/ngwr-toast.d.ts +14 -5
- package/types/ngwr-tour-testing.d.ts +143 -0
- package/types/ngwr-tree.d.ts +7 -0
- package/types/ngwr-typewriter-testing.d.ts +201 -0
- package/types/ngwr-version.d.ts +1 -1
- package/types/ngwr-waves-testing.d.ts +121 -0
- package/types/ngwr-window-testing.d.ts +263 -0
- package/types/ngwr-window.d.ts +27 -10
- package/window/styles/_index.scss +44 -16
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import { TestKey, ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
|
|
2
|
+
import { WrButtonHarness } from 'ngwr/button/testing';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @license
|
|
6
|
+
*
|
|
7
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
8
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
9
|
+
*/
|
|
10
|
+
const ARROWS$1 = {
|
|
11
|
+
left: TestKey.LEFT_ARROW,
|
|
12
|
+
right: TestKey.RIGHT_ARROW,
|
|
13
|
+
up: TestKey.UP_ARROW,
|
|
14
|
+
down: TestKey.DOWN_ARROW,
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Test harness for one event chip inside a {@link WrEventCalendarHarness}.
|
|
18
|
+
*
|
|
19
|
+
* **The visible text is `aria-hidden`; the name is on the button.** A chip prints a
|
|
20
|
+
* time and a title for the eye, and publishes both as one `aria-label` for a screen
|
|
21
|
+
* reader — so {@link getLabel} is what the control announces and {@link getTitle} is
|
|
22
|
+
* what is drawn. They are separate because a consumer's `[wrCalendarEvent]` template
|
|
23
|
+
* replaces the drawn half and not the announced one.
|
|
24
|
+
*
|
|
25
|
+
* **Moving and resizing are on the keyboard here, and not because of jsdom.** The
|
|
26
|
+
* pointer drag is real, but the same two operations are bound to the arrows —
|
|
27
|
+
* `Alt` to move, `Alt` + `Shift` to resize — precisely so the calendar is usable
|
|
28
|
+
* without one. That is the path this harness drives; a synthetic drag would divide
|
|
29
|
+
* by a rect that measures 0×0 anyway.
|
|
30
|
+
*
|
|
31
|
+
* @see https://ngwr.dev/guides/testing
|
|
32
|
+
*/
|
|
33
|
+
class WrEventCalendarChipHarness extends ComponentHarness {
|
|
34
|
+
static hostSelector = '.wr-event-calendar__chip';
|
|
35
|
+
/** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
|
|
36
|
+
static with(options = {}) {
|
|
37
|
+
return new HarnessPredicate(WrEventCalendarChipHarness, options)
|
|
38
|
+
.addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))
|
|
39
|
+
.addOption('title', options.title, (harness, title) => HarnessPredicate.stringMatches(harness.getTitle(), title))
|
|
40
|
+
.addOption('band', options.band, async (harness, band) => (await harness.isBand()) === band);
|
|
41
|
+
}
|
|
42
|
+
/** What the chip announces — its time and title in one string. */
|
|
43
|
+
async getLabel() {
|
|
44
|
+
return (await this.host()).getAttribute('aria-label');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The title as drawn, or `null` when a consumer template replaced it.
|
|
48
|
+
*
|
|
49
|
+
* The default chip prints its own `.wr-event-calendar__chip-title`; a
|
|
50
|
+
* `[wrCalendarEvent]` template renders whatever the consumer wrote instead, and
|
|
51
|
+
* there is no title element to find. {@link getText} is the fallback that always
|
|
52
|
+
* answers.
|
|
53
|
+
*/
|
|
54
|
+
async getTitle() {
|
|
55
|
+
const title = await this.locatorForOptional('.wr-event-calendar__chip-title')();
|
|
56
|
+
return title ? title.text() : null;
|
|
57
|
+
}
|
|
58
|
+
/** The chip's whole rendered text, whoever drew it. */
|
|
59
|
+
async getText() {
|
|
60
|
+
return (await this.host()).text();
|
|
61
|
+
}
|
|
62
|
+
/** The time as printed on a timed chip, or `null` on a band, which prints none. */
|
|
63
|
+
async getTime() {
|
|
64
|
+
const time = await this.locatorForOptional('.wr-event-calendar__chip-time')();
|
|
65
|
+
return time ? time.text() : null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Whether this is a day-spanning BAND rather than a timed chip.
|
|
69
|
+
*
|
|
70
|
+
* One shape covers two places — a month cell and the all-day row — and the other
|
|
71
|
+
* is the block in a time grid. They size themselves completely differently (a
|
|
72
|
+
* `calc()` width across columns versus a percentage height down one), which is why
|
|
73
|
+
* the two are worth telling apart.
|
|
74
|
+
*/
|
|
75
|
+
async isBand() {
|
|
76
|
+
return (await this.host()).hasClass('wr-event-calendar__chip--band');
|
|
77
|
+
}
|
|
78
|
+
/** Whether the event started before this cell — the chip is drawn cut off at the leading edge. */
|
|
79
|
+
async continuesBefore() {
|
|
80
|
+
return (await this.host()).hasClass('wr-event-calendar__chip--continues-before');
|
|
81
|
+
}
|
|
82
|
+
/** Whether the event runs past this cell. */
|
|
83
|
+
async continuesAfter() {
|
|
84
|
+
return (await this.host()).hasClass('wr-event-calendar__chip--continues-after');
|
|
85
|
+
}
|
|
86
|
+
/** Whether the chip is mid-drag. */
|
|
87
|
+
async isDragging() {
|
|
88
|
+
return (await this.host()).hasClass('wr-event-calendar__chip--dragging');
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Whether the chip offers a resize grab area.
|
|
92
|
+
*
|
|
93
|
+
* It is `aria-hidden` and not focusable on purpose — the same resize is `Alt` +
|
|
94
|
+
* `Shift` + arrows on the chip itself, so a second tab stop here would lead
|
|
95
|
+
* nowhere. Its
|
|
96
|
+
* presence is still the mouse affordance, and it is dropped on a chip whose event
|
|
97
|
+
* continues past this cell.
|
|
98
|
+
*/
|
|
99
|
+
async hasResizeHandle() {
|
|
100
|
+
return (await this.locatorForOptional('.wr-event-calendar__resize')()) !== null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Click the chip — which emits `eventClick` and does NOT bubble to the cell
|
|
104
|
+
* underneath.
|
|
105
|
+
*
|
|
106
|
+
* On an editable calendar this needs `document.elementFromPoint` to exist: the CDK
|
|
107
|
+
* dispatches a whole pointer sequence, and the chip's `pointerdown` hit-tests for
|
|
108
|
+
* the cell under the cursor. jsdom implements no such method, so stub it in your
|
|
109
|
+
* spec — `() => null` is the honest answer where nothing has a box — or the click
|
|
110
|
+
* throws out of the listener.
|
|
111
|
+
*/
|
|
112
|
+
async click() {
|
|
113
|
+
await (await this.host()).click();
|
|
114
|
+
}
|
|
115
|
+
/** Move keyboard focus to the chip. */
|
|
116
|
+
async focus() {
|
|
117
|
+
return (await this.host()).focus();
|
|
118
|
+
}
|
|
119
|
+
/** Whether the chip currently holds focus. */
|
|
120
|
+
async isFocused() {
|
|
121
|
+
return (await this.host()).isFocused();
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Move the event — `Alt` + an arrow, a day sideways or a slot vertically.
|
|
125
|
+
*
|
|
126
|
+
* The calendar never mutates `events`: this emits `eventChange` and the host is
|
|
127
|
+
* expected to apply it. A spec whose host ignores the output is asserting a
|
|
128
|
+
* CANCELLED move, which is the contract and the most common surprise here. A
|
|
129
|
+
* calendar without `editable` ignores the keys entirely — the same gate the pointer
|
|
130
|
+
* path uses.
|
|
131
|
+
*/
|
|
132
|
+
async move(arrow) {
|
|
133
|
+
await (await this.host()).sendKeys({ alt: true }, ARROWS$1[arrow]);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Resize the event — `Alt` + `Shift` + an arrow, which moves the END alone.
|
|
137
|
+
*
|
|
138
|
+
* A resize that would end at or before the start emits nothing rather than
|
|
139
|
+
* inverting the event, which is the pointer path's rule too.
|
|
140
|
+
*/
|
|
141
|
+
async resize(arrow) {
|
|
142
|
+
await (await this.host()).sendKeys({ alt: true, shift: true }, ARROWS$1[arrow]);
|
|
143
|
+
}
|
|
144
|
+
/** Press a bare arrow — which the chip ignores, letting the grid's own navigation run. */
|
|
145
|
+
async pressArrow(arrow) {
|
|
146
|
+
await (await this.host()).sendKeys(ARROWS$1[arrow]);
|
|
147
|
+
}
|
|
148
|
+
/** Press Escape — handing focus back to the cell the chip sits in. */
|
|
149
|
+
async sendEscape() {
|
|
150
|
+
await (await this.host()).sendKeys(TestKey.ESCAPE);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @license
|
|
156
|
+
*
|
|
157
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
158
|
+
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
|
|
159
|
+
*/
|
|
160
|
+
const ARROWS = {
|
|
161
|
+
left: TestKey.LEFT_ARROW,
|
|
162
|
+
right: TestKey.RIGHT_ARROW,
|
|
163
|
+
up: TestKey.UP_ARROW,
|
|
164
|
+
down: TestKey.DOWN_ARROW,
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Test harness for `<wr-event-calendar>` — month, week and day in one component.
|
|
168
|
+
*
|
|
169
|
+
* **Every chip lives inside the `role="gridcell"` where its event STARTS**, and
|
|
170
|
+
* reaches out from there with a `calc()` width or a percentage height. That is a
|
|
171
|
+
* deliberate structural choice — a floating events layer would leave `role="row"`
|
|
172
|
+
* owning something other than cells — and it is why {@link getChips} can be scoped
|
|
173
|
+
* to a cell at all.
|
|
174
|
+
*
|
|
175
|
+
* **The view is read from the DOM, not from the input.** A month renders one grid
|
|
176
|
+
* shape and week / day another, and those two are told apart by how many day columns
|
|
177
|
+
* they draw. That is the honest answer for a harness: `view` is a model a consumer
|
|
178
|
+
* can write, and the grid is what the user sees.
|
|
179
|
+
*
|
|
180
|
+
* **Nothing here mutates the events.** A move or a resize emits `eventChange` and
|
|
181
|
+
* the host applies it — so a spec whose host ignores the output is asserting a
|
|
182
|
+
* CANCELLED gesture, which is the contract rather than a bug.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* const calendar = await loader.getHarness(WrEventCalendarHarness);
|
|
187
|
+
*
|
|
188
|
+
* expect(await calendar.getView()).toBe('month');
|
|
189
|
+
* const [standup] = await calendar.getChips({ title: 'Standup' });
|
|
190
|
+
* await standup.move('right');
|
|
191
|
+
* ```
|
|
192
|
+
*
|
|
193
|
+
* @see https://ngwr.dev/guides/testing
|
|
194
|
+
*/
|
|
195
|
+
class WrEventCalendarHarness extends ComponentHarness {
|
|
196
|
+
static hostSelector = 'wr-event-calendar';
|
|
197
|
+
/** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */
|
|
198
|
+
static with(options = {}) {
|
|
199
|
+
return new HarnessPredicate(WrEventCalendarHarness, options)
|
|
200
|
+
.addOption('title', options.title, (harness, title) => HarnessPredicate.stringMatches(harness.getTitle(), title))
|
|
201
|
+
.addOption('view', options.view, async (harness, view) => (await harness.getView()) === view);
|
|
202
|
+
}
|
|
203
|
+
grid = this.locatorFor('.wr-event-calendar__grid');
|
|
204
|
+
/**
|
|
205
|
+
* Which view is on screen.
|
|
206
|
+
*
|
|
207
|
+
* `month` has its own grid modifier; `week` and `day` share the time grid and are
|
|
208
|
+
* told apart by the column count the component publishes on it. Reading the DOM
|
|
209
|
+
* rather than the `view` model is the point — that model is an input a consumer can
|
|
210
|
+
* set to anything, and this is what got rendered.
|
|
211
|
+
*/
|
|
212
|
+
async getView() {
|
|
213
|
+
const grid = await this.grid();
|
|
214
|
+
if (await grid.hasClass('wr-event-calendar__grid--month'))
|
|
215
|
+
return 'month';
|
|
216
|
+
const columns = (await this.locatorForAll('.wr-event-calendar__colhead')()).length;
|
|
217
|
+
return columns > 1 ? 'week' : 'day';
|
|
218
|
+
}
|
|
219
|
+
/** The header title — the month, the week's range, or the day. */
|
|
220
|
+
async getTitle() {
|
|
221
|
+
return (await this.locatorFor('.wr-event-calendar__title')()).text();
|
|
222
|
+
}
|
|
223
|
+
/** Whether the header is rendered at all (`hideHeader`). */
|
|
224
|
+
async hasHeader() {
|
|
225
|
+
return (await this.locatorForOptional('.wr-event-calendar__header')()) !== null;
|
|
226
|
+
}
|
|
227
|
+
/** The grid's accessible name. */
|
|
228
|
+
async getAccessibleName() {
|
|
229
|
+
return (await this.grid()).getAttribute('aria-label');
|
|
230
|
+
}
|
|
231
|
+
/** Step back one month, week or day, depending on the view. */
|
|
232
|
+
async previous() {
|
|
233
|
+
await (await this.step('prev')).click();
|
|
234
|
+
}
|
|
235
|
+
/** Step forward one month, week or day. */
|
|
236
|
+
async next() {
|
|
237
|
+
await (await this.step('next')).click();
|
|
238
|
+
}
|
|
239
|
+
/** Press the Today button — jumping to the period containing today. */
|
|
240
|
+
async goToday() {
|
|
241
|
+
await (await this.headerButton('.wr-event-calendar__today', 'goToday')).click();
|
|
242
|
+
}
|
|
243
|
+
/** The back arrow's accessible name — the two steps are icon-only. */
|
|
244
|
+
async getPreviousLabel() {
|
|
245
|
+
return (await this.stepElement('prev')).getAttribute('aria-label');
|
|
246
|
+
}
|
|
247
|
+
/** The forward arrow's accessible name. */
|
|
248
|
+
async getNextLabel() {
|
|
249
|
+
return (await this.stepElement('next')).getAttribute('aria-label');
|
|
250
|
+
}
|
|
251
|
+
/** The Today button's label. */
|
|
252
|
+
async getTodayLabel() {
|
|
253
|
+
return (await this.headerButton('.wr-event-calendar__today', 'getTodayLabel')).getText();
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* The view switcher's labels, in order — empty when the calendar was given a single
|
|
257
|
+
* view, which drops the switcher entirely.
|
|
258
|
+
*/
|
|
259
|
+
async getViewLabels() {
|
|
260
|
+
const buttons = await this.locatorForAll(WrButtonHarness.with({ selector: '.wr-event-calendar__view' }))();
|
|
261
|
+
return Promise.all(buttons.map(button => button.getText()));
|
|
262
|
+
}
|
|
263
|
+
/** The label of the view currently pressed, or `null` when there is no switcher. */
|
|
264
|
+
async getActiveViewLabel() {
|
|
265
|
+
for (const button of await this.locatorForAll('.wr-event-calendar__view')()) {
|
|
266
|
+
if ((await button.getAttribute('aria-pressed')) === 'true')
|
|
267
|
+
return button.text();
|
|
268
|
+
}
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Switch view by the label the switcher prints.
|
|
273
|
+
*
|
|
274
|
+
* By label rather than by the `WrCalendarView` value, because the label is what the
|
|
275
|
+
* DOM has — the value never reaches it, and a spec that passed one would be naming
|
|
276
|
+
* something only the component can see. {@link getViewLabels} lists them.
|
|
277
|
+
*/
|
|
278
|
+
async setView(label) {
|
|
279
|
+
for (const button of await this.locatorForAll('.wr-event-calendar__view')()) {
|
|
280
|
+
if ((await button.text()) === label) {
|
|
281
|
+
await button.click();
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
throw new Error(`WrEventCalendarHarness.setView("${label}"): the switcher offers: ` +
|
|
286
|
+
`${(await this.getViewLabels()).join(', ')}.`);
|
|
287
|
+
}
|
|
288
|
+
/** The weekday column headers of the month view. */
|
|
289
|
+
async getWeekdayNames() {
|
|
290
|
+
const names = await this.locatorForAll('.wr-event-calendar__weekday')();
|
|
291
|
+
return Promise.all(names.map(name => name.text()));
|
|
292
|
+
}
|
|
293
|
+
/** The day numbers of the month grid, in order — 42 of them, spill days included. */
|
|
294
|
+
async getDayNumbers() {
|
|
295
|
+
const days = await this.locatorForAll('.wr-event-calendar__daynum')();
|
|
296
|
+
return Promise.all(days.map(day => day.text()));
|
|
297
|
+
}
|
|
298
|
+
/** The time-gutter labels down the left of a week or day view. */
|
|
299
|
+
async getSlotLabels() {
|
|
300
|
+
const gutters = await this.locatorForAll('.wr-event-calendar__row .wr-event-calendar__gutter')();
|
|
301
|
+
return Promise.all(gutters.map(gutter => gutter.text()));
|
|
302
|
+
}
|
|
303
|
+
/** Whether the time views drew an all-day band, which they only do when something needs it. */
|
|
304
|
+
async hasAllDayRow() {
|
|
305
|
+
return (await this.locatorForOptional('.wr-event-calendar__row--allday')()) !== null;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Every chip on screen, in DOM order.
|
|
309
|
+
*
|
|
310
|
+
* A multi-day event has ONE chip, in the cell where it starts — the band reaches
|
|
311
|
+
* across the rest — so this counts events-in-view rather than event-days.
|
|
312
|
+
*/
|
|
313
|
+
async getChips(filters = {}) {
|
|
314
|
+
return this.locatorForAll(WrEventCalendarChipHarness.with(filters))();
|
|
315
|
+
}
|
|
316
|
+
/** The chips' announced names, in DOM order. */
|
|
317
|
+
async getChipLabels() {
|
|
318
|
+
const chips = await this.getChips();
|
|
319
|
+
return Promise.all(chips.map(chip => chip.getLabel()));
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* The "+N more" overflow buttons of the month view, by the text they print.
|
|
323
|
+
*
|
|
324
|
+
* A month cell shows `maxLanes` chips and hides the rest behind one of these, which
|
|
325
|
+
* opens the day view — so its presence is the only sign that a cell has more than
|
|
326
|
+
* it is showing.
|
|
327
|
+
*/
|
|
328
|
+
async getOverflowLabels() {
|
|
329
|
+
const more = await this.locatorForAll('.wr-event-calendar__more')();
|
|
330
|
+
return Promise.all(more.map(button => button.text()));
|
|
331
|
+
}
|
|
332
|
+
/** Click the first "+N more" button, which drops to the day view for that date. */
|
|
333
|
+
async openOverflow() {
|
|
334
|
+
const [more] = await this.locatorForAll('.wr-event-calendar__more')();
|
|
335
|
+
if (!more) {
|
|
336
|
+
throw new Error('WrEventCalendarHarness.openOverflow(): no cell is overflowing. A month cell only draws the button ' +
|
|
337
|
+
'once it has more events than `maxLanes` lets it show.');
|
|
338
|
+
}
|
|
339
|
+
await more.click();
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Click an empty part of a cell — which emits `slotClick` with its date and minutes.
|
|
343
|
+
*
|
|
344
|
+
* A cell is addressed by its DAY and, in a time view, the minutes from midnight of
|
|
345
|
+
* its row (`-1` means a month cell or the all-day band). Both come off the two data
|
|
346
|
+
* attributes every cell publishes for the drag's own hit-testing, which makes them
|
|
347
|
+
* the one stable way to name a cell from outside — a row index would mean something
|
|
348
|
+
* different in each view.
|
|
349
|
+
*/
|
|
350
|
+
async clickCell(day, minutes = -1) {
|
|
351
|
+
await (await this.requireCell(day, minutes, 'clickCell')).click();
|
|
352
|
+
}
|
|
353
|
+
/** A cell's accessible name — the date, and the time for a slot. */
|
|
354
|
+
async getCellLabel(day, minutes = -1) {
|
|
355
|
+
return (await this.requireCell(day, minutes, 'getCellLabel')).getAttribute('aria-label');
|
|
356
|
+
}
|
|
357
|
+
/** The chips inside ONE cell, which is where a chip's event starts. */
|
|
358
|
+
async getCellChips(day, minutes = -1) {
|
|
359
|
+
const cell = await this.requireCell(day, minutes, 'getCellChips');
|
|
360
|
+
const index = await cell.getAttribute('data-cell-index');
|
|
361
|
+
return this.locatorForAll(WrEventCalendarChipHarness.with({ ancestor: `[data-cell-index="${index}"]` }))();
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* The grid's single tab stop, as `"<day>:<minutes>"`.
|
|
365
|
+
*
|
|
366
|
+
* The cursor roves rather than every cell being tabbable, and it is RAW — it
|
|
367
|
+
* survives a view switch, so the same pair means a different cell in a month grid
|
|
368
|
+
* and a week one.
|
|
369
|
+
*/
|
|
370
|
+
async getCursor() {
|
|
371
|
+
for (const cell of await this.locatorForAll('.wr-event-calendar__cell')()) {
|
|
372
|
+
if ((await cell.getAttribute('tabindex')) === '0')
|
|
373
|
+
return cell.getAttribute('data-cell-index');
|
|
374
|
+
}
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
/** Move keyboard focus to a cell, which is also what moves the roving cursor onto it. */
|
|
378
|
+
async focusCell(day, minutes = -1) {
|
|
379
|
+
await (await this.requireCell(day, minutes, 'focusCell')).focus();
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Press an arrow on the cell that currently holds the cursor.
|
|
383
|
+
*
|
|
384
|
+
* Sent to that cell rather than to the grid, because the component listens per cell
|
|
385
|
+
* — the element carrying the keyboard should be the one that holds focus — and
|
|
386
|
+
* anything originating inside a chip is deliberately let through untouched.
|
|
387
|
+
*/
|
|
388
|
+
async pressArrow(arrow) {
|
|
389
|
+
await (await this.cursorCell('pressArrow')).sendKeys(ARROWS[arrow]);
|
|
390
|
+
}
|
|
391
|
+
/** Press Home on the cursor cell — the first day of its week. */
|
|
392
|
+
async pressHome() {
|
|
393
|
+
await (await this.cursorCell('pressHome')).sendKeys(TestKey.HOME);
|
|
394
|
+
}
|
|
395
|
+
/** Press End on the cursor cell — the last day of its week. */
|
|
396
|
+
async pressEnd() {
|
|
397
|
+
await (await this.cursorCell('pressEnd')).sendKeys(TestKey.END);
|
|
398
|
+
}
|
|
399
|
+
async step(direction) {
|
|
400
|
+
return this.locatorFor(WrButtonHarness.with({ selector: `.wr-event-calendar__step--${direction}` }))();
|
|
401
|
+
}
|
|
402
|
+
async stepElement(direction) {
|
|
403
|
+
return this.locatorFor(`.wr-event-calendar__step--${direction}`)();
|
|
404
|
+
}
|
|
405
|
+
async headerButton(selector, method) {
|
|
406
|
+
const button = await this.locatorForOptional(WrButtonHarness.with({ selector }))();
|
|
407
|
+
if (!button) {
|
|
408
|
+
throw new Error(`WrEventCalendarHarness.${method}(): there is no header — this calendar was opened with ` +
|
|
409
|
+
'`hideHeader`, so its navigation belongs to the page around it.');
|
|
410
|
+
}
|
|
411
|
+
return button;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* The stamp a cell publishes for a day — local midnight, which is what the
|
|
415
|
+
* component's own `startOfDay` produces. Taking a `Date` and normalising here keeps
|
|
416
|
+
* the timestamp out of every spec.
|
|
417
|
+
*/
|
|
418
|
+
stamp(day) {
|
|
419
|
+
return new Date(day.getFullYear(), day.getMonth(), day.getDate()).getTime();
|
|
420
|
+
}
|
|
421
|
+
async requireCell(day, minutes, method) {
|
|
422
|
+
const stamp = this.stamp(day);
|
|
423
|
+
const cell = await this.locatorForOptional(`[data-cell-date="${stamp}"][data-cell-minutes="${minutes}"]`)();
|
|
424
|
+
if (!cell) {
|
|
425
|
+
throw new Error(`WrEventCalendarHarness.${method}(${day.toDateString()}, ${minutes}): no such cell in the ` +
|
|
426
|
+
`${await this.getView()} view — it is not on screen, or \`minutes\` names no row. Pass -1 for a ` +
|
|
427
|
+
'month cell or the all-day band.');
|
|
428
|
+
}
|
|
429
|
+
return cell;
|
|
430
|
+
}
|
|
431
|
+
async cursorCell(method) {
|
|
432
|
+
for (const cell of await this.locatorForAll('.wr-event-calendar__cell')()) {
|
|
433
|
+
if ((await cell.getAttribute('tabindex')) === '0')
|
|
434
|
+
return cell;
|
|
435
|
+
}
|
|
436
|
+
throw new Error(`WrEventCalendarHarness.${method}(): no cell holds the roving cursor, so there is nothing to send a key ` +
|
|
437
|
+
'to. Call focusCell() first.');
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Generated bundle index. Do not edit.
|
|
443
|
+
*/
|
|
444
|
+
|
|
445
|
+
export { WrEventCalendarChipHarness, WrEventCalendarHarness };
|
|
446
|
+
//# sourceMappingURL=ngwr-event-calendar-testing.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngwr-event-calendar-testing.mjs","sources":["../../../projects/lib/event-calendar/testing/wr-event-calendar-chip-harness.ts","../../../projects/lib/event-calendar/testing/wr-event-calendar-harness.ts","../../../projects/lib/event-calendar/testing/ngwr-event-calendar-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';\n\nimport type { WrEventCalendarChipHarnessFilters } from './interfaces';\n\n/** The arrow keys, by the direction a user would say out loud. */\ntype Arrow = 'left' | 'right' | 'up' | 'down';\n\nconst ARROWS: Record<Arrow, TestKey> = {\n left: TestKey.LEFT_ARROW,\n right: TestKey.RIGHT_ARROW,\n up: TestKey.UP_ARROW,\n down: TestKey.DOWN_ARROW,\n};\n\n/**\n * Test harness for one event chip inside a {@link WrEventCalendarHarness}.\n *\n * **The visible text is `aria-hidden`; the name is on the button.** A chip prints a\n * time and a title for the eye, and publishes both as one `aria-label` for a screen\n * reader — so {@link getLabel} is what the control announces and {@link getTitle} is\n * what is drawn. They are separate because a consumer's `[wrCalendarEvent]` template\n * replaces the drawn half and not the announced one.\n *\n * **Moving and resizing are on the keyboard here, and not because of jsdom.** The\n * pointer drag is real, but the same two operations are bound to the arrows —\n * `Alt` to move, `Alt` + `Shift` to resize — precisely so the calendar is usable\n * without one. That is the path this harness drives; a synthetic drag would divide\n * by a rect that measures 0×0 anyway.\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrEventCalendarChipHarness extends ComponentHarness {\n static hostSelector = '.wr-event-calendar__chip';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrEventCalendarChipHarnessFilters = {}): HarnessPredicate<WrEventCalendarChipHarness> {\n return new HarnessPredicate(WrEventCalendarChipHarness, options)\n .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label))\n .addOption('title', options.title, (harness, title) => HarnessPredicate.stringMatches(harness.getTitle(), title))\n .addOption('band', options.band, async (harness, band) => (await harness.isBand()) === band);\n }\n\n /** What the chip announces — its time and title in one string. */\n async getLabel(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-label');\n }\n\n /**\n * The title as drawn, or `null` when a consumer template replaced it.\n *\n * The default chip prints its own `.wr-event-calendar__chip-title`; a\n * `[wrCalendarEvent]` template renders whatever the consumer wrote instead, and\n * there is no title element to find. {@link getText} is the fallback that always\n * answers.\n */\n async getTitle(): Promise<string | null> {\n const title = await this.locatorForOptional('.wr-event-calendar__chip-title')();\n return title ? title.text() : null;\n }\n\n /** The chip's whole rendered text, whoever drew it. */\n async getText(): Promise<string> {\n return (await this.host()).text();\n }\n\n /** The time as printed on a timed chip, or `null` on a band, which prints none. */\n async getTime(): Promise<string | null> {\n const time = await this.locatorForOptional('.wr-event-calendar__chip-time')();\n return time ? time.text() : null;\n }\n\n /**\n * Whether this is a day-spanning BAND rather than a timed chip.\n *\n * One shape covers two places — a month cell and the all-day row — and the other\n * is the block in a time grid. They size themselves completely differently (a\n * `calc()` width across columns versus a percentage height down one), which is why\n * the two are worth telling apart.\n */\n async isBand(): Promise<boolean> {\n return (await this.host()).hasClass('wr-event-calendar__chip--band');\n }\n\n /** Whether the event started before this cell — the chip is drawn cut off at the leading edge. */\n async continuesBefore(): Promise<boolean> {\n return (await this.host()).hasClass('wr-event-calendar__chip--continues-before');\n }\n\n /** Whether the event runs past this cell. */\n async continuesAfter(): Promise<boolean> {\n return (await this.host()).hasClass('wr-event-calendar__chip--continues-after');\n }\n\n /** Whether the chip is mid-drag. */\n async isDragging(): Promise<boolean> {\n return (await this.host()).hasClass('wr-event-calendar__chip--dragging');\n }\n\n /**\n * Whether the chip offers a resize grab area.\n *\n * It is `aria-hidden` and not focusable on purpose — the same resize is `Alt` +\n * `Shift` + arrows on the chip itself, so a second tab stop here would lead\n * nowhere. Its\n * presence is still the mouse affordance, and it is dropped on a chip whose event\n * continues past this cell.\n */\n async hasResizeHandle(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-event-calendar__resize')()) !== null;\n }\n\n /**\n * Click the chip — which emits `eventClick` and does NOT bubble to the cell\n * underneath.\n *\n * On an editable calendar this needs `document.elementFromPoint` to exist: the CDK\n * dispatches a whole pointer sequence, and the chip's `pointerdown` hit-tests for\n * the cell under the cursor. jsdom implements no such method, so stub it in your\n * spec — `() => null` is the honest answer where nothing has a box — or the click\n * throws out of the listener.\n */\n async click(): Promise<void> {\n await (await this.host()).click();\n }\n\n /** Move keyboard focus to the chip. */\n async focus(): Promise<void> {\n return (await this.host()).focus();\n }\n\n /** Whether the chip currently holds focus. */\n async isFocused(): Promise<boolean> {\n return (await this.host()).isFocused();\n }\n\n /**\n * Move the event — `Alt` + an arrow, a day sideways or a slot vertically.\n *\n * The calendar never mutates `events`: this emits `eventChange` and the host is\n * expected to apply it. A spec whose host ignores the output is asserting a\n * CANCELLED move, which is the contract and the most common surprise here. A\n * calendar without `editable` ignores the keys entirely — the same gate the pointer\n * path uses.\n */\n async move(arrow: Arrow): Promise<void> {\n await (await this.host()).sendKeys({ alt: true }, ARROWS[arrow]);\n }\n\n /**\n * Resize the event — `Alt` + `Shift` + an arrow, which moves the END alone.\n *\n * A resize that would end at or before the start emits nothing rather than\n * inverting the event, which is the pointer path's rule too.\n */\n async resize(arrow: Arrow): Promise<void> {\n await (await this.host()).sendKeys({ alt: true, shift: true }, ARROWS[arrow]);\n }\n\n /** Press a bare arrow — which the chip ignores, letting the grid's own navigation run. */\n async pressArrow(arrow: Arrow): Promise<void> {\n await (await this.host()).sendKeys(ARROWS[arrow]);\n }\n\n /** Press Escape — handing focus back to the cell the chip sits in. */\n async sendEscape(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.ESCAPE);\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, TestKey, type TestElement } from '@angular/cdk/testing';\n\nimport { WrButtonHarness } from 'ngwr/button/testing';\nimport type { WrCalendarView } from 'ngwr/event-calendar';\n\nimport type { WrEventCalendarChipHarnessFilters, WrEventCalendarHarnessFilters } from './interfaces';\nimport { WrEventCalendarChipHarness } from './wr-event-calendar-chip-harness';\n\n/** The arrow keys, by the direction a user would say out loud. */\ntype Arrow = 'left' | 'right' | 'up' | 'down';\n\nconst ARROWS: Record<Arrow, TestKey> = {\n left: TestKey.LEFT_ARROW,\n right: TestKey.RIGHT_ARROW,\n up: TestKey.UP_ARROW,\n down: TestKey.DOWN_ARROW,\n};\n\n/**\n * Test harness for `<wr-event-calendar>` — month, week and day in one component.\n *\n * **Every chip lives inside the `role=\"gridcell\"` where its event STARTS**, and\n * reaches out from there with a `calc()` width or a percentage height. That is a\n * deliberate structural choice — a floating events layer would leave `role=\"row\"`\n * owning something other than cells — and it is why {@link getChips} can be scoped\n * to a cell at all.\n *\n * **The view is read from the DOM, not from the input.** A month renders one grid\n * shape and week / day another, and those two are told apart by how many day columns\n * they draw. That is the honest answer for a harness: `view` is a model a consumer\n * can write, and the grid is what the user sees.\n *\n * **Nothing here mutates the events.** A move or a resize emits `eventChange` and\n * the host applies it — so a spec whose host ignores the output is asserting a\n * CANCELLED gesture, which is the contract rather than a bug.\n *\n * @example\n * ```ts\n * const calendar = await loader.getHarness(WrEventCalendarHarness);\n *\n * expect(await calendar.getView()).toBe('month');\n * const [standup] = await calendar.getChips({ title: 'Standup' });\n * await standup.move('right');\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrEventCalendarHarness extends ComponentHarness {\n static hostSelector = 'wr-event-calendar';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrEventCalendarHarnessFilters = {}): HarnessPredicate<WrEventCalendarHarness> {\n return new HarnessPredicate(WrEventCalendarHarness, options)\n .addOption('title', options.title, (harness, title) => HarnessPredicate.stringMatches(harness.getTitle(), title))\n .addOption('view', options.view, async (harness, view) => (await harness.getView()) === view);\n }\n\n private readonly grid = this.locatorFor('.wr-event-calendar__grid');\n\n /**\n * Which view is on screen.\n *\n * `month` has its own grid modifier; `week` and `day` share the time grid and are\n * told apart by the column count the component publishes on it. Reading the DOM\n * rather than the `view` model is the point — that model is an input a consumer can\n * set to anything, and this is what got rendered.\n */\n async getView(): Promise<WrCalendarView> {\n const grid = await this.grid();\n if (await grid.hasClass('wr-event-calendar__grid--month')) return 'month';\n\n const columns = (await this.locatorForAll('.wr-event-calendar__colhead')()).length;\n return columns > 1 ? 'week' : 'day';\n }\n\n /** The header title — the month, the week's range, or the day. */\n async getTitle(): Promise<string> {\n return (await this.locatorFor('.wr-event-calendar__title')()).text();\n }\n\n /** Whether the header is rendered at all (`hideHeader`). */\n async hasHeader(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-event-calendar__header')()) !== null;\n }\n\n /** The grid's accessible name. */\n async getAccessibleName(): Promise<string | null> {\n return (await this.grid()).getAttribute('aria-label');\n }\n\n /** Step back one month, week or day, depending on the view. */\n async previous(): Promise<void> {\n await (await this.step('prev')).click();\n }\n\n /** Step forward one month, week or day. */\n async next(): Promise<void> {\n await (await this.step('next')).click();\n }\n\n /** Press the Today button — jumping to the period containing today. */\n async goToday(): Promise<void> {\n await (await this.headerButton('.wr-event-calendar__today', 'goToday')).click();\n }\n\n /** The back arrow's accessible name — the two steps are icon-only. */\n async getPreviousLabel(): Promise<string | null> {\n return (await this.stepElement('prev')).getAttribute('aria-label');\n }\n\n /** The forward arrow's accessible name. */\n async getNextLabel(): Promise<string | null> {\n return (await this.stepElement('next')).getAttribute('aria-label');\n }\n\n /** The Today button's label. */\n async getTodayLabel(): Promise<string> {\n return (await this.headerButton('.wr-event-calendar__today', 'getTodayLabel')).getText();\n }\n\n /**\n * The view switcher's labels, in order — empty when the calendar was given a single\n * view, which drops the switcher entirely.\n */\n async getViewLabels(): Promise<string[]> {\n const buttons = await this.locatorForAll(WrButtonHarness.with({ selector: '.wr-event-calendar__view' }))();\n return Promise.all(buttons.map(button => button.getText()));\n }\n\n /** The label of the view currently pressed, or `null` when there is no switcher. */\n async getActiveViewLabel(): Promise<string | null> {\n for (const button of await this.locatorForAll('.wr-event-calendar__view')()) {\n if ((await button.getAttribute('aria-pressed')) === 'true') return button.text();\n }\n return null;\n }\n\n /**\n * Switch view by the label the switcher prints.\n *\n * By label rather than by the `WrCalendarView` value, because the label is what the\n * DOM has — the value never reaches it, and a spec that passed one would be naming\n * something only the component can see. {@link getViewLabels} lists them.\n */\n async setView(label: string): Promise<void> {\n for (const button of await this.locatorForAll('.wr-event-calendar__view')()) {\n if ((await button.text()) === label) {\n await button.click();\n return;\n }\n }\n throw new Error(\n `WrEventCalendarHarness.setView(\"${label}\"): the switcher offers: ` +\n `${(await this.getViewLabels()).join(', ')}.`\n );\n }\n\n /** The weekday column headers of the month view. */\n async getWeekdayNames(): Promise<string[]> {\n const names = await this.locatorForAll('.wr-event-calendar__weekday')();\n return Promise.all(names.map(name => name.text()));\n }\n\n /** The day numbers of the month grid, in order — 42 of them, spill days included. */\n async getDayNumbers(): Promise<string[]> {\n const days = await this.locatorForAll('.wr-event-calendar__daynum')();\n return Promise.all(days.map(day => day.text()));\n }\n\n /** The time-gutter labels down the left of a week or day view. */\n async getSlotLabels(): Promise<string[]> {\n const gutters = await this.locatorForAll('.wr-event-calendar__row .wr-event-calendar__gutter')();\n return Promise.all(gutters.map(gutter => gutter.text()));\n }\n\n /** Whether the time views drew an all-day band, which they only do when something needs it. */\n async hasAllDayRow(): Promise<boolean> {\n return (await this.locatorForOptional('.wr-event-calendar__row--allday')()) !== null;\n }\n\n /**\n * Every chip on screen, in DOM order.\n *\n * A multi-day event has ONE chip, in the cell where it starts — the band reaches\n * across the rest — so this counts events-in-view rather than event-days.\n */\n async getChips(filters: WrEventCalendarChipHarnessFilters = {}): Promise<WrEventCalendarChipHarness[]> {\n return this.locatorForAll(WrEventCalendarChipHarness.with(filters))();\n }\n\n /** The chips' announced names, in DOM order. */\n async getChipLabels(): Promise<(string | null)[]> {\n const chips = await this.getChips();\n return Promise.all(chips.map(chip => chip.getLabel()));\n }\n\n /**\n * The \"+N more\" overflow buttons of the month view, by the text they print.\n *\n * A month cell shows `maxLanes` chips and hides the rest behind one of these, which\n * opens the day view — so its presence is the only sign that a cell has more than\n * it is showing.\n */\n async getOverflowLabels(): Promise<string[]> {\n const more = await this.locatorForAll('.wr-event-calendar__more')();\n return Promise.all(more.map(button => button.text()));\n }\n\n /** Click the first \"+N more\" button, which drops to the day view for that date. */\n async openOverflow(): Promise<void> {\n const [more] = await this.locatorForAll('.wr-event-calendar__more')();\n if (!more) {\n throw new Error(\n 'WrEventCalendarHarness.openOverflow(): no cell is overflowing. A month cell only draws the button ' +\n 'once it has more events than `maxLanes` lets it show.'\n );\n }\n await more.click();\n }\n\n /**\n * Click an empty part of a cell — which emits `slotClick` with its date and minutes.\n *\n * A cell is addressed by its DAY and, in a time view, the minutes from midnight of\n * its row (`-1` means a month cell or the all-day band). Both come off the two data\n * attributes every cell publishes for the drag's own hit-testing, which makes them\n * the one stable way to name a cell from outside — a row index would mean something\n * different in each view.\n */\n async clickCell(day: Date, minutes = -1): Promise<void> {\n await (await this.requireCell(day, minutes, 'clickCell')).click();\n }\n\n /** A cell's accessible name — the date, and the time for a slot. */\n async getCellLabel(day: Date, minutes = -1): Promise<string | null> {\n return (await this.requireCell(day, minutes, 'getCellLabel')).getAttribute('aria-label');\n }\n\n /** The chips inside ONE cell, which is where a chip's event starts. */\n async getCellChips(day: Date, minutes = -1): Promise<WrEventCalendarChipHarness[]> {\n const cell = await this.requireCell(day, minutes, 'getCellChips');\n const index = await cell.getAttribute('data-cell-index');\n return this.locatorForAll(WrEventCalendarChipHarness.with({ ancestor: `[data-cell-index=\"${index}\"]` }))();\n }\n\n /**\n * The grid's single tab stop, as `\"<day>:<minutes>\"`.\n *\n * The cursor roves rather than every cell being tabbable, and it is RAW — it\n * survives a view switch, so the same pair means a different cell in a month grid\n * and a week one.\n */\n async getCursor(): Promise<string | null> {\n for (const cell of await this.locatorForAll('.wr-event-calendar__cell')()) {\n if ((await cell.getAttribute('tabindex')) === '0') return cell.getAttribute('data-cell-index');\n }\n return null;\n }\n\n /** Move keyboard focus to a cell, which is also what moves the roving cursor onto it. */\n async focusCell(day: Date, minutes = -1): Promise<void> {\n await (await this.requireCell(day, minutes, 'focusCell')).focus();\n }\n\n /**\n * Press an arrow on the cell that currently holds the cursor.\n *\n * Sent to that cell rather than to the grid, because the component listens per cell\n * — the element carrying the keyboard should be the one that holds focus — and\n * anything originating inside a chip is deliberately let through untouched.\n */\n async pressArrow(arrow: Arrow): Promise<void> {\n await (await this.cursorCell('pressArrow')).sendKeys(ARROWS[arrow]);\n }\n\n /** Press Home on the cursor cell — the first day of its week. */\n async pressHome(): Promise<void> {\n await (await this.cursorCell('pressHome')).sendKeys(TestKey.HOME);\n }\n\n /** Press End on the cursor cell — the last day of its week. */\n async pressEnd(): Promise<void> {\n await (await this.cursorCell('pressEnd')).sendKeys(TestKey.END);\n }\n\n private async step(direction: 'prev' | 'next'): Promise<WrButtonHarness> {\n return this.locatorFor(WrButtonHarness.with({ selector: `.wr-event-calendar__step--${direction}` }))();\n }\n\n private async stepElement(direction: 'prev' | 'next'): Promise<TestElement> {\n return this.locatorFor(`.wr-event-calendar__step--${direction}`)();\n }\n\n private async headerButton(selector: string, method: string): Promise<WrButtonHarness> {\n const button = await this.locatorForOptional(WrButtonHarness.with({ selector }))();\n if (!button) {\n throw new Error(\n `WrEventCalendarHarness.${method}(): there is no header — this calendar was opened with ` +\n '`hideHeader`, so its navigation belongs to the page around it.'\n );\n }\n return button;\n }\n\n /**\n * The stamp a cell publishes for a day — local midnight, which is what the\n * component's own `startOfDay` produces. Taking a `Date` and normalising here keeps\n * the timestamp out of every spec.\n */\n private stamp(day: Date): number {\n return new Date(day.getFullYear(), day.getMonth(), day.getDate()).getTime();\n }\n\n private async requireCell(day: Date, minutes: number, method: string): Promise<TestElement> {\n const stamp = this.stamp(day);\n const cell = await this.locatorForOptional(`[data-cell-date=\"${stamp}\"][data-cell-minutes=\"${minutes}\"]`)();\n\n if (!cell) {\n throw new Error(\n `WrEventCalendarHarness.${method}(${day.toDateString()}, ${minutes}): no such cell in the ` +\n `${await this.getView()} view — it is not on screen, or \\`minutes\\` names no row. Pass -1 for a ` +\n 'month cell or the all-day band.'\n );\n }\n return cell;\n }\n\n private async cursorCell(method: string): Promise<TestElement> {\n for (const cell of await this.locatorForAll('.wr-event-calendar__cell')()) {\n if ((await cell.getAttribute('tabindex')) === '0') return cell;\n }\n throw new Error(\n `WrEventCalendarHarness.${method}(): no cell holds the roving cursor, so there is nothing to send a key ` +\n 'to. Call focusCell() first.'\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["ARROWS"],"mappings":"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"}
|