testing.mjs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. import { ComponentHarness, HarnessPredicate, ContentContainerComponentHarness, TestKey, parallel } from '@angular/cdk/testing';
  2. /** Harness for interacting with a standard Material chip avatar in tests. */
  3. class MatChipAvatarHarness extends ComponentHarness {
  4. static hostSelector = '.mat-mdc-chip-avatar';
  5. /**
  6. * Gets a `HarnessPredicate` that can be used to search for a chip avatar with specific
  7. * attributes.
  8. * @param options Options for filtering which input instances are considered a match.
  9. * @return a `HarnessPredicate` configured with the given options.
  10. */
  11. static with(options = {}) {
  12. return new HarnessPredicate(this, options);
  13. }
  14. }
  15. /** Harness for interacting with a standard Material chip remove button in tests. */
  16. class MatChipRemoveHarness extends ComponentHarness {
  17. static hostSelector = '.mat-mdc-chip-remove';
  18. /**
  19. * Gets a `HarnessPredicate` that can be used to search for a chip remove with specific
  20. * attributes.
  21. * @param options Options for filtering which input instances are considered a match.
  22. * @return a `HarnessPredicate` configured with the given options.
  23. */
  24. static with(options = {}) {
  25. return new HarnessPredicate(this, options);
  26. }
  27. /** Clicks the remove button. */
  28. async click() {
  29. return (await this.host()).click();
  30. }
  31. }
  32. /** Harness for interacting with a mat-chip in tests. */
  33. class MatChipHarness extends ContentContainerComponentHarness {
  34. _primaryAction = this.locatorFor('.mdc-evolution-chip__action--primary');
  35. static hostSelector = '.mat-mdc-basic-chip, .mat-mdc-chip';
  36. /**
  37. * Gets a `HarnessPredicate` that can be used to search for a chip with specific attributes.
  38. * @param options Options for narrowing the search.
  39. * @return a `HarnessPredicate` configured with the given options.
  40. */
  41. static with(options = {}) {
  42. return new HarnessPredicate(this, options)
  43. .addOption('text', options.text, (harness, label) => {
  44. return HarnessPredicate.stringMatches(harness.getText(), label);
  45. })
  46. .addOption('disabled', options.disabled, async (harness, disabled) => {
  47. return (await harness.isDisabled()) === disabled;
  48. });
  49. }
  50. /** Gets a promise for the text content the option. */
  51. async getText() {
  52. return (await this.host()).text({
  53. exclude: '.mat-mdc-chip-avatar, .mat-mdc-chip-trailing-icon, .mat-icon',
  54. });
  55. }
  56. /** Whether the chip is disabled. */
  57. async isDisabled() {
  58. return (await this.host()).hasClass('mat-mdc-chip-disabled');
  59. }
  60. /** Delete a chip from the set. */
  61. async remove() {
  62. const hostEl = await this.host();
  63. await hostEl.sendKeys(TestKey.DELETE);
  64. }
  65. /**
  66. * Gets the remove button inside of a chip.
  67. * @param filter Optionally filters which chips are included.
  68. */
  69. async getRemoveButton(filter = {}) {
  70. return this.locatorFor(MatChipRemoveHarness.with(filter))();
  71. }
  72. /**
  73. * Gets the avatar inside a chip.
  74. * @param filter Optionally filters which avatars are included.
  75. */
  76. async getAvatar(filter = {}) {
  77. return this.locatorForOptional(MatChipAvatarHarness.with(filter))();
  78. }
  79. }
  80. /** Harness for interacting with a grid's chip input in tests. */
  81. class MatChipInputHarness extends ComponentHarness {
  82. static hostSelector = '.mat-mdc-chip-input';
  83. /**
  84. * Gets a `HarnessPredicate` that can be used to search for a chip input with specific
  85. * attributes.
  86. * @param options Options for filtering which input instances are considered a match.
  87. * @return a `HarnessPredicate` configured with the given options.
  88. */
  89. static with(options = {}) {
  90. return new HarnessPredicate(this, options)
  91. .addOption('value', options.value, async (harness, value) => {
  92. return (await harness.getValue()) === value;
  93. })
  94. .addOption('placeholder', options.placeholder, async (harness, placeholder) => {
  95. return (await harness.getPlaceholder()) === placeholder;
  96. })
  97. .addOption('disabled', options.disabled, async (harness, disabled) => {
  98. return (await harness.isDisabled()) === disabled;
  99. });
  100. }
  101. /** Whether the input is disabled. */
  102. async isDisabled() {
  103. return (await this.host()).getProperty('disabled');
  104. }
  105. /** Whether the input is required. */
  106. async isRequired() {
  107. return (await this.host()).getProperty('required');
  108. }
  109. /** Gets the value of the input. */
  110. async getValue() {
  111. // The "value" property of the native input is never undefined.
  112. return await (await this.host()).getProperty('value');
  113. }
  114. /** Gets the placeholder of the input. */
  115. async getPlaceholder() {
  116. return await (await this.host()).getProperty('placeholder');
  117. }
  118. /**
  119. * Focuses the input and returns a promise that indicates when the
  120. * action is complete.
  121. */
  122. async focus() {
  123. return (await this.host()).focus();
  124. }
  125. /**
  126. * Blurs the input and returns a promise that indicates when the
  127. * action is complete.
  128. */
  129. async blur() {
  130. return (await this.host()).blur();
  131. }
  132. /** Whether the input is focused. */
  133. async isFocused() {
  134. return (await this.host()).isFocused();
  135. }
  136. /**
  137. * Sets the value of the input. The value will be set by simulating
  138. * keypresses that correspond to the given value.
  139. */
  140. async setValue(newValue) {
  141. const inputEl = await this.host();
  142. await inputEl.clear();
  143. // We don't want to send keys for the value if the value is an empty
  144. // string in order to clear the value. Sending keys with an empty string
  145. // still results in unnecessary focus events.
  146. if (newValue) {
  147. await inputEl.sendKeys(newValue);
  148. }
  149. }
  150. /** Sends a chip separator key to the input element. */
  151. async sendSeparatorKey(key) {
  152. const inputEl = await this.host();
  153. return inputEl.sendKeys(key);
  154. }
  155. }
  156. /** Harness for interacting with a mat-chip-option in tests. */
  157. class MatChipOptionHarness extends MatChipHarness {
  158. static hostSelector = '.mat-mdc-chip-option';
  159. /**
  160. * Gets a `HarnessPredicate` that can be used to search for a chip option with specific
  161. * attributes.
  162. * @param options Options for narrowing the search.
  163. * @return a `HarnessPredicate` configured with the given options.
  164. */
  165. static with(options = {}) {
  166. return new HarnessPredicate(MatChipOptionHarness, options)
  167. .addOption('text', options.text, (harness, label) => HarnessPredicate.stringMatches(harness.getText(), label))
  168. .addOption('selected', options.selected, async (harness, selected) => (await harness.isSelected()) === selected);
  169. }
  170. /** Whether the chip is selected. */
  171. async isSelected() {
  172. return (await this.host()).hasClass('mat-mdc-chip-selected');
  173. }
  174. /** Selects the given chip. Only applies if it's selectable. */
  175. async select() {
  176. if (!(await this.isSelected())) {
  177. await this.toggle();
  178. }
  179. }
  180. /** Deselects the given chip. Only applies if it's selectable. */
  181. async deselect() {
  182. if (await this.isSelected()) {
  183. await this.toggle();
  184. }
  185. }
  186. /** Toggles the selected state of the given chip. */
  187. async toggle() {
  188. return (await this._primaryAction()).click();
  189. }
  190. }
  191. /** Harness for interacting with a mat-chip-listbox in tests. */
  192. class MatChipListboxHarness extends ComponentHarness {
  193. static hostSelector = '.mat-mdc-chip-listbox';
  194. /**
  195. * Gets a `HarnessPredicate` that can be used to search for a chip listbox with specific
  196. * attributes.
  197. * @param options Options for narrowing the search.
  198. * @return a `HarnessPredicate` configured with the given options.
  199. */
  200. static with(options = {}) {
  201. return new HarnessPredicate(this, options).addOption('disabled', options.disabled, async (harness, disabled) => {
  202. return (await harness.isDisabled()) === disabled;
  203. });
  204. }
  205. /** Gets whether the chip listbox is disabled. */
  206. async isDisabled() {
  207. return (await (await this.host()).getAttribute('aria-disabled')) === 'true';
  208. }
  209. /** Gets whether the chip listbox is required. */
  210. async isRequired() {
  211. return (await (await this.host()).getAttribute('aria-required')) === 'true';
  212. }
  213. /** Gets whether the chip listbox is in multi selection mode. */
  214. async isMultiple() {
  215. return (await (await this.host()).getAttribute('aria-multiselectable')) === 'true';
  216. }
  217. /** Gets whether the orientation of the chip list. */
  218. async getOrientation() {
  219. const orientation = await (await this.host()).getAttribute('aria-orientation');
  220. return orientation === 'vertical' ? 'vertical' : 'horizontal';
  221. }
  222. /**
  223. * Gets the list of chips inside the chip list.
  224. * @param filter Optionally filters which chips are included.
  225. */
  226. async getChips(filter = {}) {
  227. return this.locatorForAll(MatChipOptionHarness.with(filter))();
  228. }
  229. /**
  230. * Selects a chip inside the chip list.
  231. * @param filter An optional filter to apply to the child chips.
  232. * All the chips matching the filter will be selected.
  233. */
  234. async selectChips(filter = {}) {
  235. const chips = await this.getChips(filter);
  236. if (!chips.length) {
  237. throw Error(`Cannot find chip matching filter ${JSON.stringify(filter)}`);
  238. }
  239. await parallel(() => chips.map(chip => chip.select()));
  240. }
  241. }
  242. /** Harness for interacting with an editable chip's input in tests. */
  243. class MatChipEditInputHarness extends ComponentHarness {
  244. static hostSelector = '.mat-chip-edit-input';
  245. /**
  246. * Gets a `HarnessPredicate` that can be used to search for a chip edit input with specific
  247. * attributes.
  248. * @param options Options for filtering which input instances are considered a match.
  249. * @return a `HarnessPredicate` configured with the given options.
  250. */
  251. static with(options = {}) {
  252. return new HarnessPredicate(this, options);
  253. }
  254. /** Sets the value of the input. */
  255. async setValue(value) {
  256. const host = await this.host();
  257. // @breaking-change 16.0.0 Remove this null check once `setContenteditableValue`
  258. // becomes a required method.
  259. if (!host.setContenteditableValue) {
  260. throw new Error('Cannot set chip edit input value, because test ' +
  261. 'element does not implement the `setContenteditableValue` method.');
  262. }
  263. return host.setContenteditableValue(value);
  264. }
  265. }
  266. /** Harness for interacting with a mat-chip-row in tests. */
  267. class MatChipRowHarness extends MatChipHarness {
  268. static hostSelector = '.mat-mdc-chip-row';
  269. /** Whether the chip is editable. */
  270. async isEditable() {
  271. return (await this.host()).hasClass('mat-mdc-chip-editable');
  272. }
  273. /** Whether the chip is currently being edited. */
  274. async isEditing() {
  275. return (await this.host()).hasClass('mat-mdc-chip-editing');
  276. }
  277. /** Sets the chip row into an editing state, if it is editable. */
  278. async startEditing() {
  279. if (!(await this.isEditable())) {
  280. throw new Error('Cannot begin editing a chip that is not editable.');
  281. }
  282. return (await this.host()).dispatchEvent('dblclick');
  283. }
  284. /** Stops editing the chip, if it was in the editing state. */
  285. async finishEditing() {
  286. if (await this.isEditing()) {
  287. await (await this.host()).sendKeys(TestKey.ENTER);
  288. }
  289. }
  290. /** Gets the edit input inside the chip row. */
  291. async getEditInput(filter = {}) {
  292. return this.locatorFor(MatChipEditInputHarness.with(filter))();
  293. }
  294. }
  295. /** Harness for interacting with a mat-chip-grid in tests. */
  296. class MatChipGridHarness extends ComponentHarness {
  297. static hostSelector = '.mat-mdc-chip-grid';
  298. /**
  299. * Gets a `HarnessPredicate` that can be used to search for a chip grid with specific attributes.
  300. * @param options Options for filtering which chip grid instances are considered a match.
  301. * @return a `HarnessPredicate` configured with the given options.
  302. */
  303. static with(options = {}) {
  304. return new HarnessPredicate(this, options).addOption('disabled', options.disabled, async (harness, disabled) => {
  305. return (await harness.isDisabled()) === disabled;
  306. });
  307. }
  308. /** Gets whether the chip grid is disabled. */
  309. async isDisabled() {
  310. return (await (await this.host()).getAttribute('aria-disabled')) === 'true';
  311. }
  312. /** Gets whether the chip grid is required. */
  313. async isRequired() {
  314. return await (await this.host()).hasClass('mat-mdc-chip-list-required');
  315. }
  316. /** Gets whether the chip grid is invalid. */
  317. async isInvalid() {
  318. return (await (await this.host()).getAttribute('aria-invalid')) === 'true';
  319. }
  320. /** Gets promise of the harnesses for the chip rows. */
  321. getRows(filter = {}) {
  322. return this.locatorForAll(MatChipRowHarness.with(filter))();
  323. }
  324. /** Gets promise of the chip text input harness. */
  325. getInput(filter = {}) {
  326. return this.locatorFor(MatChipInputHarness.with(filter))();
  327. }
  328. }
  329. /** Harness for interacting with a mat-chip-set in tests. */
  330. class MatChipSetHarness extends ComponentHarness {
  331. static hostSelector = '.mat-mdc-chip-set';
  332. /**
  333. * Gets a `HarnessPredicate` that can be used to search for a chip set with specific attributes.
  334. * @param options Options for filtering which chip set instances are considered a match.
  335. * @return a `HarnessPredicate` configured with the given options.
  336. */
  337. static with(options = {}) {
  338. return new HarnessPredicate(this, options);
  339. }
  340. /** Gets promise of the harnesses for the chips. */
  341. async getChips(filter = {}) {
  342. return await this.locatorForAll(MatChipHarness.with(filter))();
  343. }
  344. }
  345. export { MatChipAvatarHarness, MatChipEditInputHarness, MatChipGridHarness, MatChipHarness, MatChipInputHarness, MatChipListboxHarness, MatChipOptionHarness, MatChipRemoveHarness, MatChipRowHarness, MatChipSetHarness };
  346. //# sourceMappingURL=testing.mjs.map