selectOptions.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import { getConfig } from '@testing-library/dom';
  2. import { isElementType } from '../utils/misc/isElementType.js';
  3. import '../utils/dataTransfer/Clipboard.js';
  4. import { isDisabled } from '../utils/misc/isDisabled.js';
  5. import { wait } from '../utils/misc/wait.js';
  6. import { hasPointerEvents } from '../utils/pointer/cssPointerEvents.js';
  7. import '../event/behavior/click.js';
  8. import '../event/behavior/cut.js';
  9. import '../event/behavior/keydown.js';
  10. import '../event/behavior/keypress.js';
  11. import '../event/behavior/keyup.js';
  12. import '../event/behavior/paste.js';
  13. import { focusElement } from '../event/focus.js';
  14. async function selectOptions(select, values) {
  15. return selectOptionsBase.call(this, true, select, values);
  16. }
  17. async function deselectOptions(select, values) {
  18. return selectOptionsBase.call(this, false, select, values);
  19. }
  20. async function selectOptionsBase(newValue, select, values) {
  21. if (!newValue && !select.multiple) {
  22. throw getConfig().getElementError(`Unable to deselect an option in a non-multiple select. Use selectOptions to change the selection instead.`, select);
  23. }
  24. const valArray = Array.isArray(values) ? values : [
  25. values
  26. ];
  27. const allOptions = Array.from(select.querySelectorAll('option, [role="option"]'));
  28. const selectedOptions = valArray.map((val)=>{
  29. if (typeof val !== 'string' && allOptions.includes(val)) {
  30. return val;
  31. } else {
  32. const matchingOption = allOptions.find((o)=>o.value === val || o.innerHTML === val);
  33. if (matchingOption) {
  34. return matchingOption;
  35. } else {
  36. throw getConfig().getElementError(`Value "${String(val)}" not found in options`, select);
  37. }
  38. }
  39. }).filter((option)=>!isDisabled(option));
  40. if (isDisabled(select) || !selectedOptions.length) return;
  41. const selectOption = (option)=>{
  42. option.selected = newValue;
  43. this.dispatchUIEvent(select, 'input', {
  44. bubbles: true,
  45. cancelable: false,
  46. composed: true
  47. });
  48. this.dispatchUIEvent(select, 'change');
  49. };
  50. if (isElementType(select, 'select')) {
  51. if (select.multiple) {
  52. for (const option of selectedOptions){
  53. const withPointerEvents = this.config.pointerEventsCheck === 0 ? true : hasPointerEvents(this, option);
  54. // events fired for multiple select are weird. Can't use hover...
  55. if (withPointerEvents) {
  56. this.dispatchUIEvent(option, 'pointerover');
  57. this.dispatchUIEvent(select, 'pointerenter');
  58. this.dispatchUIEvent(option, 'mouseover');
  59. this.dispatchUIEvent(select, 'mouseenter');
  60. this.dispatchUIEvent(option, 'pointermove');
  61. this.dispatchUIEvent(option, 'mousemove');
  62. this.dispatchUIEvent(option, 'pointerdown');
  63. this.dispatchUIEvent(option, 'mousedown');
  64. }
  65. focusElement(select);
  66. if (withPointerEvents) {
  67. this.dispatchUIEvent(option, 'pointerup');
  68. this.dispatchUIEvent(option, 'mouseup');
  69. }
  70. selectOption(option);
  71. if (withPointerEvents) {
  72. this.dispatchUIEvent(option, 'click');
  73. }
  74. await wait(this.config);
  75. }
  76. } else if (selectedOptions.length === 1) {
  77. const withPointerEvents = this.config.pointerEventsCheck === 0 ? true : hasPointerEvents(this, select);
  78. // the click to open the select options
  79. if (withPointerEvents) {
  80. await this.click(select);
  81. } else {
  82. focusElement(select);
  83. }
  84. selectOption(selectedOptions[0]);
  85. if (withPointerEvents) {
  86. // the browser triggers another click event on the select for the click on the option
  87. // this second click has no 'down' phase
  88. this.dispatchUIEvent(select, 'pointerover');
  89. this.dispatchUIEvent(select, 'pointerenter');
  90. this.dispatchUIEvent(select, 'mouseover');
  91. this.dispatchUIEvent(select, 'mouseenter');
  92. this.dispatchUIEvent(select, 'pointerup');
  93. this.dispatchUIEvent(select, 'mouseup');
  94. this.dispatchUIEvent(select, 'click');
  95. }
  96. await wait(this.config);
  97. } else {
  98. throw getConfig().getElementError(`Cannot select multiple options on a non-multiple select`, select);
  99. }
  100. } else if (select.getAttribute('role') === 'listbox') {
  101. for (const option of selectedOptions){
  102. await this.click(option);
  103. await this.unhover(option);
  104. }
  105. } else {
  106. throw getConfig().getElementError(`Cannot select options on elements that are neither select nor listbox elements`, select);
  107. }
  108. }
  109. export { deselectOptions, selectOptions };