Konsist: add test Data class state MUST not have default value, and fix existing issues

This commit is contained in:
Benoit Marty 2023-10-10 17:29:58 +02:00 committed by Benoit Marty
parent 308a0c0f57
commit de31f8a73a
13 changed files with 135 additions and 56 deletions

View file

@ -22,6 +22,7 @@ import com.lemonappdev.konsist.api.Konsist
import com.lemonappdev.konsist.api.ext.list.modifierprovider.withoutModifier import com.lemonappdev.konsist.api.ext.list.modifierprovider.withoutModifier
import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf
import com.lemonappdev.konsist.api.ext.list.withAllParentsOf import com.lemonappdev.konsist.api.ext.list.withAllParentsOf
import com.lemonappdev.konsist.api.ext.list.withNameEndingWith
import com.lemonappdev.konsist.api.ext.list.withTopLevel import com.lemonappdev.konsist.api.ext.list.withTopLevel
import com.lemonappdev.konsist.api.ext.list.withoutName import com.lemonappdev.konsist.api.ext.list.withoutName
import com.lemonappdev.konsist.api.ext.list.withoutNameEndingWith import com.lemonappdev.konsist.api.ext.list.withoutNameEndingWith
@ -84,4 +85,19 @@ class KonsistTest {
} }
} }
} }
@Test
fun `Data class state MUST not have default value`() {
Konsist
.scopeFromProject()
.classes()
.withNameEndingWith("State")
.assertTrue { classDeclaration ->
classDeclaration.constructors.all { constructorDeclaration ->
constructorDeclaration.parameters.all { parameterDeclaration ->
parameterDeclaration.defaultValue == null
}
}
}
}
} }

View file

