compose-multiplatform icon indicating copy to clipboard operation
compose-multiplatform copied to clipboard

Dialog boxes are untestable on Compose Desktop

Open Mindstormer619 opened this issue 3 years ago • 6 comments

Problem

Using the Compose UI Testing library, we cannot test dialog boxes on Compose Desktop. The following example construct works on Android, but not on Desktop.

Example Code

// Test Logic
    @Test
    fun `reproduce dialog test failure`() {
        ui(compose) {
            setContent {
                var isDialogOpen by remember { mutableStateOf(false) }
                DialogHolder(isDialogOpen) {v -> isDialogOpen = v}
            }

            onNodeWithText("Open").performClick()
            awaitIdle()

            onNodeWithText("Close").assertExists()
        }
    } }
// --------------
@Composable fun DialogHolder(isDialogOpen: Boolean, updateDialog: (Boolean) -> Unit) {
    Button(onClick = { updateDialog(true) }) {
        Text("Open")
    }

    if (isDialogOpen) {
        Dialog(onCloseRequest = { updateDialog(false) }) {
            Button(onClick = { updateDialog(false) }) {
                Text("Close")
            }
        }
    }
}

Expectation

Test passes. Content within dialog box is detected.

Actual Result

Test fails with the following error:

Failed: assertExists.
Reason: Expected exactly '1' node but could not find any node that satisfies: (Text + EditableText contains 'Close' (ignoreCase: false))

java.lang.AssertionError: Failed: assertExists.
Reason: Expected exactly '1' node but could not find any node that satisfies: (Text + EditableText contains 'Close' (ignoreCase: false))

    at androidx.compose.ui.test.SemanticsNodeInteraction.fetchOneOrDie(SemanticsNodeInteraction.kt:162)
    at androidx.compose.ui.test.SemanticsNodeInteraction.assertExists(SemanticsNodeInteraction.kt:137)
...

Version Info

  • Kotlin: 1.6.10
  • Jetpack Compose: 1.1.1
  • JVM Target: 11

Links

Relevant Stackoverflow question (asked by me): https://stackoverflow.com/q/72507535/3477606.

Mindstormer619 avatar Jun 09 '22 17:06 Mindstormer619

Are we able to confirm this issue? Is there a workaround?

Mindstormer619 avatar Jun 15 '22 16:06 Mindstormer619

I think this goes beyond dialog boxes, Windows of any kind can't be 'seen through'.

    @Test
    fun `compose test with a window`() {
        runComposeRule(composeRule) {
            setContent {
                Window({}) {
                    Row {
                        Text("Test.")
                    }
                }
            }
...
composeRule.onRoot(useUnmergedTree = true).printToString(5)

returns

Printing with useUnmergedTree = 'true'
Node #1 at (l=0.0, t=0.0, r=0.0, b=0.0)px

For comparison, removing the Window

    @Test
    fun `compose test without a window`() {
        runComposeRule(composeRule) {
            setContent {
                  Row {
                      Text("Test.")
                  }
            }
...
composeRule.onRoot(useUnmergedTree = true).printToString(5)

returns

Printing with useUnmergedTree = 'true'
Node #1 at (l=0.0, t=0.0, r=30.0, b=17.0)px
 |-Node #2 at (l=0.0, t=0.0, r=30.0, b=17.0)px
   Text = '[Test.]'
   Actions = [GetTextLayoutResult]

Is anyone aware whether end to end testing is workable by any means right now?

jamesreprise avatar Jun 28 '22 15:06 jamesreprise

Seeing as a simple assertIsDisplayed fails because a TODO() right at the moment, I think it's obvious that UI testing is not currently available on desktop.

serandel avatar Jun 29 '22 04:06 serandel

@serandel Enough works for basic UI testing at the moment (you can verify that nodes with specific matchers exist in the composition using assertExists()) but it doesn't seem like even slightly complex usecases are present.

On that note, I'm not entirely sure what assertIsDisplayed() is supposed to do differently than assertExists(). Is there a way to have elements live in the composition which are somehow hidden?

Mindstormer619 avatar Jun 29 '22 04:06 Mindstormer619

@serandel Enough works for basic UI testing at the moment (you can verify that nodes with specific matchers exist in the composition using assertExists())

Looking at the number of TODO() in this package I respectfully disagree.

On that note, I'm not entirely sure what assertIsDisplayed() is supposed to do differently than assertExists(). Is there a way to have elements live in the composition which are somehow hidden?

Looking at the source code, a main difference is that it tests that the view is not outside the clipped rect of the window. Because it's using the Espresso ViewMatchers under the hood, I'm not exactly sure if it also checks that the view is not hidden behind any other UI component, but I suspect it's not.

serandel avatar Jun 29 '22 05:06 serandel

Bumping this for attention. Can I at least have an idea of when/if this is planned to be fixed?

Mindstormer619 avatar Aug 31 '22 18:08 Mindstormer619

This has been fixed in Compose Multiplatform 1.5:

    @Composable
    fun DialogHolder(isDialogOpen: Boolean, updateDialog: (Boolean) -> Unit) {
        Button(onClick = { updateDialog(true) }) {
            Text("Open")
        }

        if (isDialogOpen) {
            DialogWindow(onCloseRequest = { updateDialog(false) }) {
                Button(onClick = { updateDialog(false) }) {
                    Text("Close")
                }
            }
        }
    }

    @Test
    fun `reproduce dialog test failure`() {
        rule.setContent {
            var isDialogOpen by remember { mutableStateOf(false) }
            DialogHolder(isDialogOpen) {v -> isDialogOpen = v}
        }

        rule.onNodeWithText("Open").performClick()
        rule.onNodeWithText("Close").assertExists()
    }

m-sasha avatar Aug 20 '23 08:08 m-sasha

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.

okushnikov avatar Jul 14 '24 16:07 okushnikov