Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
- Fix `IllegalStateException` when registering `onDrawListener`
- Fix SIGABRT native crashes on Motorola devices when encoding a video
- Mention javadoc and sources for published artifacts in Gradle `.module` metadata ([#3936](https://github.com/getsentry/sentry-java/pull/3936))
- (Jetpack Compose) Modifier.sentryTag now uses Modifier.Node ([#4029](https://github.com/getsentry/sentry-java/pull/4029))
- This allows Composables that use this modifier to be skippable

### Dependencies

Expand Down
1 change: 1 addition & 0 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ object Config {
val javaFaker = "com.github.javafaker:javafaker:1.0.2"
val msgpack = "org.msgpack:msgpack-core:0.9.8"
val leakCanaryInstrumentation = "com.squareup.leakcanary:leakcanary-android-instrumentation:2.14"
val composeUiTestJunit4 = "androidx.compose.ui:ui-test-junit4:1.6.8"
}

object QualityPlugins {
Expand Down
5 changes: 5 additions & 0 deletions sentry-compose/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ kotlin {
implementation(Config.TestLibs.mockitoKotlin)
implementation(Config.TestLibs.mockitoInline)
implementation(Config.Libs.composeNavigation)
implementation(Config.TestLibs.robolectric)
implementation(Config.TestLibs.androidxRunner)
implementation(Config.TestLibs.androidxJunit)
implementation(Config.TestLibs.androidxTestRules)
implementation(Config.TestLibs.composeUiTestJunit4)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package io.sentry.compose

import androidx.compose.ui.Modifier
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.SemanticsModifierNode
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.semantics.SemanticsConfiguration
import androidx.compose.ui.semantics.SemanticsModifier
import androidx.compose.ui.semantics.SemanticsPropertyKey
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.SemanticsPropertyReceiver

public object SentryModifier {

Expand All @@ -19,11 +24,35 @@ public object SentryModifier {
)

@JvmStatic
public fun Modifier.sentryTag(tag: String): Modifier {
return semantics(
properties = {
this[SentryTag] = tag
public fun Modifier.sentryTag(tag: String): Modifier =
this then SentryTagModifierNodeElement(tag)

private data class SentryTagModifierNodeElement(val tag: String) :
ModifierNodeElement<SentryTagModifierNode>(), SemanticsModifier {

override val semanticsConfiguration: SemanticsConfiguration =
SemanticsConfiguration().also {
it[SentryTag] = tag
}
)

override fun create(): SentryTagModifierNode = SentryTagModifierNode(tag)

override fun update(node: SentryTagModifierNode) {
node.tag = tag
}

override fun InspectorInfo.inspectableProperties() {
name = "sentryTag"
properties["tag"] = tag
}
}

private class SentryTagModifierNode(var tag: String) :
Modifier.Node(),
SemanticsModifierNode {

override fun SemanticsPropertyReceiver.applySemantics() {
this[SentryTag] = tag
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.sentry.compose

import android.app.Application
import android.content.ComponentName
import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.Box
import androidx.compose.ui.Modifier
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.sentry.compose.SentryModifier.sentryTag
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestWatcher
import org.junit.runner.Description
import org.junit.runner.RunWith
import org.robolectric.Shadows
import org.robolectric.annotation.Config

@RunWith(AndroidJUnit4::class)
@Config(sdk = [30])
class SentryModifierComposeTest {

companion object {
private const val TAG_VALUE = "ExampleTagValue"
}

// workaround for robolectric tests with composeRule
// from https://github.com/robolectric/robolectric/pull/4736#issuecomment-1831034882
@get:Rule(order = 1)
val addActivityToRobolectricRule = object : TestWatcher() {
override fun starting(description: Description?) {
super.starting(description)
val appContext: Application = ApplicationProvider.getApplicationContext()
Shadows.shadowOf(appContext.packageManager).addActivityIfNotPresent(
ComponentName(
appContext.packageName,
ComponentActivity::class.java.name
)
)
}
}

@get:Rule(order = 2)
val rule = createComposeRule()

@Test
fun sentryModifierAppliesTag() {
rule.setContent {
Box(modifier = Modifier.sentryTag(TAG_VALUE))
}
rule.onNode(
SemanticsMatcher(TAG_VALUE) {
it.config.find { (key, _) -> key.name == SentryModifier.TAG }?.value == TAG_VALUE
}
).assertExists()
}
}
Loading