elements.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import { By } from 'selenium-webdriver'
  2. import fs from 'fs'
  3. import { expect } from 'chai'
  4. import { Condition } from 'selenium-webdriver'
  5. export const DEFAULT_UI_WAIT_MS = 3000
  6. // Keep Selenium helpers in lockstep with the frontend DOM id helpers.
  7. export {
  8. ARGUMENT_FIELD_ID_PREFIX,
  9. argumentFieldChoicesId,
  10. argumentFieldId,
  11. argumentFieldValidationElementId,
  12. argumentFieldValueId
  13. } from '../../frontend/resources/vue/utils/argumentFieldIds.js'
  14. const executionDialogStatusBy = By.css('.execution-dialog-status')
  15. export async function getActionButtons () {
  16. // Currently, only the active dashboard's contents are rendered,
  17. // so we don't need to scope the selector by dashboard title.
  18. return await webdriver.findElements(By.css('.action-button button'))
  19. }
  20. export async function getExecutionDialogOutput() {
  21. await webdriver.wait(new Condition('Dialog with long int is visible', async () => {
  22. const dialog = await webdriver.findElement({ id: 'execution-results-popup' })
  23. return await dialog.isDisplayed()
  24. }));
  25. const ret = await webdriver.executeScript('return window.logEntries.get(window.executionDialog.executionTrackingId).output')
  26. return ret
  27. }
  28. export async function closeExecutionDialog() {
  29. const btnClose = await webdriver.findElements(By.css('[title="Close"]'))
  30. await btnClose[0].click()
  31. }
  32. export function takeScreenshotOnFailure (test, webdriver) {
  33. if (test.state === 'failed') {
  34. const title = test.fullTitle();
  35. console.log(`Test failed, taking screenshot: ${title}`);
  36. takeScreenshot(webdriver, title);
  37. }
  38. }
  39. export function takeScreenshot (webdriver, title) {
  40. return webdriver.takeScreenshot().then((img) => {
  41. fs.mkdirSync('screenshots', { recursive: true });
  42. title = title.replaceAll('config: ', '')
  43. title = title.replaceAll(/[\(\)\|\*\<\>\:]/g, "_")
  44. title = title + '.failed-test'
  45. fs.writeFileSync('screenshots/' + title + '.png', img, 'base64')
  46. })
  47. }
  48. export async function waitForDashboardLoaded(timeoutMs = DEFAULT_UI_WAIT_MS, expectedTitle = null) {
  49. await webdriver.wait(new Condition('wait for loaded-dashboard', async function () {
  50. const body = await webdriver.findElement(By.tagName('body'))
  51. const attr = await body.getAttribute('loaded-dashboard')
  52. console.log('loaded-dashboard: ', attr)
  53. if (attr == null || attr === '') {
  54. return false
  55. }
  56. if (expectedTitle != null) {
  57. return attr === expectedTitle
  58. }
  59. return true
  60. }), timeoutMs)
  61. }
  62. export async function waitForLogsPage(timeoutMs = DEFAULT_UI_WAIT_MS) {
  63. await webdriver.wait(new Condition('wait for logs page', async () => {
  64. const url = await webdriver.getCurrentUrl()
  65. return url.includes('/logs/') && !url.endsWith('/logs')
  66. }), timeoutMs)
  67. }
  68. export async function waitForArgumentFormPage(timeoutMs = DEFAULT_UI_WAIT_MS) {
  69. await webdriver.wait(new Condition('wait for argument form page', async () => {
  70. const url = await webdriver.getCurrentUrl()
  71. return url.includes('/actionBinding/') && url.includes('/argumentForm')
  72. }), timeoutMs)
  73. }
  74. export async function waitForArgumentFormReady(timeoutMs = DEFAULT_UI_WAIT_MS) {
  75. await webdriver.wait(new Condition('wait for argument form ready', async () => {
  76. const body = await webdriver.findElement(By.tagName('body'))
  77. const attr = await body.getAttribute('loaded-argument-form')
  78. return attr != null && attr !== ''
  79. }), timeoutMs)
  80. }
  81. export async function waitForExecutionComplete(timeoutMs = DEFAULT_UI_WAIT_MS) {
  82. await webdriver.wait(new Condition('wait for execution status', async () => {
  83. const statusElements = await webdriver.findElements(executionDialogStatusBy)
  84. return statusElements.length > 0
  85. }), timeoutMs)
  86. await webdriver.wait(new Condition('wait for execution to finish', async () => {
  87. try {
  88. const statusElement = await webdriver.findElement(executionDialogStatusBy)
  89. const statusText = await statusElement.getText()
  90. return !statusText.includes('Still running') && !statusText.includes('Queued')
  91. } catch (e) {
  92. return false
  93. }
  94. }), timeoutMs)
  95. }
  96. export async function getRootAndWait() {
  97. await webdriver.get(runner.baseUrl())
  98. await waitForDashboardLoaded()
  99. }
  100. export async function closeSidebar() {
  101. await webdriver.findElement(By.id('sidebar-toggler-button')).click()
  102. const sidebar = await webdriver.findElement(By.id('mainnav'))
  103. const neededLeft = '-250px' // Assuming sidebar is closed at this position
  104. let lastLeft = ''
  105. await webdriver.wait(new Condition('wait for sidebar to close', async function() {
  106. const left = await sidebar.getCssValue('left')
  107. if (left !== lastLeft) {
  108. lastLeft = left
  109. console.log('Sidebar left changed to: ', left)
  110. return false
  111. } else {
  112. console.log('Sidebar closed, left is: *' + left, left === neededLeft ? ' (as expected)' : '')
  113. return left === neededLeft
  114. }
  115. }), DEFAULT_UI_WAIT_MS)
  116. }
  117. export async function openSidebar() {
  118. await webdriver.findElement(By.id('sidebar-toggler-button')).click()
  119. const sidebar = await webdriver.findElement(By.id('mainnav'))
  120. let lastLeft = 0
  121. await webdriver.wait(new Condition('wait for sidebar to open', async function() {
  122. const left = await sidebar.getCssValue('left')
  123. if (left !== lastLeft) {
  124. lastLeft = left
  125. console.log('Sidebar left changed to: ', left)
  126. return false
  127. } else {
  128. console.log('Sidebar opened, left is: ', left)
  129. return true
  130. }
  131. }), DEFAULT_UI_WAIT_MS)
  132. }
  133. export async function getNavigationLinks() {
  134. // Exclude section containers and legacy section header rows; count only link items.
  135. const navigationLinks = await webdriver.findElements(
  136. By.css('.navigation-links li:not(.nav-section-header-item):not(.nav-section)')
  137. )
  138. return navigationLinks
  139. }
  140. export async function requireExecutionDialogStatus (webdriver, expected) {
  141. await webdriver.wait(new Condition('wait for action to be running', async function () {
  142. const dialogStatus = await webdriver.findElement(executionDialogStatusBy)
  143. const actual = await dialogStatus.getText()
  144. if (actual === expected) {
  145. return true
  146. } else {
  147. console.log('Waiting for domStatus text to be: ', expected, ', it is currently: ', actual)
  148. return false
  149. }
  150. }), DEFAULT_UI_WAIT_MS)
  151. }
  152. export async function findExecutionDialog (webdriver) {
  153. return webdriver.findElement(By.id('execution-results-popup'))
  154. }
  155. export async function getActionButton (webdriver, title) {
  156. const buttons = await webdriver.findElements(By.css('[title="' + title + '"]'))
  157. expect(buttons).to.have.length(1)
  158. return buttons[0]
  159. }
  160. export async function getTerminalBuffer() {
  161. try {
  162. const output = await webdriver.executeScript(`
  163. if (window.terminal && window.terminal.getBufferAsString) {
  164. return window.terminal.getBufferAsString();
  165. }
  166. return null;
  167. `)
  168. return output
  169. } catch (e) {
  170. console.log('[getTerminalBuffer] Error:', e.message)
  171. return null
  172. }
  173. }