suggestionsBrowserKey.mjs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. import { describe, it, before, after } from 'mocha'
  2. import { expect } from 'chai'
  3. import { By, Condition } from 'selenium-webdriver'
  4. import {
  5. getRootAndWait,
  6. getActionButton,
  7. takeScreenshotOnFailure,
  8. } from '../../lib/elements.js'
  9. async function openArgumentForm() {
  10. await getRootAndWait()
  11. const btn = await getActionButton(webdriver, 'Test suggestionsBrowserKey')
  12. await btn.click()
  13. await webdriver.wait(
  14. new Condition('wait for argument form page', async () => {
  15. const url = await webdriver.getCurrentUrl()
  16. return url.includes('/actionBinding/') && url.includes('/argumentForm')
  17. }),
  18. 5000
  19. )
  20. }
  21. async function getTestInput() {
  22. return await webdriver.findElement(By.id('testInput'))
  23. }
  24. async function getTestInput2() {
  25. return await webdriver.findElement(By.id('testInput2'))
  26. }
  27. async function getDatalistOptions(inputName = 'testInput') {
  28. return await webdriver.findElements(By.css(`datalist#${inputName}-choices option`))
  29. }
  30. async function submitForm() {
  31. const submitButton = await webdriver.findElement(By.css('button[name="start"]'))
  32. await submitButton.click()
  33. }
  34. async function waitForLogsPage() {
  35. await webdriver.wait(
  36. new Condition('wait for logs page', async () => {
  37. const url = await webdriver.getCurrentUrl()
  38. return url.includes('/logs/') && !url.endsWith('/logs')
  39. }),
  40. 15000
  41. )
  42. }
  43. async function waitForExecutionComplete() {
  44. await webdriver.wait(
  45. new Condition('wait for execution status', async () => {
  46. const statusElements = await webdriver.findElements(By.id('execution-dialog-status'))
  47. return statusElements.length > 0
  48. }),
  49. 5000
  50. )
  51. await webdriver.wait(
  52. new Condition('wait for execution to finish', async () => {
  53. try {
  54. const statusElement = await webdriver.findElement(By.id('execution-dialog-status'))
  55. const statusText = await statusElement.getText()
  56. return !statusText.includes('Executing')
  57. } catch (e) {
  58. return false
  59. }
  60. }),
  61. 5000
  62. )
  63. await webdriver.sleep(500)
  64. }
  65. async function getLocalStorageItem(key) {
  66. return await webdriver.executeScript(`return localStorage.getItem('${key}')`)
  67. }
  68. async function clearLocalStorage() {
  69. await webdriver.executeScript('return localStorage.clear()')
  70. }
  71. describe('config: suggestionsBrowserKey', function () {
  72. before(async function () {
  73. await runner.start('suggestionsBrowserKey')
  74. })
  75. after(async () => {
  76. await runner.stop()
  77. })
  78. afterEach(function () {
  79. takeScreenshotOnFailure(this.currentTest, webdriver)
  80. })
  81. it('Input fields with suggestionsBrowserKey are rendered', async function () {
  82. await openArgumentForm()
  83. const input1 = await getTestInput()
  84. expect(await input1.getTagName()).to.equal('input')
  85. expect(await input1.getAttribute('type')).to.equal('text')
  86. const label1 = await webdriver.findElement(By.css('label[for="testInput"]'))
  87. expect(await label1.getText()).to.contain('Test Input')
  88. const input2 = await getTestInput2()
  89. expect(await input2.getTagName()).to.equal('input')
  90. expect(await input2.getAttribute('type')).to.equal('text')
  91. const label2 = await webdriver.findElement(By.css('label[for="testInput2"]'))
  92. expect(await label2.getText()).to.contain('Test Input 2')
  93. })
  94. it('Submitting form saves value to localStorage', async function () {
  95. this.timeout(15000)
  96. // Clear localStorage first
  97. await clearLocalStorage()
  98. await openArgumentForm()
  99. const input = await getTestInput()
  100. // Use default argument type "ascii" (alphanumeric only) so tests pass when
  101. // config does not set a looser type (e.g. CI merge base without type lines).
  102. const testValue = 'testvalue123'
  103. await input.clear()
  104. await input.sendKeys(testValue)
  105. await submitForm()
  106. await waitForLogsPage()
  107. await waitForExecutionComplete()
  108. // Verify value was saved to localStorage
  109. const stored = await getLocalStorageItem('olivetin-suggestions-test-suggestions-key')
  110. expect(stored).to.not.be.null
  111. const suggestions = JSON.parse(stored)
  112. expect(suggestions).to.be.an('array')
  113. expect(suggestions).to.include(testValue)
  114. })
  115. it('Previously saved values appear in datalist', async function () {
  116. this.timeout(15000)
  117. // First, save a value to localStorage
  118. const testValue = 'savedsuggestion456'
  119. await webdriver.executeScript(`
  120. const key = 'olivetin-suggestions-test-suggestions-key';
  121. localStorage.setItem(key, JSON.stringify(['${testValue}']));
  122. `)
  123. // Open the form
  124. await openArgumentForm()
  125. // Check that datalist exists and contains the saved value
  126. const datalist = await webdriver.findElement(By.id('testInput-choices'))
  127. expect(datalist).to.not.be.null
  128. const options = await getDatalistOptions()
  129. expect(options.length).to.be.greaterThan(0)
  130. // Check if the saved value appears in the datalist
  131. let foundValue = false
  132. for (const option of options) {
  133. const value = await option.getAttribute('value')
  134. if (value === testValue) {
  135. foundValue = true
  136. break
  137. }
  138. }
  139. expect(foundValue).to.be.true
  140. })
  141. it('Multiple submissions accumulate suggestions', async function () {
  142. this.timeout(20000)
  143. // Clear localStorage first
  144. await clearLocalStorage()
  145. // Submit first value
  146. await openArgumentForm()
  147. const input1 = await getTestInput()
  148. await input1.clear()
  149. await input1.sendKeys('firstvalue')
  150. await submitForm()
  151. await waitForLogsPage()
  152. await waitForExecutionComplete()
  153. // Submit second value
  154. await openArgumentForm()
  155. const input2 = await getTestInput()
  156. await input2.clear()
  157. await input2.sendKeys('secondvalue')
  158. await submitForm()
  159. await waitForLogsPage()
  160. await waitForExecutionComplete()
  161. // Verify both values are in localStorage
  162. const stored = await getLocalStorageItem('olivetin-suggestions-test-suggestions-key')
  163. expect(stored).to.not.be.null
  164. const suggestions = JSON.parse(stored)
  165. expect(suggestions).to.be.an('array')
  166. expect(suggestions).to.include('firstvalue')
  167. expect(suggestions).to.include('secondvalue')
  168. expect(suggestions[0]).to.equal('secondvalue') // Most recent should be first
  169. })
  170. it('Empty values are not saved to localStorage', async function () {
  171. this.timeout(15000)
  172. // Clear localStorage first
  173. await clearLocalStorage()
  174. await openArgumentForm()
  175. const input = await getTestInput()
  176. // Leave input empty (or clear it if it has a default)
  177. await input.clear()
  178. await submitForm()
  179. await waitForLogsPage()
  180. await waitForExecutionComplete()
  181. // Verify empty value was not saved - localStorage should be null or empty-equivalent
  182. const stored = await getLocalStorageItem('olivetin-suggestions-test-suggestions-key')
  183. // Should be null OR empty JSON array string ("[]") OR parse to empty array
  184. if (stored !== null) {
  185. const suggestions = JSON.parse(stored)
  186. expect(suggestions).to.be.an('array')
  187. expect(suggestions).to.have.length(0)
  188. }
  189. // If stored is null, that's also acceptable - no assertion needed
  190. })
  191. it('Suggestions are shared across inputs with the same suggestionsBrowserKey', async function () {
  192. this.timeout(20000)
  193. // Clear localStorage first
  194. await clearLocalStorage()
  195. // Submit a value using the first input
  196. await openArgumentForm()
  197. const input1 = await getTestInput()
  198. await input1.clear()
  199. await input1.sendKeys('sharedfrominput1')
  200. await submitForm()
  201. await waitForLogsPage()
  202. await waitForExecutionComplete()
  203. // Open the form again and verify the value appears in both datalists
  204. await openArgumentForm()
  205. // Check first input's datalist
  206. const datalist1 = await webdriver.findElement(By.id('testInput-choices'))
  207. expect(datalist1).to.not.be.null
  208. const options1 = await getDatalistOptions('testInput')
  209. let foundInInput1 = false
  210. for (const option of options1) {
  211. const value = await option.getAttribute('value')
  212. if (value === 'sharedfrominput1') {
  213. foundInInput1 = true
  214. break
  215. }
  216. }
  217. expect(foundInInput1).to.be.true
  218. // Check second input's datalist
  219. const datalist2 = await webdriver.findElement(By.id('testInput2-choices'))
  220. expect(datalist2).to.not.be.null
  221. const options2 = await getDatalistOptions('testInput2')
  222. let foundInInput2 = false
  223. for (const option of options2) {
  224. const value = await option.getAttribute('value')
  225. if (value === 'sharedfrominput1') {
  226. foundInInput2 = true
  227. break
  228. }
  229. }
  230. expect(foundInInput2).to.be.true
  231. // Now submit a value using the second input
  232. const input2 = await getTestInput2()
  233. await input2.clear()
  234. await input2.sendKeys('sharedfrominput2')
  235. await submitForm()
  236. await waitForLogsPage()
  237. await waitForExecutionComplete()
  238. // Verify both values appear in both datalists
  239. await openArgumentForm()
  240. // Check that both values are in the first input's datalist
  241. const options1After = await getDatalistOptions('testInput')
  242. let foundValue1 = false
  243. let foundValue2 = false
  244. for (const option of options1After) {
  245. const value = await option.getAttribute('value')
  246. if (value === 'sharedfrominput1') {
  247. foundValue1 = true
  248. }
  249. if (value === 'sharedfrominput2') {
  250. foundValue2 = true
  251. }
  252. }
  253. expect(foundValue1).to.be.true
  254. expect(foundValue2).to.be.true
  255. // Check that both values are in the second input's datalist
  256. const options2After = await getDatalistOptions('testInput2')
  257. foundValue1 = false
  258. foundValue2 = false
  259. for (const option of options2After) {
  260. const value = await option.getAttribute('value')
  261. if (value === 'sharedfrominput1') {
  262. foundValue1 = true
  263. }
  264. if (value === 'sharedfrominput2') {
  265. foundValue2 = true
  266. }
  267. }
  268. expect(foundValue1).to.be.true
  269. expect(foundValue2).to.be.true
  270. })
  271. })