selectOptions.js 5.1 KB

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