mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 02:29:55 +02:00
fix(app): standardize branding and disable telemetry
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">vnidrop</string>
|
<string name="app_name">VniDrop</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ fun main(args: Array<String>) {
|
|||||||
Window(
|
Window(
|
||||||
onCloseRequest = ::exitApplication,
|
onCloseRequest = ::exitApplication,
|
||||||
state = windowState,
|
state = windowState,
|
||||||
title = "vnidrop",
|
title = "VniDrop",
|
||||||
// Compose keeps edge resizers active for this client-decorated Linux window.
|
// Compose keeps edge resizers active for this client-decorated Linux window.
|
||||||
undecorated = linux,
|
undecorated = linux,
|
||||||
) {
|
) {
|
||||||
@@ -166,7 +166,7 @@ private fun WindowScope.MacOsTitleBar() {
|
|||||||
) {
|
) {
|
||||||
Box(modifier = Modifier.fillMaxSize().padding(end = MacOsTrafficLightsWidth)) {
|
Box(modifier = Modifier.fillMaxSize().padding(end = MacOsTrafficLightsWidth)) {
|
||||||
BasicText(
|
BasicText(
|
||||||
text = "vnidrop",
|
text = "VniDrop",
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
color = colors.foregroundDefault,
|
color = colors.foregroundDefault,
|
||||||
@@ -195,7 +195,7 @@ private fun WindowScope.LinuxTitleBar(
|
|||||||
.background(colors.backgroundSurface200),
|
.background(colors.backgroundSurface200),
|
||||||
) {
|
) {
|
||||||
BasicText(
|
BasicText(
|
||||||
text = "vnidrop",
|
text = "VniDrop",
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
color = colors.foregroundDefault,
|
color = colors.foregroundDefault,
|
||||||
|
|||||||
@@ -16,8 +16,9 @@ android.useAndroidX=true
|
|||||||
# VniDrop: compile-time diagnostics/telemetry product surface.
|
# VniDrop: compile-time diagnostics/telemetry product surface.
|
||||||
# false → no Share-diagnostics toggle, no telemetry or crash auto-upload stack.
|
# false → no Share-diagnostics toggle, no telemetry or crash auto-upload stack.
|
||||||
# Bug report UI remains available (user-initiated).
|
# Bug report UI remains available (user-initiated).
|
||||||
# Override per build: ./gradlew … -Pvnidrop.diagnostics.included=false
|
# Enable per build only when endpoint and ingest key are configured:
|
||||||
vnidrop.diagnostics.included=true
|
# ./gradlew … -Pvnidrop.diagnostics.included=true
|
||||||
|
vnidrop.diagnostics.included=false
|
||||||
# Cloudflare Worker base URL (no trailing slash). Both endpoint/key empty → NoOp transport.
|
# Cloudflare Worker base URL (no trailing slash). Both endpoint/key empty → NoOp transport.
|
||||||
# Example: https://vnidrop-diagnostics.<subdomain>.workers.dev
|
# Example: https://vnidrop-diagnostics.<subdomain>.workers.dev
|
||||||
vnidrop.diagnostics.endpoint=
|
vnidrop.diagnostics.endpoint=
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
TEAM_ID=
|
TEAM_ID=
|
||||||
|
|
||||||
PRODUCT_NAME=vnidrop
|
PRODUCT_NAME=VniDrop
|
||||||
PRODUCT_BUNDLE_IDENTIFIER=com.vnidrop.app.vnidrop$(TEAM_ID)
|
PRODUCT_BUNDLE_IDENTIFIER=com.vnidrop.app.vnidrop$(TEAM_ID)
|
||||||
|
|
||||||
CURRENT_PROJECT_VERSION=1
|
CURRENT_PROJECT_VERSION=1
|
||||||
MARKETING_VERSION=1.0
|
MARKETING_VERSION=1.0
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
FA325F1B4E7D8FFDF19A5C4A /* vnidrop.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = vnidrop.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
FA325F1B4E7D8FFDF19A5C4A /* VniDrop.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VniDrop.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
C13A067056BA9F38FD87A539 /* Products */ = {
|
C13A067056BA9F38FD87A539 /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
FA325F1B4E7D8FFDF19A5C4A /* vnidrop.app */,
|
FA325F1B4E7D8FFDF19A5C4A /* VniDrop.app */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
);
|
);
|
||||||
productName = iosApp;
|
productName = iosApp;
|
||||||
productReference = FA325F1B4E7D8FFDF19A5C4A /* vnidrop.app */;
|
productReference = FA325F1B4E7D8FFDF19A5C4A /* VniDrop.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ plugins {
|
|||||||
// included=false: no Share-diagnostics toggle, no telemetry/crash auto-upload stack.
|
// included=false: no Share-diagnostics toggle, no telemetry/crash auto-upload stack.
|
||||||
// endpoint/key both empty: transport is NoOp (safe default until Cloudflare is deployed).
|
// endpoint/key both empty: transport is NoOp (safe default until Cloudflare is deployed).
|
||||||
val diagnosticsIncluded: Boolean =
|
val diagnosticsIncluded: Boolean =
|
||||||
(findProperty("vnidrop.diagnostics.included") as String?)?.toBooleanStrictOrNull() ?: true
|
(findProperty("vnidrop.diagnostics.included") as String?)?.toBooleanStrictOrNull() ?: false
|
||||||
val diagnosticsEndpoint: String =
|
val diagnosticsEndpoint: String =
|
||||||
(findProperty("vnidrop.diagnostics.endpoint") as String?)?.trim().orEmpty()
|
(findProperty("vnidrop.diagnostics.endpoint") as String?)?.trim().orEmpty()
|
||||||
val diagnosticsIngestKey: String =
|
val diagnosticsIngestKey: String =
|
||||||
(findProperty("vnidrop.diagnostics.ingestKey") as String?)?.trim().orEmpty()
|
(findProperty("vnidrop.diagnostics.ingestKey") as String?)?.trim().orEmpty()
|
||||||
check(diagnosticsEndpoint.isEmpty() == diagnosticsIngestKey.isEmpty()) {
|
check(!diagnosticsIncluded || diagnosticsEndpoint.isEmpty() == diagnosticsIngestKey.isEmpty()) {
|
||||||
"vnidrop.diagnostics.endpoint and vnidrop.diagnostics.ingestKey must be configured together"
|
"vnidrop.diagnostics.endpoint and vnidrop.diagnostics.ingestKey must be configured together"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,8 +56,8 @@ val generateDiagnosticsBuildConfig by tasks.registering {
|
|||||||
description = "Generates DiagnosticsBuildConfig from vnidrop.diagnostics.* properties"
|
description = "Generates DiagnosticsBuildConfig from vnidrop.diagnostics.* properties"
|
||||||
val outputDir = diagnosticsBuildConfigDir
|
val outputDir = diagnosticsBuildConfigDir
|
||||||
val included = diagnosticsIncluded
|
val included = diagnosticsIncluded
|
||||||
val endpoint = diagnosticsEndpoint
|
val endpoint = if (included) diagnosticsEndpoint else ""
|
||||||
val ingestKey = diagnosticsIngestKey
|
val ingestKey = if (included) diagnosticsIngestKey else ""
|
||||||
inputs.property("vnidrop.diagnostics.included", included)
|
inputs.property("vnidrop.diagnostics.included", included)
|
||||||
inputs.property("vnidrop.diagnostics.endpoint", endpoint)
|
inputs.property("vnidrop.diagnostics.endpoint", endpoint)
|
||||||
inputs.property("vnidrop.diagnostics.ingestKey", ingestKey)
|
inputs.property("vnidrop.diagnostics.ingestKey", ingestKey)
|
||||||
|
|||||||
@@ -18,13 +18,14 @@ class DiagnosticsCoordinator(
|
|||||||
val crashReporter: CrashReporter,
|
val crashReporter: CrashReporter,
|
||||||
val bugReports: BugReportService,
|
val bugReports: BugReportService,
|
||||||
private val scope: CoroutineScope,
|
private val scope: CoroutineScope,
|
||||||
|
private val included: Boolean = DiagnosticsBuildConfig.INCLUDED,
|
||||||
) {
|
) {
|
||||||
fun start() {
|
fun start() {
|
||||||
// Install id is useful for bug-report correlation even without telemetry.
|
// Install id is useful for bug-report correlation even without telemetry.
|
||||||
scope.launch {
|
scope.launch {
|
||||||
preferencesRepository.ensureDiagnosticsInstallId()
|
preferencesRepository.ensureDiagnosticsInstallId()
|
||||||
}
|
}
|
||||||
if (!DiagnosticsBuildConfig.INCLUDED) return
|
if (!included) return
|
||||||
crashReporter.startObservingPreferences()
|
crashReporter.startObservingPreferences()
|
||||||
crashReporter.installUnhandledExceptionHandler()
|
crashReporter.installUnhandledExceptionHandler()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
@@ -33,7 +34,7 @@ class DiagnosticsCoordinator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun record(name: String, properties: Map<String, String> = emptyMap()) {
|
fun record(name: String, properties: Map<String, String> = emptyMap()) {
|
||||||
if (!DiagnosticsBuildConfig.INCLUDED) return
|
if (!included) return
|
||||||
telemetry.record(name, properties)
|
telemetry.record(name, properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +46,7 @@ class DiagnosticsCoordinator(
|
|||||||
preferencesRepository: PreferencesRepository,
|
preferencesRepository: PreferencesRepository,
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
transport: DiagnosticsTransport = NoOpDiagnosticsTransport(),
|
transport: DiagnosticsTransport = NoOpDiagnosticsTransport(),
|
||||||
|
included: Boolean = DiagnosticsBuildConfig.INCLUDED,
|
||||||
): DiagnosticsCoordinator {
|
): DiagnosticsCoordinator {
|
||||||
val breadcrumbs = BreadcrumbBuffer()
|
val breadcrumbs = BreadcrumbBuffer()
|
||||||
val crashStore = createPendingCrashStore(appDataDir)
|
val crashStore = createPendingCrashStore(appDataDir)
|
||||||
@@ -53,6 +55,7 @@ class DiagnosticsCoordinator(
|
|||||||
transport = transport,
|
transport = transport,
|
||||||
breadcrumbs = breadcrumbs,
|
breadcrumbs = breadcrumbs,
|
||||||
scope = scope,
|
scope = scope,
|
||||||
|
included = included,
|
||||||
)
|
)
|
||||||
val crashReporter = CrashReporter(
|
val crashReporter = CrashReporter(
|
||||||
store = crashStore,
|
store = crashStore,
|
||||||
@@ -78,6 +81,7 @@ class DiagnosticsCoordinator(
|
|||||||
crashReporter = crashReporter,
|
crashReporter = crashReporter,
|
||||||
bugReports = bugReports,
|
bugReports = bugReports,
|
||||||
scope = scope,
|
scope = scope,
|
||||||
|
included = included,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ fun createDiagnosticsTransport(
|
|||||||
platform: String,
|
platform: String,
|
||||||
installIdProvider: suspend () -> String,
|
installIdProvider: suspend () -> String,
|
||||||
): DiagnosticsTransport = buildDiagnosticsTransport(
|
): DiagnosticsTransport = buildDiagnosticsTransport(
|
||||||
|
included = DiagnosticsBuildConfig.INCLUDED,
|
||||||
endpoint = DiagnosticsBuildConfig.ENDPOINT,
|
endpoint = DiagnosticsBuildConfig.ENDPOINT,
|
||||||
ingestKey = DiagnosticsBuildConfig.INGEST_KEY,
|
ingestKey = DiagnosticsBuildConfig.INGEST_KEY,
|
||||||
appVersion = appVersion,
|
appVersion = appVersion,
|
||||||
@@ -127,12 +128,14 @@ fun createDiagnosticsTransport(
|
|||||||
)
|
)
|
||||||
|
|
||||||
internal fun buildDiagnosticsTransport(
|
internal fun buildDiagnosticsTransport(
|
||||||
|
included: Boolean = true,
|
||||||
endpoint: String,
|
endpoint: String,
|
||||||
ingestKey: String,
|
ingestKey: String,
|
||||||
appVersion: String,
|
appVersion: String,
|
||||||
platform: String,
|
platform: String,
|
||||||
installIdProvider: suspend () -> String,
|
installIdProvider: suspend () -> String,
|
||||||
): DiagnosticsTransport {
|
): DiagnosticsTransport {
|
||||||
|
if (!included) return NoOpDiagnosticsTransport()
|
||||||
val normalizedEndpoint = endpoint.trim()
|
val normalizedEndpoint = endpoint.trim()
|
||||||
val normalizedIngestKey = ingestKey.trim()
|
val normalizedIngestKey = ingestKey.trim()
|
||||||
if (normalizedEndpoint.isEmpty() && normalizedIngestKey.isEmpty()) return NoOpDiagnosticsTransport()
|
if (normalizedEndpoint.isEmpty() && normalizedIngestKey.isEmpty()) return NoOpDiagnosticsTransport()
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class TelemetryRecorder(
|
|||||||
private val flushIntervalMillis: Long = DefaultFlushIntervalMillis,
|
private val flushIntervalMillis: Long = DefaultFlushIntervalMillis,
|
||||||
private val retryBackoffMillis: Long = DefaultRetryBackoffMillis,
|
private val retryBackoffMillis: Long = DefaultRetryBackoffMillis,
|
||||||
private val automaticRetryCount: Int = DefaultAutomaticRetryCount,
|
private val automaticRetryCount: Int = DefaultAutomaticRetryCount,
|
||||||
|
private val included: Boolean = true,
|
||||||
) {
|
) {
|
||||||
private val bufferMutex = Mutex()
|
private val bufferMutex = Mutex()
|
||||||
private val state = AtomicReference(TelemetryState())
|
private val state = AtomicReference(TelemetryState())
|
||||||
@@ -42,22 +43,24 @@ class TelemetryRecorder(
|
|||||||
require(flushIntervalMillis > 0) { "flushIntervalMillis must be positive" }
|
require(flushIntervalMillis > 0) { "flushIntervalMillis must be positive" }
|
||||||
require(retryBackoffMillis > 0) { "retryBackoffMillis must be positive" }
|
require(retryBackoffMillis > 0) { "retryBackoffMillis must be positive" }
|
||||||
require(automaticRetryCount >= 0) { "automaticRetryCount must not be negative" }
|
require(automaticRetryCount >= 0) { "automaticRetryCount must not be negative" }
|
||||||
scope.launch {
|
if (included) {
|
||||||
preferencesRepository.preferences
|
scope.launch {
|
||||||
.map { it.diagnosticsEnabled }
|
preferencesRepository.preferences
|
||||||
.distinctUntilChanged()
|
.map { it.diagnosticsEnabled }
|
||||||
.collect { isEnabled ->
|
.distinctUntilChanged()
|
||||||
updateState { current ->
|
.collect { isEnabled ->
|
||||||
if (isEnabled) current.copy(enabled = true) else TelemetryState(enabled = false)
|
updateState { current ->
|
||||||
|
if (isEnabled) current.copy(enabled = true) else TelemetryState(enabled = false)
|
||||||
|
}
|
||||||
|
flushSignals.trySend(Unit)
|
||||||
}
|
}
|
||||||
flushSignals.trySend(Unit)
|
}
|
||||||
}
|
scope.launch { runAutomaticFlushes() }
|
||||||
}
|
}
|
||||||
scope.launch { runAutomaticFlushes() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun record(name: String, properties: Map<String, String> = emptyMap()) {
|
fun record(name: String, properties: Map<String, String> = emptyMap()) {
|
||||||
if (!DiagnosticsBuildConfig.INCLUDED) return
|
if (!included) return
|
||||||
val sanitizedName = sanitizeDiagnosticName(name)
|
val sanitizedName = sanitizeDiagnosticName(name)
|
||||||
if (sanitizedName.isBlank()) return
|
if (sanitizedName.isBlank()) return
|
||||||
val sanitizedProperties = sanitizeDiagnosticProperties(properties)
|
val sanitizedProperties = sanitizeDiagnosticProperties(properties)
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ class SettingsViewModel(
|
|||||||
private val messages: UiMessageController,
|
private val messages: UiMessageController,
|
||||||
private val bugReports: BugReportService,
|
private val bugReports: BugReportService,
|
||||||
private val diagnostics: DiagnosticsCoordinator? = null,
|
private val diagnostics: DiagnosticsCoordinator? = null,
|
||||||
|
private val diagnosticsIncluded: Boolean = DiagnosticsBuildConfig.INCLUDED,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _state = MutableStateFlow(
|
private val _state = MutableStateFlow(
|
||||||
SettingsState(
|
SettingsState(
|
||||||
@@ -200,7 +201,7 @@ class SettingsViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setDiagnosticsEnabled(enabled: Boolean) {
|
fun setDiagnosticsEnabled(enabled: Boolean) {
|
||||||
if (!DiagnosticsBuildConfig.INCLUDED) return
|
if (!diagnosticsIncluded) return
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
preferencesRepository.setDiagnosticsEnabled(enabled)
|
preferencesRepository.setDiagnosticsEnabled(enabled)
|
||||||
diagnostics?.record(
|
diagnostics?.record(
|
||||||
|
|||||||
@@ -273,6 +273,19 @@ class DiagnosticsTest {
|
|||||||
assertIs<NoOpDiagnosticsTransport>(transport)
|
assertIs<NoOpDiagnosticsTransport>(transport)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun excludedTransportIgnoresIncompleteConfiguration() {
|
||||||
|
val transport = buildDiagnosticsTransport(
|
||||||
|
included = false,
|
||||||
|
endpoint = "https://diag.example",
|
||||||
|
ingestKey = "",
|
||||||
|
appVersion = "1.0",
|
||||||
|
platform = "Test",
|
||||||
|
installIdProvider = { "id" },
|
||||||
|
)
|
||||||
|
assertIs<NoOpDiagnosticsTransport>(transport)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun noOpTransportReportsUnavailableDelivery() = runTest {
|
fun noOpTransportReportsUnavailableDelivery() = runTest {
|
||||||
val result = NoOpDiagnosticsTransport().sendEvents(
|
val result = NoOpDiagnosticsTransport().sendEvents(
|
||||||
@@ -329,6 +342,27 @@ class DiagnosticsTest {
|
|||||||
assertEquals(1, breadcrumbs.snapshot().size)
|
assertEquals(1, breadcrumbs.snapshot().size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun excludedTelemetryDoesNotStartOrRecordEvents() = runTest {
|
||||||
|
val transport = RecordingDiagnosticsTransport()
|
||||||
|
val breadcrumbs = BreadcrumbBuffer()
|
||||||
|
val recorder = TelemetryRecorder(
|
||||||
|
preferencesRepository = fakePrefs(diagnosticsEnabled = true),
|
||||||
|
transport = transport,
|
||||||
|
breadcrumbs = breadcrumbs,
|
||||||
|
scope = TestScope(UnconfinedTestDispatcher(testScheduler)),
|
||||||
|
flushThreshold = 1,
|
||||||
|
included = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
recorder.record("app_open")
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
assertEquals(0, recorder.pendingCount())
|
||||||
|
assertTrue(transport.events.isEmpty())
|
||||||
|
assertTrue(breadcrumbs.snapshot().isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun telemetryRetainsColdStartEventsUntilConsentLoads() = runTest {
|
fun telemetryRetainsColdStartEventsUntilConsentLoads() = runTest {
|
||||||
val backing = fakePrefs(diagnosticsEnabled = true)
|
val backing = fakePrefs(diagnosticsEnabled = true)
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ class ViewModelsTest {
|
|||||||
fun settingsTogglesDiagnosticsPreference() = runTest {
|
fun settingsTogglesDiagnosticsPreference() = runTest {
|
||||||
Dispatchers.setMain(StandardTestDispatcher(testScheduler))
|
Dispatchers.setMain(StandardTestDispatcher(testScheduler))
|
||||||
val preferences = preferences()
|
val preferences = preferences()
|
||||||
val viewModel = settingsViewModel(preferences)
|
val viewModel = settingsViewModel(preferences, diagnosticsIncluded = true)
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
assertFalse(viewModel.state.value.diagnosticsEnabled)
|
assertFalse(viewModel.state.value.diagnosticsEnabled)
|
||||||
viewModel.setDiagnosticsEnabled(true)
|
viewModel.setDiagnosticsEnabled(true)
|
||||||
@@ -167,6 +167,20 @@ class ViewModelsTest {
|
|||||||
assertTrue(viewModel.state.value.diagnosticsEnabled)
|
assertTrue(viewModel.state.value.diagnosticsEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun settingsIgnoresDiagnosticsOptInWhenExcluded() = runTest {
|
||||||
|
Dispatchers.setMain(StandardTestDispatcher(testScheduler))
|
||||||
|
val preferences = preferences()
|
||||||
|
val viewModel = settingsViewModel(preferences)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
viewModel.setDiagnosticsEnabled(true)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
assertFalse(preferences.mutablePreferences.value.diagnosticsEnabled)
|
||||||
|
assertFalse(viewModel.state.value.diagnosticsEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun settingsSubmitsBugReportAndClearsForm() = runTest {
|
fun settingsSubmitsBugReportAndClearsForm() = runTest {
|
||||||
Dispatchers.setMain(StandardTestDispatcher(testScheduler))
|
Dispatchers.setMain(StandardTestDispatcher(testScheduler))
|
||||||
@@ -647,6 +661,7 @@ class ViewModelsTest {
|
|||||||
notifications: FakeNotificationService = FakeNotificationService(),
|
notifications: FakeNotificationService = FakeNotificationService(),
|
||||||
transport: DiagnosticsTransport = RecordingDiagnosticsTransport(),
|
transport: DiagnosticsTransport = RecordingDiagnosticsTransport(),
|
||||||
fileSystem: FakeFileSystemService = FakeFileSystemService(folder),
|
fileSystem: FakeFileSystemService = FakeFileSystemService(folder),
|
||||||
|
diagnosticsIncluded: Boolean = false,
|
||||||
) = SettingsViewModel(
|
) = SettingsViewModel(
|
||||||
environment(),
|
environment(),
|
||||||
{ DeviceInfo("Device", "Model", "OS", "Wi-Fi", "80%") },
|
{ DeviceInfo("Device", "Model", "OS", "Wi-Fi", "80%") },
|
||||||
@@ -662,6 +677,7 @@ class ViewModelsTest {
|
|||||||
platform = "Test",
|
platform = "Test",
|
||||||
logReader = { "sample log line" },
|
logReader = { "sample log line" },
|
||||||
),
|
),
|
||||||
|
diagnosticsIncluded = diagnosticsIncluded,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun receivedTransfer(id: ULong, status: TransferStatus) = Transfer(
|
private fun receivedTransfer(id: ULong, status: TransferStatus) = Transfer(
|
||||||
|
|||||||
Reference in New Issue
Block a user