chore: fix flakey test in suggestionsBrowserKey

This commit is contained in:
jamesread 2026-06-15 01:09:13 +01:00
parent c586d4fb21
commit 1499d5bdf5
3 changed files with 76 additions and 24 deletions

View File

@ -8,12 +8,20 @@ const BANNER_DELAY_MS = 2000
let reconnectAttempt = 0 let reconnectAttempt = 0
let reconnectTimer = null let reconnectTimer = null
let listenersInitialized = false let listenersInitialized = false
let eventStreamGeneration = 0
let eventStreamAbortController = null
function shouldConnectEventStream () { function shouldConnectEventStream () {
return window.initResponse && !window.initResponse.loginRequired return window.initResponse && !window.initResponse.loginRequired
} }
export function stopEventStream () { export function stopEventStream () {
eventStreamGeneration++
if (eventStreamAbortController != null) {
eventStreamAbortController.abort()
eventStreamAbortController = null
}
if (reconnectTimer != null) { if (reconnectTimer != null) {
clearTimeout(reconnectTimer) clearTimeout(reconnectTimer)
reconnectTimer = null reconnectTimer = null
@ -105,6 +113,12 @@ async function reconnectWebsocket () {
return return
} }
const streamGeneration = ++eventStreamGeneration
if (eventStreamAbortController != null) {
eventStreamAbortController.abort()
}
eventStreamAbortController = new AbortController()
connectionState.reconnecting = true connectionState.reconnecting = true
connectionState.connected = false connectionState.connected = false
if (connectionState.disconnectedAt == null) { if (connectionState.disconnectedAt == null) {
@ -115,7 +129,7 @@ async function reconnectWebsocket () {
try { try {
window.websocketAvailable = true window.websocketAvailable = true
const stream = window.client.eventStream() const stream = window.client.eventStream({}, { signal: eventStreamAbortController.signal })
connectionState.connected = true connectionState.connected = true
connectionState.reconnecting = false connectionState.reconnecting = false
connectionState.disconnectedAt = null connectionState.disconnectedAt = null
@ -123,15 +137,25 @@ async function reconnectWebsocket () {
connectionState.scheduledReconnectDelayMs = 0 connectionState.scheduledReconnectDelayMs = 0
connectionState.showDisconnectedBanner = false connectionState.showDisconnectedBanner = false
for await (const e of stream) { for await (const e of stream) {
if (streamGeneration !== eventStreamGeneration) {
return
}
if (reconnectAttempt !== 0) { if (reconnectAttempt !== 0) {
reconnectAttempt = 0 reconnectAttempt = 0
} }
handleEvent(e) handleEvent(e)
} }
} catch (err) { } catch (err) {
if (streamGeneration !== eventStreamGeneration) {
return
}
console.error('Websocket connection failed: ', err) console.error('Websocket connection failed: ', err)
} }
if (streamGeneration !== eventStreamGeneration) {
return
}
window.websocketAvailable = false window.websocketAvailable = false
connectionState.connected = false connectionState.connected = false
connectionState.reconnecting = false connectionState.reconnecting = false

View File

@ -45,7 +45,7 @@
</div> </div>
<div class="buttons"> <div class="buttons">
<button name="start" type="submit" :disabled="hasConfirmation && !confirmationChecked"> <button name="start" type="submit" :disabled="!formReady || (hasConfirmation && !confirmationChecked)">
Start Start
</button> </button>
<button name="cancel" type="button" @click="handleCancel"> <button name="cancel" type="button" @click="handleCancel">
@ -58,7 +58,7 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, nextTick } from 'vue' import { ref, onMounted, onUnmounted, nextTick } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { requestReconnectNow } from '../../../js/websocket.js' import { requestReconnectNow } from '../../../js/websocket.js'
@ -75,6 +75,7 @@ const hasConfirmation = ref(false)
const formErrors = ref({}) const formErrors = ref({})
const actionArguments = ref([]) const actionArguments = ref([])
const popupOnStart = ref('') const popupOnStart = ref('')
const formReady = ref(false)
// Computed properties // Computed properties
@ -87,6 +88,10 @@ const props = defineProps({
// Methods // Methods
async function setup() { async function setup() {
formReady.value = false
document.body.removeAttribute('loaded-argument-form')
try {
const ret = await window.client.getActionBinding({ const ret = await window.client.getActionBinding({
bindingId: props.bindingId bindingId: props.bindingId
}) })
@ -139,6 +144,12 @@ async function setup() {
await validateArgument(arg, argValues.value[arg.name] || '') await validateArgument(arg, argValues.value[arg.name] || '')
} }
} }
formReady.value = true
document.body.setAttribute('loaded-argument-form', props.bindingId)
} catch (err) {
console.error('Failed to load argument form:', err)
}
} }
function getQueryParamValue(paramName) { function getQueryParamValue(paramName) {
@ -394,6 +405,10 @@ async function startAction(actionArgs) {
async function handleSubmit(event) { async function handleSubmit(event) {
event.preventDefault() event.preventDefault()
if (!formReady.value) {
return
}
if (popupOnStart.value === 'history') { if (popupOnStart.value === 'history') {
router.push(`/action/${props.bindingId}`) router.push(`/action/${props.bindingId}`)
return return
@ -470,6 +485,10 @@ defineExpose({
onMounted(() => { onMounted(() => {
setup() setup()
}) })
onUnmounted(() => {
document.body.removeAttribute('loaded-argument-form')
})
</script> </script>
<style scoped> <style scoped>

View File

@ -19,6 +19,15 @@ async function openArgumentForm() {
}), }),
5000 5000
) )
await webdriver.wait(
new Condition('wait for argument form ready', async () => {
const body = await webdriver.findElement(By.css('body'))
const attr = await body.getAttribute('loaded-argument-form')
return attr != null && attr !== ''
}),
5000
)
} }
async function getTestInput() { async function getTestInput() {
@ -62,7 +71,7 @@ async function waitForExecutionComplete() {
try { try {
const statusElement = await webdriver.findElement(By.id('execution-dialog-status')) const statusElement = await webdriver.findElement(By.id('execution-dialog-status'))
const statusText = await statusElement.getText() const statusText = await statusElement.getText()
return !statusText.includes('Executing') return !statusText.includes('Still running')
} catch (e) { } catch (e) {
return false return false
} }