Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.github.droidkaigi.confsched.common.compose

import androidx.compose.runtime.staticCompositionLocalOf

val LocalTestMode = staticCompositionLocalOf { false }
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.github.droidkaigi.confsched.data.profile

import dev.zacsweers.metro.Inject
import io.github.droidkaigi.confsched.model.profile.ProfileSubscriptionKey
import io.github.droidkaigi.confsched.model.profile.ProfileWithImages
import kotlinx.coroutines.flow.map
import qrcode.QRCode
import soil.query.SubscriptionId
import soil.query.buildSubscriptionKey

@Inject
public class FakeProfileSubscriptionKey(
private val dataStore: ProfileDataStore,
) : ProfileSubscriptionKey by buildSubscriptionKey(
id = SubscriptionId("profile"),
subscribe = {
dataStore.getProfileOrNull().map { profile ->
if (profile == null) return@map ProfileWithImages()

if (profile.imagePath.isEmpty() || profile.link.isEmpty()) return@map ProfileWithImages(profile)

val qrImageByteArray = QRCode.ofSquares()
.build(profile.link)
.renderToBytes()

// For testing, we'll provide a dummy image that can be decoded
val profileImageByteArray = if (profile.imagePath.isNotBlank()) {
byteArrayOf(
0x89.toByte(), 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, // PNG signature
0x00, 0x00, 0x00, 0x0D,
0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
0x08, 0x06, 0x00, 0x00, 0x00,
0x1F, 0x15, 0xC4.toByte(), 0x89.toByte(),
0x00, 0x00, 0x00, 0x0A,
0x49, 0x44, 0x41, 0x54,
0x78.toByte(), 0x9C.toByte(), 0x63, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x01,
0x0D, 0x0A, 0x2D, 0xB4.toByte(),
0x00, 0x00, 0x00, 0x00,
0x49, 0x45, 0x4E, 0x44,
0xAE.toByte(), 0x42, 0x60, 0x82.toByte(),
)
} else {
null
}

ProfileWithImages(
profile = profile,
profileImageByteArray = profileImageByteArray,
qrImageByteArray = qrImageByteArray,
)
}
},
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import io.github.droidkaigi.confsched.data.DataScope
import io.github.droidkaigi.confsched.data.ProfileDataStoreQualifier
import io.github.droidkaigi.confsched.model.profile.Profile
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.serialization.json.Json

@SingleIn(DataScope::class)
@Inject
public class ProfileDataStore(
@param:ProfileDataStoreQualifier private val dataStore: DataStore<Preferences>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,12 @@ data class Profile(
val imagePath: String = "",
val theme: ProfileCardTheme = ProfileCardTheme.DarkPill,
)

fun Profile.Companion.fake(): Profile {
return Profile(
nickName = "User",
occupation = "Developer",
link = "https://2025.droidkaigi.jp",
imagePath = "image.png",
)
}
1 change: 1 addition & 0 deletions core/testing/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ kotlin {
implementation(projects.core.data)
implementation(projects.feature.sessions)
implementation(projects.feature.about)
implementation(projects.feature.profile)
implementation(projects.feature.contributors)
implementation(projects.feature.eventmap)
implementation(projects.feature.staff)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import io.github.droidkaigi.confsched.data.about.FakeBuildConfigProvider
import io.github.droidkaigi.confsched.data.about.FakeLicensesJsonReader
import io.github.droidkaigi.confsched.data.contributors.DefaultContributorsApiClient
import io.github.droidkaigi.confsched.data.eventmap.DefaultEventMapApiClient
import io.github.droidkaigi.confsched.data.profile.DefaultProfileSubscriptionKey
import io.github.droidkaigi.confsched.data.sessions.DefaultSessionsApiClient
import io.github.droidkaigi.confsched.data.staff.DefaultStaffApiClient
import kotlinx.coroutines.CoroutineDispatcher
Expand All @@ -25,6 +26,7 @@ import kotlinx.coroutines.CoroutineDispatcher
DefaultContributorsApiClient::class,
DefaultEventMapApiClient::class,
DefaultStaffApiClient::class,
DefaultProfileSubscriptionKey::class,
CoroutineDispatcher::class,
],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import io.github.droidkaigi.confsched.common.compose.LocalTestMode
import io.github.droidkaigi.confsched.designsystem.theme.KaigiTheme
import io.github.droidkaigi.confsched.designsystem.theme.changoFontFamily
import kotlinx.coroutines.CoroutineScope
Expand All @@ -30,6 +31,7 @@ fun TestDefaultsProvider(
CompositionLocalProvider(
LocalLifecycleOwner provides FakeLocalLifecycleOwner(),
LocalViewModelStoreOwner provides FakeViewModelStoreOwner(),
LocalTestMode provides true,
content = content,
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.github.droidkaigi.confsched.testing.di

import dev.zacsweers.metro.Provider
import dev.zacsweers.metro.Provides
import io.github.confsched.profile.ProfileScreenContext
import io.github.droidkaigi.confsched.testing.robot.profile.ProfileScreenRobot

interface ProfileScreenTestGraph : ProfileScreenContext.Factory {
val profileScreenRobotProvider: Provider<ProfileScreenRobot>

@Provides
fun provideProfileScreenContext(): ProfileScreenContext {
return createProfileScreenContext()
}
}

fun createProfileScreenTestGraph(): ProfileScreenTestGraph = createTestAppGraph()
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,21 @@ import io.github.droidkaigi.confsched.data.contributors.ContributorsApiClient
import io.github.droidkaigi.confsched.data.contributors.FakeContributorsApiClient
import io.github.droidkaigi.confsched.data.eventmap.EventMapApiClient
import io.github.droidkaigi.confsched.data.eventmap.FakeEventMapApiClient
import io.github.droidkaigi.confsched.data.profile.FakeProfileSubscriptionKey
import io.github.droidkaigi.confsched.data.sessions.FakeSessionsApiClient
import io.github.droidkaigi.confsched.data.sessions.SessionsApiClient
import io.github.droidkaigi.confsched.data.staff.FakeStaffApiClient
import io.github.droidkaigi.confsched.data.staff.StaffApiClient
import io.github.droidkaigi.confsched.model.buildconfig.BuildConfigProvider
import io.github.droidkaigi.confsched.model.profile.ProfileSubscriptionKey

internal interface TestAppGraph :
TimetableScreenTestGraph,
TimetableItemDetailScreenTestGraph,
AboutScreenTestGraph,
ContributorsScreenTestGraph,
EventMapScreenTestGraph,
ProfileScreenTestGraph,
StaffScreenTestGraph,
SettingsScreenTestGraph,
FavoritesScreenTestGraph,
Expand All @@ -45,6 +48,9 @@ internal interface TestAppGraph :
@Binds
val FakeEventMapApiClient.binds: EventMapApiClient

@Binds
val FakeProfileSubscriptionKey.binds: ProfileSubscriptionKey

@UseProductionApi
@Provides
fun provideUseProductionApiBaseUrl(): Boolean = false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.github.droidkaigi.confsched.testing.robot.profile

import dev.zacsweers.metro.Inject
import io.github.droidkaigi.confsched.data.profile.ProfileDataStore
import io.github.droidkaigi.confsched.model.profile.Profile
import io.github.droidkaigi.confsched.model.profile.fake
import io.github.droidkaigi.confsched.testing.robot.profile.ProfileDataStoreRobot.ProfileInputStatus

interface ProfileDataStoreRobot {
enum class ProfileInputStatus {
AllNotEntered,
NoInputOtherThanImage,
AllEntered,
}

suspend fun setupProfileDataStore(status: ProfileInputStatus)
}

@Inject
class DefaultProfileDataStoreRobot(
private val profileDataStore: ProfileDataStore,
) : ProfileDataStoreRobot {
override suspend fun setupProfileDataStore(status: ProfileInputStatus) {
when (status) {
ProfileInputStatus.AllNotEntered -> Unit
ProfileInputStatus.NoInputOtherThanImage -> {
profileDataStore.saveProfile(
profile = Profile.fake().copy(
imagePath = "",
),
)
}
ProfileInputStatus.AllEntered -> {
profileDataStore.saveProfile(Profile.fake())
}
}
}
}
Loading
Loading