`waitForIdle()` seems oddly flaky if the last thin...
# compose-desktop
t
waitForIdle()
seems oddly flaky if the last thing that happened was a dialog appearing or disappearing. I had an
assertExists()
fail to find the thing, but then find it a moment later when it tried with
useUnmergedTree = true
- and no, it was in the merged tree too.
I wedged in a
Copy code
SwingUtilities.invokeAndWait { }
after waiting for idle, and now it seems completely reliable
which makes me wonder, isn't this the kind of thing
waitForIdle()
should already be doing?
a
Can you post a reproducer?
t
without my entire app? 🙂
the test itself is fairly simple but the code it's testing isn't
A repro much simpler than what I had:
Copy code
package garden.ephemeral.calculator.ui.repro

import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.window.DialogWindow
import androidx.compose.ui.window.Window
import kotlinx.coroutines.launch
import org.junit.Rule
import org.junit.Test
import javax.swing.SwingUtilities

class BetterErrorHandlingTest_CutDown {
    // XXX: Forced to use JUnit 4 for UI tests because Compose only provides JUnit 4 support
    @get:Rule
    val compose = createComposeRule()

    private fun commonTestContent() {
        compose.setContent {
            var showErrorDialog by remember { mutableStateOf(false) }
            Window(onCloseRequest = {}) {
                val scope = rememberCoroutineScope()
                TextButton(modifier = Modifier.Companion.testTag("TestButton"), onClick = {
                    scope.launch {
                        showErrorDialog = true
                    }
                }) {
                    Text(text = "Button")
                }
            }
            if (showErrorDialog) {
                DialogWindow(onCloseRequest = { showErrorDialog = false }) {
                    Surface(modifier = Modifier.testTag("BetterErrorPane")) {

                    }
                }
            }
        }
    }

    @Test
    fun `calling waitForIdle only`() {
        commonTestContent()

        compose.onNodeWithTag("TestButton").performClick()
        compose.waitForIdle()
        compose.onNodeWithTag("BetterErrorPane").assertExists()
    }

    @Test
    fun `calling waitForIdle and then SwingUtilities invokeAndWait`() {
        commonTestContent()

        compose.onNodeWithTag("TestButton").performClick()
        compose.waitForIdle()
        SwingUtilities.invokeAndWait {  }
        compose.onNodeWithTag("BetterErrorPane").assertExists()
    }
}
a
You shouldn’t use Window or DialogWindow in tests
t
but that's what's being tested
so....
I just can't test the error dialog behaviour?
I mean, with the SwingUtilities call in there, it does seem to be reliable again
I mean, this will extend way beyond error dialogs - any dialog I want to test, you're saying I can't test
a
It’s just not currently supported
t
kind of an oversight, don't you think? for a UI testing framework to not support one of the most common components that might appear in a UI?
a
You can test the content of dialogs and windows
Not an oversight, but a known limitation.
t
this is way up there with "we don't have a data table component yet" :)
so you're saying some people sat down and decided to make the car without the wheels, and just call it a limitation... I was assuming that some development practices were in play whereby a "minimum viable product" could be released with massive gaps in the featureset
to be clear though I do blame google for most of it, they seem to be moving very slowly with material3 too
I saw a post somewhere from years ago saying it would "definitely" get a data table
it's interesting, though. if I remove the top Window, it becomes reliable too, even though it does still use DialogWindow - no window physically appears, but it passes anyway
unfortunately though, I can't add a window exception handler without doing it outside the Window call 😕
a
The testing environment, by design, was meant to be “virtual”. The content is rendered into an offscreen canvas without ever opening a real window. We later realized that it wouldn’t work for Window or DialogWindow, and there is a way to fix that (by making them virtual too), but we haven’t gotten to it yet.
t
it almost seems like DialogWindow is being made virtual but when I tried to figure out how by setting breakpoints, it didn't stop at them 😄
ok this time it popped up, I think most times it just finishes so fast I don't get to see it