Skip to content

Commit 6ba4c16

Browse files
committed
New dashboard tests
1 parent 52aba99 commit 6ba4c16

File tree

10 files changed

+258
-18
lines changed

10 files changed

+258
-18
lines changed

composeApp/src/androidInstrumentedTest/kotlin/org/ooni/probe/screenshots/AutomateScreenshotsTest.kt

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,19 @@ import ooniprobe.composeapp.generated.resources.Res
2929
import ooniprobe.composeapp.generated.resources.Settings_About_Label
3030
import ooniprobe.composeapp.generated.resources.Settings_Advanced_Label
3131
import ooniprobe.composeapp.generated.resources.Settings_Privacy_Label
32-
import ooniprobe.composeapp.generated.resources.Settings_Proxy_Enabled
3332
import ooniprobe.composeapp.generated.resources.Settings_Proxy_Label
33+
import ooniprobe.composeapp.generated.resources.Settings_Proxy_Psiphon
3434
import ooniprobe.composeapp.generated.resources.Settings_Sharing_UploadResults_Description
3535
import ooniprobe.composeapp.generated.resources.Settings_TestOptions_Label
3636
import ooniprobe.composeapp.generated.resources.Settings_Title
3737
import ooniprobe.composeapp.generated.resources.Settings_Websites_Categories_Label
3838
import ooniprobe.composeapp.generated.resources.Settings_Websites_CustomURL_Title
39+
import ooniprobe.composeapp.generated.resources.TestResults
3940
import ooniprobe.composeapp.generated.resources.TestResults_Overview_Tab_Label
4041
import ooniprobe.composeapp.generated.resources.Test_Dash_Fullname
4142
import ooniprobe.composeapp.generated.resources.Test_Performance_Fullname
4243
import ooniprobe.composeapp.generated.resources.Test_Websites_Fullname
44+
import ooniprobe.composeapp.generated.resources.Tests_Title
4345
import ooniprobe.composeapp.generated.resources.app_name
4446
import org.junit.AfterClass
4547
import org.junit.Before
@@ -106,6 +108,7 @@ class AutomateScreenshotsTest {
106108
runTest {
107109
if (!isOoni) return@runTest
108110
preferences.setValueByKey(SettingsKey.FIRST_RUN, true)
111+
preferences.setValueByKey(SettingsKey.TESTS_MOVED_NOTICE, true)
109112
start()
110113

111114
with(compose) {
@@ -153,6 +156,32 @@ class AutomateScreenshotsTest {
153156
}
154157
}
155158

159+
@Test
160+
fun tests() =
161+
runTest {
162+
if (!isOoni) return@runTest
163+
skipOnboarding()
164+
defaultSettings()
165+
start()
166+
167+
with(compose) {
168+
wait { onNodeWithContentDescription(Res.string.app_name).isDisplayed() }
169+
170+
wait(timeout = 30.seconds) {
171+
onNodeWithText(Res.string.Dashboard_Progress_UpdateLink_Label)
172+
.isNotDisplayed()
173+
}
174+
175+
clickOnText(Res.string.Tests_Title)
176+
177+
wait {
178+
onNodeWithText(Res.string.Test_Websites_Fullname).isDisplayed()
179+
}
180+
Screengrab.screenshot("2_" + locale())
181+
Screengrab.screenshot("22-tests")
182+
}
183+
}
184+
156185
@Test
157186
fun runTests() =
158187
runTest {
@@ -257,17 +286,15 @@ class AutomateScreenshotsTest {
257286

258287
// back
259288
clickOnContentDescription(Res.string.Common_Back)
260-
261289
wait { onNodeWithText(Res.string.Settings_About_Label).isDisplayed() }
262290

263291
clickOnText(Res.string.Settings_Proxy_Label)
264-
wait { onNodeWithText(Res.string.Settings_Proxy_Enabled).isDisplayed() }
292+
wait { onNodeWithText(Res.string.Settings_Proxy_Psiphon).isDisplayed() }
265293

266294
Screengrab.screenshot("14-proxy")
267295

268296
// back
269297
clickOnContentDescription(Res.string.Common_Back)
270-
271298
wait { onNodeWithText(Res.string.Settings_About_Label).isDisplayed() }
272299

273300
clickOnText(Res.string.Settings_Advanced_Label)
@@ -298,14 +325,14 @@ class AutomateScreenshotsTest {
298325
with(compose) {
299326
wait { onNodeWithContentDescription(Res.string.app_name).isDisplayed() }
300327

301-
clickOnText(Res.string.TestResults_Overview_Tab_Label)
328+
clickOnText(Res.string.TestResults)
302329

303330
wait { onNodeWithText(Res.string.Test_Websites_Fullname).isDisplayed() }
304331

305332
Screengrab.screenshot("17-results")
306333

307334
Thread.sleep(3000)
308-
Screengrab.screenshot("2_" + locale())
335+
Screengrab.screenshot("3_" + locale())
309336

310337
clickOnText(Res.string.Test_Websites_Fullname)
311338

@@ -320,7 +347,7 @@ class AutomateScreenshotsTest {
320347
checkTextAnywhereInsideWebView("https://z-lib.org/")
321348

322349
Screengrab.screenshot("19-website-measurement-anomaly")
323-
Screengrab.screenshot("3_" + locale())
350+
Screengrab.screenshot("4_" + locale())
324351

325352
clickOnContentDescription(Res.string.Common_Back)
326353
wait { onNodeWithText(Res.string.Test_Websites_Fullname).isDisplayed() }
@@ -335,7 +362,7 @@ class AutomateScreenshotsTest {
335362
Screengrab.screenshot("20-dash-measurement")
336363

337364
Thread.sleep(3000)
338-
Screengrab.screenshot("4_" + locale())
365+
Screengrab.screenshot("5_" + locale())
339366
}
340367
}
341368

@@ -353,12 +380,13 @@ class AutomateScreenshotsTest {
353380
.isNotDisplayed()
354381
}
355382

383+
clickOnText(Res.string.Tests_Title)
356384
clickOnText(Res.string.Test_Websites_Fullname)
357385
wait { onNodeWithText(Res.string.Test_Websites_Fullname).isDisplayed() }
358386
clickOnText(Res.string.Dashboard_Overview_ChooseWebsites)
359387
wait { onNodeWithText(Res.string.Settings_Websites_CustomURL_Title).isDisplayed() }
360388
Screengrab.screenshot("21-choose-websites")
361-
Screengrab.screenshot("5_" + locale())
389+
Screengrab.screenshot("6_" + locale())
362390
}
363391
}
364392

@@ -387,14 +415,18 @@ class AutomateScreenshotsTest {
387415

388416
Screengrab.screenshot("1_${locale()}")
389417

418+
clickOnText(Res.string.Tests_Title)
419+
wait { onNodeWithContentDescription(Res.string.Test_Websites_Fullname).isDisplayed() }
420+
Screengrab.screenshot("2_${locale()}")
421+
390422
clickOnText(Res.string.Settings_Title)
391423

392424
wait { onNodeWithContentDescription(Res.string.Settings_About_Label).isDisplayed() }
393425

394426
clickOnText(Res.string.Settings_About_Label)
395427

396428
wait { onNodeWithTag("AboutScreen").isDisplayed() }
397-
Screengrab.screenshot("5_${locale()}")
429+
Screengrab.screenshot("6_${locale()}")
398430

399431
clickOnContentDescription(Res.string.Common_Back)
400432

@@ -403,21 +435,21 @@ class AutomateScreenshotsTest {
403435
wait { onNodeWithText(trustedName).isDisplayed() }
404436

405437
Thread.sleep(3000)
406-
Screengrab.screenshot("2_${locale()}")
438+
Screengrab.screenshot("3_${locale()}")
407439

408440
clickOnText(trustedName)
409441

410442
wait(10.seconds) { onNodeWithText("https://www.dw.com").isDisplayed() }
411443

412444
// Screenshot was coming up empty, so we need to explicitly sleep here
413445
Thread.sleep(3000)
414-
Screengrab.screenshot("3_${locale()}")
446+
Screengrab.screenshot("4_${locale()}")
415447

416448
clickOnText("https://www.dw.com")
417449

418450
checkTextAnywhereInsideWebView("https://www.dw.com")
419451

420-
Screengrab.screenshot("4_${locale()}")
452+
Screengrab.screenshot("5_${locale()}")
421453
}
422454
}
423455

composeApp/src/androidInstrumentedTest/kotlin/org/ooni/probe/uitesting/RunningTestsTest.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import ooniprobe.composeapp.generated.resources.Common_Expand
1111
import ooniprobe.composeapp.generated.resources.Dashboard_LastResults_SeeResults
1212
import ooniprobe.composeapp.generated.resources.Dashboard_RunTests_RunButton_Label
1313
import ooniprobe.composeapp.generated.resources.Dashboard_RunTests_SelectNone
14-
import ooniprobe.composeapp.generated.resources.Dashboard_LastResults_SeeResults
1514
import ooniprobe.composeapp.generated.resources.Measurement_Title
1615
import ooniprobe.composeapp.generated.resources.OONIRun_Run
1716
import ooniprobe.composeapp.generated.resources.Res

composeApp/src/androidInstrumentedTest/kotlin/org/ooni/probe/uitesting/helpers/StateTestHelpers.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ suspend fun skipOnboarding() {
88
listOf(
99
SettingsKey.FIRST_RUN to false,
1010
SettingsKey.TESTS_MOVED_NOTICE to true,
11-
)
11+
),
1212
)
1313
}
1414

composeApp/src/commonMain/kotlin/org/ooni/probe/ui/articles/ArticlesScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ fun ArticlesScreen(
7777
),
7878
state = lazyListState,
7979
) {
80-
items(state.articles, key = { it.url }) { article ->
80+
items(state.articles, key = { it.url.value }) { article ->
8181
ArticleCell(
8282
article = article,
8383
onClick = { onEvent(ArticlesViewModel.Event.ArticleClicked(article)) },
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.ooni.probe.data.repositories
2+
3+
import kotlinx.coroutines.Dispatchers
4+
import kotlinx.coroutines.flow.first
5+
import kotlinx.coroutines.test.runTest
6+
import org.ooni.probe.di.Dependencies
7+
import org.ooni.testing.createTestDatabaseDriver
8+
import org.ooni.testing.factories.ArticleModelFactory
9+
import kotlin.test.BeforeTest
10+
import kotlin.test.Test
11+
import kotlin.test.assertEquals
12+
import kotlin.test.assertFalse
13+
import kotlin.test.assertTrue
14+
15+
class ArticleRepositoryTest {
16+
private lateinit var subject: ArticleRepository
17+
18+
@BeforeTest
19+
fun before() {
20+
subject = ArticleRepository(
21+
database = Dependencies.buildDatabase(::createTestDatabaseDriver),
22+
backgroundContext = Dispatchers.Default,
23+
)
24+
}
25+
26+
@Test
27+
fun refreshAndList() =
28+
runTest {
29+
val articleToRemove = ArticleModelFactory.build()
30+
val articleToKeep = ArticleModelFactory.build()
31+
val articleToAdd = ArticleModelFactory.build()
32+
subject.refresh(listOf(articleToRemove, articleToKeep))
33+
subject.refresh(listOf(articleToKeep, articleToAdd))
34+
35+
val result = subject.list().first()
36+
37+
assertEquals(2, result.size)
38+
assertFalse(result.contains(articleToRemove))
39+
assertTrue(result.contains(articleToKeep))
40+
assertTrue(result.contains(articleToAdd))
41+
}
42+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.ooni.probe.domain.articles
2+
3+
import kotlinx.coroutines.test.runTest
4+
import org.ooni.engine.models.Success
5+
import kotlin.test.Test
6+
import kotlin.test.assertEquals
7+
import kotlin.test.assertTrue
8+
9+
class GetFindingsTest {
10+
@Test
11+
fun invoke() =
12+
runTest {
13+
val subject = GetFindings(
14+
httpDo = { _, _, _ -> Success(API_RESPONSE) },
15+
)
16+
17+
val articles = subject().get()!!
18+
assertEquals(2, articles.size)
19+
with(articles.first()) {
20+
assertTrue(url.value.endsWith("8025203600"))
21+
assertEquals("Indonesia blocked access to the Internet Archive", title)
22+
assertEquals("This report shares OONI data on the blocking of the Internet Archive in Indonesia in May 2025.", description)
23+
assertEquals(2025, time.year)
24+
}
25+
}
26+
27+
companion object {
28+
private const val API_RESPONSE =
29+
"{\"incidents\":[{\"id\":\"8025203600\",\"email_address\":\"\",\"title\":\"Indonesia blocked access to the Internet Archive\",\"short_description\":\"This report shares OONI data on the blocking of the Internet Archive in Indonesia in May 2025.\",\"slug\":\"2025-indonesia-blocked-access-to-the-internet-archive\",\"start_time\":\"2025-05-26T00:00:00.000000Z\",\"create_time\":\"2025-06-13T07:35:49.000000Z\",\"update_time\":\"2025-06-13T07:35:49.000000Z\",\"end_time\":\"2025-05-29T00:00:00.000000Z\",\"reported_by\":\"Elizaveta Yachmeneva, Maria Xynou\",\"creator_account_id\":\"\",\"published\":true,\"event_type\":\"incident\",\"ASNs\":[23693,63859,24203,17451,136119,7713,18004,23951,139447],\"CCs\":[\"ID\"],\"themes\":[],\"tags\":[\"censorship\",\"archive.org\"],\"test_names\":[\"web_connectivity\"],\"domains\":[\"archive.org\"],\"links\":[],\"mine\":false},{\"id\":\"178720534001\",\"email_address\":\"\",\"title\":\"Malaysia blocked MalaysiaNow and website of former MP\",\"short_description\":\"This report shares OONI data on the blocking of news media outlet MalaysiaNow and of a website which belongs to a former Malaysian Member of Parliament (Wee Choo Keong). \",\"slug\":null,\"start_time\":\"2023-06-28T00:00:00.000000Z\",\"create_time\":\"2023-12-19T09:07:46.000000Z\",\"update_time\":\"2025-06-02T11:50:22.000000Z\",\"end_time\":\"2024-09-07T00:00:00.000000Z\",\"reported_by\":\"Maria Xynou\",\"creator_account_id\":\"\",\"published\":true,\"event_type\":\"incident\",\"ASNs\":[10030,4788,4818,9534,38466,45960,38322,4818],\"CCs\":[\"MY\"],\"themes\":[\"news_media\"],\"tags\":[\"censorship\",\"MalaysiaNow\",\"Wee Choo Keong\"],\"test_names\":[\"web_connectivity\"],\"domains\":[\"www.malaysianow.com\",\"weechookeong.com\"],\"links\":[],\"mine\":false}]}"
30+
}
31+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.ooni.probe.domain.articles
2+
3+
import kotlinx.coroutines.test.runTest
4+
import org.ooni.engine.models.Success
5+
import org.ooni.probe.data.models.ArticleModel
6+
import kotlin.test.Test
7+
import kotlin.test.assertEquals
8+
9+
class GetRssFeedTest {
10+
@Test
11+
fun invoke() =
12+
runTest {
13+
val subject = GetRSSFeed(
14+
httpDo = { _, _, _ -> Success(RSS_FEED) },
15+
url = "https://example.org",
16+
source = ArticleModel.Source.Blog,
17+
)
18+
19+
val articles = subject().get()!!
20+
assertEquals(1, articles.size)
21+
with(articles.first()) {
22+
assertEquals("https://ooni.org/post/2025-gg-omg-village/", url.value)
23+
assertEquals("Join us at the OMG Village at the Global Gathering 2025!", title)
24+
assertEquals(2025, time.year)
25+
}
26+
}
27+
28+
companion object {
29+
private const val RSS_FEED =
30+
"<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?><rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"><channel><title>Blog posts on OONI: Open Observatory of Network Interference</title><link>https://ooni.org/blog/</link><description>Recent content in Blog posts on OONI: Open Observatory of Network Interference</description><generator>Hugo</generator><language>en</language><atom:link href=\"https://ooni.org/blog/index.xml\" rel=\"self\" type=\"application/rss+xml\"/><item><title>Join us at the OMG Village at the Global Gathering 2025!</title><link>https://ooni.org/post/2025-gg-omg-village/</link><pubDate>Mon, 01 Sep 2025 00:00:00 +0000</pubDate><guid>https://ooni.org/post/2025-gg-omg-village/</guid><description>&lt;p>Are you attending the upcoming &lt;a href=\"https://wiki.digitalrights.community/index.php?title=Global_Gathering_2025\">Global Gathering&lt;/a> event in Estoril, Portugal? Are you interested in investigating internet shutdowns and censorship, and curious to learn more about the tools and open datasets that support this work?&lt;/p></description></item></channel></rss>"
31+
}
32+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.ooni.probe.domain.results
2+
3+
import kotlinx.coroutines.flow.first
4+
import kotlinx.coroutines.flow.flowOf
5+
import kotlinx.coroutines.test.runTest
6+
import org.ooni.testing.factories.ResultModelFactory
7+
import kotlin.test.Test
8+
import kotlin.test.assertEquals
9+
import kotlin.test.assertNull
10+
import kotlin.test.assertTrue
11+
12+
class GetLastRunTest {
13+
@Test
14+
fun nullWhenNoResults() =
15+
runTest {
16+
val subject = GetLastRun(
17+
getLastResults = { _ -> flowOf(emptyList()) },
18+
getPreference = { _ -> flowOf(null) },
19+
)
20+
assertNull(subject().first())
21+
}
22+
23+
@Test
24+
fun nullWhenResultIsDismissed() =
25+
runTest {
26+
val result1 = ResultModelFactory.buildWithNetworkAndAggregates(
27+
result = ResultModelFactory.build(descriptorName = "websites"),
28+
)
29+
30+
val subject = GetLastRun(
31+
getLastResults = { _ -> flowOf(listOf(result1)) },
32+
getPreference = { _ -> flowOf(result1.result.id?.value) },
33+
)
34+
35+
assertNull(subject().first())
36+
}
37+
38+
@Test
39+
fun doesNotRepeatDescriptors() =
40+
runTest {
41+
val result1 = ResultModelFactory.buildWithNetworkAndAggregates(
42+
result = ResultModelFactory.build(descriptorName = "websites", isDone = true),
43+
)
44+
val result2 = ResultModelFactory.buildWithNetworkAndAggregates(
45+
result = ResultModelFactory.build(descriptorName = "websites", isDone = true),
46+
)
47+
48+
val subject = GetLastRun(
49+
getLastResults = { _ -> flowOf(listOf(result1, result2)) },
50+
getPreference = { _ -> flowOf(null) },
51+
)
52+
53+
val run = subject().first()!!
54+
assertEquals(1, run.results.size)
55+
assertTrue(run.results.contains(result1))
56+
}
57+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.ooni.testing.factories
2+
3+
import kotlinx.datetime.LocalDate
4+
import kotlinx.datetime.LocalDateTime
5+
import kotlinx.datetime.atTime
6+
import org.ooni.probe.data.models.ArticleModel
7+
import org.ooni.probe.shared.today
8+
import kotlin.random.Random
9+
10+
object ArticleModelFactory {
11+
fun build(
12+
url: ArticleModel.Url = ArticleModel.Url("https://example.org/${Random.nextInt()}"),
13+
title: String = "Title",
14+
description: String? = null,
15+
time: LocalDateTime = LocalDate.today().atTime(0, 0),
16+
source: ArticleModel.Source = ArticleModel.Source.Blog,
17+
) = ArticleModel(
18+
url = url,
19+
title = title,
20+
description = description,
21+
time = time,
22+
source = source,
23+
)
24+
}

0 commit comments

Comments
 (0)