@@ -80,18 +80,24 @@ class LongPressMenuTest {
8080 RuleChain .emptyRuleChain()
8181 }
8282
83+ /* *
84+ * Utility to build a [LongPressable] with dummy data for testing.
85+ */
8386 private fun getLongPressable (
8487 title : String = "title",
8588 url : String? = "https : // example.com",
8689 thumbnailUrl : String? = "android.resource : // ${ctx.packageName}/${R.drawable.placeholder_thumbnail_video}",
8790 uploader : String? = "uploader",
88- uploaderUrl : String? = "https : // example.com",
8991 viewCount : Long? = 42,
9092 streamType : StreamType ? = StreamType .VIDEO_STREAM ,
9193 uploadDate : Either <String , OffsetDateTime >? = Either .left("2026"),
9294 decoration : LongPressable .Decoration ? = LongPressable .Decoration .Duration (9478)
93- ) = LongPressable (title, url, thumbnailUrl, uploader, uploaderUrl, viewCount, streamType, uploadDate, decoration)
95+ ) = LongPressable (title, url, thumbnailUrl, uploader, viewCount, streamType, uploadDate, decoration)
9496
97+ /* *
98+ * Sets up the [LongPressMenu] in the [composeRule] Compose content for running tests. Handles
99+ * setting dialog settings via shared preferences, and closing the dialog when it is dismissed.
100+ */
95101 private fun setLongPressMenu (
96102 longPressable : LongPressable = getLongPressable(),
97103 longPressActions : List <LongPressAction > = LongPressAction .Type .entries.map { it.buildAction { } },
@@ -114,8 +120,10 @@ class LongPressMenuTest {
114120 }
115121 }
116122
117- // the three tests below all call this function to ensure that the editor button is shown
118- // independently of the long press menu contents
123+ /* *
124+ * The three tests below all call this function to ensure that the editor button is shown
125+ * independently of the long press menu contents.
126+ */
119127 private fun assertEditorIsEnteredAndExitedProperly () {
120128 composeRule.onNodeWithContentDescription(R .string.long_press_menu_enabled_actions_description)
121129 .assertDoesNotExist()
@@ -256,13 +264,16 @@ class LongPressMenuTest {
256264
257265 @Test
258266 fun testHeaderUploadDate1 () {
267+ // here the upload date is an unparsed String we have to use as-is
268+ // (e.g. the extractor could not parse it)
259269 setLongPressMenu(getLongPressable(uploadDate = Either .left(" abcd" )))
260270 composeRule.onNodeWithText(" abcd" , substring = true )
261271 .assertIsDisplayed()
262272 }
263273
264274 @Test
265275 fun testHeaderUploadDate2 () {
276+ // here the upload date is a proper OffsetDateTime that can be formatted properly
266277 val date = OffsetDateTime .now()
267278 .minus(2 , ChronoUnit .HOURS )
268279 .minus(50 , ChronoUnit .MILLIS )
@@ -382,7 +393,7 @@ class LongPressMenuTest {
382393 }
383394
384395 @Test
385- @SdkSuppress(minSdkVersion = Build .VERSION_CODES .N )
396+ @SdkSuppress(minSdkVersion = Build .VERSION_CODES .N ) // setDisplaySize not available on API < 24
386397 fun testHeaderSpansAllWidthIfSmallScreen () {
387398 onDevice().setDisplaySize(
388399 widthSizeClass = WidthSizeClass .COMPACT ,
@@ -397,12 +408,13 @@ class LongPressMenuTest {
397408 val header = composeRule.onNodeWithTag(" LongPressMenuHeader" )
398409 .fetchSemanticsNode()
399410 .boundsInRoot
411+ // checks that the header is roughly as large as the row that contains it
400412 assertInRange(row.left, row.left + 24 .dp.value, header.left)
401413 assertInRange(row.right - 24 .dp.value, row.right, header.right)
402414 }
403415
404416 @Test
405- @SdkSuppress(minSdkVersion = Build .VERSION_CODES .N )
417+ @SdkSuppress(minSdkVersion = Build .VERSION_CODES .N ) // setDisplaySize not available on API < 24
406418 fun testHeaderIsNotFullWidthIfLargeScreen () {
407419 onDevice().setDisplaySize(
408420 widthSizeClass = WidthSizeClass .EXPANDED ,
@@ -417,20 +429,25 @@ class LongPressMenuTest {
417429 val header = composeRule.onNodeWithTag(" LongPressMenuHeader" )
418430 .fetchSemanticsNode()
419431 .boundsInRoot
432+ // checks that the header is definitely smaller than the row that contains it
420433 assertInRange(row.left, row.left + 24 .dp.value, header.left)
421- assertNotInRange(row.right - 24 .dp.value, row.right , header.right)
434+ assertNotInRange(row.right - 24 .dp.value, Float . MAX_VALUE , header.right)
422435 }
423436
424- // the tests below all call this function to test, under different conditions, that the shown
425- // actions are the intersection between the available and the enabled actions
437+ /* *
438+ * The tests below all call this function to test, under different conditions, that the shown
439+ * actions are the intersection between the available and the enabled actions.
440+ */
426441 fun assertOnlyAndAllArrangedActionsDisplayed (
427442 availableActions : List <LongPressAction .Type >,
428443 actionArrangement : List <LongPressAction .Type >,
429444 expectedShownActions : List <LongPressAction .Type >
430445 ) {
431446 setLongPressMenu(
432447 longPressActions = availableActions.map { it.buildAction {} },
433- isHeaderEnabled = ((availableActions.size + actionArrangement.size) % 2 ) == 0 ,
448+ // whether the header is enabled or not shouldn't influence the result, so enable it
449+ // at random (but still deterministically)
450+ isHeaderEnabled = ((expectedShownActions + availableActions).sumOf { it.id } % 2 ) == 0 ,
434451 actionArrangement = actionArrangement
435452 )
436453 for (type in LongPressAction .Type .entries) {
@@ -551,7 +568,7 @@ class LongPressMenuTest {
551568 }
552569
553570 @Test
554- @SdkSuppress(minSdkVersion = Build .VERSION_CODES .N )
571+ @SdkSuppress(minSdkVersion = Build .VERSION_CODES .N ) // setDisplaySize not available on API < 24
555572 fun testAllActionsOnSmallScreenAreScrollable () {
556573 onDevice().setDisplaySize(
557574 widthSizeClass = WidthSizeClass .COMPACT ,
@@ -620,6 +637,9 @@ class LongPressMenuTest {
620637 longPressActions = listOf (BackgroundShuffled .buildAction { delay(500 ) })
621638 )
622639
640+ // test that the loading circle appears while the action is being performed; note that there
641+ // is no way to test that the long press menu contents disappear, because in the current
642+ // implementation they just become hidden below the loading circle (with touches suppressed)
623643 composeRule.onNode(SemanticsMatcher .keyIsDefined(ProgressBarRangeInfo ))
624644 .assertDoesNotExist()
625645 composeRule.onNodeWithText(BackgroundShuffled .label)
@@ -643,6 +663,8 @@ class LongPressMenuTest {
643663 )
644664 )
645665
666+ // make sure that a snackbar is shown after the dialog gets dismissed,
667+ // see https://stackoverflow.com/a/33245290
646668 onView(withId(com.google.android.material.R .id.snackbar_text))
647669 .check(doesNotExist())
648670 composeRule.onNodeWithText(BackgroundShuffled .label)
0 commit comments