testing.mjs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. import { coerceBooleanProperty } from '@angular/cdk/coercion';
  2. import { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';
  3. /** Harness for interacting with a mat-radio-group in tests. */
  4. class MatRadioGroupHarness extends ComponentHarness {
  5. /** The selector for the host element of a `MatRadioGroup` instance. */
  6. static hostSelector = '.mat-mdc-radio-group';
  7. _buttonClass = MatRadioButtonHarness;
  8. /**
  9. * Gets a `HarnessPredicate` that can be used to search for a radio group with specific
  10. * attributes.
  11. * @param options Options for filtering which radio group instances are considered a match.
  12. * @return a `HarnessPredicate` configured with the given options.
  13. */
  14. static with(options = {}) {
  15. return new HarnessPredicate(this, options).addOption('name', options.name, MatRadioGroupHarness._checkRadioGroupName);
  16. }
  17. /** Gets the name of the radio-group. */
  18. async getName() {
  19. const hostName = await this._getGroupNameFromHost();
  20. // It's not possible to always determine the "name" of a radio-group by reading
  21. // the attribute. This is because the radio-group does not set the "name" as an
  22. // element attribute if the "name" value is set through a binding.
  23. if (hostName !== null) {
  24. return hostName;
  25. }
  26. // In case we couldn't determine the "name" of a radio-group by reading the
  27. // "name" attribute, we try to determine the "name" of the group by going
  28. // through all radio buttons.
  29. const radioNames = await this._getNamesFromRadioButtons();
  30. if (!radioNames.length) {
  31. return null;
  32. }
  33. if (!this._checkRadioNamesInGroupEqual(radioNames)) {
  34. throw Error('Radio buttons in radio-group have mismatching names.');
  35. }
  36. return radioNames[0];
  37. }
  38. /** Gets the id of the radio-group. */
  39. async getId() {
  40. return (await this.host()).getProperty('id');
  41. }
  42. /** Gets the checked radio-button in a radio-group. */
  43. async getCheckedRadioButton() {
  44. for (let radioButton of await this.getRadioButtons()) {
  45. if (await radioButton.isChecked()) {
  46. return radioButton;
  47. }
  48. }
  49. return null;
  50. }
  51. /** Gets the checked value of the radio-group. */
  52. async getCheckedValue() {
  53. const checkedRadio = await this.getCheckedRadioButton();
  54. if (!checkedRadio) {
  55. return null;
  56. }
  57. return checkedRadio.getValue();
  58. }
  59. /**
  60. * Gets a list of radio buttons which are part of the radio-group.
  61. * @param filter Optionally filters which radio buttons are included.
  62. */
  63. async getRadioButtons(filter) {
  64. return this.locatorForAll(this._buttonClass.with(filter))();
  65. }
  66. /**
  67. * Checks a radio button in this group.
  68. * @param filter An optional filter to apply to the child radio buttons. The first tab matching
  69. * the filter will be selected.
  70. */
  71. async checkRadioButton(filter) {
  72. const radioButtons = await this.getRadioButtons(filter);
  73. if (!radioButtons.length) {
  74. throw Error(`Could not find radio button matching ${JSON.stringify(filter)}`);
  75. }
  76. return radioButtons[0].check();
  77. }
  78. /** Gets the name attribute of the host element. */
  79. async _getGroupNameFromHost() {
  80. return (await this.host()).getAttribute('name');
  81. }
  82. /** Gets a list of the name attributes of all child radio buttons. */
  83. async _getNamesFromRadioButtons() {
  84. const groupNames = [];
  85. for (let radio of await this.getRadioButtons()) {
  86. const radioName = await radio.getName();
  87. if (radioName !== null) {
  88. groupNames.push(radioName);
  89. }
  90. }
  91. return groupNames;
  92. }
  93. /** Checks if the specified radio names are all equal. */
  94. _checkRadioNamesInGroupEqual(radioNames) {
  95. let groupName = null;
  96. for (let radioName of radioNames) {
  97. if (groupName === null) {
  98. groupName = radioName;
  99. }
  100. else if (groupName !== radioName) {
  101. return false;
  102. }
  103. }
  104. return true;
  105. }
  106. /**
  107. * Checks if a radio-group harness has the given name. Throws if a radio-group with
  108. * matching name could be found but has mismatching radio-button names.
  109. */
  110. static async _checkRadioGroupName(harness, name) {
  111. // Check if there is a radio-group which has the "name" attribute set
  112. // to the expected group name. It's not possible to always determine
  113. // the "name" of a radio-group by reading the attribute. This is because
  114. // the radio-group does not set the "name" as an element attribute if the
  115. // "name" value is set through a binding.
  116. if ((await harness._getGroupNameFromHost()) === name) {
  117. return true;
  118. }
  119. // Check if there is a group with radio-buttons that all have the same
  120. // expected name. This implies that the group has the given name. It's
  121. // not possible to always determine the name of a radio-group through
  122. // the attribute because there is
  123. const radioNames = await harness._getNamesFromRadioButtons();
  124. if (radioNames.indexOf(name) === -1) {
  125. return false;
  126. }
  127. if (!harness._checkRadioNamesInGroupEqual(radioNames)) {
  128. throw Error(`The locator found a radio-group with name "${name}", but some ` +
  129. `radio-button's within the group have mismatching names, which is invalid.`);
  130. }
  131. return true;
  132. }
  133. }
  134. /** Harness for interacting with a mat-radio-button in tests. */
  135. class MatRadioButtonHarness extends ComponentHarness {
  136. /** The selector for the host element of a `MatRadioButton` instance. */
  137. static hostSelector = '.mat-mdc-radio-button';
  138. /**
  139. * Gets a `HarnessPredicate` that can be used to search for a radio button with specific
  140. * attributes.
  141. * @param options Options for filtering which radio button instances are considered a match.
  142. * @return a `HarnessPredicate` configured with the given options.
  143. */
  144. static with(options = {}) {
  145. return new HarnessPredicate(this, options)
  146. .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabelText(), label))
  147. .addOption('name', options.name, async (harness, name) => (await harness.getName()) === name)
  148. .addOption('checked', options.checked, async (harness, checked) => (await harness.isChecked()) == checked);
  149. }
  150. _textLabel = this.locatorFor('label');
  151. _clickLabel = this._textLabel;
  152. _input = this.locatorFor('input');
  153. /** Whether the radio-button is checked. */
  154. async isChecked() {
  155. const checked = (await this._input()).getProperty('checked');
  156. return coerceBooleanProperty(await checked);
  157. }
  158. /** Whether the radio-button is disabled. */
  159. async isDisabled() {
  160. const input = await this._input();
  161. const disabled = await input.getAttribute('disabled');
  162. if (disabled !== null) {
  163. return coerceBooleanProperty(disabled);
  164. }
  165. return (await input.getAttribute('aria-disabled')) === 'true';
  166. }
  167. /** Whether the radio-button is required. */
  168. async isRequired() {
  169. const required = (await this._input()).getAttribute('required');
  170. return coerceBooleanProperty(await required);
  171. }
  172. /** Gets the radio-button's name. */
  173. async getName() {
  174. return (await this._input()).getAttribute('name');
  175. }
  176. /** Gets the radio-button's id. */
  177. async getId() {
  178. return (await this.host()).getProperty('id');
  179. }
  180. /**
  181. * Gets the value of the radio-button. The radio-button value will be converted to a string.
  182. *
  183. * Note: This means that for radio-button's with an object as a value `[object Object]` is
  184. * intentionally returned.
  185. */
  186. async getValue() {
  187. return (await this._input()).getProperty('value');
  188. }
  189. /** Gets the radio-button's label text. */
  190. async getLabelText() {
  191. return (await this._textLabel()).text();
  192. }
  193. /** Focuses the radio-button. */
  194. async focus() {
  195. return (await this._input()).focus();
  196. }
  197. /** Blurs the radio-button. */
  198. async blur() {
  199. return (await this._input()).blur();
  200. }
  201. /** Whether the radio-button is focused. */
  202. async isFocused() {
  203. return (await this._input()).isFocused();
  204. }
  205. /**
  206. * Puts the radio-button in a checked state by clicking it if it is currently unchecked,
  207. * or doing nothing if it is already checked.
  208. */
  209. async check() {
  210. if (!(await this.isChecked())) {
  211. return (await this._clickLabel()).click();
  212. }
  213. }
  214. }
  215. export { MatRadioButtonHarness, MatRadioGroupHarness };
  216. //# sourceMappingURL=testing.mjs.map