@ -17,9 +17,9 @@
package io.element.android.features.location.impl.common.permissions package io.element.android.features.location.impl.common.permissions
data class PermissionsState( data class PermissionsState(
val permissions: Permissions = Permissions.NoneGranted, val permissions: Permissions,
val shouldShowRationale: Boolean = false, val shouldShowRationale: Boolean,
val eventSink: (PermissionsEvents) -> Unit = {}, val eventSink: (PermissionsEvents) -> Unit,
) { ) {
sealed interface Permissions { sealed interface Permissions {
data object AllGranted : Permissions data object AllGranted : Permissions

View file

@ -17,11 +17,11 @@
package io.element.android.features.location.impl.send package io.element.android.features.location.impl.send
data class SendLocationState( data class SendLocationState(
val permissionDialog: Dialog = Dialog.None, val permissionDialog: Dialog,
val mode: Mode = Mode.PinLocation, val mode: Mode,
val hasLocationPermission: Boolean = false, val hasLocationPermission: Boolean,
val appName: String = "AppName", val appName: String,
val eventSink: (SendLocationEvents) -> Unit = {}, val eventSink: (SendLocationEvents) -> Unit,
) { ) {
sealed interface Mode { sealed interface Mode {
data object SenderLocation : Mode data object SenderLocation : Mode

View file

@ -23,35 +23,44 @@ private const val APP_NAME = "ApplicationName"
class SendLocationStateProvider : PreviewParameterProvider<SendLocationState> { class SendLocationStateProvider : PreviewParameterProvider<SendLocationState> {
override val values: Sequence<SendLocationState> override val values: Sequence<SendLocationState>
get() = sequenceOf( get() = sequenceOf(
SendLocationState( aSendLocationState(
permissionDialog = SendLocationState.Dialog.None, permissionDialog = SendLocationState.Dialog.None,
mode = SendLocationState.Mode.PinLocation, mode = SendLocationState.Mode.PinLocation,
hasLocationPermission = false, hasLocationPermission = false,
appName = APP_NAME,
), ),
SendLocationState( aSendLocationState(
permissionDialog = SendLocationState.Dialog.PermissionDenied, permissionDialog = SendLocationState.Dialog.PermissionDenied,
mode = SendLocationState.Mode.PinLocation, mode = SendLocationState.Mode.PinLocation,
hasLocationPermission = false, hasLocationPermission = false,
appName = APP_NAME,
), ),
SendLocationState( aSendLocationState(
permissionDialog = SendLocationState.Dialog.PermissionRationale, permissionDialog = SendLocationState.Dialog.PermissionRationale,
mode = SendLocationState.Mode.PinLocation, mode = SendLocationState.Mode.PinLocation,
hasLocationPermission = false, hasLocationPermission = false,
appName = APP_NAME,
), ),
SendLocationState( aSendLocationState(
permissionDialog = SendLocationState.Dialog.None, permissionDialog = SendLocationState.Dialog.None,
mode = SendLocationState.Mode.PinLocation, mode = SendLocationState.Mode.PinLocation,
hasLocationPermission = true, hasLocationPermission = true,
appName = APP_NAME,
), ),
SendLocationState( aSendLocationState(
permissionDialog = SendLocationState.Dialog.None, permissionDialog = SendLocationState.Dialog.None,
mode = SendLocationState.Mode.SenderLocation, mode = SendLocationState.Mode.SenderLocation,
hasLocationPermission = true, hasLocationPermission = true,
appName = APP_NAME,
), ),
) )
} }
private fun aSendLocationState(
permissionDialog: SendLocationState.Dialog,
mode: SendLocationState.Mode,
hasLocationPermission: Boolean,
): SendLocationState {
return SendLocationState(
permissionDialog = permissionDialog,
mode = mode,
hasLocationPermission = hasLocationPermission,
appName = APP_NAME,
eventSink = {}
)
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.location.impl
import io.element.android.features.location.impl.common.permissions.PermissionsState
fun aPermissionsState(
permissions: PermissionsState.Permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale: Boolean = false,
): PermissionsState {
return PermissionsState(
permissions = permissions,
shouldShowRationale = shouldShowRationale,
eventSink = {},
)
}

View file

@ -26,7 +26,11 @@ class PermissionsPresenterFake : PermissionsPresenter {
events += event events += event
} }
private var state = PermissionsState(eventSink = ::handleEvent) private var state = PermissionsState(
permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false,
eventSink = ::handleEvent
)
set(value) { set(value) {
field = value.copy(eventSink = ::handleEvent) field = value.copy(eventSink = ::handleEvent)
} }

View file

@ -22,6 +22,7 @@ import app.cash.turbine.test
import com.google.common.truth.Truth import com.google.common.truth.Truth
import im.vector.app.features.analytics.plan.Composer import im.vector.app.features.analytics.plan.Composer
import io.element.android.features.location.api.Location import io.element.android.features.location.api.Location
import io.element.android.features.location.impl.aPermissionsState
import io.element.android.features.location.impl.common.actions.FakeLocationActions import io.element.android.features.location.impl.common.actions.FakeLocationActions
import io.element.android.features.location.impl.common.permissions.PermissionsEvents import io.element.android.features.location.impl.common.permissions.PermissionsEvents
import io.element.android.features.location.impl.common.permissions.PermissionsPresenter import io.element.android.features.location.impl.common.permissions.PermissionsPresenter
@ -65,7 +66,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `initial state with permissions granted`() = runTest { fun `initial state with permissions granted`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.AllGranted, permissions = PermissionsState.Permissions.AllGranted,
shouldShowRationale = false, shouldShowRationale = false,
) )
@ -92,7 +93,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `initial state with permissions partially granted`() = runTest { fun `initial state with permissions partially granted`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.SomeGranted, permissions = PermissionsState.Permissions.SomeGranted,
shouldShowRationale = false, shouldShowRationale = false,
) )
@ -119,7 +120,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `initial state with permissions denied`() = runTest { fun `initial state with permissions denied`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false, shouldShowRationale = false,
) )
@ -145,7 +146,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `initial state with permissions denied once`() = runTest { fun `initial state with permissions denied once`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = true, shouldShowRationale = true,
) )
@ -171,7 +172,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `rationale dialog dismiss`() = runTest { fun `rationale dialog dismiss`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = true, shouldShowRationale = true,
) )
@ -202,7 +203,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `rationale dialog continue`() = runTest { fun `rationale dialog continue`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = true, shouldShowRationale = true,
) )
@ -230,7 +231,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `permission denied dialog dismiss`() = runTest { fun `permission denied dialog dismiss`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false, shouldShowRationale = false,
) )
@ -261,7 +262,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `share sender location`() = runTest { fun `share sender location`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.AllGranted, permissions = PermissionsState.Permissions.AllGranted,
shouldShowRationale = false, shouldShowRationale = false,
) )
@ -317,7 +318,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `share pin location`() = runTest { fun `share pin location`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false, shouldShowRationale = false,
) )
@ -373,7 +374,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `composer context passes through analytics`() = runTest { fun `composer context passes through analytics`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false, shouldShowRationale = false,
) )
@ -419,7 +420,7 @@ class SendLocationPresenterTest {
@Test @Test
fun `open settings activity`() = runTest { fun `open settings activity`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false, shouldShowRationale = false,
) )

