Skip to content

Commit 38d8959

Browse files
committed
add UX test for Cookie
1 parent ae72098 commit 38d8959

File tree

6 files changed

+156
-1
lines changed

6 files changed

+156
-1
lines changed

src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/AppCheckbox.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import androidx.compose.runtime.Composable
88
import androidx.compose.ui.Alignment
99
import androidx.compose.ui.Modifier
1010
import androidx.compose.ui.graphics.Color
11+
import androidx.compose.ui.semantics.SemanticsPropertyKey
12+
import androidx.compose.ui.semantics.semantics
1113
import androidx.compose.ui.unit.Dp
1214
import androidx.compose.ui.unit.dp
1315
import com.sunnychung.application.multiplatform.hellohttp.ux.local.LocalColor
@@ -28,7 +30,11 @@ fun AppCheckbox(
2830
Row(
2931
horizontalArrangement = Arrangement.spacedBy(spacingWithLabel),
3032
verticalAlignment = Alignment.CenterVertically,
31-
modifier = modifier.clickable(onClick = clickAction),
33+
modifier = modifier
34+
.clickable(onClick = clickAction)
35+
.semantics {
36+
set(IsChecked, checked)
37+
}
3238
) {
3339
AppImageButton(
3440
resource = if (checked) {
@@ -54,3 +60,5 @@ fun AppCheckbox(
5460
label?.invoke()
5561
}
5662
}
63+
64+
val IsChecked = SemanticsPropertyKey<Boolean>("IsChecked")

src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/ProjectAndEnvironmentView.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ fun ProjectAndEnvironmentViewV2(
360360
dialogTextFieldValue = selectedSubproject!!.name
361361
dialogIsCreate = false
362362
},
363+
modifier = Modifier.testTag(TestTag.EditSubprojectButton.name)
363364
)
364365
AppDeleteButton {
365366
selectedSubproject ?: return@AppDeleteButton

src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/SubprojectEditorDialogView.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import androidx.compose.ui.Alignment
2626
import androidx.compose.ui.Modifier
2727
import androidx.compose.ui.focus.FocusRequester
2828
import androidx.compose.ui.focus.focusRequester
29+
import androidx.compose.ui.platform.testTag
2930
import androidx.compose.ui.text.style.TextOverflow
3031
import androidx.compose.ui.unit.dp
3132
import com.sunnychung.application.multiplatform.hellohttp.AppContext
@@ -144,6 +145,7 @@ private fun ConfigurationEditor(
144145
onSubprojectUpdate()
145146
},
146147
size = 24.dp,
148+
modifier = Modifier.testTag(TestTag.SubprojectEditorCookieCheckbox.name)
147149
)
148150
}
149151
PayloadLimitEditorView(

src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/TestTag.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ enum class TestTag {
88
ProjectNameAndSubprojectNameDialogDoneButton,
99
FirstTimeCreateProjectButton,
1010
FirstTimeCreateSubprojectButton,
11+
EditSubprojectButton,
12+
SubprojectEditorCookieCheckbox,
1113
EditEnvironmentsButton,
1214
EnvironmentDialogCreateButton,
1315
EnvironmentDialogEnvNameTextField,

ux-and-transport-test/src/test/kotlin/com/sunnychung/application/multiplatform/hellohttp/test/RequestResponseTest.kt

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import androidx.compose.ui.test.ExperimentalTestApi
66
import androidx.compose.ui.test.assertTextEquals
77
import androidx.compose.ui.test.onAllNodesWithTag
88
import androidx.compose.ui.test.onNodeWithTag
9+
import com.fasterxml.jackson.core.type.TypeReference
10+
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
911
import com.sunnychung.application.multiplatform.hellohttp.AppContext
1012
import com.sunnychung.application.multiplatform.hellohttp.loadNativeLibraries
1113
import com.sunnychung.application.multiplatform.hellohttp.model.ContentType
@@ -20,6 +22,7 @@ import com.sunnychung.application.multiplatform.hellohttp.model.UserKeyValuePair
2022
import com.sunnychung.application.multiplatform.hellohttp.model.UserRequestExample
2123
import com.sunnychung.application.multiplatform.hellohttp.model.UserRequestTemplate
2224
import com.sunnychung.application.multiplatform.hellohttp.network.ReactorNettyHttpTransportClient
25+
import com.sunnychung.application.multiplatform.hellohttp.test.payload.RequestData
2326
import com.sunnychung.application.multiplatform.hellohttp.util.uuidString
2427
import com.sunnychung.application.multiplatform.hellohttp.ux.TestTag
2528
import com.sunnychung.lib.multiplatform.kdatetime.extension.milliseconds
@@ -128,6 +131,7 @@ class RequestResponseTest(testName: String, httpVersion: HttpConfig.HttpProtocol
128131
val earlyErrorUrl = "$httpUrlPrefix/rest/earlyError"
129132
val errorUrl = "$httpUrlPrefix/rest/error"
130133
val bigDocumentUrl = "$httpUrlPrefix/rest/bigDocument"
134+
val cookieUrl = "$httpUrlPrefix/rest/cookie"
131135
val environment = environment(httpVersion = httpVersion, isSsl = isSsl, isMTls = isMTls)
132136

133137
@JvmField
@@ -748,6 +752,92 @@ class RequestResponseTest(testName: String, httpVersion: HttpConfig.HttpProtocol
748752
)
749753
}
750754

755+
@Test
756+
fun receiveAndSendCookie() = runTest {
757+
run {
758+
createProjectIfNeeded()
759+
enableCookieForCurrentSubproject()
760+
}
761+
762+
run {
763+
val setCookieRequest = UserRequestTemplate(
764+
id = uuidString(),
765+
method = "POST",
766+
url = cookieUrl,
767+
examples = listOf(
768+
UserRequestExample(
769+
id = uuidString(),
770+
name = "Base",
771+
contentType = ContentType.Json,
772+
body = StringBody(
773+
"""
774+
{
775+
"cook1": "abcdeeeee",
776+
"auth": "asgfa2980jngfnGsbXsMGKLFeo4acbkmloe421TtjTERSjfgnngkVMZKNo39r/ghsIOWhP=="
777+
}
778+
""".trimIndent()
779+
),
780+
)
781+
)
782+
)
783+
784+
createAndSendHttpRequest(
785+
request = setCookieRequest,
786+
timeout = 2500.milliseconds(),
787+
environment = environment,
788+
isExpectResponseBody = false,
789+
)
790+
791+
onNodeWithTag(TestTag.ResponseStatus.name).assertTextEquals("204 No Content")
792+
// val responseBody = onNodeWithTag(TestTag.ResponseBody.name).fetchSemanticsNodeWithRetry(this)
793+
// .getTexts()
794+
// .single()
795+
// println("responseBody = $responseBody")
796+
// assertEquals("", responseBody)
797+
}
798+
799+
run {
800+
val getCookieRequest = UserRequestTemplate(
801+
id = uuidString(),
802+
method = "GET",
803+
url = cookieUrl,
804+
examples = listOf(
805+
UserRequestExample(
806+
id = uuidString(),
807+
name = "Base",
808+
contentType = ContentType.None,
809+
body = null,
810+
)
811+
)
812+
)
813+
814+
createAndSendHttpRequest(
815+
request = getCookieRequest,
816+
timeout = 2500.milliseconds(),
817+
environment = environment,
818+
isExpectResponseBody = true
819+
)
820+
821+
onNodeWithTag(TestTag.ResponseStatus.name).assertTextEquals("200 OK")
822+
val responseBody = onNodeWithTag(TestTag.ResponseBody.name).fetchSemanticsNodeWithRetry(this)
823+
.getTexts()
824+
.single()
825+
println(responseBody)
826+
827+
data class NameValuePair(val name: String, val value: String)
828+
829+
val resp = jacksonObjectMapper().readValue(responseBody, object : TypeReference<List<Pair<String, List<NameValuePair>>>>() {})!!
830+
resp.first { it.first == "cook1" }.second.let {
831+
assertEquals(1, it.size)
832+
assertEquals("abcdeeeee", it.first().value)
833+
}
834+
resp.first { it.first == "auth" }.second.let {
835+
assertEquals(1, it.size)
836+
assertEquals("asgfa2980jngfnGsbXsMGKLFeo4acbkmloe421TtjTERSjfgnngkVMZKNo39r/ghsIOWhP==", it.first().value)
837+
}
838+
}
839+
}
840+
751841
/************** Special Cases **************/
752842

753843
@Test

ux-and-transport-test/src/test/kotlin/com/sunnychung/application/multiplatform/hellohttp/test/RequestResponseTestUtil.kt

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
package com.sunnychung.application.multiplatform.hellohttp.test
44

5+
import androidx.compose.ui.InternalComposeUiApi
6+
import androidx.compose.ui.input.key.Key
7+
import androidx.compose.ui.input.key.KeyEvent
8+
import androidx.compose.ui.input.key.KeyEventType
59
import androidx.compose.ui.platform.LocalDensity
610
import androidx.compose.ui.semantics.SemanticsNode
711
import androidx.compose.ui.semantics.SemanticsProperties
@@ -28,6 +32,7 @@ import androidx.compose.ui.test.onAllNodesWithText
2832
import androidx.compose.ui.test.onNodeWithTag
2933
import androidx.compose.ui.test.onNodeWithText
3034
import androidx.compose.ui.test.performClick
35+
import androidx.compose.ui.test.performKeyPress
3136
import androidx.compose.ui.test.performScrollToNode
3237
import androidx.compose.ui.test.performTextClearance
3338
import androidx.compose.ui.test.performTextInput
@@ -54,6 +59,7 @@ import com.sunnychung.application.multiplatform.hellohttp.test.payload.RequestDa
5459
import com.sunnychung.application.multiplatform.hellohttp.util.executeWithTimeout
5560
import com.sunnychung.application.multiplatform.hellohttp.ux.AppView
5661
import com.sunnychung.application.multiplatform.hellohttp.ux.DropDownDisplayTexts
62+
import com.sunnychung.application.multiplatform.hellohttp.ux.IsChecked
5763
import com.sunnychung.application.multiplatform.hellohttp.ux.TestTag
5864
import com.sunnychung.application.multiplatform.hellohttp.ux.TestTagPart
5965
import com.sunnychung.application.multiplatform.hellohttp.ux.buildTestTag
@@ -440,6 +446,52 @@ suspend fun DesktopComposeUiTest.selectEnvironment(environment: TestEnvironment)
440446
waitForIdle()
441447
}
442448

449+
@OptIn(InternalComposeUiApi::class)
450+
suspend fun DesktopComposeUiTest.enableCookieForCurrentSubproject() {
451+
println("enableCookieForCurrentSubproject")
452+
453+
onNodeWithTag(TestTag.EditSubprojectButton.name)
454+
.assertIsDisplayedWithRetry(this)
455+
.performClickWithRetry(this)
456+
457+
waitUntil {
458+
waitForIdle()
459+
460+
onAllNodes(
461+
hasTestTag(TestTag.SubprojectEditorCookieCheckbox.name)
462+
.and(isFocusable()),
463+
useUnmergedTree = true
464+
)
465+
.fetchSemanticsNodesWithRetry(this)
466+
.isNotEmpty()
467+
}
468+
469+
val checkbox = onNodeWithTag(TestTag.SubprojectEditorCookieCheckbox.name)
470+
.assertIsDisplayedWithRetry(this)
471+
472+
val checkboxNode = checkbox.fetchSemanticsNodeWithRetry(this)
473+
474+
if (checkboxNode.config.getOrNull(IsChecked) != true) {
475+
checkbox.performClickWithRetry(this)
476+
waitForIdle()
477+
}
478+
479+
checkbox.performKeyPress(KeyEvent(Key.Escape, KeyEventType.KeyDown))
480+
checkbox.performKeyPress(KeyEvent(Key.Escape, KeyEventType.KeyUp))
481+
482+
waitUntil {
483+
waitForIdle()
484+
485+
onAllNodes(
486+
hasTestTag(TestTag.SubprojectEditorCookieCheckbox.name)
487+
.and(isFocusable()),
488+
useUnmergedTree = true
489+
)
490+
.fetchSemanticsNodesWithRetry(this)
491+
.isEmpty()
492+
}
493+
}
494+
443495
/**
444496
* @param name A unique name.
445497
*/

0 commit comments

Comments
 (0)