-
Notifications
You must be signed in to change notification settings - Fork 73
Expand file tree
/
Copy pathelement.spec-helper.ts
More file actions
230 lines (212 loc) · 7.07 KB
/
element.spec-helper.ts
File metadata and controls
230 lines (212 loc) · 7.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
/* istanbul ignore file */
import { DebugElement } from '@angular/core';
import { ComponentFixture } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
/**
* Spec helpers for working with the DOM
*/
/**
* Returns a selector for the `data-testid` attribute with the given attribute value.
*
* @param testId Test id set by `data-testid`
*
*/
const testIdSelector: (testId: string) => string = (testId: string) => `[data-testid="${testId}"]`;
/**
* Finds a single element inside the Component by the given CSS selector.
* Throws an error if no element was found.
*
* @param fixture Component fixture
* @param selector CSS selector
*
*/
export const queryByCss: <T>(
fixture: ComponentFixture<T>,
selector: string,
) => DebugElement = <T>(fixture: ComponentFixture<T>, selector: string) => {
// The return type of DebugElement#query() is declared as DebugElement,
// but the actual return type is DebugElement | null.
// See https://github.com/angular/angular/issues/22449.
const debugElement = fixture.debugElement.query(By.css(selector));
// Fail on null so the return type is always DebugElement.
if (!debugElement) {
throw new Error(`queryByCss: Element with ${selector} not found`);
}
return debugElement;
};
/**
* Finds an element inside the Component by the given `data-testid` attribute.
* Throws an error if no element was found.
*
* @param fixture Component fixture
* @param testId Test id set by `data-testid`
*
*/
export const findEl = <T>(fixture: ComponentFixture<T>, testId: string): DebugElement => queryByCss<T>(fixture, testIdSelector(testId));
/**
* Finds all elements with the given `data-testid` attribute.
*
* @param fixture Component fixture
* @param testId Test id set by `data-testid`
*/
export const findEls = <T>(fixture: ComponentFixture<T>, testId: string): DebugElement[] => fixture.debugElement.queryAll(By.css(testIdSelector(testId)));
/**
* Gets the text content of an element with the given `data-testid` attribute.
*
* @param fixture Component fixture
* @param testId Test id set by `data-testid`
*/
export const getText = <T>(fixture: ComponentFixture<T>, testId: string): string => findEl(fixture, testId).nativeElement.textContent;
/**
* Expects that the element with the given `data-testid` attribute
* has the given text content.
*
* @param fixture Component fixture
* @param testId Test id set by `data-testid`
* @param text Expected text
*/
export const expectText = <T>(
fixture: ComponentFixture<T>,
testId: string,
text: string,
): void => {
void expect(getText(fixture, testId)).toBe(text);
};
/**
* Expects that the element with the given `data-testid` attribute
* has the given text content.
*
* @param fixture Component fixture
* @param text Expected text
*/
export const expectContainedText = <T>(fixture: ComponentFixture<T>, text: string): void => {
void expect(fixture.nativeElement.textContent).toContain(text);
};
/**
* Expects that a component has the given text content.
* Both the component text content and the expected text are trimmed for reliability.
*
* @param fixture Component fixture
* @param text Expected text
*/
export const expectContent = <T>(fixture: ComponentFixture<T>, text: string): void => {
void expect(fixture.nativeElement.textContent).toBe(text);
};
/**
* Dispatches a fake event (synthetic event) at the given element.
*
* @param element Element that is the target of the event
* @param type Event name, e.g. `input`
* @param bubbles Whether the event bubbles up in the DOM tree
*/
export const dispatchFakeEvent = (
element: EventTarget,
type: string,
bubbles: boolean = false,
): void => {
const event: Event = new Event(type, {
bubbles,
cancelable: false,
});
element.dispatchEvent(event);
};
/**
* Enters text into a form field (`input`, `textarea` or `select` element).
* Triggers appropriate events so Angular takes notice of the change.
* If you listen for the `change` event on `input` or `textarea`,
* you need to trigger it separately.
*
* @param element Form field
* @param value Form field value
*/
export const setFieldElementValue = (
element: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement,
value: string,
): void => {
element.value = value;
// Dispatch an `input` or `change` fake event
// so Angular form bindings take notice of the change.
const isSelect = element instanceof HTMLSelectElement;
dispatchFakeEvent(element, isSelect ? 'change' : 'input', isSelect ? false : true);
};
/**
* Sets the value of a form field with the given `data-testid` attribute.
*
* @param fixture Component fixture
* @param testId Test id set by `data-testid`
* @param value Form field value
*/
export const setFieldValue = <T>(
fixture: ComponentFixture<T>,
testId: string,
value: string,
): void => {
setFieldElementValue(findEl(fixture, testId).nativeElement, value);
};
/**
* Checks or unchecks a checkbox or radio button.
* Triggers appropriate events so Angular takes notice of the change.
*
* @param fixture Component fixture
* @param testId Test id set by `data-testid`
* @param checked Whether the checkbox or radio should be checked
*/
export const checkField = <T>(
fixture: ComponentFixture<T>,
testId: string,
checked: boolean,
): void => {
const { nativeElement } = findEl(fixture, testId);
nativeElement.checked = checked;
// Dispatch a `change` fake event so Angular form bindings take notice of the change.
dispatchFakeEvent(nativeElement, 'change');
};
/**
* Makes a fake click event that provides the most important properties.
* Sets the button to left.
* The event can be passed to DebugElement#triggerEventHandler.
*
* @param target Element that is the target of the click event
*/
export const makeClickEvent = (target: EventTarget): Partial<MouseEvent> => ({
preventDefault: () => void {},
stopPropagation: () => void {},
stopImmediatePropagation: () => void {},
type: 'click',
target,
currentTarget: target,
bubbles: true,
cancelable: true,
button: 0,
});
/**
* Emulates a left click on the element with the given `data-testid` attribute.
*
* @param fixture Component fixture
* @param testId Test id set by `data-testid`
*/
export const click = <T>(fixture: ComponentFixture<T>, testId: string): void => {
const element = findEl(fixture, testId);
const event = makeClickEvent(element.nativeElement);
element.triggerEventHandler('click', event);
};
/**
* Finds a nested Component by its selector, e.g. `app-example`.
* Throws an error if no element was found.
* Use this only for shallow component testing.
* When finding other elements, use `findEl` / `findEls` and `data-testid` attributes.
*
* @param fixture Fixture of the parent Component
* @param selector Element selector, e.g. `app-example`
*/
export const findComponent = <T>(
fixture: ComponentFixture<T>,
selector: string,
): DebugElement => queryByCss(fixture, selector);
/**
* Finds all nested Components by its selector, e.g. `app-example`.
*/
export const findComponents = <T>(
fixture: ComponentFixture<T>,
selector: string,
): DebugElement[] => fixture.debugElement.queryAll(By.css(selector));