View file

@ -21,6 +21,7 @@ import app.cash.molecule.moleculeFlow
import app.cash.turbine.test import app.cash.turbine.test
import com.google.common.truth.Truth import com.google.common.truth.Truth
import io.element.android.features.location.api.Location import io.element.android.features.location.api.Location
import io.element.android.features.location.impl.aPermissionsState
import io.element.android.features.location.impl.common.actions.FakeLocationActions import io.element.android.features.location.impl.common.actions.FakeLocationActions
import io.element.android.features.location.impl.common.permissions.PermissionsEvents import io.element.android.features.location.impl.common.permissions.PermissionsEvents
import io.element.android.features.location.impl.common.permissions.PermissionsPresenter import io.element.android.features.location.impl.common.permissions.PermissionsPresenter
@ -55,7 +56,7 @@ class ShowLocationPresenterTest {
@Test @Test
fun `emits initial state with no location permission`() = runTest { fun `emits initial state with no location permission`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false, shouldShowRationale = false,
) )
@ -75,7 +76,7 @@ class ShowLocationPresenterTest {
@Test @Test
fun `emits initial state location permission denied once`() = runTest { fun `emits initial state location permission denied once`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = true, shouldShowRationale = true,
) )
@ -94,7 +95,7 @@ class ShowLocationPresenterTest {
@Test @Test
fun `emits initial state with location permission`() = runTest { fun `emits initial state with location permission`() = runTest {
permissionsPresenterFake.givenState(PermissionsState(permissions = PermissionsState.Permissions.AllGranted)) permissionsPresenterFake.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -109,7 +110,7 @@ class ShowLocationPresenterTest {
@Test @Test
fun `emits initial state with partial location permission`() = runTest { fun `emits initial state with partial location permission`() = runTest {
permissionsPresenterFake.givenState(PermissionsState(permissions = PermissionsState.Permissions.SomeGranted)) permissionsPresenterFake.givenState(aPermissionsState(permissions = PermissionsState.Permissions.SomeGranted))
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -137,7 +138,7 @@ class ShowLocationPresenterTest {
@Test @Test
fun `centers on user location`() = runTest { fun `centers on user location`() = runTest {
permissionsPresenterFake.givenState(PermissionsState(permissions = PermissionsState.Permissions.AllGranted)) permissionsPresenterFake.givenState(aPermissionsState(permissions = PermissionsState.Permissions.AllGranted))
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@ -166,7 +167,7 @@ class ShowLocationPresenterTest {
@Test @Test
fun `rationale dialog dismiss`() = runTest { fun `rationale dialog dismiss`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = true, shouldShowRationale = true,
) )
@ -197,7 +198,7 @@ class ShowLocationPresenterTest {
@Test @Test
fun `rationale dialog continue`() = runTest { fun `rationale dialog continue`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = true, shouldShowRationale = true,
) )
@ -225,7 +226,7 @@ class ShowLocationPresenterTest {
@Test @Test
fun `permission denied dialog dismiss`() = runTest { fun `permission denied dialog dismiss`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false, shouldShowRationale = false,
) )
@ -256,7 +257,7 @@ class ShowLocationPresenterTest {
@Test @Test
fun `open settings activity`() = runTest { fun `open settings activity`() = runTest {
permissionsPresenterFake.givenState( permissionsPresenterFake.givenState(
PermissionsState( aPermissionsState(
permissions = PermissionsState.Permissions.NoneGranted, permissions = PermissionsState.Permissions.NoneGranted,
shouldShowRationale = false, shouldShowRationale = false,
) )
@ -290,7 +291,6 @@ class ShowLocationPresenterTest {
} }
} }
companion object { companion object {
private const val A_DESCRIPTION = "My happy place" private const val A_DESCRIPTION = "My happy place"
} }

View file

@ -19,15 +19,14 @@ package io.element.android.features.roomdetails.impl.invite
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
data class RoomInviteMembersState( data class RoomInviteMembersState(
val canInvite: Boolean = false, val canInvite: Boolean,
val searchQuery: String = "", val searchQuery: String,
val searchResults: SearchBarResultState<ImmutableList<InvitableUser>> = SearchBarResultState.NotSearching(), val searchResults: SearchBarResultState<ImmutableList<InvitableUser>>,
val selectedUsers: ImmutableList<MatrixUser> = persistentListOf(), val selectedUsers: ImmutableList<MatrixUser>,
val isSearchActive: Boolean = false, val isSearchActive: Boolean,
val eventSink: (RoomInviteMembersEvents) -> Unit = {}, val eventSink: (RoomInviteMembersEvents) -> Unit,
) )
data class InvitableUser( data class InvitableUser(

View file

@ -18,20 +18,22 @@ package io.element.android.features.roomdetails.impl.invite
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUser
import io.element.android.libraries.matrix.ui.components.aMatrixUserList import io.element.android.libraries.matrix.ui.components.aMatrixUserList
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
internal class RoomInviteMembersStateProvider : PreviewParameterProvider<RoomInviteMembersState> { internal class RoomInviteMembersStateProvider : PreviewParameterProvider<RoomInviteMembersState> {
override val values: Sequence<RoomInviteMembersState> override val values: Sequence<RoomInviteMembersState>
get() = sequenceOf( get() = sequenceOf(
RoomInviteMembersState(), aRoomInviteMembersState(),
RoomInviteMembersState(canInvite = true, selectedUsers = aMatrixUserList().toImmutableList()), aRoomInviteMembersState(canInvite = true, selectedUsers = aMatrixUserList().toImmutableList()),
RoomInviteMembersState(isSearchActive = true, searchQuery = "some query"), aRoomInviteMembersState(isSearchActive = true, searchQuery = "some query"),
RoomInviteMembersState(isSearchActive = true, searchQuery = "some query", selectedUsers = aMatrixUserList().toImmutableList()), aRoomInviteMembersState(isSearchActive = true, searchQuery = "some query", selectedUsers = aMatrixUserList().toImmutableList()),
RoomInviteMembersState(isSearchActive = true, searchQuery = "some query", searchResults = SearchBarResultState.NoResults()), aRoomInviteMembersState(isSearchActive = true, searchQuery = "some query", searchResults = SearchBarResultState.NoResults()),
RoomInviteMembersState( aRoomInviteMembersState(
isSearchActive = true, isSearchActive = true,
canInvite = true, canInvite = true,
searchQuery = "some query", searchQuery = "some query",
@ -48,7 +50,7 @@ internal class RoomInviteMembersStateProvider : PreviewParameterProvider<RoomInv
) )
) )
), ),
RoomInviteMembersState( aRoomInviteMembersState(
isSearchActive = true, isSearchActive = true,
canInvite = true, canInvite = true,
searchQuery = "@alice:server.org", searchQuery = "@alice:server.org",
@ -64,3 +66,20 @@ internal class RoomInviteMembersStateProvider : PreviewParameterProvider<RoomInv
), ),
) )
} }
private fun aRoomInviteMembersState(
canInvite: Boolean = false,
searchQuery: String = "",
searchResults: SearchBarResultState<ImmutableList<InvitableUser>> = SearchBarResultState.NotSearching(),
selectedUsers: ImmutableList<MatrixUser> = persistentListOf(),
isSearchActive: Boolean = false,
): RoomInviteMembersState {
return RoomInviteMembersState(
canInvite = canInvite,
searchQuery = searchQuery,
searchResults = searchResults,
selectedUsers = selectedUsers,
isSearchActive = isSearchActive,
eventSink = {},
)
}

View file

@ -23,7 +23,7 @@ data class RoomMemberDetailsState(
val userName: String?, val userName: String?,
val avatarUrl: String?, val avatarUrl: String?,
val isBlocked: Async<Boolean>, val isBlocked: Async<Boolean>,
val displayConfirmationDialog: ConfirmationDialog? = null, val displayConfirmationDialog: ConfirmationDialog?,
val isCurrentUser: Boolean, val isCurrentUser: Boolean,
val eventSink: (RoomMemberDetailsEvents) -> Unit val eventSink: (RoomMemberDetailsEvents) -> Unit
) { ) {

View file

@ -37,6 +37,7 @@ fun aRoomMemberDetailsState() = RoomMemberDetailsState(
userName = "Daniel", userName = "Daniel",
avatarUrl = null, avatarUrl = null,
isBlocked = Async.Success(false), isBlocked = Async.Success(false),
displayConfirmationDialog = null,
isCurrentUser = false, isCurrentUser = false,
eventSink = {}, eventSink = {},
) )

View file

@ -50,7 +50,7 @@ internal class SymbolNode(
* @param position the initial symbol position * @param position the initial symbol position
*/ */
public class SymbolState( public class SymbolState(
position: LatLng = LatLng(0.0, 0.0) position: LatLng
) { ) {
/** /**
* Current position of the symbol. * Current position of the symbol.