Skip to content

Commit 6a28814

Browse files
authored
Merge pull request #1526 from vector-im/feature/bma/konsist
Konsist
2 parents 7c6397d + 7c5a41f commit 6a28814

File tree

159 files changed

+1580
-985
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

159 files changed

+1580
-985
lines changed

.idea/dictionaries/shared.xml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CONTRIBUTING.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* [knit](#knit)
1919
* [lint](#lint)
2020
* [Unit tests](#unit-tests)
21+
* [konsist](#konsist)
2122
* [Tests](#tests)
2223
* [Accessibility](#accessibility)
2324
* [Jetpack Compose](#jetpack-compose)
@@ -156,6 +157,10 @@ Make sure the following commands execute without any error:
156157
./gradlew test
157158
</pre>
158159

160+
#### konsist
161+
162+
[konsist](https://github.com/LemonAppDev/konsist) is setup in the project to check that the architecture and the naming rules are followed. Konsist tests are classical Unit tests.
163+
159164
### Tests
160165

161166
Element X is currently supported on Android Marshmallow (API 23+): please test your change on an Android device (or Android emulator) running with API 23. Many issues can happen (including crashes) on older devices.

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ dependencies {
230230
testImplementation(libs.test.truth)
231231
testImplementation(libs.test.turbine)
232232
testImplementation(projects.libraries.matrix.test)
233+
testImplementation(libs.test.konsist)
233234

234235
ksp(libs.showkase.processor)
235236
}

app/src/main/kotlin/io/element/android/x/MainActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import com.bumble.appyx.core.integrationpoint.NodeComponentActivity
3434
import com.bumble.appyx.core.plugin.NodeReadyObserver
3535
import io.element.android.libraries.architecture.bindings
3636
import io.element.android.libraries.core.log.logger.LoggerTag
37-
import io.element.android.libraries.designsystem.utils.LocalSnackbarDispatcher
37+
import io.element.android.libraries.designsystem.utils.snackbar.LocalSnackbarDispatcher
3838
import io.element.android.libraries.theme.ElementTheme
3939
import io.element.android.x.di.AppBindings
4040
import io.element.android.x.intent.SafeUriHandler

app/src/main/kotlin/io/element/android/x/di/AppBindings.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package io.element.android.x.di
1818

1919
import com.squareup.anvil.annotations.ContributesTo
2020
import io.element.android.features.rageshake.api.reporter.BugReporter
21-
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
21+
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
2222
import io.element.android.libraries.di.AppScope
2323
import io.element.android.libraries.matrix.api.tracing.TracingService
2424

app/src/main/kotlin/io/element/android/x/di/AppModule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import io.element.android.features.messages.impl.timeline.components.customreact
2828
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
2929
import io.element.android.libraries.core.meta.BuildMeta
3030
import io.element.android.libraries.core.meta.BuildType
31-
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
31+
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
3232
import io.element.android.libraries.di.AppScope
3333
import io.element.android.libraries.di.ApplicationContext
3434
import io.element.android.libraries.di.DefaultPreferences
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Copyright (c) 2023 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.app
18+
19+
import androidx.compose.runtime.Composable
20+
import com.lemonappdev.konsist.api.KoModifier
21+
import com.lemonappdev.konsist.api.Konsist
22+
import com.lemonappdev.konsist.api.ext.list.constructors
23+
import com.lemonappdev.konsist.api.ext.list.modifierprovider.withoutModifier
24+
import com.lemonappdev.konsist.api.ext.list.modifierprovider.withoutOverrideModifier
25+
import com.lemonappdev.konsist.api.ext.list.parameters
26+
import com.lemonappdev.konsist.api.ext.list.properties
27+
import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf
28+
import com.lemonappdev.konsist.api.ext.list.withAllParentsOf
29+
import com.lemonappdev.konsist.api.ext.list.withNameEndingWith
30+
import com.lemonappdev.konsist.api.ext.list.withReturnType
31+
import com.lemonappdev.konsist.api.ext.list.withTopLevel
32+
import com.lemonappdev.konsist.api.ext.list.withoutName
33+
import com.lemonappdev.konsist.api.ext.list.withoutNameEndingWith
34+
import com.lemonappdev.konsist.api.verify.assertFalse
35+
import com.lemonappdev.konsist.api.verify.assertTrue
36+
import io.element.android.libraries.architecture.Presenter
37+
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
38+
import org.junit.Test
39+
40+
class KonsistTest {
41+
42+
@Test
43+
fun `Classes extending 'Presenter' should have 'Presenter' suffix`() {
44+
Konsist.scopeFromProject()
45+
.classes()
46+
.withAllParentsOf(Presenter::class)
47+
.assertTrue {
48+
it.name.endsWith("Presenter")
49+
}
50+
}
51+
52+
@Test
53+
fun `Functions with '@PreviewsDayNight' annotation should have 'Preview' suffix`() {
54+
Konsist
55+
.scopeFromProject()
56+
.functions()
57+
.withAllAnnotationsOf(PreviewsDayNight::class)
58+
.assertTrue {
59+
it.hasNameEndingWith("Preview") &&
60+
it.hasNameEndingWith("LightPreview").not() &&
61+
it.hasNameEndingWith("DarkPreview").not()
62+
}
63+
}
64+
65+
@Test
66+
fun `Top level function with '@Composable' annotation starting with a upper case should be placed in a file with the same name`() {
67+
Konsist
68+
.scopeFromProject()
69+
.functions()
70+
.withTopLevel()
71+
.withoutModifier(KoModifier.PRIVATE)
72+
.withoutNameEndingWith("Preview")
73+
.withAllAnnotationsOf(Composable::class)
74+
.withoutName(
75+
// Add some exceptions...
76+
"OutlinedButton",
77+
"TextButton",
78+
"SimpleAlertDialogContent",
79+
)
80+
.assertTrue(
81+
additionalMessage =
82+
"""
83+
Please check the filename. It should match the top level Composable function. If the filename is correct:
84+
- consider making the Composable private or moving it to its own file
85+
- at last resort, you can add an exception in the Konsist test
86+
""".trimIndent()
87+
) {
88+
if (it.name.first().isLowerCase()) {
89+
true
90+
} else {
91+
val fileName = it.containingFile.name.removeSuffix(".kt")
92+
fileName == it.name
93+
}
94+
}
95+
}
96+
97+
@Test
98+
fun `Data class state MUST not have default value`() {
99+
Konsist
100+
.scopeFromProject()
101+
.classes()
102+
.withNameEndingWith("State")
103+
.withoutName(
104+
"CameraPositionState",
105+
)
106+
.constructors
107+
.parameters
108+
.assertTrue { parameterDeclaration ->
109+
parameterDeclaration.defaultValue == null &&
110+
// Using parameterDeclaration.defaultValue == null is not enough apparently,
111+
// Also check that the text does not contain an equal sign
112+
parameterDeclaration.text.contains("=").not()
113+
}
114+
}
115+
116+
@Test
117+
fun `Function which creates Presenter in test MUST be named 'createPresenterName'`() {
118+
Konsist
119+
.scopeFromTest()
120+
.functions()
121+
.withReturnType { it.name.endsWith("Presenter") }
122+
.withoutOverrideModifier()
123+
.assertTrue { functionDeclaration ->
124+
functionDeclaration.name == "create${functionDeclaration.returnType?.name}"
125+
}
126+
}
127+
128+
@Test
129+
fun `no field should have 'm' prefix`() {
130+
Konsist
131+
.scopeFromProject()
132+
.classes()
133+
.properties()
134+
.assertFalse {
135+
val secondCharacterIsUppercase = it.name.getOrNull(1)?.isUpperCase() ?: false
136+
it.name.startsWith('m') && secondCharacterIsUppercase
137+
}
138+
}
139+
}

appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
package io.element.android.appnav
1818

19-
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
20-
import io.element.android.libraries.designsystem.utils.SnackbarMessage
19+
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
20+
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
2121
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
2222
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
2323
import io.element.android.libraries.matrix.api.verification.VerificationFlowState

appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ import io.element.android.libraries.architecture.animation.rememberDefaultTransi
5858
import io.element.android.libraries.architecture.createNode
5959
import io.element.android.libraries.architecture.waitForChildAttached
6060
import io.element.android.libraries.deeplink.DeeplinkData
61-
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
61+
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
6262
import io.element.android.libraries.di.SessionScope
6363
import io.element.android.libraries.matrix.api.MatrixClient
6464
import io.element.android.libraries.matrix.api.core.MAIN_SPACE

appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class RootPresenterTest {
4242

4343
@Test
4444
fun `present - initial state`() = runTest {
45-
val presenter = createPresenter()
45+
val presenter = createRootPresenter()
4646
moleculeFlow(RecompositionMode.Immediate) {
4747
presenter.present()
4848
}.test {
@@ -54,7 +54,7 @@ class RootPresenterTest {
5454

5555
@Test
5656
fun `present - passes app error state`() = runTest {
57-
val presenter = createPresenter(
57+
val presenter = createRootPresenter(
5858
appErrorService = DefaultAppErrorStateService().apply {
5959
showError("Bad news", "Something bad happened")
6060
}
@@ -75,7 +75,7 @@ class RootPresenterTest {
7575
}
7676
}
7777

78-
private fun createPresenter(
78+
private fun createRootPresenter(
7979
appErrorService: AppErrorStateService = DefaultAppErrorStateService()
8080
): RootPresenter {
8181
val crashDataStore = FakeCrashDataStore()

0 commit comments

Comments
 (0)