index.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. import { getChartAlias, Slice } from 'cypress/utils/vizPlugins';
  20. export * from './vizPlugins';
  21. export { default as parsePostForm } from './parsePostForm';
  22. export interface ChartSpec {
  23. name: string;
  24. viz: string;
  25. }
  26. const viewTypeIcons = {
  27. card: 'appstore',
  28. list: 'unordered-list',
  29. };
  30. export function setGridMode(type: 'card' | 'list') {
  31. const icon = viewTypeIcons[type];
  32. cy.get(`[aria-label="${icon}"]`).click();
  33. }
  34. export function toggleBulkSelect() {
  35. cy.getBySel('bulk-select').click();
  36. }
  37. export function clearAllInputs() {
  38. cy.get('body').then($body => {
  39. if ($body.find('.ant-select-clear').length) {
  40. cy.get('.ant-select-clear').click({ multiple: true, force: true });
  41. }
  42. });
  43. }
  44. const toSlicelike = ($chart: JQuery<HTMLElement>): Slice => {
  45. const chartId = $chart.attr('data-test-chart-id');
  46. const vizType = $chart.attr('data-test-viz-type');
  47. return {
  48. slice_id: chartId ? parseInt(chartId, 10) : null,
  49. form_data: {
  50. viz_type: vizType || null,
  51. },
  52. };
  53. };
  54. export function getChartGridComponent({ name, viz }: ChartSpec) {
  55. return cy
  56. .get(`[data-test-chart-name="${name}"]`)
  57. .should('have.attr', 'data-test-viz-type', viz);
  58. }
  59. export function getChartAliasBySpec(chart: ChartSpec) {
  60. return getChartGridComponent(chart).then($chart =>
  61. cy.wrap(getChartAlias(toSlicelike($chart))),
  62. );
  63. }
  64. export function getChartAliasesBySpec(charts: readonly ChartSpec[]) {
  65. const aliases: string[] = [];
  66. charts.forEach(chart =>
  67. getChartAliasBySpec(chart).then(alias => {
  68. aliases.push(alias);
  69. }),
  70. );
  71. // Wrapping the aliases is key.
  72. // That way callers can chain off this function
  73. // and actually get the list of aliases.
  74. return cy.wrap(aliases);
  75. }
  76. export function waitForChartLoad(chart: ChartSpec) {
  77. return getChartGridComponent(chart).then(gridComponent => {
  78. const chartId = gridComponent.attr('data-test-chart-id');
  79. // the chart should load in under half a minute
  80. return (
  81. cy
  82. // this id only becomes visible when the chart is loaded
  83. .get(`#chart-id-${chartId}`, {
  84. timeout: 30000,
  85. })
  86. .should('be.visible')
  87. // return the chart grid component
  88. .then(() => gridComponent)
  89. );
  90. });
  91. }
  92. /**
  93. * Drag an element and drop it to another element.
  94. * Usage:
  95. * drag(source).to(target);
  96. */
  97. export function drag(selector: string, content: string | number | RegExp) {
  98. const dataTransfer = { data: {} };
  99. return {
  100. to(target: string | Cypress.Chainable) {
  101. cy.get('.dragdroppable')
  102. .contains(selector, content)
  103. .trigger('mousedown', { which: 1, force: true });
  104. cy.get('.dragdroppable')
  105. .contains(selector, content)
  106. .trigger('dragstart', { dataTransfer, force: true });
  107. cy.get('.dragdroppable')
  108. .contains(selector, content)
  109. .trigger('drag', { force: true });
  110. (typeof target === 'string' ? cy.get(target) : target)
  111. .trigger('dragover', { dataTransfer, force: true })
  112. .trigger('drop', { dataTransfer, force: true })
  113. .trigger('dragend', { dataTransfer, force: true })
  114. .trigger('mouseup', { which: 1, force: true });
  115. },
  116. };
  117. }
  118. export function resize(selector: string) {
  119. return {
  120. to(cordX: number, cordY: number) {
  121. cy.get(selector).trigger('mousedown', { which: 1, force: true });
  122. cy.get(selector).trigger('mousemove', {
  123. which: 1,
  124. cordX,
  125. cordY,
  126. force: true,
  127. });
  128. cy.get(selector).trigger('mouseup', { which: 1, force: true });
  129. },
  130. };
  131. }
  132. export const setSelectSearchInput = (
  133. $input: any,
  134. value: string,
  135. async = false,
  136. ) => {
  137. // Ant Design 5 Select crashes Chromium with type/click events when showSearch is true.
  138. // This copies the value directly to the input element as a workaround.
  139. const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
  140. window.HTMLInputElement.prototype,
  141. 'value',
  142. )?.set;
  143. nativeInputValueSetter?.call($input[0], value);
  144. // Trigger the input and change events
  145. if (async) {
  146. $input[0].dispatchEvent(new Event('mousedown', { bubbles: true }));
  147. }
  148. $input[0].dispatchEvent(new Event('input', { bubbles: true }));
  149. $input[0].dispatchEvent(new Event('change', { bubbles: true }));
  150. cy.get('.ant-select-item-option-content').should('exist').first().click({
  151. force: true,
  152. });
  153. };