From 0950316eabafc17afda79c6143d5c4c3eccecc20 Mon Sep 17 00:00:00 2001 From: Shagen Ogandzhanian Date: Mon, 13 Oct 2025 14:30:20 +0200 Subject: [PATCH 1/8] DEV TO REVERT Enable Firefox --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 072107fc95927..17e98c0eeb9cd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -128,9 +128,9 @@ kotlinx.atomicfu.enableNativeIrTransformation=true kotlinx.atomicfu.enableJsIrTransformation=true # In which browsers run web tests -jetbrains.androidx.web.tests.enableChrome=true +jetbrains.androidx.web.tests.enableChrome=false jetbrains.androidx.web.tests.enableChromium=false -jetbrains.androidx.web.tests.enableFirefox=false +jetbrains.androidx.web.tests.enableFirefox=true jetbrains.androidx.web.tests.enableSafari=false # WA for a build issue on Linux agents From 55c07fc7c0c8f9335044dce719d1023eef55403c Mon Sep 17 00:00:00 2001 From: Shagen Ogandzhanian Date: Mon, 13 Oct 2025 17:49:01 +0200 Subject: [PATCH 2/8] Move methods used exclusively in CfWA11YTest to CfWA11YTest --- .../androidx/compose/ui/OnCanvasTests.kt | 32 ----------------- .../compose/ui/platform/a11y/CfWA11YTest.kt | 34 +++++++++++++++++++ 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/OnCanvasTests.kt b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/OnCanvasTests.kt index 6089a74a6311f..61ddb727a3b23 100644 --- a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/OnCanvasTests.kt +++ b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/OnCanvasTests.kt @@ -76,16 +76,6 @@ internal interface OnCanvasTests { private fun getContainer() = document.getElementById(containerId) ?: error("failed to get canvas with id ${containerId}") - private fun getAppRoot() = getShadowRoot().children[0] as HTMLElement - - fun getA11YContainer(): HTMLElement? { - return if (getAppRoot().children.length < 3) { - null - } else { - // The expected order is: canvas, interop container
, a11y container
- getAppRoot().children[2] as HTMLElement - } - } fun getShadowRoot(): ExtendedShadowRoot = (getContainer().shadowRoot as? ExtendedShadowRoot) ?: error("failed to get shadowRoot") @@ -111,28 +101,6 @@ internal interface OnCanvasTests { } } - suspend fun awaitA11YChanges() { - val a11yContainer = getA11YContainer() ?: return - - fun skipFramesUntil(condition: () -> Boolean, onTrue: () -> Unit) { - window.requestAnimationFrame { - if (!condition()) { - skipFramesUntil(condition, onTrue) - } else { - onTrue() - } - } - } - - suspendCoroutine { continuation -> - val initialContent = a11yContainer.innerHTML - skipFramesUntil( - condition = { a11yContainer.innerHTML != initialContent }, - onTrue = { continuation.resumeWith(Result.success(Unit)) } - ) - } - } - fun dispatchEvents(vararg events: Event) { dispatchEvents(getCanvas(), *events) } diff --git a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt index 71bcd660cb226..1cccff52e4747 100644 --- a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt +++ b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.OnCanvasTests import androidx.compose.ui.currentTimeMillis import androidx.compose.ui.platform.testTag +import kotlin.coroutines.suspendCoroutine import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertEquals @@ -36,6 +37,7 @@ import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.assertTrue import kotlinx.browser.document +import kotlinx.browser.window import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -45,6 +47,38 @@ import org.w3c.dom.HTMLElement import org.w3c.dom.get class CfWA11YTest : OnCanvasTests { + private fun getAppRoot() = getShadowRoot().children[0] as HTMLElement + + suspend fun awaitA11YChanges() { + val a11yContainer = getA11YContainer() ?: return + + fun skipFramesUntil(condition: () -> Boolean, onTrue: () -> Unit) { + window.requestAnimationFrame { + if (!condition()) { + skipFramesUntil(condition, onTrue) + } else { + onTrue() + } + } + } + + suspendCoroutine { continuation -> + val initialContent = a11yContainer.innerHTML + skipFramesUntil( + condition = { a11yContainer.innerHTML != initialContent }, + onTrue = { continuation.resumeWith(Result.success(Unit)) } + ) + } + } + + fun getA11YContainer(): HTMLElement? { + return if (getAppRoot().children.length < 3) { + null + } else { + // The expected order is: canvas, interop container
, a11y container
+ getAppRoot().children[2] as HTMLElement + } + } @Test fun a11yButtonClick() = runApplicationTest { From 6fdb53d65e46404b00b0f628a6edd3b8c0d8529f Mon Sep 17 00:00:00 2001 From: Shagen Ogandzhanian Date: Mon, 13 Oct 2025 17:51:56 +0200 Subject: [PATCH 3/8] Simplify skip condition in skipFramesUntil --- .../androidx/compose/ui/platform/a11y/CfWA11YTest.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt index 1cccff52e4747..c6fd7bb137e16 100644 --- a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt +++ b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt @@ -36,7 +36,6 @@ import kotlin.test.assertNotEquals import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.assertTrue -import kotlinx.browser.document import kotlinx.browser.window import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -52,10 +51,10 @@ class CfWA11YTest : OnCanvasTests { suspend fun awaitA11YChanges() { val a11yContainer = getA11YContainer() ?: return - fun skipFramesUntil(condition: () -> Boolean, onTrue: () -> Unit) { + fun skipFramesUntil(skipCondition: () -> Boolean, onTrue: () -> Unit) { window.requestAnimationFrame { - if (!condition()) { - skipFramesUntil(condition, onTrue) + if (skipCondition()) { + skipFramesUntil(skipCondition, onTrue) } else { onTrue() } @@ -65,7 +64,7 @@ class CfWA11YTest : OnCanvasTests { suspendCoroutine { continuation -> val initialContent = a11yContainer.innerHTML skipFramesUntil( - condition = { a11yContainer.innerHTML != initialContent }, + skipCondition = { a11yContainer.innerHTML == initialContent }, onTrue = { continuation.resumeWith(Result.success(Unit)) } ) } From 86bcdec70aca0867a5f4ae6cf4d3852336d96a7c Mon Sep 17 00:00:00 2001 From: Shagen Ogandzhanian Date: Mon, 13 Oct 2025 18:32:48 +0200 Subject: [PATCH 4/8] skipFramesUntil is a COntinuation.extension --- .../compose/ui/platform/a11y/CfWA11YTest.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt index c6fd7bb137e16..32dc6e9ce9cec 100644 --- a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt +++ b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.OnCanvasTests import androidx.compose.ui.currentTimeMillis import androidx.compose.ui.platform.testTag +import kotlin.coroutines.Continuation import kotlin.coroutines.suspendCoroutine import kotlin.test.Ignore import kotlin.test.Test @@ -48,29 +49,28 @@ import org.w3c.dom.get class CfWA11YTest : OnCanvasTests { private fun getAppRoot() = getShadowRoot().children[0] as HTMLElement - suspend fun awaitA11YChanges() { + private suspend fun awaitA11YChanges() { val a11yContainer = getA11YContainer() ?: return - fun skipFramesUntil(skipCondition: () -> Boolean, onTrue: () -> Unit) { + fun Continuation.skipFramesUntil(skipCondition: () -> Boolean) { window.requestAnimationFrame { if (skipCondition()) { - skipFramesUntil(skipCondition, onTrue) + skipFramesUntil(skipCondition) } else { - onTrue() + resumeWith(Result.success(Unit)) } } } suspendCoroutine { continuation -> val initialContent = a11yContainer.innerHTML - skipFramesUntil( - skipCondition = { a11yContainer.innerHTML == initialContent }, - onTrue = { continuation.resumeWith(Result.success(Unit)) } - ) + continuation.skipFramesUntil { + a11yContainer.innerHTML == initialContent + } } } - fun getA11YContainer(): HTMLElement? { + private fun getA11YContainer(): HTMLElement? { return if (getAppRoot().children.length < 3) { null } else { From 479a81591275e215a2e856c88cca3c62a058afd6 Mon Sep 17 00:00:00 2001 From: Shagen Ogandzhanian Date: Mon, 13 Oct 2025 18:34:06 +0200 Subject: [PATCH 5/8] Revert "DEV TO REVERT Enable Firefox" This reverts commit 0950316eabafc17afda79c6143d5c4c3eccecc20. --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 17e98c0eeb9cd..072107fc95927 100644 --- a/gradle.properties +++ b/gradle.properties @@ -128,9 +128,9 @@ kotlinx.atomicfu.enableNativeIrTransformation=true kotlinx.atomicfu.enableJsIrTransformation=true # In which browsers run web tests -jetbrains.androidx.web.tests.enableChrome=false +jetbrains.androidx.web.tests.enableChrome=true jetbrains.androidx.web.tests.enableChromium=false -jetbrains.androidx.web.tests.enableFirefox=true +jetbrains.androidx.web.tests.enableFirefox=false jetbrains.androidx.web.tests.enableSafari=false # WA for a build issue on Linux agents From 6e7bd2a672b6fff0da6fe1f1aef438e324427723 Mon Sep 17 00:00:00 2001 From: Shagen Ogandzhanian Date: Mon, 13 Oct 2025 19:05:45 +0200 Subject: [PATCH 6/8] cp CMP-9069/a11yButtonClick-firefox --- .../androidx/compose/ui/platform/a11y/CfWA11YTest.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt index 32dc6e9ce9cec..2f3909e497cfa 100644 --- a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt +++ b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt @@ -91,6 +91,7 @@ class CfWA11YTest : OnCanvasTests { } } + awaitIdle() val a11yContainer = getA11YContainer() @@ -104,9 +105,10 @@ class CfWA11YTest : OnCanvasTests { assertEquals("button", button1.getAttribute("role")) assertEquals("Button1", button1.innerText) - repeat(3) { + repeat(3) { repeatCounter -> button1.click() - assertEquals(it + 1, clickCounter) + awaitIdle() + assertEquals(repeatCounter + 1, clickCounter) } } @@ -160,11 +162,11 @@ class CfWA11YTest : OnCanvasTests { assertTrue(button2.isConnected) - repeat(3) { + repeat(3) { repeatCounter -> button1.click() button2.click() - assertEquals(it + 1, clickCounter1) - assertEquals(it + 1, clickCounter2) + assertEquals(repeatCounter + 1, clickCounter1) + assertEquals(repeatCounter + 1, clickCounter2) } showButton2 = false From 7b722647da2f3f6e0e652249b8472d6e93322252 Mon Sep 17 00:00:00 2001 From: Shagen Ogandzhanian Date: Mon, 13 Oct 2025 19:18:27 +0200 Subject: [PATCH 7/8] Unignore everything --- .../kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt index 2f3909e497cfa..ae2b8bcff4552 100644 --- a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt +++ b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt @@ -29,7 +29,6 @@ import androidx.compose.ui.currentTimeMillis import androidx.compose.ui.platform.testTag import kotlin.coroutines.Continuation import kotlin.coroutines.suspendCoroutine -import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -241,7 +240,6 @@ class CfWA11YTest : OnCanvasTests { assertEquals("Button3", buttonsContainer.children[2]!!.innerHTML) } - @Ignore // Sometimes fails on latest firefox FIXME: https://youtrack.jetbrains.com/issue/CMP-8955 @Test fun changesMustBeBatched() = runApplicationTest { var show1 by mutableStateOf(true) @@ -292,7 +290,6 @@ class CfWA11YTest : OnCanvasTests { assertTrue(waitedForChangesMs in 50..150, "Changes must be batched, waited for $waitedForChangesMs ms. Allowed tolerance 50ms was exceeded") } - @Ignore // Sometimes fails on latest firefox FIXME: https://youtrack.jetbrains.com/issue/CMP-8955 @Test fun changesMustBeAppliedDespiteConstantDebounceAfter1Second() = runApplicationTest { var show1 by mutableStateOf(true) @@ -359,7 +356,6 @@ class CfWA11YTest : OnCanvasTests { ) } - @Ignore // Sometimes fails on latest firefox FIXME: https://youtrack.jetbrains.com/issue/CMP-8955 @Test fun noChangesFor1SecondTheDebounceShouldWork() = runApplicationTest { var show by mutableStateOf(true) From ebb53f4a642c03c4df97d7a52e1ff298a4bbc50e Mon Sep 17 00:00:00 2001 From: Shagen Ogandzhanian Date: Tue, 14 Oct 2025 14:11:12 +0200 Subject: [PATCH 8/8] Rename Continuation.skipFramesUntil to Continuation.waitUntil --- .../androidx/compose/ui/platform/a11y/CfWA11YTest.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt index ae2b8bcff4552..a147e59f0d7e2 100644 --- a/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt +++ b/compose/ui/ui/src/webTest/kotlin/androidx/compose/ui/platform/a11y/CfWA11YTest.kt @@ -51,10 +51,10 @@ class CfWA11YTest : OnCanvasTests { private suspend fun awaitA11YChanges() { val a11yContainer = getA11YContainer() ?: return - fun Continuation.skipFramesUntil(skipCondition: () -> Boolean) { + fun Continuation.waitUntil(waitUntilCondition: () -> Boolean) { window.requestAnimationFrame { - if (skipCondition()) { - skipFramesUntil(skipCondition) + if (!waitUntilCondition()) { + waitUntil(waitUntilCondition) } else { resumeWith(Result.success(Unit)) } @@ -63,8 +63,8 @@ class CfWA11YTest : OnCanvasTests { suspendCoroutine { continuation -> val initialContent = a11yContainer.innerHTML - continuation.skipFramesUntil { - a11yContainer.innerHTML == initialContent + continuation.waitUntil { + a11yContainer.innerHTML != initialContent } } }