diff --git a/.gitignore b/.gitignore
index 3ef7bc950..4e25c2edf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
!/.idea/codeStyleSettings.xml
!/.idea/codeStyles
!/.idea/dictionaries
+!/.idea/inspectionProfiles
*.iml
.gradle
build
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 000000000..e1ca550c0
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.run/catalog.run.xml b/.run/catalog.run.xml
new file mode 100644
index 000000000..dca53c83d
--- /dev/null
+++ b/.run/catalog.run.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index f5a9bef72..c896da0e3 100644
--- a/README.md
+++ b/README.md
@@ -3,10 +3,11 @@
QuackQuack
-
+
```kotlin
-QuackText(
+
+ import java.awt.ColorQuackText(
modifier = Modifier
.background(color = Color.White)
.span(
@@ -14,7 +15,7 @@ QuackText(
style = SpanStyle(color = QuackColor.DuckieOrange),
)
.padding(30.dp),
- text = "QuackQuack is an awesome ui kit created by the Duckie team.",
+ text = "QuackQuack is an awesome design system created by the Duckie team.",
typography = QuackTypography.Body1,
)
```
diff --git a/animation/build.gradle.kts b/animation/build.gradle.kts
index 3a737e23d..0117934f3 100644
--- a/animation/build.gradle.kts
+++ b/animation/build.gradle.kts
@@ -5,13 +5,13 @@
* Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
*/
-@file:Suppress("INLINE_FROM_HIGHER_PLATFORM")
-
plugins {
quackquack("android-library")
quackquack("android-compose")
quackquack("android-compose-metrics")
quackquack("kotlin-explicit-api")
+ quackquack("test-junit")
+ quackquack("test-roborazzi")
quackquack("quack-publishing")
}
@@ -25,4 +25,5 @@ dependencies {
libs.compose.animation,
projects.util.orArtifact(),
)
+ testImplementation(libs.compose.foundation)
}
diff --git a/animation/report/compose-metrics/animation_debug-module.json b/animation/report/compose-metrics/animation_debug-module.json
index 814d99280..4d2c0dbf8 100644
--- a/animation/report/compose-metrics/animation_debug-module.json
+++ b/animation/report/compose-metrics/animation_debug-module.json
@@ -1,25 +1,25 @@
{
- "skippableComposables": 2,
- "restartableComposables": 2,
+ "skippableComposables": 0,
+ "restartableComposables": 0,
"readonlyComposables": 0,
- "totalComposables": 5,
- "restartGroups": 2,
- "totalGroups": 5,
- "staticArguments": 1,
- "certainArguments": 25,
- "knownStableArguments": 42,
- "knownUnstableArguments": 9,
- "unknownStableArguments": 1,
- "totalArguments": 52,
+ "totalComposables": 3,
+ "restartGroups": 0,
+ "totalGroups": 3,
+ "staticArguments": 0,
+ "certainArguments": 18,
+ "knownStableArguments": 30,
+ "knownUnstableArguments": 11,
+ "unknownStableArguments": 0,
+ "totalArguments": 41,
"markedStableClasses": 0,
"inferredStableClasses": 0,
- "inferredUnstableClasses": 1,
+ "inferredUnstableClasses": 0,
"inferredUncertainClasses": 0,
"effectivelyStableClasses": 0,
- "totalClasses": 1,
- "memoizedLambdas": 1,
- "singletonLambdas": 1,
+ "totalClasses": 0,
+ "memoizedLambdas": 0,
+ "singletonLambdas": 0,
"singletonComposableLambdas": 0,
"composableLambdas": 0,
- "totalLambdas": 1
+ "totalLambdas": 0
}
\ No newline at end of file
diff --git a/animation/report/compose-metrics/animation_debugUnitTest-module.json b/animation/report/compose-metrics/animation_debugUnitTest-module.json
new file mode 100644
index 000000000..939b96db0
--- /dev/null
+++ b/animation/report/compose-metrics/animation_debugUnitTest-module.json
@@ -0,0 +1,25 @@
+{
+ "skippableComposables": 3,
+ "restartableComposables": 3,
+ "readonlyComposables": 0,
+ "totalComposables": 3,
+ "restartGroups": 3,
+ "totalGroups": 3,
+ "staticArguments": 7,
+ "certainArguments": 0,
+ "knownStableArguments": 40,
+ "knownUnstableArguments": 3,
+ "unknownStableArguments": 0,
+ "totalArguments": 43,
+ "markedStableClasses": 0,
+ "inferredStableClasses": 0,
+ "inferredUnstableClasses": 0,
+ "inferredUncertainClasses": 3,
+ "effectivelyStableClasses": 0,
+ "totalClasses": 3,
+ "memoizedLambdas": 6,
+ "singletonLambdas": 0,
+ "singletonComposableLambdas": 3,
+ "composableLambdas": 3,
+ "totalLambdas": 6
+}
\ No newline at end of file
diff --git a/animation/report/compose-metrics/animation_release-module.json b/animation/report/compose-metrics/animation_release-module.json
index 814d99280..4d2c0dbf8 100644
--- a/animation/report/compose-metrics/animation_release-module.json
+++ b/animation/report/compose-metrics/animation_release-module.json
@@ -1,25 +1,25 @@
{
- "skippableComposables": 2,
- "restartableComposables": 2,
+ "skippableComposables": 0,
+ "restartableComposables": 0,
"readonlyComposables": 0,
- "totalComposables": 5,
- "restartGroups": 2,
- "totalGroups": 5,
- "staticArguments": 1,
- "certainArguments": 25,
- "knownStableArguments": 42,
- "knownUnstableArguments": 9,
- "unknownStableArguments": 1,
- "totalArguments": 52,
+ "totalComposables": 3,
+ "restartGroups": 0,
+ "totalGroups": 3,
+ "staticArguments": 0,
+ "certainArguments": 18,
+ "knownStableArguments": 30,
+ "knownUnstableArguments": 11,
+ "unknownStableArguments": 0,
+ "totalArguments": 41,
"markedStableClasses": 0,
"inferredStableClasses": 0,
- "inferredUnstableClasses": 1,
+ "inferredUnstableClasses": 0,
"inferredUncertainClasses": 0,
"effectivelyStableClasses": 0,
- "totalClasses": 1,
- "memoizedLambdas": 1,
- "singletonLambdas": 1,
+ "totalClasses": 0,
+ "memoizedLambdas": 0,
+ "singletonLambdas": 0,
"singletonComposableLambdas": 0,
"composableLambdas": 0,
- "totalLambdas": 1
+ "totalLambdas": 0
}
\ No newline at end of file
diff --git a/animation/report/compose-metrics/animation_releaseUnitTest-module.json b/animation/report/compose-metrics/animation_releaseUnitTest-module.json
new file mode 100644
index 000000000..939b96db0
--- /dev/null
+++ b/animation/report/compose-metrics/animation_releaseUnitTest-module.json
@@ -0,0 +1,25 @@
+{
+ "skippableComposables": 3,
+ "restartableComposables": 3,
+ "readonlyComposables": 0,
+ "totalComposables": 3,
+ "restartGroups": 3,
+ "totalGroups": 3,
+ "staticArguments": 7,
+ "certainArguments": 0,
+ "knownStableArguments": 40,
+ "knownUnstableArguments": 3,
+ "unknownStableArguments": 0,
+ "totalArguments": 43,
+ "markedStableClasses": 0,
+ "inferredStableClasses": 0,
+ "inferredUnstableClasses": 0,
+ "inferredUncertainClasses": 3,
+ "effectivelyStableClasses": 0,
+ "totalClasses": 3,
+ "memoizedLambdas": 6,
+ "singletonLambdas": 0,
+ "singletonComposableLambdas": 3,
+ "composableLambdas": 3,
+ "totalLambdas": 6
+}
\ No newline at end of file
diff --git a/animation/report/compose-reports/animation_debug-classes.txt b/animation/report/compose-reports/animation_debug-classes.txt
index c9a7251f4..e69de29bb 100644
--- a/animation/report/compose-reports/animation_debug-classes.txt
+++ b/animation/report/compose-reports/animation_debug-classes.txt
@@ -1,4 +0,0 @@
-unstable class QuackAnimationSpec {
- stable var snapMode: Boolean
- = Unstable
-}
diff --git a/animation/report/compose-reports/animation_debug-composables.csv b/animation/report/compose-reports/animation_debug-composables.csv
index 488e36fe4..4cf6c74ce 100644
--- a/animation/report/compose-reports/animation_debug-composables.csv
+++ b/animation/report/compose-reports/animation_debug-composables.csv
@@ -1,6 +1,4 @@
package,name,composable,skippable,restartable,readonly,inline,isLambda,hasDefaults,defaultsGroup,groups,calls,
-team.duckie.quackquack.animation.QuackAnimatedContent,QuackAnimatedContent,1,1,1,0,0,0,0,0,1,1,
-team.duckie.quackquack.animation.QuackAnimatedVisibility,QuackAnimatedVisibility,1,1,1,0,0,0,0,0,1,1,
team.duckie.quackquack.animation.animatedQuackBorderAsState,animatedQuackBorderAsState,1,0,0,0,0,0,0,0,1,2,
team.duckie.quackquack.animation.animateQuackColorAsState,animateQuackColorAsState,1,0,0,0,0,0,0,0,1,2,
-team.duckie.quackquack.animation.animatedQuackTextStyleAsState,animatedQuackTextStyleAsState,1,0,0,0,0,0,0,0,1,5,
+team.duckie.quackquack.animation.animatedQuackTypographyAsState,animatedQuackTypographyAsState,1,0,0,0,0,0,0,0,1,5,
diff --git a/animation/report/compose-reports/animation_debug-composables.txt b/animation/report/compose-reports/animation_debug-composables.txt
index d4d21b3c9..47fc8ab6c 100644
--- a/animation/report/compose-reports/animation_debug-composables.txt
+++ b/animation/report/compose-reports/animation_debug-composables.txt
@@ -1,31 +1,20 @@
-restartable skippable fun QuackAnimatedContent(
- stable modifier: Modifier? = @static Companion
- targetState: T
- stable label: String? = @static "AnimatedContent"
- stable content: @[ExtensionFunctionType] Function4
-)
-restartable skippable scheme("[0, [0]]") fun QuackAnimatedVisibility(
- stable modifier: Modifier? = @static Companion
- stable visible: Boolean
- stable label: String? = @static "AnimatedVisibility"
- stable otherEnterAnimation: EnterTransition? = @static null
- stable otherExitAnimation: ExitTransition? = @static null
- stable content: @[ExtensionFunctionType] Function3
-)
fun animatedQuackBorderAsState(
stable targetValue: QuackBorder
- stable label: String? = @static "QuackBorderAnimation"
+ unstable animationSpec: AnimationSpec? = @static quackTween()
+ stable label: String? = @static "QuackBorder"
stable widthAnimationfinishedListener: Function1<@[ParameterName(name = 'dp')] Dp, Unit>? = @static null
stable colorAnimationFinishedListener: Function1<@[ParameterName(name = 'color')] QuackColor, Unit>? = @static null
): QuackBorder
fun animateQuackColorAsState(
stable targetValue: QuackColor
- stable label: String? = @static "animateQuackColorAsState"
+ unstable animationSpec: AnimationSpec? = @static quackTween()
+ stable label: String? = @static "QuackColor"
stable finishedListener: Function1<@[ParameterName(name = 'color')] QuackColor, Unit>? = @static null
): State
-fun animatedQuackTextStyleAsState(
+fun animatedQuackTypographyAsState(
stable targetValue: QuackTypography
- stable label: String? = @static "animatedQuackTextStyleAsState"
+ unstable animationSpec: AnimationSpec? = @static quackTween()
+ stable label: String? = @static "QuackTypography"
stable colorAnimationFinishedListener: Function1<@[ParameterName(name = 'color')] QuackColor, Unit>? = @static null
stable sizeAnimationFinishedListener: Function1<@[ParameterName(name = 'size')] Float, Unit>? = @static null
stable letterSpacingAnimationFinishedListener: Function1<@[ParameterName(name = 'letterSpacing')] Float, Unit>? = @static null
diff --git a/animation/report/compose-reports/animation_debugUnitTest-classes.txt b/animation/report/compose-reports/animation_debugUnitTest-classes.txt
new file mode 100644
index 000000000..41465a9f6
--- /dev/null
+++ b/animation/report/compose-reports/animation_debugUnitTest-classes.txt
@@ -0,0 +1,12 @@
+runtime class QuackBorderSnapshot {
+ runtime val snapshotPath: SnapshotPathGeneratorRule
+ = Runtime(SnapshotPathGeneratorRule)
+}
+runtime class QuackColorSnapshot {
+ runtime val snapshotPath: SnapshotPathGeneratorRule
+ = Runtime(SnapshotPathGeneratorRule)
+}
+runtime class QuackTypographySnapshot {
+ runtime val snapshotPath: SnapshotPathGeneratorRule
+ = Runtime(SnapshotPathGeneratorRule)
+}
diff --git a/animation/report/compose-reports/animation_debugUnitTest-composables.csv b/animation/report/compose-reports/animation_debugUnitTest-composables.csv
new file mode 100644
index 000000000..975e679ba
--- /dev/null
+++ b/animation/report/compose-reports/animation_debugUnitTest-composables.csv
@@ -0,0 +1 @@
+package,name,composable,skippable,restartable,readonly,inline,isLambda,hasDefaults,defaultsGroup,groups,calls,
diff --git a/animation/report/compose-reports/animation_debugUnitTest-composables.txt b/animation/report/compose-reports/animation_debugUnitTest-composables.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/animation/report/compose-reports/animation_release-classes.txt b/animation/report/compose-reports/animation_release-classes.txt
index c9a7251f4..e69de29bb 100644
--- a/animation/report/compose-reports/animation_release-classes.txt
+++ b/animation/report/compose-reports/animation_release-classes.txt
@@ -1,4 +0,0 @@
-unstable class QuackAnimationSpec {
- stable var snapMode: Boolean
- = Unstable
-}
diff --git a/animation/report/compose-reports/animation_release-composables.csv b/animation/report/compose-reports/animation_release-composables.csv
index 488e36fe4..4cf6c74ce 100644
--- a/animation/report/compose-reports/animation_release-composables.csv
+++ b/animation/report/compose-reports/animation_release-composables.csv
@@ -1,6 +1,4 @@
package,name,composable,skippable,restartable,readonly,inline,isLambda,hasDefaults,defaultsGroup,groups,calls,
-team.duckie.quackquack.animation.QuackAnimatedContent,QuackAnimatedContent,1,1,1,0,0,0,0,0,1,1,
-team.duckie.quackquack.animation.QuackAnimatedVisibility,QuackAnimatedVisibility,1,1,1,0,0,0,0,0,1,1,
team.duckie.quackquack.animation.animatedQuackBorderAsState,animatedQuackBorderAsState,1,0,0,0,0,0,0,0,1,2,
team.duckie.quackquack.animation.animateQuackColorAsState,animateQuackColorAsState,1,0,0,0,0,0,0,0,1,2,
-team.duckie.quackquack.animation.animatedQuackTextStyleAsState,animatedQuackTextStyleAsState,1,0,0,0,0,0,0,0,1,5,
+team.duckie.quackquack.animation.animatedQuackTypographyAsState,animatedQuackTypographyAsState,1,0,0,0,0,0,0,0,1,5,
diff --git a/animation/report/compose-reports/animation_release-composables.txt b/animation/report/compose-reports/animation_release-composables.txt
index d4d21b3c9..47fc8ab6c 100644
--- a/animation/report/compose-reports/animation_release-composables.txt
+++ b/animation/report/compose-reports/animation_release-composables.txt
@@ -1,31 +1,20 @@
-restartable skippable fun QuackAnimatedContent(
- stable modifier: Modifier? = @static Companion
- targetState: T
- stable label: String? = @static "AnimatedContent"
- stable content: @[ExtensionFunctionType] Function4
-)
-restartable skippable scheme("[0, [0]]") fun QuackAnimatedVisibility(
- stable modifier: Modifier? = @static Companion
- stable visible: Boolean
- stable label: String? = @static "AnimatedVisibility"
- stable otherEnterAnimation: EnterTransition? = @static null
- stable otherExitAnimation: ExitTransition? = @static null
- stable content: @[ExtensionFunctionType] Function3
-)
fun animatedQuackBorderAsState(
stable targetValue: QuackBorder
- stable label: String? = @static "QuackBorderAnimation"
+ unstable animationSpec: AnimationSpec? = @static quackTween()
+ stable label: String? = @static "QuackBorder"
stable widthAnimationfinishedListener: Function1<@[ParameterName(name = 'dp')] Dp, Unit>? = @static null
stable colorAnimationFinishedListener: Function1<@[ParameterName(name = 'color')] QuackColor, Unit>? = @static null
): QuackBorder
fun animateQuackColorAsState(
stable targetValue: QuackColor
- stable label: String? = @static "animateQuackColorAsState"
+ unstable animationSpec: AnimationSpec? = @static quackTween()
+ stable label: String? = @static "QuackColor"
stable finishedListener: Function1<@[ParameterName(name = 'color')] QuackColor, Unit>? = @static null
): State
-fun animatedQuackTextStyleAsState(
+fun animatedQuackTypographyAsState(
stable targetValue: QuackTypography
- stable label: String? = @static "animatedQuackTextStyleAsState"
+ unstable animationSpec: AnimationSpec? = @static quackTween()
+ stable label: String? = @static "QuackTypography"
stable colorAnimationFinishedListener: Function1<@[ParameterName(name = 'color')] QuackColor, Unit>? = @static null
stable sizeAnimationFinishedListener: Function1<@[ParameterName(name = 'size')] Float, Unit>? = @static null
stable letterSpacingAnimationFinishedListener: Function1<@[ParameterName(name = 'letterSpacing')] Float, Unit>? = @static null
diff --git a/animation/report/compose-reports/animation_releaseUnitTest-classes.txt b/animation/report/compose-reports/animation_releaseUnitTest-classes.txt
new file mode 100644
index 000000000..41465a9f6
--- /dev/null
+++ b/animation/report/compose-reports/animation_releaseUnitTest-classes.txt
@@ -0,0 +1,12 @@
+runtime class QuackBorderSnapshot {
+ runtime val snapshotPath: SnapshotPathGeneratorRule
+ = Runtime(SnapshotPathGeneratorRule)
+}
+runtime class QuackColorSnapshot {
+ runtime val snapshotPath: SnapshotPathGeneratorRule
+ = Runtime(SnapshotPathGeneratorRule)
+}
+runtime class QuackTypographySnapshot {
+ runtime val snapshotPath: SnapshotPathGeneratorRule
+ = Runtime(SnapshotPathGeneratorRule)
+}
diff --git a/animation/report/compose-reports/animation_releaseUnitTest-composables.csv b/animation/report/compose-reports/animation_releaseUnitTest-composables.csv
new file mode 100644
index 000000000..975e679ba
--- /dev/null
+++ b/animation/report/compose-reports/animation_releaseUnitTest-composables.csv
@@ -0,0 +1 @@
+package,name,composable,skippable,restartable,readonly,inline,isLambda,hasDefaults,defaultsGroup,groups,calls,
diff --git a/animation/report/compose-reports/animation_releaseUnitTest-composables.txt b/animation/report/compose-reports/animation_releaseUnitTest-composables.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/animation/src/main/kotlin/team/duckie/quackquack/animation/AnimatedContent.kt b/animation/src/main/kotlin/team/duckie/quackquack/animation/AnimatedContent.kt
deleted file mode 100644
index 472939fa3..000000000
--- a/animation/src/main/kotlin/team/duckie/quackquack/animation/AnimatedContent.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Designed and developed by Duckie Team, 2022~2023
- *
- * Licensed under the MIT.
- * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/master/LICENSE
- */
-
-package team.duckie.quackquack.animation
-
-import androidx.compose.animation.AnimatedContent
-import androidx.compose.animation.AnimatedVisibilityScope
-import androidx.compose.animation.ExperimentalAnimationApi
-import androidx.compose.animation.SizeTransform
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.with
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-
-/**
- * 컴포저블의 상태에 변화가 있을 때 자동으로 해당 상태에 맞춰 애니메이션을 적용합니다.
- *
- * @param targetState 변화를 감지할 컴포저블의 상태값
- * @param label Animation Inspector에서 이 애니메이션을 구분할 별칭
- * @param content 상태 변화 애니메이션을 적용할 컴포저블 컨텐츠. 인자로는 애니메이션이
- * 적용되고 있는 [targetState]의 값이 들어옵니다. **애니메이션이 적용되기 위해선 필수로
- * 이 값을 상태로 적용해야 합니다.**
- */
-@OptIn(ExperimentalAnimationApi::class)
-@Composable
-public fun QuackAnimatedContent(
- modifier: Modifier = Modifier,
- targetState: T,
- label: String = "AnimatedContent",
- content: @Composable AnimatedVisibilityScope.(animatedTargetState: T) -> Unit,
-) {
- AnimatedContent(
- modifier = modifier,
- targetState = targetState,
- transitionSpec = {
- fadeIn(
- animationSpec = QuackAnimationSpec(),
- ) with fadeOut(
- animationSpec = QuackAnimationSpec(),
- ) using SizeTransform(
- clip = false,
- sizeAnimationSpec = { _, _ ->
- QuackAnimationSpec()
- },
- )
- },
- label = label,
- content = content,
- )
-}
diff --git a/animation/src/main/kotlin/team/duckie/quackquack/animation/AnimatedVisibility.kt b/animation/src/main/kotlin/team/duckie/quackquack/animation/AnimatedVisibility.kt
deleted file mode 100644
index 3ff05c44d..000000000
--- a/animation/src/main/kotlin/team/duckie/quackquack/animation/AnimatedVisibility.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Designed and developed by Duckie Team 2023.
- *
- * Licensed under the MIT.
- * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
- */
-
-package team.duckie.quackquack.animation
-
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.AnimatedVisibilityScope
-import androidx.compose.animation.EnterTransition
-import androidx.compose.animation.ExitTransition
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-
-/**
- * 컴포저블의 visiblilty 변화에 애니메이션을 적용합니다.
- *
- * @param visible visibility 여부
- * @param label Animation Inspector에서 이 애니메이션을 구분할 별칭
- * @param otherEnterAnimation 추가로 더할 enter 애니메이션
- * @param otherExitAnimation 추가로 더할 exit 애니메이션
- * @param content visiblilty 애니메이션이 적용될 컴포저블 컨텐츠
- */
-@Composable
-public fun QuackAnimatedVisibility(
- modifier: Modifier = Modifier,
- visible: Boolean,
- label: String = "AnimatedVisibility",
- otherEnterAnimation: EnterTransition? = null,
- otherExitAnimation: ExitTransition? = null,
- content: @Composable AnimatedVisibilityScope.() -> Unit,
-) {
- AnimatedVisibility(
- modifier = modifier,
- visible = visible,
- enter = fadeIn(QuackAnimationSpec()).optionalAdd(otherEnterAnimation),
- exit = fadeOut(QuackAnimationSpec()).optionalAdd(otherExitAnimation),
- label = label,
- content = content,
- )
-}
-
-private fun EnterTransition.optionalAdd(additional: EnterTransition?): EnterTransition {
- return if (additional != null) plus(additional) else this
-}
-
-private fun ExitTransition.optionalAdd(additional: ExitTransition?): ExitTransition {
- return if (additional != null) plus(additional) else this
-}
diff --git a/animation/src/main/kotlin/team/duckie/quackquack/animation/border.kt b/animation/src/main/kotlin/team/duckie/quackquack/animation/border.kt
index a9fda1f9c..9d66525ae 100644
--- a/animation/src/main/kotlin/team/duckie/quackquack/animation/border.kt
+++ b/animation/src/main/kotlin/team/duckie/quackquack/animation/border.kt
@@ -5,8 +5,11 @@
* Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
*/
+@file:Suppress("UNCHECKED_CAST")
+
package team.duckie.quackquack.animation
+import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -19,6 +22,7 @@ import team.duckie.quackquack.material.QuackColor
* [QuackBorder.color]의 변화에 애니메이션이 적용됩니다.
*
* @param targetValue 애니메이션을 적용할 [QuackBorder]
+ * @param animationSpec 애니메이션에 적용할 [애니메이션 스펙][AnimationSpec]
* @param label Animation Inspector에서 이 애니메이션을 구분할 별칭
* @param widthAnimationfinishedListener [QuackBorder.thickness]의 애니메이션이
* 끝나면 호출될 콜백
@@ -30,18 +34,20 @@ import team.duckie.quackquack.material.QuackColor
@Composable
public fun animatedQuackBorderAsState(
targetValue: QuackBorder,
- label: String = "QuackBorderAnimation",
+ animationSpec: AnimationSpec = quackTween(),
+ label: String = "QuackBorder",
widthAnimationfinishedListener: ((dp: Dp) -> Unit)? = null,
colorAnimationFinishedListener: ((color: QuackColor) -> Unit)? = null,
): QuackBorder {
val widthAnimationState by animateDpAsState(
targetValue = targetValue.thickness,
- animationSpec = QuackAnimationSpec(),
- finishedListener = widthAnimationfinishedListener,
+ animationSpec = animationSpec as AnimationSpec,
label = label,
+ finishedListener = widthAnimationfinishedListener,
)
val colorAnimationState by animateQuackColorAsState(
targetValue = targetValue.color,
+ animationSpec = animationSpec as AnimationSpec,
label = label,
finishedListener = colorAnimationFinishedListener,
)
diff --git a/animation/src/main/kotlin/team/duckie/quackquack/animation/color.kt b/animation/src/main/kotlin/team/duckie/quackquack/animation/color.kt
index 44f0031a9..373ab64c2 100644
--- a/animation/src/main/kotlin/team/duckie/quackquack/animation/color.kt
+++ b/animation/src/main/kotlin/team/duckie/quackquack/animation/color.kt
@@ -9,6 +9,7 @@
package team.duckie.quackquack.animation
+import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.AnimationVector4D
import androidx.compose.animation.core.TwoWayConverter
import androidx.compose.animation.core.animateValueAsState
@@ -155,6 +156,7 @@ private val QuackColorVectorConverter: (colorSpace: ColorSpace) -> TwoWayConvert
* [QuackColor]에 변경이 있을 때 애니메이션을 적용합니다.
*
* @param targetValue 색상 변경을 감지할 [QuackColor]
+ * @param animationSpec 애니메이션에 적용할 [애니메이션 스펙][AnimationSpec]
* @param label Animation Inspector에서 이 애니메이션을 구분할 별칭
* @param finishedListener 애니메이션이 끝났을 때 실행될 콜백
*
@@ -163,7 +165,8 @@ private val QuackColorVectorConverter: (colorSpace: ColorSpace) -> TwoWayConvert
@Composable
public fun animateQuackColorAsState(
targetValue: QuackColor,
- label: String = "animateQuackColorAsState",
+ animationSpec: AnimationSpec = quackTween(),
+ label: String = "QuackColor",
finishedListener: ((color: QuackColor) -> Unit)? = null,
): State {
val converter = remember(targetValue.value.colorSpace) {
@@ -172,8 +175,8 @@ public fun animateQuackColorAsState(
return animateValueAsState(
targetValue = targetValue,
typeConverter = converter,
- animationSpec = QuackAnimationSpec(),
- finishedListener = finishedListener,
+ animationSpec = animationSpec,
label = label,
+ finishedListener = finishedListener,
)
}
diff --git a/animation/src/main/kotlin/team/duckie/quackquack/animation/spec.kt b/animation/src/main/kotlin/team/duckie/quackquack/animation/spec.kt
index 144949e3a..fb8c10097 100644
--- a/animation/src/main/kotlin/team/duckie/quackquack/animation/spec.kt
+++ b/animation/src/main/kotlin/team/duckie/quackquack/animation/spec.kt
@@ -8,76 +8,22 @@
package team.duckie.quackquack.animation
import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.DurationBasedAnimationSpec
+import androidx.compose.animation.core.Easing
import androidx.compose.animation.core.LinearEasing
-import androidx.compose.animation.core.SnapSpec
import androidx.compose.animation.core.TweenSpec
-import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
import androidx.compose.runtime.Stable
-import team.duckie.quackquack.util.DelicateQuackQuackApi
-/**
- * 꽥꽥에서 기본적으로 사용할 애니메이션의 기본 지속 시간
- *
- * Playground에서 [QuackAnimationMillis] 편집 후 기본값으로 되돌리고 싶을 때
- * 기본값으로 사용할 수 있습니다.
- */
+/** 꽥꽥에서 기본으로 사용할 애니메이션의 지속 시간 */
public const val QuackDefaultAnimationMillis: Int = 150
-/**
- * 꽥꽥에서 사용할 애니메이션의 지속 시간
- *
- * 애니메이션 디버깅 용도로 수정을 허용합니다. Transition API를 사용해 애니메이션을
- * 디버깅하는 방법도 있지만 컴포즈에서 Preview가 최적하게 돌아가지 않아 수동 애니메이션
- * 디버깅도 고려합니다.
- *
- * Playground에서 자유로운 지속 시간 편집으로 쉬운 디버깅이 가능합니다.
- */
-public var QuackAnimationMillis: Int = QuackDefaultAnimationMillis
-
-/** 꽥꽥에서 사용할 [AnimationSpec]의 정보 */
-public object QuackAnimationSpec {
- /**
- * 일부 환경에서는 애니메이션이 없이 진행돼야 할 때도 있습니다. 이 값을 true로
- * 설정하면 모든 애니메이션을 무시합니다.
- *
- * **이 값의 변경은 모든 애니메이션에 영향을 미치므로 신중하게 사용해야 합니다.**
- */
- @DelicateQuackQuackApi
- public var snapMode: Boolean = false
-
- /**
- * 꽥꽥에서 사용할 [애니메이션의 기본 스펙][AnimationSpec]
- *
- * @return 덕키에서 사용할 [AnimationSpec]. [snapMode] 에 따라 반환값이 달라집니다.
- * false라면 덕키에서 사용하는 애니메이션 스펙인 [TweenSpec]이 반환되고, true라면
- * [SnapSpec]이 반환됩니다.
- *
- * @see snapMode
- */
- @OptIn(DelicateQuackQuackApi::class)
- public operator fun invoke(): DurationBasedAnimationSpec = when (snapMode) {
- true -> snap()
- else -> tween(
- durationMillis = QuackAnimationMillis,
- easing = LinearEasing,
- )
- }
-}
+/** 꽥꽥에서 기본으로 사용할 애니메이션의 [Easing] */
+public val QuackDefaultAnimationEasing: Easing = LinearEasing
-/**
- * [QuackAnimationSpec.snapMode] 대신에 한 번만 선택적으로 애니메이션 여부를 결정하기
- * 위해 사용할 수 있습니다.
- *
- * @param useAnimation 애니메이션을 사용할지 여부
- *
- * @return [useAnimation] 여부에 따른 [DurationBasedAnimationSpec]
- */
+/** 꽥꽥에서 기본으로 사용할 애니메이션의 [AnimationSpec] */
@Stable
-public fun quackOptionalAnimationSpec(useAnimation: Boolean): DurationBasedAnimationSpec {
- return when (useAnimation) {
- true -> QuackAnimationSpec()
- else -> snap()
- }
-}
+public fun quackTween(): TweenSpec =
+ tween(
+ durationMillis = QuackDefaultAnimationMillis,
+ easing = QuackDefaultAnimationEasing,
+ )
diff --git a/animation/src/main/kotlin/team/duckie/quackquack/animation/typography.kt b/animation/src/main/kotlin/team/duckie/quackquack/animation/typography.kt
index 747b5a04c..93e315cee 100644
--- a/animation/src/main/kotlin/team/duckie/quackquack/animation/typography.kt
+++ b/animation/src/main/kotlin/team/duckie/quackquack/animation/typography.kt
@@ -5,8 +5,11 @@
* Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
*/
+@file:Suppress("UNCHECKED_CAST")
+
package team.duckie.quackquack.animation
+import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.TwoWayConverter
import androidx.compose.animation.core.animateFloatAsState
@@ -18,30 +21,28 @@ import kotlin.math.roundToInt
import team.duckie.quackquack.material.QuackColor
import team.duckie.quackquack.material.QuackTypography
import team.duckie.quackquack.material.toSp
-import team.duckie.quackquack.util.AllowMagicNumber
/**
* [TextAlign]의 [TwoWayConverter]를 구현합니다.
*
- * [TextAlign]의 생성자와 필드가 다 internal 이기에 [TextAlign]의 인자 값으로 될 수 있는
- * 1~6 까지의 [Int] 값을 애니메이션하여 각각 값에 맞는 [TextAlign]을 찾아서 생성하는 식으로
+ * [TextAlign]의 생성자와 필드가 다 `internal`이기에 [TextAlign]의 인자 값으로 될 수 있는
+ * 1~6까지의 [Int] 값을 애니메이션하여 각각 값에 맞는 [TextAlign]을 찾아서 생성하는 식으로
* 구현됩니다.
*/
-public val TextAlignVectorConverter: TwoWayConverter =
- object : TwoWayConverter {
- val values = TextAlign.values()
+public val TextAlign.Companion.VectorConverter: TwoWayConverter
+ get() = object : TwoWayConverter {
+ val values = values()
+
override val convertFromVector: (vector: AnimationVector1D) -> TextAlign
get() = { vector ->
- @AllowMagicNumber(because = "TextAlign 의 range 를 나타냅니다.")
- val index = vector.value.roundToInt().coerceIn(
- // TextAlign 의 값이 1 부터 시작
- range = 1..6,
- )
+ // TextAlign의 값이 1 부터 시작
+ val index = vector.value.roundToInt().coerceIn(1, 6)
values[index]
}
+
override val convertToVector: (value: TextAlign) -> AnimationVector1D
get() = { value ->
- // TextAlign 의 값이 1 부터 시작해서 +1
+ // TextAlign의 값이 1 부터 시작해서 +1
AnimationVector1D(values.indexOf(value) + 1f)
}
}
@@ -57,6 +58,7 @@ public val TextAlignVectorConverter: TwoWayConverter = quackTween(),
+ label: String = "QuackTypography",
colorAnimationFinishedListener: ((color: QuackColor) -> Unit)? = null,
sizeAnimationFinishedListener: ((size: Float) -> Unit)? = null,
letterSpacingAnimationFinishedListener: ((letterSpacing: Float) -> Unit)? = null,
@@ -83,31 +86,32 @@ public fun animatedQuackTextStyleAsState(
): QuackTypography {
val targetColorAnimationState by animateQuackColorAsState(
targetValue = targetValue.color,
+ animationSpec = animationSpec as AnimationSpec,
label = label,
finishedListener = colorAnimationFinishedListener,
)
val targetSizeAnimationState by animateFloatAsState(
targetValue = targetValue.size.value,
- animationSpec = QuackAnimationSpec(),
+ animationSpec = animationSpec as AnimationSpec,
label = label,
finishedListener = sizeAnimationFinishedListener,
)
val targetLetterSpacingAnimationState by animateFloatAsState(
targetValue = targetValue.letterSpacing.value,
- animationSpec = QuackAnimationSpec(),
+ animationSpec = animationSpec as AnimationSpec,
label = label,
finishedListener = letterSpacingAnimationFinishedListener,
)
val targetLineHeightAnimationState by animateFloatAsState(
targetValue = targetValue.lineHeight.value,
- animationSpec = QuackAnimationSpec(),
+ animationSpec = animationSpec as AnimationSpec,
label = label,
finishedListener = lineHeightAnimationFinishedListener,
)
val targetTextAlignAnimationState by animateValueAsState(
targetValue = targetValue.textAlign,
- typeConverter = TextAlignVectorConverter,
- animationSpec = QuackAnimationSpec(),
+ typeConverter = TextAlign.VectorConverter,
+ animationSpec = animationSpec as AnimationSpec,
label = label,
finishedListener = textAlignAnimationFinishedListener,
)
diff --git a/animation/src/test/kotlin/team/duckie/quackquack/animation/snapshot/QuackBorderSnapshot.kt b/animation/src/test/kotlin/team/duckie/quackquack/animation/snapshot/QuackBorderSnapshot.kt
new file mode 100644
index 000000000..ce64e114f
--- /dev/null
+++ b/animation/src/test/kotlin/team/duckie/quackquack/animation/snapshot/QuackBorderSnapshot.kt
@@ -0,0 +1,56 @@
+/*
+ * Designed and developed by Duckie Team 2023.
+ *
+ * Licensed under the MIT.
+ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
+ */
+
+package team.duckie.quackquack.animation.snapshot
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.github.takahirom.roborazzi.captureRoboImage
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import team.duckie.quackquack.animation.animatedQuackBorderAsState
+import team.duckie.quackquack.material.QuackBorder
+import team.duckie.quackquack.material.QuackColor
+import team.duckie.quackquack.material.quackBorder
+import team.duckie.quackquack.util.compose.snapshot.test.SnapshotPathGeneratorRule
+
+@RunWith(AndroidJUnit4::class)
+class QuackBorderSnapshot {
+ @get:Rule
+ val snapshotPath = SnapshotPathGeneratorRule("border")
+
+ @Test
+ fun animated_without_exception() {
+ captureRoboImage(snapshotPath()) {
+ var state by remember { mutableStateOf(false) }
+
+ LaunchedEffect(Unit) {
+ state = true
+ }
+
+ Box(
+ Modifier
+ .size(30.dp)
+ .quackBorder(
+ border = animatedQuackBorderAsState(
+ if (state) QuackBorder(thickness = 5.dp, color = QuackColor.Success)
+ else QuackBorder(thickness = 1.dp, color = QuackColor.Alert),
+ ),
+ ),
+ )
+ }
+ }
+}
diff --git a/animation/src/test/kotlin/team/duckie/quackquack/animation/snapshot/QuackColorSnapshot.kt b/animation/src/test/kotlin/team/duckie/quackquack/animation/snapshot/QuackColorSnapshot.kt
new file mode 100644
index 000000000..f7006228d
--- /dev/null
+++ b/animation/src/test/kotlin/team/duckie/quackquack/animation/snapshot/QuackColorSnapshot.kt
@@ -0,0 +1,54 @@
+/*
+ * Designed and developed by Duckie Team 2023.
+ *
+ * Licensed under the MIT.
+ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
+ */
+
+package team.duckie.quackquack.animation.snapshot
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.github.takahirom.roborazzi.captureRoboImage
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import team.duckie.quackquack.animation.animateQuackColorAsState
+import team.duckie.quackquack.material.QuackColor
+import team.duckie.quackquack.util.compose.snapshot.test.SnapshotPathGeneratorRule
+
+@RunWith(AndroidJUnit4::class)
+class QuackColorSnapshot {
+ @get:Rule
+ val snapshotPath = SnapshotPathGeneratorRule("color")
+
+ @Test
+ fun animated_without_exception() {
+ captureRoboImage(snapshotPath()) {
+ var state by remember { mutableStateOf(false) }
+
+ LaunchedEffect(Unit) {
+ state = true
+ }
+
+ Box(
+ Modifier
+ .size(30.dp)
+ .background(
+ color = animateQuackColorAsState(
+ if (state) QuackColor.Success else QuackColor.Alert,
+ ).value.value,
+ ),
+ )
+ }
+ }
+}
diff --git a/animation/src/test/kotlin/team/duckie/quackquack/animation/snapshot/QuackTypographySnapshot.kt b/animation/src/test/kotlin/team/duckie/quackquack/animation/snapshot/QuackTypographySnapshot.kt
new file mode 100644
index 000000000..22a6436ad
--- /dev/null
+++ b/animation/src/test/kotlin/team/duckie/quackquack/animation/snapshot/QuackTypographySnapshot.kt
@@ -0,0 +1,49 @@
+/*
+ * Designed and developed by Duckie Team 2023.
+ *
+ * Licensed under the MIT.
+ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
+ */
+
+package team.duckie.quackquack.animation.snapshot
+
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.github.takahirom.roborazzi.captureRoboImage
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import team.duckie.quackquack.animation.animatedQuackTypographyAsState
+import team.duckie.quackquack.material.QuackColor
+import team.duckie.quackquack.material.QuackTypography
+import team.duckie.quackquack.util.compose.snapshot.test.SnapshotPathGeneratorRule
+
+@RunWith(AndroidJUnit4::class)
+class QuackTypographySnapshot {
+ @get:Rule
+ val snapshotPath = SnapshotPathGeneratorRule("typography")
+
+ @Test
+ fun animated_without_exception() {
+ captureRoboImage(snapshotPath()) {
+ var state by remember { mutableStateOf(false) }
+
+ LaunchedEffect(Unit) {
+ state = true
+ }
+
+ BasicText(
+ text = "Hello, World!",
+ style = animatedQuackTypographyAsState(
+ if (state) QuackTypography.Large1.change(color = QuackColor.Success)
+ else QuackTypography.Body3.change(color = QuackColor.Alert),
+ ).asComposeStyle(),
+ )
+ }
+ }
+}
diff --git a/animation/src/test/resources/robolectric.properties b/animation/src/test/resources/robolectric.properties
new file mode 100644
index 000000000..17e6de451
--- /dev/null
+++ b/animation/src/test/resources/robolectric.properties
@@ -0,0 +1,8 @@
+#
+# Designed and developed by Duckie Team 2023.
+#
+# Licensed under the MIT.
+# Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
+#
+
+sdk=33
\ No newline at end of file
diff --git a/animation/src/test/snapshots/border/animated_without_exception.png b/animation/src/test/snapshots/border/animated_without_exception.png
new file mode 100644
index 000000000..a0a541551
Binary files /dev/null and b/animation/src/test/snapshots/border/animated_without_exception.png differ
diff --git a/animation/src/test/snapshots/color/animated_without_exception.png b/animation/src/test/snapshots/color/animated_without_exception.png
new file mode 100644
index 000000000..17efab7da
Binary files /dev/null and b/animation/src/test/snapshots/color/animated_without_exception.png differ
diff --git a/animation/src/test/snapshots/typography/animated_without_exception.png b/animation/src/test/snapshots/typography/animated_without_exception.png
new file mode 100644
index 000000000..36b96bd76
Binary files /dev/null and b/animation/src/test/snapshots/typography/animated_without_exception.png differ
diff --git a/assets/QuackTextSnapshot_ModifierSpan.png b/assets/QuackTextSnapshot_ModifierSpan.png
deleted file mode 100644
index 23949273c..000000000
Binary files a/assets/QuackTextSnapshot_ModifierSpan.png and /dev/null differ
diff --git a/assets/awesome-quackquack.png b/assets/awesome-quackquack.png
new file mode 100644
index 000000000..966a234dd
Binary files /dev/null and b/assets/awesome-quackquack.png differ
diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts
index 0ebecdc1d..46b8bf398 100644
--- a/bom/build.gradle.kts
+++ b/bom/build.gradle.kts
@@ -20,7 +20,7 @@ dependencies {
api(
ArtifactConfig.of(project).toString()
.also { artifact ->
- println("BOM publishing: $artifact")
+ println("BOM loading: $artifact")
},
)
}
diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts
index a62337fa8..a51010ddc 100644
--- a/build-logic/build.gradle.kts
+++ b/build-logic/build.gradle.kts
@@ -18,12 +18,12 @@ gradlePlugin {
"AndroidApplicationPlugin" to "android-application",
"AndroidLibraryPlugin" to "android-library",
"AndroidGmdPlugin" to "android-gmd",
- "AndroidLintPlugin" to "android-lint",
"AndroidComposePlugin" to "android-compose",
"AndroidComposeMetricsPlugin" to "android-compose-metrics",
"JvmKotlinPlugin" to "jvm-kotlin",
"TestJUnitPlugin" to "test-junit",
"TestKotestPlugin" to "test-kotest",
+ "TestRoborazziPlugin" to "test-roborazzi",
"KotlinExplicitApiPlugin" to "kotlin-explicit-api",
"QuackMavenPublishingPlugin" to "quack-publishing",
)
diff --git a/build-logic/gradle.properties b/build-logic/gradle.properties
index 8a837eacf..4297b6044 100644
--- a/build-logic/gradle.properties
+++ b/build-logic/gradle.properties
@@ -8,7 +8,9 @@ org.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC
org.gradle.configureondemand=true
org.gradle.parallel=true
org.gradle.caching=true
+
# https://blog.gradle.org/introducing-file-system-watching
org.gradle.vfs.watch=true
+
# https://docs.gradle.org/7.6/userguide/configuration_cache.html
-org.gradle.unsafe.configuration-cache=true
+org.gradle.configuration-cache=true
diff --git a/build-logic/src/main/kotlin/ProjectOrArtifact.kt b/build-logic/src/main/kotlin/ProjectOrArtifact.kt
index d5d27e634..1a4f10980 100644
--- a/build-logic/src/main/kotlin/ProjectOrArtifact.kt
+++ b/build-logic/src/main/kotlin/ProjectOrArtifact.kt
@@ -47,14 +47,9 @@ private const val useArtifact = false
*
* @see useArtifact
*/
-fun ProjectDependency.orArtifact(): Any {
- return if (useArtifact) {
- val artifact = ArtifactConfig.of(dependencyProject)
- artifact.toString()
- } else {
- this
- }
-}
+fun ProjectDependency.orArtifact(): Any =
+ if (useArtifact) ArtifactConfig.of(dependencyProject).toString()
+ else this
/**
* [useArtifact]가 false일 때만 주어진 [block]을 실행합니다.
diff --git a/build-logic/src/main/kotlin/QuackMavenPublishingPlugin.kt b/build-logic/src/main/kotlin/QuackMavenPublishingPlugin.kt
index aa0aa7c78..1f8039b7a 100644
--- a/build-logic/src/main/kotlin/QuackMavenPublishingPlugin.kt
+++ b/build-logic/src/main/kotlin/QuackMavenPublishingPlugin.kt
@@ -35,7 +35,7 @@ class QuackMavenPublishingPlugin : Plugin {
}
val (group, module, version) = ArtifactConfig.of(this).also { artifact ->
- logger.warn("Publishing $artifact...")
+ logger.warn("Loading $artifact...")
}
extensions.configure {
diff --git a/build-logic/src/main/kotlin/build-logics.kt b/build-logic/src/main/kotlin/build-logics.kt
index 5434852fd..e392100dc 100644
--- a/build-logic/src/main/kotlin/build-logics.kt
+++ b/build-logic/src/main/kotlin/build-logics.kt
@@ -20,6 +20,8 @@ import internal.libs
import internal.androidExtensions
import internal.isAndroidProject
import internal.setupJunit
+import java.io.File
+import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
@@ -74,15 +76,6 @@ internal class AndroidGmdPlugin : BuildLogicPlugin({
configureGmd(androidExtensions)
})
-internal class AndroidLintPlugin : BuildLogicPlugin({
- applyPlugins(Plugins.AndroidLint)
-
- dependencies {
- add("compileOnly", libs.findBundle("android-lint").get())
- add("testImplementation", libs.findBundle("test-android-lint").get())
- }
-})
-
internal class AndroidComposePlugin : BuildLogicPlugin({
configureCompose(androidExtensions)
})
@@ -122,8 +115,6 @@ internal class JvmKotlinPlugin : BuildLogicPlugin({
dependencies.add("detektPlugins", libs.findLibrary("detekt-plugin-formatting").get())
})
-// prefix가 `Jvm`이 아니라 `Test`인 이유:
-// 적용 타켓(android or pure)에 따라 `useJUnitPlatform()` 방식이 달라짐
internal class TestJUnitPlugin : BuildLogicPlugin({
useTestPlatformForTarget()
dependencies {
@@ -133,16 +124,57 @@ internal class TestJUnitPlugin : BuildLogicPlugin({
)
}
})
+
internal class TestKotestPlugin : BuildLogicPlugin({
useTestPlatformForTarget()
dependencies.add("testImplementation", libs.findLibrary("test-kotest-framework").get())
})
+internal class TestRoborazziPlugin : BuildLogicPlugin({
+ if (!isAndroidProject) throw GradleException("roborazzi only supports Android projects.")
+
+ androidExtensions.testOptions {
+ unitTests {
+ isIncludeAndroidResources = true
+ isReturnDefaultValues = true
+ all { test ->
+ test.systemProperty("robolectric.graphicsMode", "NATIVE")
+ }
+ }
+ }
+
+ val properties = File(projectDir, "src/test/resources/robolectric.properties")
+ if (!properties.exists()) {
+ properties.parentFile.mkdirs()
+ properties.createNewFile()
+ properties.writeText(
+ """
+ #
+ # Designed and developed by Duckie Team 2023.
+ #
+ # Licensed under the MIT.
+ # Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
+ #
+
+ sdk=33
+ """.trimIndent(),
+ )
+ }
+
+ applyPlugins(libs.findPlugin("test-roborazzi").get().get().pluginId)
+
+ dependencies.add("testImplementation", libs.findLibrary("test-robolectric").get())
+ dependencies.add("testImplementation", libs.findLibrary("test-junit-compose").get())
+ dependencies.add("testImplementation", libs.findLibrary("test-kotlin-coroutines").get()) // needed for compose-ui-test
+ dependencies.add("testImplementation", libs.findBundle("test-roborazzi").get())
+ dependencies.add("testImplementation", project(":util-compose-snapshot-test"))
+})
+
internal class KotlinExplicitApiPlugin : BuildLogicPlugin({
tasks
.matching { task ->
task is KotlinCompile &&
- !task.name.contains("test", ignoreCase = true)
+ !task.name.contains("test", ignoreCase = true)
}
.configureEach {
if (!project.hasProperty("kotlin.optOutExplicitApi")) {
@@ -190,6 +222,7 @@ private fun Project.useTestPlatformForTarget() {
}
}
}
+
tasks.withType().configureEach {
setTestConfiguration()
}
diff --git a/build-logic/src/main/kotlin/internal/extensions.kt b/build-logic/src/main/kotlin/internal/extensions.kt
index 85f05cdc9..118d9eb9d 100644
--- a/build-logic/src/main/kotlin/internal/extensions.kt
+++ b/build-logic/src/main/kotlin/internal/extensions.kt
@@ -12,12 +12,13 @@ package internal
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
+import org.gradle.api.artifacts.VersionCatalog
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.kotlin.dsl.NamedDomainObjectContainerScope
import org.gradle.kotlin.dsl.getByType
-internal val Project.libs
+internal val Project.libs: VersionCatalog
get() = extensions.getByType().named("libs")
internal fun Project.applyPlugins(vararg plugins: String) {
@@ -26,11 +27,10 @@ internal fun Project.applyPlugins(vararg plugins: String) {
internal inline operator fun > C.invoke(
configuration: Action>,
-): C {
- return apply {
+) =
+ apply {
configuration.execute(NamedDomainObjectContainerScope.of(this))
}
-}
internal inline fun DependencyHandler.setupJunit(core: Any, engine: Any) {
add("testImplementation", core)
diff --git a/build-logic/src/main/kotlin/plugins.kt b/build-logic/src/main/kotlin/plugins.kt
index 01884ed82..fb3b3cf0d 100644
--- a/build-logic/src/main/kotlin/plugins.kt
+++ b/build-logic/src/main/kotlin/plugins.kt
@@ -13,5 +13,4 @@ internal object Plugins {
const val AndroidApplication = "com.android.application"
const val AndroidLibrary = "com.android.library"
- const val AndroidLint = "com.android.lint"
}
diff --git a/build.gradle.kts b/build.gradle.kts
index 00328db07..c81ee906b 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -15,6 +15,7 @@ plugins {
alias(libs.plugins.kotlin.dokka)
alias(libs.plugins.gradle.dependency.graph)
alias(libs.plugins.gradle.dependency.handler.extensions)
+ alias(libs.plugins.test.roborazzi) apply false
idea
}
@@ -116,6 +117,6 @@ allprojects {
}
}
-tasks.register(name = "cleanAll", type = Delete::class) {
+tasks.register("cleanAll", type = Delete::class) {
allprojects.map(Project::getBuildDir).forEach(::delete)
}
diff --git a/buildSrc/src/main/kotlin/plugin.kt b/buildSrc/src/main/kotlin/plugin.kt
index 8409baf9c..f1332aee4 100644
--- a/buildSrc/src/main/kotlin/plugin.kt
+++ b/buildSrc/src/main/kotlin/plugin.kt
@@ -5,10 +5,13 @@
* Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
*/
+@file:Suppress("NOTHING_TO_INLINE")
+
import org.gradle.plugin.use.PluginDependenciesSpec
import org.gradle.plugin.use.PluginDependencySpec
-@Suppress("NOTHING_TO_INLINE")
-inline fun PluginDependenciesSpec.quackquack(pluginName: String): PluginDependencySpec {
- return id("quackquack.plugin.$pluginName")
-}
+inline fun PluginDependenciesSpec.quackquack(pluginName: String): PluginDependencySpec =
+ id("quackquack.plugin.$pluginName")
+
+inline fun PluginDependenciesSpec.android(pluginId: String): PluginDependencySpec =
+ id("com.android.$pluginId")
diff --git a/material-icon/build.gradle.kts b/material-icon/build.gradle.kts
index e4be5184e..cbefbcf94 100644
--- a/material-icon/build.gradle.kts
+++ b/material-icon/build.gradle.kts
@@ -12,37 +12,19 @@ plugins {
quackquack("android-compose")
quackquack("kotlin-explicit-api")
quackquack("test-junit")
+ quackquack("test-roborazzi")
quackquack("quack-publishing")
alias(libs.plugins.test.roborazzi)
}
android {
namespace = "team.duckie.quackquack.material.icon"
-
- defaultConfig {
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- }
-
- testOptions {
- unitTests {
- isIncludeAndroidResources = true
- isReturnDefaultValues = true
- all { test ->
- test.systemProperty("robolectric.graphicsMode", "NATIVE")
- }
- }
- }
}
dependencies {
implementation(libs.compose.ui.core)
-
testImplementations(
libs.compose.foundation,
- libs.test.robolectric,
- libs.test.junit.compose,
libs.test.kotest.assertion.core,
- libs.test.kotlin.coroutines, // needed for compose-ui-test
- libs.bundles.test.roborazzi,
)
}
diff --git a/material-icon/src/test/kotlin/team/duckie/quackquack/material/icon/QuackIconSnapshot.kt b/material-icon/src/test/kotlin/team/duckie/quackquack/material/icon/snapshot/QuackIconSnapshot.kt
similarity index 79%
rename from material-icon/src/test/kotlin/team/duckie/quackquack/material/icon/QuackIconSnapshot.kt
rename to material-icon/src/test/kotlin/team/duckie/quackquack/material/icon/snapshot/QuackIconSnapshot.kt
index e5a53e95e..48dd54ed5 100644
--- a/material-icon/src/test/kotlin/team/duckie/quackquack/material/icon/QuackIconSnapshot.kt
+++ b/material-icon/src/test/kotlin/team/duckie/quackquack/material/icon/snapshot/QuackIconSnapshot.kt
@@ -7,7 +7,7 @@
@file:OptIn(ExperimentalLayoutApi::class)
-package team.duckie.quackquack.material.icon
+package team.duckie.quackquack.material.icon.snapshot
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -24,16 +24,23 @@ import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.github.takahirom.roborazzi.captureRoboImage
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
+import team.duckie.quackquack.material.icon.AllIcons
+import team.duckie.quackquack.material.icon.QuackIcon
+import team.duckie.quackquack.util.compose.snapshot.test.SnapshotPathGeneratorRule
+@Config(qualifiers = RobolectricDeviceQualifiers.NexusOne)
@RunWith(AndroidJUnit4::class)
class QuackIconSnapshot {
- @Config(qualifiers = RobolectricDeviceQualifiers.NexusOne)
+ @get:Rule
+ val snapshotPath = SnapshotPathGeneratorRule("icon")
+
@Test
fun icons() {
- captureRoboImage("src/test/snapshots/icons.png") {
+ captureRoboImage(snapshotPath()) {
FlowRow(
modifier = Modifier.fillMaxSize(),
horizontalArrangement = Arrangement.spacedBy(
diff --git a/material-icon/src/test/snapshots/icons.png b/material-icon/src/test/snapshots/icon/icons.png
similarity index 100%
rename from material-icon/src/test/snapshots/icons.png
rename to material-icon/src/test/snapshots/icon/icons.png
diff --git a/material/build.gradle.kts b/material/build.gradle.kts
index bfb39cfcf..3d9f775d0 100644
--- a/material/build.gradle.kts
+++ b/material/build.gradle.kts
@@ -5,31 +5,19 @@
* Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
*/
-@file:Suppress("INLINE_FROM_HIGHER_PLATFORM", "UnstableApiUsage")
-
plugins {
quackquack("android-library")
quackquack("android-compose")
quackquack("kotlin-explicit-api")
quackquack("test-junit")
+ quackquack("test-roborazzi")
quackquack("quack-publishing")
alias(libs.plugins.kotlin.dataclass.nocopy)
- alias(libs.plugins.test.roborazzi)
}
android {
namespace = "team.duckie.quackquack.material"
resourcePrefix = "quack_"
-
- testOptions {
- unitTests {
- isIncludeAndroidResources = true
- isReturnDefaultValues = true
- all { test ->
- test.systemProperty("robolectric.graphicsMode", "NATIVE")
- }
- }
- }
}
dependencies {
@@ -43,12 +31,5 @@ dependencies {
libs.androidx.core.ktx, // needed for androidx.core.graphics (used in SquircleShape)
projects.util.orArtifact(),
)
-
- testImplementations(
- libs.test.robolectric,
- libs.test.junit.compose,
- libs.test.kotlin.coroutines, // needed for compose-ui-test
- libs.test.kotest.assertion.core,
- libs.bundles.test.roborazzi,
- )
+ testImplementation(libs.test.kotest.assertion.core)
}
diff --git a/material/src/main/kotlin/team/duckie/quackquack/material/border.kt b/material/src/main/kotlin/team/duckie/quackquack/material/border.kt
index 02e47c32b..e99379c5f 100644
--- a/material/src/main/kotlin/team/duckie/quackquack/material/border.kt
+++ b/material/src/main/kotlin/team/duckie/quackquack/material/border.kt
@@ -42,9 +42,8 @@ public class QuackBorder(
/** [QuackBorder] 를 [BorderStroke]로 변환합니다. */
@Stable
- public fun asComposeBorder(): BorderStroke {
- return BorderStroke(width = thickness, brush = brush)
- }
+ public fun asComposeBorder(): BorderStroke =
+ BorderStroke(width = thickness, brush = brush)
}
/**
@@ -59,17 +58,18 @@ public class QuackBorder(
public fun Modifier.quackBorder(
border: QuackBorder?,
shape: Shape = RectangleShape,
-): Modifier = applyIf(border != null) {
- inspectable(
- inspectorInfo = debugInspectorInfo {
- name = "quackBorder"
- properties["border"] = border
- properties["shape"] = shape
- },
- ) {
- border(
- border = border!!.asComposeBorder(),
- shape = shape,
- )
+): Modifier =
+ applyIf(border != null) {
+ inspectable(
+ inspectorInfo = debugInspectorInfo {
+ name = "quackBorder"
+ properties["border"] = border
+ properties["shape"] = shape
+ },
+ ) {
+ border(
+ border = border!!.asComposeBorder(),
+ shape = shape,
+ )
+ }
}
-}
diff --git a/material/src/main/kotlin/team/duckie/quackquack/material/color.kt b/material/src/main/kotlin/team/duckie/quackquack/material/color.kt
index 2799c2edd..22d00dff0 100644
--- a/material/src/main/kotlin/team/duckie/quackquack/material/color.kt
+++ b/material/src/main/kotlin/team/duckie/quackquack/material/color.kt
@@ -50,13 +50,9 @@ public value class QuackColor(public val value: Color) : ReadOnlyProperty Unit)? = null,
-): Modifier = inspectable(
- inspectorInfo = debugInspectorInfo {
- name = "quackSurface"
- properties["shape"] = shape
- properties["backgroundColor"] = backgroundColor
- properties["border"] = border
- properties["elevation"] = elevation
- properties["role"] = role
- properties["rippleEnabled"] = rippleEnabled
- properties["rippleColor"] = rippleColor
- properties["onClick"] = onClick
- },
-) {
- this
- .shadow(
- elevation = elevation,
- shape = shape,
- clip = false,
- )
- .clip(shape = shape)
- .background(
- color = backgroundColor.value,
- shape = shape,
- )
- .quackClickable(
- role = role,
- rippleEnabled = rippleEnabled,
- rippleColor = rippleColor,
- onClick = onClick,
- )
- .quackBorder(
- border = border,
- shape = shape,
- )
-}
+): Modifier =
+ inspectable(
+ inspectorInfo = debugInspectorInfo {
+ name = "quackSurface"
+ properties["shape"] = shape
+ properties["backgroundColor"] = backgroundColor
+ properties["border"] = border
+ properties["elevation"] = elevation
+ properties["role"] = role
+ properties["rippleEnabled"] = rippleEnabled
+ properties["rippleColor"] = rippleColor
+ properties["onClick"] = onClick
+ },
+ ) {
+ this
+ .shadow(
+ elevation = elevation,
+ shape = shape,
+ clip = false,
+ )
+ .clip(shape = shape)
+ .background(
+ color = backgroundColor.value,
+ shape = shape,
+ )
+ .quackClickable(
+ role = role,
+ rippleEnabled = rippleEnabled,
+ rippleColor = rippleColor,
+ onClick = onClick,
+ )
+ .quackBorder(
+ border = border,
+ shape = shape,
+ )
+ }
diff --git a/material/src/main/kotlin/team/duckie/quackquack/material/padding.kt b/material/src/main/kotlin/team/duckie/quackquack/material/padding.kt
index 2ad44bcd2..4a1ff2470 100644
--- a/material/src/main/kotlin/team/duckie/quackquack/material/padding.kt
+++ b/material/src/main/kotlin/team/duckie/quackquack/material/padding.kt
@@ -37,18 +37,16 @@ public value class QuackPadding internal constructor(@PublishedApi internal val
/** [QuackPadding] 값을 [PaddingValues]로 변환합니다. */
@Stable
- public fun asPaddingValues(): PaddingValues {
- return PaddingValues(horizontal = horizontal, vertical = vertical)
- }
+ public fun asPaddingValues(): PaddingValues =
+ PaddingValues(horizontal = horizontal, vertical = vertical)
/** [QuackPadding]의 일부 값을 변경하여 반환합니다. */
@Stable
public fun copy(
horizontal: Dp = this.horizontal,
vertical: Dp = this.vertical,
- ): QuackPadding {
- return QuackPadding(horizontal = horizontal, vertical = vertical)
- }
+ ): QuackPadding =
+ QuackPadding(horizontal = horizontal, vertical = vertical)
}
/**
@@ -58,6 +56,5 @@ public value class QuackPadding internal constructor(@PublishedApi internal val
* @param vertical 위쪽과 아래쪽 가장자리에 적용될 패딩 값
*/
@Stable
-public fun QuackPadding(horizontal: Dp = 0.dp, vertical: Dp = 0.dp): QuackPadding {
- return QuackPadding(packFloats(horizontal.value, vertical.value))
-}
+public fun QuackPadding(horizontal: Dp = 0.dp, vertical: Dp = 0.dp): QuackPadding =
+ QuackPadding(packFloats(horizontal.value, vertical.value))
diff --git a/material/src/main/kotlin/team/duckie/quackquack/material/shape/SquircleShape.kt b/material/src/main/kotlin/team/duckie/quackquack/material/shape/SquircleShape.kt
index fe3098851..447642926 100644
--- a/material/src/main/kotlin/team/duckie/quackquack/material/shape/SquircleShape.kt
+++ b/material/src/main/kotlin/team/duckie/quackquack/material/shape/SquircleShape.kt
@@ -35,20 +35,19 @@ public object SquircleShape : Shape {
size: Size,
layoutDirection: LayoutDirection,
density: Density,
- ): Outline.Generic {
- return Outline.Generic(
+ ): Outline.Generic =
+ Outline.Generic(
path = createSquirclePath(
size = size,
smoothing = SMOOTHING,
),
)
- }
private fun createSquirclePath(
size: Size,
smoothing: Double,
- ): androidx.compose.ui.graphics.Path {
- return Path().apply {
+ ): androidx.compose.ui.graphics.Path =
+ Path().apply {
val oversize = size.width * OVERSAMPLING_MULTIPLIER
val squircleRadius = (oversize / 2F).toInt()
@@ -87,7 +86,6 @@ public object SquircleShape : Shape {
),
)
}.asComposePath()
- }
private fun evalSquircleFun(x: Int, poweredRadius: Double, smoothing: Double) =
(poweredRadius - abs(x.toDouble().pow(smoothing))).pow(1 / smoothing).toFloat()
diff --git a/material/src/main/kotlin/team/duckie/quackquack/material/theme/theme.kt b/material/src/main/kotlin/team/duckie/quackquack/material/theme/theme.kt
index f968057d4..b5ce9b265 100644
--- a/material/src/main/kotlin/team/duckie/quackquack/material/theme/theme.kt
+++ b/material/src/main/kotlin/team/duckie/quackquack/material/theme/theme.kt
@@ -19,6 +19,7 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.staticCompositionLocalOf
import team.duckie.quackquack.material.QuackColor
+import team.duckie.quackquack.ui.plugin.EmptyQuackPlugins
import team.duckie.quackquack.ui.plugin.LocalQuackPlugins
import team.duckie.quackquack.ui.plugin.QuackPlugins
import team.duckie.quackquack.ui.plugin.rememberQuackPlugins
@@ -31,28 +32,6 @@ import team.duckie.quackquack.ui.plugin.rememberQuackPlugins
public val LocalQuackTextFieldTheme: ProvidableCompositionLocal =
staticCompositionLocalOf { DefaultTextFieldTheme }
-/**
- * 꽥꽥에서 사용하는 컴포저블 테마를 제공합니다.
- * 이 테마에서는 다음과 같은 작업을 진행합니다.
- *
- * 1. OverscrollEffect 제거
- * 2. 꽥꽥 컴포넌트에서 사용할 커서(cursor) 테마 제공
- *
- * @param content 꽥꽥 디자인에 맞게 표시할 컴포저블 컨텐츠
- */
-@Composable
-public fun QuackTheme(content: @Composable () -> Unit) {
- CompositionLocalProvider(
- LocalOverscrollConfiguration provides null,
- LocalTextSelectionColors provides TextSelectionColors(
- handleColor = QuackColor.DuckieOrange.value,
- backgroundColor = QuackColor.DuckieOrange.change(alpha = 0.2f).value,
- ),
- LocalQuackTextFieldTheme provides DefaultTextFieldTheme,
- content = content,
- )
-}
-
/**
* 꽥꽥에서 사용하는 컴포저블 테마를 제공합니다.
* 이 테마에서는 다음과 같은 작업을 진행합니다.
@@ -65,7 +44,7 @@ public fun QuackTheme(content: @Composable () -> Unit) {
* @param content 꽥꽥 디자인에 맞게 표시할 컴포저블 컨텐츠
*/
@Composable
-public fun QuackTheme(plugins: QuackPlugins, content: @Composable () -> Unit) {
+public fun QuackTheme(plugins: QuackPlugins = EmptyQuackPlugins, content: @Composable () -> Unit) {
CompositionLocalProvider(
LocalOverscrollConfiguration provides null,
LocalTextSelectionColors provides TextSelectionColors(
diff --git a/material/src/main/kotlin/team/duckie/quackquack/material/typography.kt b/material/src/main/kotlin/team/duckie/quackquack/material/typography.kt
index 7e6c0a423..7e4e5b8b7 100644
--- a/material/src/main/kotlin/team/duckie/quackquack/material/typography.kt
+++ b/material/src/main/kotlin/team/duckie/quackquack/material/typography.kt
@@ -56,8 +56,8 @@ public data class QuackTypography(
) {
/** [QuackTypography]을 컴포즈 Text 컴포넌트에 사용하기 위해 [TextStyle]로 변환합니다. */
@Stable
- public fun asComposeStyle(): TextStyle {
- return TextStyle(
+ public fun asComposeStyle(): TextStyle =
+ TextStyle(
color = color.value,
fontSize = size,
fontFamily = fontFamily,
@@ -66,7 +66,6 @@ public data class QuackTypography(
textAlign = textAlign,
lineHeight = lineHeight,
)
- }
/**
* 정해진 [QuackTypography]에서 일부 값만 변경이 필요할 때가 있습니다. 이를 대응하기 위해
@@ -86,10 +85,9 @@ public data class QuackTypography(
public fun change(
color: QuackColor = this.color,
textAlign: TextAlign = this.textAlign,
- ): QuackTypography {
- return if (color == this.color && textAlign == this.textAlign) {
- this
- } else {
+ ): QuackTypography =
+ if (color == this.color && textAlign == this.textAlign) this
+ else
QuackTypography(
color = color,
size = size,
@@ -98,8 +96,6 @@ public data class QuackTypography(
lineHeight = lineHeight,
textAlign = textAlign,
)
- }
- }
public companion object {
@Stable
@@ -206,7 +202,3 @@ public inline val FontWeight.Companion.Regular: FontWeight get() = Normal
@Suppress("NOTHING_TO_INLINE")
@Stable
public inline fun Float.toSp(): TextUnit = TextUnit(value = this, type = TextUnitType.Sp)
-
-/** [List]의 component 정의가 5까지만 있어서 6번째 component를 추가로 정의합니다. */
-@Suppress("MagicNumber")
-public operator fun List.component6(): T = get(index = 5)
diff --git a/material/src/main/res/README.md b/material/src/main/res/README.md
deleted file mode 100644
index 16e16f492..000000000
--- a/material/src/main/res/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-### 아이콘 주의 사항
-
-- 모든 아이콘의 선 색은 `#000` 이 아닌, 디자인에 맞는 `#222` 를 사용합니다.
-- 모든 아이콘은 `24dp x 24dp` 크기를 가집니다. 기본 스펙이 16dp인 아이콘도 다른 아이콘과 통일을 위해 24dp로 override 됩니다.
diff --git a/material/src/test/kotlin/team/duckie/quackquack/material/TypographySnapshot.kt b/material/src/test/kotlin/team/duckie/quackquack/material/snapshot/TypographySnapshot.kt
similarity index 52%
rename from material/src/test/kotlin/team/duckie/quackquack/material/TypographySnapshot.kt
rename to material/src/test/kotlin/team/duckie/quackquack/material/snapshot/TypographySnapshot.kt
index 860202784..ef0fcd408 100644
--- a/material/src/test/kotlin/team/duckie/quackquack/material/TypographySnapshot.kt
+++ b/material/src/test/kotlin/team/duckie/quackquack/material/snapshot/TypographySnapshot.kt
@@ -5,39 +5,48 @@
* Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
*/
-package team.duckie.quackquack.material
+package team.duckie.quackquack.material.snapshot
-import android.app.Activity
import android.widget.TextView
import androidx.compose.foundation.text.BasicText
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.res.ResourcesCompat
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.captureRoboImage
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.robolectric.Robolectric
-import org.robolectric.android.controller.ActivityController
+import team.duckie.quackquack.material.QuackTypography
+import team.duckie.quackquack.material.R
+import team.duckie.quackquack.util.compose.snapshot.test.SnapshotPathGeneratorRule
+import team.duckie.quackquack.util.compose.snapshot.test.getActivityViaRobolectric
+private const val TestMessage = "퍠꿻땗뷃휉퉶뷟퍯 <- 잘 보이니?"
+
+// FIXME: Part of #761.
@RunWith(AndroidJUnit4::class)
class TypographySnapshot {
+ @get:Rule
+ val snapshotPath = SnapshotPathGeneratorRule("typography")
+
@Test
- fun NoBreakText_Compose() {
- captureRoboImage("src/test/snapshots/NoBreakText_Compose.png") {
+ fun NoBreakText_Compose_BasicText() {
+ captureRoboImage(snapshotPath()) {
BasicText(
- text = "퍠꿻땗뷃휉퉶뷟퍯 <- 잘 보이니?",
- style = QuackTypography.Body1.asComposeStyle(),
+ text = TestMessage,
+ style = TextStyle(fontFamily = QuackTypography.Body1.fontFamily),
)
}
}
@Test
- fun NoBreakText_AndroidView() {
- captureRoboImage("src/test/snapshots/NoBreakText_AndroidView.png") {
+ fun NoBreakText_Compose_AndroidView() {
+ captureRoboImage(snapshotPath()) {
AndroidView(
factory = { context ->
TextView(context).apply {
- text = "퍠꿻땗뷃휉퉶뷟퍯 <- 잘 보이니?"
+ text = TestMessage
val typeface = ResourcesCompat.getFont(context, R.font.quack_suit_medium)!!
setTypeface(typeface)
}
@@ -47,18 +56,11 @@ class TypographySnapshot {
}
@Test
- fun NoBreakText_TextView() {
+ fun NoBreakText_View_TextView() {
TextView(getActivityViaRobolectric).apply {
- text = "퍠꿻땗뷃휉퉶뷟퍯 <- 잘 보이니?"
+ text = TestMessage
val typeface = ResourcesCompat.getFont(context, R.font.quack_suit_medium)!!
setTypeface(typeface)
- }.captureRoboImage("src/test/snapshots/NoBreakText_TextView.png")
+ }.captureRoboImage(snapshotPath())
}
}
-
-private val getActivityViaRobolectric: Activity
- inline get() {
- val activityController = Robolectric.buildActivity(Activity::class.java)
- .also(ActivityController::setup)
- return activityController.get()
- }
diff --git a/material/src/test/kotlin/team/duckie/quackquack/material/PaddingTest.kt b/material/src/test/kotlin/team/duckie/quackquack/material/unittest/PaddingTest.kt
similarity index 66%
rename from material/src/test/kotlin/team/duckie/quackquack/material/PaddingTest.kt
rename to material/src/test/kotlin/team/duckie/quackquack/material/unittest/PaddingTest.kt
index db4920366..854490543 100644
--- a/material/src/test/kotlin/team/duckie/quackquack/material/PaddingTest.kt
+++ b/material/src/test/kotlin/team/duckie/quackquack/material/unittest/PaddingTest.kt
@@ -5,11 +5,19 @@
* Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
*/
-package team.duckie.quackquack.material
+/*
+ * Designed and developed by Duckie Team 2023.
+ *
+ * Licensed under the MIT.
+ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
+ */
+
+package team.duckie.quackquack.material.unittest
import androidx.compose.ui.unit.dp
import io.kotest.matchers.shouldBe
import org.junit.Test
+import team.duckie.quackquack.material.QuackPadding
class PaddingTest {
@Test
diff --git a/material/src/test/snapshots/NoBreakText_Compose.png b/material/src/test/snapshots/NoBreakText_Compose.png
deleted file mode 100644
index 8d1de8be3..000000000
Binary files a/material/src/test/snapshots/NoBreakText_Compose.png and /dev/null differ
diff --git a/material/src/test/snapshots/NoBreakText_AndroidView.png b/material/src/test/snapshots/typography/NoBreakText_Compose_AndroidView.png
similarity index 100%
rename from material/src/test/snapshots/NoBreakText_AndroidView.png
rename to material/src/test/snapshots/typography/NoBreakText_Compose_AndroidView.png
diff --git a/material/src/test/snapshots/typography/NoBreakText_Compose_BasicText.png b/material/src/test/snapshots/typography/NoBreakText_Compose_BasicText.png
new file mode 100644
index 000000000..145051838
Binary files /dev/null and b/material/src/test/snapshots/typography/NoBreakText_Compose_BasicText.png differ
diff --git a/material/src/test/snapshots/NoBreakText_TextView.png b/material/src/test/snapshots/typography/NoBreakText_View_TextView.png
similarity index 100%
rename from material/src/test/snapshots/NoBreakText_TextView.png
rename to material/src/test/snapshots/typography/NoBreakText_View_TextView.png
diff --git a/runtime/build.gradle.kts b/runtime/build.gradle.kts
index c5798ca3c..2855de464 100644
--- a/runtime/build.gradle.kts
+++ b/runtime/build.gradle.kts
@@ -35,5 +35,7 @@ dependencies {
libs.test.kotlin.coroutines,
libs.test.mockito.core,
libs.test.mockito.kotlin,
+ projects.utilModifier,
+ projects.utilComposeRuntimeTest,
)
}
diff --git a/runtime/src/main/kotlin/team/duckie/quackquack/runtime/ComposedModifier.kt b/runtime/src/main/kotlin/team/duckie/quackquack/runtime/ComposedModifier.kt
index 8d8a72e0f..0bbb25a81 100644
--- a/runtime/src/main/kotlin/team/duckie/quackquack/runtime/ComposedModifier.kt
+++ b/runtime/src/main/kotlin/team/duckie/quackquack/runtime/ComposedModifier.kt
@@ -22,13 +22,18 @@ private class KeyedQuackComposedModifier1(
factory: @Composable Modifier.() -> Modifier,
) : QuackComposedModifier(factory) {
override fun equals(other: Any?): Boolean {
- return other is KeyedQuackComposedModifier1 &&
- fqName == other.fqName &&
- key1 == other.key1
+ if (this === other) return true
+ if (other !is KeyedQuackComposedModifier1) return false
+
+ if (fqName != other.fqName) return false
+
+ return key1 == other.key1
}
override fun hashCode(): Int {
- return 31 * fqName.hashCode() + key1.hashCode()
+ var result = fqName.hashCode()
+ result = 31 * result + (key1?.hashCode() ?: 0)
+ return result
}
}
@@ -40,16 +45,19 @@ private class KeyedQuackComposedModifier2(
factory: @Composable Modifier.() -> Modifier,
) : QuackComposedModifier(factory) {
override fun equals(other: Any?): Boolean {
- return other is KeyedQuackComposedModifier2 &&
- fqName == other.fqName &&
- key1 == other.key1 &&
- key2 == other.key2
+ if (this === other) return true
+ if (other !is KeyedQuackComposedModifier2) return false
+
+ if (fqName != other.fqName) return false
+ if (key1 != other.key1) return false
+
+ return key2 == other.key2
}
override fun hashCode(): Int {
var result = fqName.hashCode()
- result = 31 * result + key1.hashCode()
- result = 31 * result + key2.hashCode()
+ result = 31 * result + (key1?.hashCode() ?: 0)
+ result = 31 * result + (key2?.hashCode() ?: 0)
return result
}
}
@@ -63,18 +71,21 @@ private class KeyedQuackComposedModifier3(
factory: @Composable Modifier.() -> Modifier,
) : QuackComposedModifier(factory) {
override fun equals(other: Any?): Boolean {
- return other is KeyedQuackComposedModifier3 &&
- fqName == other.fqName &&
- key1 == other.key1 &&
- key2 == other.key2 &&
- key3 == other.key3
+ if (this === other) return true
+ if (other !is KeyedQuackComposedModifier3) return false
+
+ if (fqName != other.fqName) return false
+ if (key1 != other.key1) return false
+ if (key2 != other.key2) return false
+
+ return key3 == other.key3
}
override fun hashCode(): Int {
var result = fqName.hashCode()
- result = 31 * result + key1.hashCode()
- result = 31 * result + key2.hashCode()
- result = 31 * result + key3.hashCode()
+ result = 31 * result + (key1?.hashCode() ?: 0)
+ result = 31 * result + (key2?.hashCode() ?: 0)
+ result = 31 * result + (key3?.hashCode() ?: 0)
return result
}
}
@@ -86,13 +97,18 @@ private class KeyedQuackComposedModifierN(
factory: @Composable Modifier.() -> Modifier,
) : QuackComposedModifier(factory) {
override fun equals(other: Any?): Boolean {
- return other is KeyedQuackComposedModifierN &&
- fqName == other.fqName &&
- keys.contentEquals(other.keys)
+ if (this === other) return true
+ if (other !is KeyedQuackComposedModifierN) return false
+
+ if (fqName != other.fqName) return false
+
+ return keys.contentEquals(other.keys)
}
override fun hashCode(): Int {
- return 31 * fqName.hashCode() + keys.contentHashCode()
+ var result = fqName.hashCode()
+ result = 31 * result + keys.contentHashCode()
+ return result
}
}
@@ -108,9 +124,8 @@ private class KeyedQuackComposedModifierN(
* @see Composer.quackMaterializeOf
*/
@Stable
-public fun Modifier.quackComposed(factory: @Composable Modifier.() -> Modifier): Modifier {
- return then(QuackComposedModifier(factory))
-}
+public fun Modifier.quackComposed(factory: @Composable Modifier.() -> Modifier): Modifier =
+ this then QuackComposedModifier(factory)
/**
* [Modifier.composed]의 꽥꽥 버전을 구현합니다.
@@ -135,15 +150,13 @@ public fun Modifier.quackComposed(
fullyQualifiedName: String,
key1: Any?,
factory: @Composable Modifier.() -> Modifier,
-): Modifier {
- return then(
+): Modifier =
+ this then
KeyedQuackComposedModifier1(
fqName = fullyQualifiedName,
key1 = key1,
factory = factory,
- ),
- )
-}
+ )
/**
* [Modifier.composed]의 꽥꽥 버전을 구현합니다.
@@ -169,16 +182,14 @@ public fun Modifier.quackComposed(
key1: Any?,
key2: Any?,
factory: @Composable Modifier.() -> Modifier,
-): Modifier {
- return then(
+): Modifier =
+ this then
KeyedQuackComposedModifier2(
fqName = fullyQualifiedName,
key1 = key1,
key2 = key2,
factory = factory,
- ),
- )
-}
+ )
/**
* [Modifier.composed]의 꽥꽥 버전을 구현합니다.
@@ -205,17 +216,15 @@ public fun Modifier.quackComposed(
key2: Any?,
key3: Any?,
factory: @Composable Modifier.() -> Modifier,
-): Modifier {
- return then(
+): Modifier =
+ this then
KeyedQuackComposedModifier3(
fqName = fullyQualifiedName,
key1 = key1,
key2 = key2,
key3 = key3,
factory = factory,
- ),
- )
-}
+ )
/**
* [Modifier.composed]의 꽥꽥 버전을 구현합니다.
@@ -240,12 +249,10 @@ public fun Modifier.quackComposed(
fullyQualifiedName: String,
vararg keys: Any?,
factory: @Composable Modifier.() -> Modifier,
-): Modifier {
- return then(
+): Modifier =
+ this then
KeyedQuackComposedModifierN(
fqName = fullyQualifiedName,
keys = keys,
factory = factory,
- ),
- )
-}
+ )
diff --git a/runtime/src/main/kotlin/team/duckie/quackquack/runtime/materializing.kt b/runtime/src/main/kotlin/team/duckie/quackquack/runtime/materializing.kt
index d9a1719e7..a93bf3c9b 100644
--- a/runtime/src/main/kotlin/team/duckie/quackquack/runtime/materializing.kt
+++ b/runtime/src/main/kotlin/team/duckie/quackquack/runtime/materializing.kt
@@ -75,9 +75,8 @@ public fun Composer.quackMaterializeOf(
is QuackComposedModifier -> {
@Suppress("UNCHECKED_CAST")
val factory = element.factory as Modifier.(Composer, Int) -> Modifier
- val composed = factory.invoke(Modifier, this, 0) as? QuackDataModifierModel ?: error(
- message = QuackMaterializingErrors.MustProducesQuackDataModel,
- )
+ val composed = factory.invoke(Modifier, this, 0) as? QuackDataModifierModel
+ ?: error(message = QuackMaterializingErrors.MustProducesQuackDataModel)
quackDataModels += composed
acc
}
diff --git a/runtime/src/main/kotlin/team/duckie/quackquack/runtime/model.kt b/runtime/src/main/kotlin/team/duckie/quackquack/runtime/model.kt
index 093484a20..c249ec67f 100644
--- a/runtime/src/main/kotlin/team/duckie/quackquack/runtime/model.kt
+++ b/runtime/src/main/kotlin/team/duckie/quackquack/runtime/model.kt
@@ -28,5 +28,6 @@ import androidx.compose.ui.Modifier
*
* @see quackMaterializeOf
*/
+// TODO: Modifier.Node() 마이그레이션 (#636)
@Stable
public interface QuackDataModifierModel : Modifier.Element
diff --git a/runtime/src/test/kotlin/team/duckie/quackquack/runtime/MaterializingTest.kt b/runtime/src/test/kotlin/team/duckie/quackquack/runtime/MaterializingTest.kt
index 0c9106d3e..a8590ecaf 100644
--- a/runtime/src/test/kotlin/team/duckie/quackquack/runtime/MaterializingTest.kt
+++ b/runtime/src/test/kotlin/team/duckie/quackquack/runtime/MaterializingTest.kt
@@ -5,31 +5,27 @@
* Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
*/
+@file:Suppress("UnnecessaryComposedModifier")
+
package team.duckie.quackquack.runtime
-import androidx.compose.runtime.Applier
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Composition
-import androidx.compose.runtime.MonotonicFrameClock
import androidx.compose.runtime.RecomposeScope
-import androidx.compose.runtime.Recomposer
import androidx.compose.runtime.currentComposer
import androidx.compose.runtime.currentRecomposeScope
-import androidx.compose.runtime.withRunningRecomposer
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import io.kotest.assertions.throwables.shouldThrowWithMessage
import io.kotest.core.spec.style.StringSpec
import io.kotest.core.test.Enabled
-import io.kotest.matchers.collections.shouldContainExactly
import io.kotest.matchers.collections.shouldHaveSingleElement
import io.kotest.matchers.collections.shouldHaveSize
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.withContext
import org.mockito.kotlin.mock
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
+import team.duckie.quackquack.runtime.QuackMaterializingErrors.MustProducesQuackDataModel
+import team.duckie.quackquack.util.compose.runtime.test.TestMonotonicFrameClock
+import team.duckie.quackquack.util.compose.runtime.test.composed
+import team.duckie.quackquack.util.modifier.splitToList
private object QuackElement : QuackDataModifierModel
private object StdlibElement : Modifier.Element
@@ -38,17 +34,6 @@ class MaterializingTest : StringSpec() {
init {
coroutineTestScope = true
- "Modifier.Element 분리" {
- val elements = listOf(StdlibElement, QuackElement)
- var modifier: Modifier = Modifier
-
- elements.forEach { element ->
- modifier = modifier.then(element)
- }
-
- modifier.splitToList() shouldContainExactly elements
- }
-
"stdlib-Modifier와 quack-Modifier가 분리돼야 함" {
lateinit var stdlibModifiers: List
lateinit var quackModifiers: List
@@ -71,7 +56,6 @@ class MaterializingTest : StringSpec() {
lateinit var stdlibModifiers: List
lateinit var quackModifiers: List
- @Suppress("UnnecessaryComposedModifier")
val modifier = Modifier
.composed { StdlibElement }
.quackComposed { QuackElement }
@@ -89,9 +73,7 @@ class MaterializingTest : StringSpec() {
"quack-ComposedModifier가 nonquack한 값을 반환하면 ISE를 던짐" {
val modifier = Modifier.quackComposed { this }
- shouldThrowWithMessage(
- message = QuackMaterializingErrors.MustProducesQuackDataModel,
- ) {
+ shouldThrowWithMessage(MustProducesQuackDataModel) {
composed {
currentComposer.quackMaterializeOf(modifier)
}
@@ -140,11 +122,11 @@ class MaterializingTest : StringSpec() {
enabledOrReasonIf = {
Enabled.disabled(
reason = """
- - asop에 있는 recomposingKeyedComposedModifierSkips 테스트를 로컬로 돌려보면 실패함
- [https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ui/ui/src/test/kotlin/androidx/compose/ui/ComposedModifierTest.kt;l=288-320;drc=5729d22fd521d7e83ec4eb8dedd34a0c2f491738]
-
- - 안정성 테스트 코드를 어떻게 작성해야 할지 모르겠음
- """,
+ - asop에 있는 recomposingKeyedComposedModifierSkips 테스트를 로컬로 돌려보면 실패함
+ [https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ui/ui/src/test/kotlin/androidx/compose/ui/ComposedModifierTest.kt;l=288-320;drc=5729d22fd521d7e83ec4eb8dedd34a0c2f491738]
+
+ - 안정성 테스트 코드를 어떻게 작성해야 할지 모르겠음
+ """,
)
},
) {
@@ -180,64 +162,3 @@ class MaterializingTest : StringSpec() {
}
}
}
-
-private fun Modifier.splitToList(): List {
- return foldIn(mutableListOf()) { acc, element ->
- acc.apply { add(element) }
- }
-}
-
-private suspend fun composed(
- coroutineContext: CoroutineContext = TestMonotonicFrameClock(),
- withRecomposer: suspend (recomposer: Recomposer) -> Unit = {},
- withinCompositionContent: @Composable (composition: Composition) -> Unit,
-) {
- withContext(coroutineContext) {
- withRunningRecomposer { recomposer ->
- val compositon = Composition(
- applier = EmptyApplier,
- parent = recomposer,
- )
- compositon.setContent {
- withinCompositionContent(compositon)
- }
- withRecomposer(recomposer)
- }
- }
-}
-
-@Suppress("EmptyFunctionBlock")
-private object EmptyApplier : Applier {
- override val current = Unit
- override fun down(node: Unit) {}
- override fun up() {}
- override fun clear() {}
-
- override fun insertTopDown(index: Int, instance: Unit) {
- error("Unexpected")
- }
-
- override fun insertBottomUp(index: Int, instance: Unit) {
- error("Unexpected")
- }
-
- override fun remove(index: Int, count: Int) {
- error("Unexpected")
- }
-
- override fun move(from: Int, to: Int, count: Int) {
- error("Unexpected")
- }
-}
-
-private class TestMonotonicFrameClock : MonotonicFrameClock {
- private val frameChannel = Channel()
-
- suspend fun advance(frameTimeNanos: Long) {
- frameChannel.send(frameTimeNanos)
- }
-
- override suspend fun withFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R {
- return onFrame(frameChannel.receive())
- }
-}
diff --git a/ui-plugin/image/build.gradle.kts b/ui-plugin/image/build.gradle.kts
index 1122393c8..2ec4dd993 100644
--- a/ui-plugin/image/build.gradle.kts
+++ b/ui-plugin/image/build.gradle.kts
@@ -14,6 +14,7 @@ plugins {
quackquack("android-compose")
quackquack("kotlin-explicit-api")
quackquack("test-junit")
+ quackquack("test-roborazzi")
quackquack("quack-publishing")
alias(libs.plugins.test.roborazzi)
}
@@ -24,16 +25,6 @@ tasks.withType {
android {
namespace = "team.duckie.quackquack.ui.plugin.image"
-
- testOptions {
- unitTests {
- isIncludeAndroidResources = true
- isReturnDefaultValues = true
- all { test ->
- test.systemProperty("robolectric.graphicsMode", "NATIVE")
- }
- }
- }
}
dependencies {
@@ -44,11 +35,7 @@ dependencies {
)
testImplementations(
libs.coil.test,
- libs.test.robolectric,
- libs.test.junit.compose,
libs.test.kotest.assertion.core,
- libs.test.kotlin.coroutines, // needed for compose-ui-test
- libs.bundles.test.roborazzi,
projects.ui,
projects.utilComposeRuntimeTest,
)
diff --git a/ui-plugin/image/gif/build.gradle.kts b/ui-plugin/image/gif/build.gradle.kts
index b0dacb53c..99fd10032 100644
--- a/ui-plugin/image/gif/build.gradle.kts
+++ b/ui-plugin/image/gif/build.gradle.kts
@@ -12,6 +12,7 @@ plugins {
quackquack("android-compose")
quackquack("kotlin-explicit-api")
quackquack("test-junit")
+ quackquack("test-roborazzi")
quackquack("quack-publishing")
alias(libs.plugins.test.roborazzi)
}
@@ -29,16 +30,6 @@ android {
sourceSets {
getByName("test").resources.srcDir("src/test/assets")
}
-
- testOptions {
- unitTests {
- isIncludeAndroidResources = true
- isReturnDefaultValues = true
- all { test ->
- test.systemProperty("robolectric.graphicsMode", "NATIVE")
- }
- }
- }
}
dependencies {
@@ -47,11 +38,5 @@ dependencies {
libs.compose.runtime,
libs.coil.gif,
)
- testImplementations(
- libs.test.robolectric,
- libs.test.junit.compose,
- libs.test.kotlin.coroutines, // needed for compose-ui-test
- libs.bundles.test.roborazzi,
- projects.ui,
- )
+ testImplementation(projects.ui)
}
diff --git a/ui-plugin/image/gif/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/gif/QuackImageGifPluginSnapshot.kt b/ui-plugin/image/gif/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/gif/snapshot/QuackImageGifPluginSnapshot.kt
similarity index 68%
rename from ui-plugin/image/gif/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/gif/QuackImageGifPluginSnapshot.kt
rename to ui-plugin/image/gif/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/gif/snapshot/QuackImageGifPluginSnapshot.kt
index a82f809d9..5928adef0 100644
--- a/ui-plugin/image/gif/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/gif/QuackImageGifPluginSnapshot.kt
+++ b/ui-plugin/image/gif/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/gif/snapshot/QuackImageGifPluginSnapshot.kt
@@ -5,14 +5,13 @@
* Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
*/
-package team.duckie.quackquack.ui.plugin.image.gif
+package team.duckie.quackquack.ui.plugin.image.gif.snapshot
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onRoot
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.github.takahirom.roborazzi.RoborazziRule
-import java.io.File
+import com.github.takahirom.roborazzi.captureRoboImage
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
@@ -20,39 +19,34 @@ import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import team.duckie.quackquack.material.theme.QuackTheme
import team.duckie.quackquack.ui.QuackImage
+import team.duckie.quackquack.ui.plugin.image.gif.QuackImageGifPlugin
import team.duckie.quackquack.ui.plugin.rememberQuackPlugins
+import team.duckie.quackquack.util.compose.snapshot.test.SnapshotName
+import team.duckie.quackquack.util.compose.snapshot.test.SnapshotPathGeneratorRule
-@Ignore("GIF 녹화 안됨")
+@Ignore("캡처가 정상적으로 안됨")
@RunWith(AndroidJUnit4::class)
class QuackImageGifPluginSnapshot {
@get:Rule
- val compose = createAndroidComposeRule()
+ val snapshotPath = SnapshotPathGeneratorRule("QuackImageGifPlugin")
@get:Rule
- val roborazzi = RoborazziRule(
- composeRule = compose,
- captureRoot = compose.onRoot(),
- options = RoborazziRule.Options(
- captureType = RoborazziRule.CaptureType.Gif,
- outputFileProvider = { description, _, fileExtension ->
- val sdkLevel = description.methodName.split("sdk").last()
- File("src/test/snapshots/QuackImageGifPlugin/rainbow-sdk$sdkLevel.$fileExtension").also { file ->
- file.parentFile!!.mkdirs()
- }
- },
- ),
- )
+ val compose = createAndroidComposeRule()
+ @SnapshotName("sdk-28")
@Config(sdk = [28])
@Test
fun `QuackImageGifPlugin gif snapshot sdk28`() {
`QuackImageGifPlugin gif snapshot`()
+ compose.onRoot().captureRoboImage(snapshotPath())
}
+ @SnapshotName("sdk-23")
@Config(sdk = [23])
@Test
fun `QuackImageGifPlugin gif snapshot sdk23`() {
`QuackImageGifPlugin gif snapshot`()
+ compose.onRoot().captureRoboImage(snapshotPath())
}
private fun `QuackImageGifPlugin gif snapshot`() {
diff --git a/ui-plugin/image/gif/src/test/resources/robolectric.properties b/ui-plugin/image/gif/src/test/resources/robolectric.properties
new file mode 100644
index 000000000..17e6de451
--- /dev/null
+++ b/ui-plugin/image/gif/src/test/resources/robolectric.properties
@@ -0,0 +1,8 @@
+#
+# Designed and developed by Duckie Team 2023.
+#
+# Licensed under the MIT.
+# Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
+#
+
+sdk=33
\ No newline at end of file
diff --git a/ui-plugin/image/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/QuackImagePluginTest.kt b/ui-plugin/image/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/QuackImagePluginTest.kt
deleted file mode 100644
index 0a8b8ecf6..000000000
--- a/ui-plugin/image/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/QuackImagePluginTest.kt
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Designed and developed by Duckie Team 2023.
- *
- * Licensed under the MIT.
- * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
- */
-
-@file:OptIn(ExperimentalCoilApi::class)
-
-package team.duckie.quackquack.ui.plugin.image
-
-import android.content.Context
-import android.content.res.Resources
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.ColorFilter
-import android.graphics.Paint
-import android.graphics.Paint.Align
-import android.graphics.drawable.Drawable
-import android.util.TypedValue
-import androidx.activity.ComponentActivity
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import coil.ImageLoader
-import coil.annotation.ExperimentalCoilApi
-import coil.decode.DataSource
-import coil.request.ImageRequest
-import coil.request.SuccessResult
-import coil.size.Size
-import coil.test.FakeImageLoaderEngine
-import com.github.takahirom.roborazzi.captureRoboImage
-import io.kotest.matchers.maps.shouldMatchExactly
-import io.kotest.matchers.shouldBe
-import io.kotest.matchers.types.shouldBeInstanceOf
-import io.kotest.matchers.types.shouldBeSameInstanceAs
-import kotlin.math.roundToInt
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import team.duckie.quackquack.material.theme.QuackTheme
-import team.duckie.quackquack.ui.QuackImage
-import team.duckie.quackquack.ui.plugin.QuackPluginLocal
-import team.duckie.quackquack.ui.plugin.quackPluginLocal
-import team.duckie.quackquack.ui.plugin.rememberQuackPlugins
-
-@Suppress("OVERRIDE_DEPRECATION")
-private class TextDrawable(private val res: Resources, private val text: String) : Drawable() {
- private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = Color.parseColor("#FFA500") // orange
- textAlign = Align.CENTER
- textSize = TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_SP,
- 13f,
- res.displayMetrics,
- )
- }
-
- override fun draw(canvas: Canvas) {
- val x = intrinsicWidth / 2f
- val y = intrinsicHeight / 2 - (paint.descent() + paint.ascent()) / 2
-
- canvas.drawText(text, x, y, paint)
- }
-
- override fun getOpacity() = paint.alpha
-
- override fun setAlpha(alpha: Int) {
- paint.alpha = alpha
- }
-
- override fun setColorFilter(colorFilter: ColorFilter?) {
- paint.colorFilter = colorFilter
- }
-
- override fun getIntrinsicWidth() = (paint.measureText(text, 0, text.length) + .5).roundToInt()
-
- override fun getIntrinsicHeight() = paint.getFontMetricsInt(null)
-
- fun setFontSize(fontSize: Float) {
- paint.textSize = TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_SP,
- fontSize,
- res.displayMetrics,
- )
- }
-}
-
-private fun imageResultOf(drawable: Drawable, request: ImageRequest) =
- SuccessResult(
- drawable = drawable,
- request = request,
- dataSource = DataSource.MEMORY,
- )
-
-private class QuackImageCoilBuilderIntercepter(
- private val map: MutableMap = mutableMapOf(),
-) : QuackImagePlugin.CoilImageLoader {
- override fun ImageLoader.Builder.quackBuild(
- context: Context,
- src: Any?,
- contentDescription: String?,
- quackPluginLocal: QuackPluginLocal?,
- ): ImageLoader.Builder =
- components {
- val testEngine = FakeImageLoaderEngine
- .Builder()
- .default { chain ->
- val textDrawable = TextDrawable(context.resources, src.toString()).apply {
- (quackPluginLocal?.value as? Float)?.let { fontSize ->
- setFontSize(fontSize)
- }
- }
- imageResultOf(
- drawable = textDrawable,
- request = chain
- .withSize(Size(textDrawable.intrinsicWidth, textDrawable.intrinsicHeight))
- .request,
- )
- }
- .build()
- add(testEngine)
- }
- .also {
- map["context"] = context
- map["src"] = src
- map["contentDescription"] = contentDescription
- map["quackPluginLocal"] = quackPluginLocal
- }
-}
-
-@RunWith(AndroidJUnit4::class)
-class QuackImagePluginTest {
- @get:Rule
- val compose = createAndroidComposeRule()
-
- @Test
- fun `QuackImage plugin snapshot`() {
- captureRoboImage("src/test/snapshots/QuackImagePlugin/orange-string.png") {
- QuackTheme(
- plugins = rememberQuackPlugins {
- +QuackImageCoilBuilderIntercepter()
- },
- ) {
- QuackImage(
- modifier = Modifier.quackPluginLocal(30f),
- src = "Hello, World!",
- contentDescription = "orange string",
- )
- }
- }
- }
-
- @Test
- fun `QuackImage plugin intercept test`() {
- val map = mutableMapOf()
-
- var context: Context? = null
- val src = "Hello, World!"
- val contentDescription = "orange string"
- val fontSize = 30f
-
- compose.setContent {
- context = LocalContext.current
-
- QuackTheme(
- plugins = rememberQuackPlugins {
- +QuackImageCoilBuilderIntercepter(map)
- },
- ) {
- QuackImage(
- modifier = Modifier.quackPluginLocal(fontSize),
- src = src,
- contentDescription = contentDescription,
- )
- }
- }
-
- map.shouldMatchExactly(
- "context" to { it shouldBeSameInstanceAs context },
- "src" to { it shouldBe src },
- "contentDescription" to { it shouldBe contentDescription },
- "quackPluginLocal" to {
- it.shouldBeInstanceOf().value shouldBe fontSize
- },
- )
- }
-}
diff --git a/ui-plugin/image/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/common/QuackImageCoilBuilderIntercepter.kt b/ui-plugin/image/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/common/QuackImageCoilBuilderIntercepter.kt
new file mode 100644
index 000000000..86f5680bc
--- /dev/null
+++ b/ui-plugin/image/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/common/QuackImageCoilBuilderIntercepter.kt
@@ -0,0 +1,121 @@
+/*
+ * Designed and developed by Duckie Team 2023.
+ *
+ * Licensed under the MIT.
+ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
+ */
+
+@file:OptIn(ExperimentalCoilApi::class)
+@file:Suppress("OVERRIDE_DEPRECATION")
+
+package team.duckie.quackquack.ui.plugin.image.common
+
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.ColorFilter
+import android.graphics.Paint
+import android.graphics.drawable.Drawable
+import android.util.TypedValue
+import coil.ImageLoader
+import coil.annotation.ExperimentalCoilApi
+import coil.decode.DataSource
+import coil.request.ImageRequest
+import coil.request.SuccessResult
+import coil.size.Size
+import coil.test.FakeImageLoaderEngine
+import kotlin.math.roundToInt
+import team.duckie.quackquack.ui.plugin.QuackPluginLocal
+import team.duckie.quackquack.ui.plugin.image.QuackImagePlugin
+
+private class TextDrawable(private val res: Resources, private val text: String) : Drawable() {
+ private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+ color = Color.parseColor("#FFA500") // orange
+ textAlign = Paint.Align.CENTER
+ textSize = TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_SP,
+ 13f,
+ res.displayMetrics,
+ )
+ }
+
+ override fun draw(canvas: Canvas) {
+ val x = intrinsicWidth / 2f
+ val y = intrinsicHeight / 2 - (paint.descent() + paint.ascent()) / 2
+ canvas.drawText(text, x, y, paint)
+ }
+
+ override fun getOpacity() = paint.alpha
+
+ override fun setAlpha(alpha: Int) {
+ paint.alpha = alpha
+ }
+
+ override fun setColorFilter(colorFilter: ColorFilter?) {
+ paint.colorFilter = colorFilter
+ }
+
+ override fun getIntrinsicWidth() = (paint.measureText(text, 0, text.length) + .5).roundToInt()
+
+ override fun getIntrinsicHeight() = paint.getFontMetricsInt(null)
+
+ fun setFontSize(fontSize: Float) {
+ paint.textSize =
+ TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_SP,
+ fontSize,
+ res.displayMetrics,
+ )
+ }
+}
+
+private fun imageResultOf(drawable: Drawable, request: ImageRequest) =
+ SuccessResult(
+ drawable = drawable,
+ request = request,
+ dataSource = DataSource.MEMORY,
+ )
+
+class QuackImageCoilBuilderIntercepter(
+ private val map: MutableMap = mutableMapOf(),
+) : QuackImagePlugin.CoilImageLoader {
+ override fun ImageLoader.Builder.quackBuild(
+ context: Context,
+ src: Any?,
+ contentDescription: String?,
+ quackPluginLocal: QuackPluginLocal?,
+ ): ImageLoader.Builder =
+ components {
+ val testEngine =
+ FakeImageLoaderEngine
+ .Builder()
+ .default { chain ->
+ val textDrawable =
+ TextDrawable(res = context.resources, text = src.toString()).apply {
+ (quackPluginLocal?.value as? Float)?.let { fontSize ->
+ setFontSize(fontSize)
+ }
+ }
+ imageResultOf(
+ drawable = textDrawable,
+ request = chain
+ .withSize(
+ Size(
+ width = textDrawable.intrinsicWidth,
+ height = textDrawable.intrinsicHeight,
+ ),
+ )
+ .request,
+ )
+ }
+ .build()
+ add(testEngine)
+ }
+ .also {
+ map["context"] = context
+ map["src"] = src
+ map["contentDescription"] = contentDescription
+ map["quackPluginLocal"] = quackPluginLocal
+ }
+}
diff --git a/ui-plugin/image/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/snapshot/QuackImagePluginSnapshot.kt b/ui-plugin/image/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/snapshot/QuackImagePluginSnapshot.kt
new file mode 100644
index 000000000..6c8eb2fd5
--- /dev/null
+++ b/ui-plugin/image/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/snapshot/QuackImagePluginSnapshot.kt
@@ -0,0 +1,44 @@
+/*
+ * Designed and developed by Duckie Team 2023.
+ *
+ * Licensed under the MIT.
+ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
+ */
+
+package team.duckie.quackquack.ui.plugin.image.snapshot
+
+import androidx.compose.ui.Modifier
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.github.takahirom.roborazzi.captureRoboImage
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import team.duckie.quackquack.material.theme.QuackTheme
+import team.duckie.quackquack.ui.QuackImage
+import team.duckie.quackquack.ui.plugin.image.common.QuackImageCoilBuilderIntercepter
+import team.duckie.quackquack.ui.plugin.quackPluginLocal
+import team.duckie.quackquack.ui.plugin.rememberQuackPlugins
+import team.duckie.quackquack.util.compose.snapshot.test.SnapshotPathGeneratorRule
+
+@RunWith(AndroidJUnit4::class)
+class QuackImagePluginSnapshot {
+ @get:Rule
+ val snapshotPath = SnapshotPathGeneratorRule("QuackImagePlugin")
+
+ @Test
+ fun OrangeStringIntercepted() {
+ captureRoboImage(snapshotPath()) {
+ QuackTheme(
+ plugins = rememberQuackPlugins {
+ +QuackImageCoilBuilderIntercepter()
+ },
+ ) {
+ QuackImage(
+ modifier = Modifier.quackPluginLocal(30f),
+ src = "Hello, World!",
+ contentDescription = "orange string",
+ )
+ }
+ }
+ }
+}
diff --git a/ui-plugin/image/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/uitest/QuackImagePluginTest.kt b/ui-plugin/image/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/uitest/QuackImagePluginTest.kt
new file mode 100644
index 000000000..6924f8ab1
--- /dev/null
+++ b/ui-plugin/image/src/test/kotlin/team/duckie/quackquack/ui/plugin/image/uitest/QuackImagePluginTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Designed and developed by Duckie Team 2023.
+ *
+ * Licensed under the MIT.
+ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
+ */
+
+package team.duckie.quackquack.ui.plugin.image.uitest
+
+import android.content.Context
+import androidx.activity.ComponentActivity
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import io.kotest.matchers.maps.shouldMatchExactly
+import io.kotest.matchers.shouldBe
+import io.kotest.matchers.types.shouldBeInstanceOf
+import io.kotest.matchers.types.shouldBeSameInstanceAs
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import team.duckie.quackquack.material.theme.QuackTheme
+import team.duckie.quackquack.ui.QuackImage
+import team.duckie.quackquack.ui.plugin.QuackPluginLocal
+import team.duckie.quackquack.ui.plugin.image.common.QuackImageCoilBuilderIntercepter
+import team.duckie.quackquack.ui.plugin.quackPluginLocal
+import team.duckie.quackquack.ui.plugin.rememberQuackPlugins
+
+@RunWith(AndroidJUnit4::class)
+class QuackImagePluginTest {
+ @get:Rule
+ val compose = createAndroidComposeRule()
+
+ @Test
+ fun `QuackImage plugin intercept test`() {
+ val map = mutableMapOf()
+
+ var context: Context? = null
+ val src = "Hello, World!"
+ val contentDescription = "orange string"
+ val fontSize = 30f
+
+ compose.setContent {
+ context = LocalContext.current
+
+ QuackTheme(
+ plugins = rememberQuackPlugins {
+ +QuackImageCoilBuilderIntercepter(map)
+ },
+ ) {
+ QuackImage(
+ modifier = Modifier.quackPluginLocal(fontSize),
+ src = src,
+ contentDescription = contentDescription,
+ )
+ }
+ }
+
+ map.shouldMatchExactly(
+ "context" to { it shouldBeSameInstanceAs context },
+ "src" to { it shouldBe src },
+ "contentDescription" to { it shouldBe contentDescription },
+ "quackPluginLocal" to {
+ it.shouldBeInstanceOf().value shouldBe fontSize
+ },
+ )
+ }
+}
diff --git a/ui-plugin/image/src/test/snapshots/QuackImagePlugin/orange-string.png b/ui-plugin/image/src/test/snapshots/QuackImagePlugin/OrangeStringIntercepted.png
similarity index 100%
rename from ui-plugin/image/src/test/snapshots/QuackImagePlugin/orange-string.png
rename to ui-plugin/image/src/test/snapshots/QuackImagePlugin/OrangeStringIntercepted.png
diff --git a/ui-plugin/interceptor/build.gradle.kts b/ui-plugin/interceptor/build.gradle.kts
index 5118130d8..f4956e89a 100644
--- a/ui-plugin/interceptor/build.gradle.kts
+++ b/ui-plugin/interceptor/build.gradle.kts
@@ -13,8 +13,9 @@ plugins {
quackquack("android-library")
quackquack("android-compose")
quackquack("kotlin-explicit-api")
+ quackquack("test-junit")
+ quackquack("test-roborazzi")
quackquack("quack-publishing")
- alias(libs.plugins.test.roborazzi)
}
tasks.withType {
@@ -23,16 +24,6 @@ tasks.withType {
android {
namespace = "team.duckie.quackquack.ui.plugin.interceptor"
-
- testOptions {
- unitTests {
- isIncludeAndroidResources = true
- isReturnDefaultValues = true
- all { test ->
- test.systemProperty("robolectric.graphicsMode", "NATIVE")
- }
- }
- }
}
dependencies {
@@ -44,11 +35,7 @@ dependencies {
projects.utilModifier,
)
testImplementations(
- libs.test.robolectric,
- libs.test.junit.compose,
libs.test.kotest.assertion.core,
- libs.test.kotlin.coroutines, // needed for compose-ui-test
- libs.bundles.test.roborazzi,
projects.ui,
projects.utilComposeSnapshotTest,
)
diff --git a/ui-plugin/interceptor/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/QuackInterceptorPluginTest.kt b/ui-plugin/interceptor/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/QuackInterceptorPluginTest.kt
deleted file mode 100644
index b728416d8..000000000
--- a/ui-plugin/interceptor/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/QuackInterceptorPluginTest.kt
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Designed and developed by Duckie Team 2023.
- *
- * Licensed under the MIT.
- * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
- */
-
-@file:OptIn(ExperimentalQuackQuackApi::class)
-
-package team.duckie.quackquack.ui.plugin.interceptor
-
-import com.github.takahirom.roborazzi.RoborazziRule.Ignore as NoSnapshot
-import androidx.activity.ComponentActivity
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.compose.ui.test.onRoot
-import androidx.compose.ui.unit.dp
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.github.takahirom.roborazzi.RoborazziRule
-import io.kotest.assertions.throwables.shouldThrowWithMessage
-import io.kotest.matchers.maps.shouldMatchExactly
-import io.kotest.matchers.nulls.shouldNotBeNull
-import io.kotest.matchers.shouldBe
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import team.duckie.quackquack.material.QuackColor
-import team.duckie.quackquack.material.theme.QuackTheme
-import team.duckie.quackquack.ui.QuackTag
-import team.duckie.quackquack.ui.QuackTagStyle
-import team.duckie.quackquack.ui.TagStyleMarker
-import team.duckie.quackquack.ui.plugin.rememberQuackPlugins
-import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi
-import team.duckie.quackquack.util.compose.snapshot.test.SnapshotName
-import team.duckie.quackquack.util.compose.snapshot.test.snapshotPath
-
-@RunWith(AndroidJUnit4::class)
-class QuackInterceptorPluginTest {
- @get:Rule
- val compose = createAndroidComposeRule()
-
- @get:Rule
- val roborazzi = RoborazziRule(
- composeRule = compose,
- captureRoot = compose.onRoot(),
- options = RoborazziRule.Options(
- outputFileProvider = { description, _, fileExtension ->
- val snapshotName = description.getAnnotation(SnapshotName::class.java)?.name ?: description.methodName
- snapshotPath(
- domain = "QuackInterceptorPlugin",
- snapshotName = snapshotName,
- isGif = fileExtension == "gif",
- )
- },
- ),
- )
-
- @SnapshotName("QuackTagRadiusStyleIntercepted")
- @Test
- fun `style intercept works fine`() {
- val map = mutableMapOf()
-
- var interceptedStyle: QuackTagStyle? = null
- val interceptedRadius = Int.MAX_VALUE.dp
-
- compose.setContent {
- QuackTheme(
- plugins = rememberQuackPlugins {
- +QuackInterceptorPlugin.DesignToken { componentName, componentDesignToken, componentModifier, _ ->
- map["componentName"] = componentName
- map["componentDesignToken"] = componentDesignToken
- map["componentModifier"] = componentModifier
-
- (if (componentName == "QuackTag") {
- @Suppress("UNCHECKED_CAST")
- componentDesignToken as QuackTagStyle
- object : QuackTagStyle by componentDesignToken {
- override val radius = interceptedRadius
- override val colors =
- componentDesignToken.colors.copy(
- backgroundColor = QuackColor.Gray3,
- contentColor = QuackColor.Black,
- )
- }
- } else {
- componentDesignToken
- }).also { intercepttedResult ->
- @Suppress("UNCHECKED_CAST")
- interceptedStyle = intercepttedResult as QuackTagStyle
- }
- }
- },
- ) {
- QuackTag(
- text = "Intercepted Tag",
- style = QuackTagStyle.Filled,
- onClick = {},
- )
- }
- }
-
- map.shouldMatchExactly(
- "componentName" to { it shouldBe "QuackTag" },
- "componentDesignToken" to { it.toString() shouldBe QuackTagStyle.Filled.toString() },
- "componentModifier" to { it shouldBe Modifier },
- )
- interceptedStyle.shouldNotBeNull().radius shouldBe interceptedRadius
- }
-
- @NoSnapshot
- @Test
- fun InterceptedStyleTypeExceptionMessage() {
- shouldThrowWithMessage(InterceptedStyleTypeExceptionMessage) {
- compose.setContent {
- QuackTheme(
- plugins = rememberQuackPlugins {
- +QuackInterceptorPlugin.DesignToken { _, _, _, _ -> Unit }
- },
- ) {
- QuackTag(
- text = "",
- style = QuackTagStyle.Filled,
- onClick = {},
- )
- }
- }
- }
- }
-}
diff --git a/ui-plugin/interceptor/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/snapshot/QuackInterceptorPluginSnapshot.kt b/ui-plugin/interceptor/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/snapshot/QuackInterceptorPluginSnapshot.kt
new file mode 100644
index 000000000..a18ffb0c3
--- /dev/null
+++ b/ui-plugin/interceptor/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/snapshot/QuackInterceptorPluginSnapshot.kt
@@ -0,0 +1,79 @@
+/*
+ * Designed and developed by Duckie Team 2023.
+ *
+ * Licensed under the MIT.
+ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
+ */
+
+/*
+ * Designed and developed by Duckie Team 2023.
+ *
+ * Licensed under the MIT.
+ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
+ */
+
+@file:OptIn(ExperimentalQuackQuackApi::class)
+@file:Suppress("UNCHECKED_CAST")
+
+package team.duckie.quackquack.ui.plugin.interceptor.snapshot
+
+import androidx.activity.ComponentActivity
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.github.takahirom.roborazzi.captureRoboImage
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import team.duckie.quackquack.material.QuackColor
+import team.duckie.quackquack.material.theme.QuackTheme
+import team.duckie.quackquack.ui.QuackTag
+import team.duckie.quackquack.ui.QuackTagStyle
+import team.duckie.quackquack.ui.TagStyleMarker
+import team.duckie.quackquack.ui.plugin.interceptor.QuackInterceptorPlugin
+import team.duckie.quackquack.ui.plugin.rememberQuackPlugins
+import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi
+import team.duckie.quackquack.util.compose.snapshot.test.SnapshotPathGeneratorRule
+
+@RunWith(AndroidJUnit4::class)
+class QuackInterceptorPluginSnapshot {
+ @get:Rule
+ val snapshotPath = SnapshotPathGeneratorRule("QuackInterceptorPlugin")
+
+ @get:Rule
+ val compose = createAndroidComposeRule()
+
+ @Test
+ fun QuackTagBackgroundColorIntercepted() {
+ compose.setContent {
+ QuackTheme(
+ plugins = rememberQuackPlugins {
+ +QuackInterceptorPlugin.DesignToken { componentName, componentDesignToken, componentModifier, _ ->
+ (if (componentName == "QuackTag") {
+ componentDesignToken as QuackTagStyle
+ object : QuackTagStyle by componentDesignToken {
+ override val radius = Int.MAX_VALUE.dp
+ override val colors =
+ componentDesignToken.colors.copy(
+ backgroundColor = QuackColor.Gray3,
+ contentColor = QuackColor.Black,
+ )
+ }
+ } else {
+ componentDesignToken
+ })
+ }
+ },
+ ) {
+ QuackTag(
+ text = "Intercepted Tag",
+ style = QuackTagStyle.Filled,
+ onClick = {},
+ )
+ }
+ }
+
+ compose.onRoot().captureRoboImage(snapshotPath())
+ }
+}
diff --git a/ui-plugin/interceptor/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/uitest/QuackInterceptorPluginTest.kt b/ui-plugin/interceptor/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/uitest/QuackInterceptorPluginTest.kt
new file mode 100644
index 000000000..cb6cb210f
--- /dev/null
+++ b/ui-plugin/interceptor/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/uitest/QuackInterceptorPluginTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Designed and developed by Duckie Team 2023.
+ *
+ * Licensed under the MIT.
+ * Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
+ */
+
+@file:OptIn(ExperimentalQuackQuackApi::class)
+
+package team.duckie.quackquack.ui.plugin.interceptor.uitest
+
+import androidx.activity.ComponentActivity
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import io.kotest.assertions.throwables.shouldThrowWithMessage
+import io.kotest.matchers.collections.shouldContainExactly
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import team.duckie.quackquack.material.theme.QuackTheme
+import team.duckie.quackquack.ui.QuackTag
+import team.duckie.quackquack.ui.QuackTagStyle
+import team.duckie.quackquack.ui.plugin.interceptor.InterceptedStyleTypeExceptionMessage
+import team.duckie.quackquack.ui.plugin.interceptor.QuackInterceptorPlugin
+import team.duckie.quackquack.ui.plugin.rememberQuackPlugins
+import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi
+
+@RunWith(AndroidJUnit4::class)
+class QuackInterceptorPluginTest {
+ @get:Rule
+ val compose = createAndroidComposeRule()
+
+ @Test
+ fun InnerComponentsCanBeIntercepted() {
+ val componentNames = mutableListOf()
+
+ compose.setContent {
+ QuackTheme(
+ plugins = rememberQuackPlugins {
+ +QuackInterceptorPlugin.DesignToken { componentName, componentDesignToken, _, _ ->
+ componentNames += componentName
+ componentDesignToken
+ }
+ },
+ ) {
+ QuackTag(
+ text = "",
+ style = QuackTagStyle.Filled,
+ onClick = {},
+ )
+ }
+ }
+
+ // 맹글링 제거 후 assertion
+ componentNames.map { it.split("-").first() } shouldContainExactly listOf("QuackTag", "QuackText")
+ }
+
+ @Test
+ fun InterceptedStyleTypeException() {
+ shouldThrowWithMessage(InterceptedStyleTypeExceptionMessage) {
+ compose.setContent {
+ QuackTheme(
+ plugins = rememberQuackPlugins {
+ +QuackInterceptorPlugin.DesignToken { _, _, _, _ -> Unit }
+ },
+ ) {
+ QuackTag(
+ text = "",
+ style = QuackTagStyle.Filled,
+ onClick = {},
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/ui-plugin/interceptor/src/test/snapshots/QuackInterceptorPlugin/style intercept works fine.png b/ui-plugin/interceptor/src/test/snapshots/QuackInterceptorPlugin/QuackTagBackgroundColorIntercepted.png
similarity index 100%
rename from ui-plugin/interceptor/src/test/snapshots/QuackInterceptorPlugin/style intercept works fine.png
rename to ui-plugin/interceptor/src/test/snapshots/QuackInterceptorPlugin/QuackTagBackgroundColorIntercepted.png
diff --git a/ui-plugin/interceptor/textfield/build.gradle.kts b/ui-plugin/interceptor/textfield/build.gradle.kts
index ba8123cb1..a2ac57a4c 100644
--- a/ui-plugin/interceptor/textfield/build.gradle.kts
+++ b/ui-plugin/interceptor/textfield/build.gradle.kts
@@ -14,6 +14,7 @@ plugins {
quackquack("android-compose")
quackquack("kotlin-explicit-api")
quackquack("test-junit")
+ quackquack("test-roborazzi")
quackquack("quack-publishing")
alias(libs.plugins.test.roborazzi)
}
@@ -31,16 +32,6 @@ tasks.withType {
android {
namespace = "team.duckie.quackquack.ui.plugin.interceptor.textfield"
-
- testOptions {
- unitTests {
- isIncludeAndroidResources = true
- isReturnDefaultValues = true
- all { test ->
- test.systemProperty("robolectric.graphicsMode", "NATIVE")
- }
- }
- }
}
dependencies {
@@ -52,11 +43,7 @@ dependencies {
)
testImplementations(
libs.compose.foundation,
- libs.test.robolectric,
- libs.test.junit.compose,
libs.test.kotest.assertion.core,
- libs.test.kotlin.coroutines, // needed for compose-ui-test
- libs.bundles.test.roborazzi,
projects.utilComposeSnapshotTest,
)
}
diff --git a/ui-plugin/interceptor/textfield/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/textfield/QuackTextFieldFontFamilyRemovalPluginSnapshot.kt b/ui-plugin/interceptor/textfield/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/textfield/snapshot/QuackTextFieldFontFamilyRemovalPluginSnapshot.kt
similarity index 85%
rename from ui-plugin/interceptor/textfield/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/textfield/QuackTextFieldFontFamilyRemovalPluginSnapshot.kt
rename to ui-plugin/interceptor/textfield/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/textfield/snapshot/QuackTextFieldFontFamilyRemovalPluginSnapshot.kt
index 6ff3556a2..760e0f7c8 100644
--- a/ui-plugin/interceptor/textfield/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/textfield/QuackTextFieldFontFamilyRemovalPluginSnapshot.kt
+++ b/ui-plugin/interceptor/textfield/src/test/kotlin/team/duckie/quackquack/ui/plugin/interceptor/textfield/snapshot/QuackTextFieldFontFamilyRemovalPluginSnapshot.kt
@@ -7,7 +7,7 @@
@file:OptIn(ExperimentalDesignToken::class, ExperimentalQuackQuackApi::class)
-package team.duckie.quackquack.ui.plugin.interceptor.textfield
+package team.duckie.quackquack.ui.plugin.interceptor.textfield.snapshot
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.captureRoboImage
@@ -19,10 +19,13 @@ import team.duckie.quackquack.ui.QuackDefaultTextField
import team.duckie.quackquack.ui.QuackFilledTextField
import team.duckie.quackquack.ui.QuackTextFieldStyle
import team.duckie.quackquack.ui.optin.ExperimentalDesignToken
+import team.duckie.quackquack.ui.plugin.interceptor.textfield.QuackTextFieldFontFamilyRemovalPlugin
import team.duckie.quackquack.ui.plugin.rememberQuackPlugins
import team.duckie.quackquack.ui.util.ExperimentalQuackQuackApi
import team.duckie.quackquack.util.compose.snapshot.test.SnapshotPathGeneratorRule
+private const val TestMessage = "퍠꿻땗뷃휉퉶뷟퍯 <- 잘 보이니?"
+
@RunWith(AndroidJUnit4::class)
class QuackTextFieldFontFamilyRemovalPluginSnapshot {
@get:Rule
@@ -37,7 +40,7 @@ class QuackTextFieldFontFamilyRemovalPluginSnapshot {
},
) {
QuackDefaultTextField(
- value = "퍠꿻땗뷃휉퉶뷟퍯 <- 잘 보이니?",
+ value = TestMessage,
onValueChange = {},
style = QuackTextFieldStyle.Default,
)
@@ -54,7 +57,7 @@ class QuackTextFieldFontFamilyRemovalPluginSnapshot {
},
) {
QuackFilledTextField(
- value = "퍠꿻땗뷃휉퉶뷟퍯 <- 잘 보이니?",
+ value = TestMessage,
onValueChange = {},
style = QuackTextFieldStyle.FilledLarge,
)
diff --git a/ui-plugin/src/test/kotlin/team/duckie/quackquack/ui/plugin/QuackPluginsTest.kt b/ui-plugin/src/test/kotlin/team/duckie/quackquack/ui/plugin/QuackPluginsTest.kt
index f62b971c3..7e2efff21 100644
--- a/ui-plugin/src/test/kotlin/team/duckie/quackquack/ui/plugin/QuackPluginsTest.kt
+++ b/ui-plugin/src/test/kotlin/team/duckie/quackquack/ui/plugin/QuackPluginsTest.kt
@@ -5,6 +5,8 @@
* Please see full license: https://github.com/duckie-team/quack-quack-android/blob/main/LICENSE
*/
+@file:Suppress("UNCHECKED_CAST")
+
package team.duckie.quackquack.ui.plugin
import androidx.compose.runtime.collection.MutableVector
@@ -64,7 +66,6 @@ class QuackPluginsTest : StringSpec() {
pluginOrNull.shouldBeNull()
}
- @Suppress("UNCHECKED_CAST")
"filterByTypeOrNull으로 타입에 맞는 플러그인을 모두 조회할 수 있음" {
open class LocalP : QuackPlugin
open class LocalP2 : QuackPlugin
diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts
index 63fda769a..26197c211 100644
--- a/ui/build.gradle.kts
+++ b/ui/build.gradle.kts
@@ -18,6 +18,7 @@ plugins {
quackquack("kotlin-explicit-api")
quackquack("quack-publishing")
quackquack("test-junit")
+ quackquack("test-roborazzi")
alias(libs.plugins.kotlin.ksp)
alias(libs.plugins.test.roborazzi)
}
@@ -45,13 +46,8 @@ android {
}
testOptions {
- unitTests {
- isIncludeAndroidResources = true
- isReturnDefaultValues = true
- all { test ->
- test.maxHeapSize = "4G"
- test.systemProperty("robolectric.graphicsMode", "NATIVE")
- }
+ unitTests.all { test ->
+ test.maxHeapSize = "4G"
}
}
}
@@ -97,13 +93,7 @@ dependencies {
projects.casaAnnotation.orArtifact(),
)
- testImplementations(
- libs.test.robolectric,
- libs.test.junit.compose,
- libs.test.kotest.assertion.core,
- libs.test.kotlin.coroutines, // needed for compose-ui-test
- libs.bundles.test.roborazzi,
- )
+ testImplementation(libs.test.kotest.assertion.core)
androidTestImplementations(
libs.test.junit.compose,
libs.test.kotest.assertion.core,
diff --git a/ui/report/compose-metrics/ui_debug-module.json b/ui/report/compose-metrics/ui_debug-module.json
index 73b681230..495f09229 100644
--- a/ui/report/compose-metrics/ui_debug-module.json
+++ b/ui/report/compose-metrics/ui_debug-module.json
@@ -1,25 +1,25 @@
{
- "skippableComposables": 8,
- "restartableComposables": 14,
- "readonlyComposables": 1,
- "totalComposables": 52,
- "restartGroups": 14,
- "totalGroups": 66,
- "staticArguments": 54,
- "certainArguments": 264,
- "knownStableArguments": 613,
- "knownUnstableArguments": 32,
+ "skippableComposables": 2,
+ "restartableComposables": 5,
+ "readonlyComposables": 0,
+ "totalComposables": 7,
+ "restartGroups": 5,
+ "totalGroups": 8,
+ "staticArguments": 6,
+ "certainArguments": 16,
+ "knownStableArguments": 76,
+ "knownUnstableArguments": 6,
"unknownStableArguments": 0,
- "totalArguments": 645,
- "markedStableClasses": 18,
- "inferredStableClasses": 11,
+ "totalArguments": 82,
+ "markedStableClasses": 14,
+ "inferredStableClasses": 0,
"inferredUnstableClasses": 0,
"inferredUncertainClasses": 0,
- "effectivelyStableClasses": 29,
- "totalClasses": 29,
- "memoizedLambdas": 24,
- "singletonLambdas": 5,
+ "effectivelyStableClasses": 14,
+ "totalClasses": 14,
+ "memoizedLambdas": 0,
+ "singletonLambdas": 0,
"singletonComposableLambdas": 0,
- "composableLambdas": 3,
- "totalLambdas": 36
+ "composableLambdas": 0,
+ "totalLambdas": 2
}
\ No newline at end of file
diff --git a/ui/report/compose-metrics/ui_debugUnitTest-module.json b/ui/report/compose-metrics/ui_debugUnitTest-module.json
index 75d3e0c58..bfec53e19 100644
--- a/ui/report/compose-metrics/ui_debugUnitTest-module.json
+++ b/ui/report/compose-metrics/ui_debugUnitTest-module.json
@@ -1,22 +1,22 @@
{
- "skippableComposables": 142,
- "restartableComposables": 145,
+ "skippableComposables": 139,
+ "restartableComposables": 140,
"readonlyComposables": 0,
- "totalComposables": 145,
- "restartGroups": 145,
- "totalGroups": 146,
- "staticArguments": 451,
- "certainArguments": 4,
- "knownStableArguments": 1789,
- "knownUnstableArguments": 1,
+ "totalComposables": 140,
+ "restartGroups": 140,
+ "totalGroups": 140,
+ "staticArguments": 454,
+ "certainArguments": 0,
+ "knownStableArguments": 1767,
+ "knownUnstableArguments": 0,
"unknownStableArguments": 0,
- "totalArguments": 1790,
+ "totalArguments": 1767,
"markedStableClasses": 0,
- "inferredStableClasses": 3,
- "inferredUnstableClasses": 6,
- "inferredUncertainClasses": 5,
- "effectivelyStableClasses": 3,
- "totalClasses": 14,
+ "inferredStableClasses": 2,
+ "inferredUnstableClasses": 1,
+ "inferredUncertainClasses": 10,
+ "effectivelyStableClasses": 2,
+ "totalClasses": 13,
"memoizedLambdas": 221,
"singletonLambdas": 78,
"singletonComposableLambdas": 102,
diff --git a/ui/report/compose-metrics/ui_release-module.json b/ui/report/compose-metrics/ui_release-module.json
index 73b681230..d3920a71a 100644
--- a/ui/report/compose-metrics/ui_release-module.json
+++ b/ui/report/compose-metrics/ui_release-module.json
@@ -6,20 +6,20 @@
"restartGroups": 14,
"totalGroups": 66,
"staticArguments": 54,
- "certainArguments": 264,
- "knownStableArguments": 613,
+ "certainArguments": 268,
+ "knownStableArguments": 618,
"knownUnstableArguments": 32,
"unknownStableArguments": 0,
- "totalArguments": 645,
+ "totalArguments": 650,
"markedStableClasses": 18,
"inferredStableClasses": 11,
"inferredUnstableClasses": 0,
"inferredUncertainClasses": 0,
"effectivelyStableClasses": 29,
"totalClasses": 29,
- "memoizedLambdas": 24,
+ "memoizedLambdas": 23,
"singletonLambdas": 5,
"singletonComposableLambdas": 0,
"composableLambdas": 3,
- "totalLambdas": 36
+ "totalLambdas": 35
}
\ No newline at end of file
diff --git a/ui/report/compose-metrics/ui_releaseUnitTest-module.json b/ui/report/compose-metrics/ui_releaseUnitTest-module.json
index 75d3e0c58..bfec53e19 100644
--- a/ui/report/compose-metrics/ui_releaseUnitTest-module.json
+++ b/ui/report/compose-metrics/ui_releaseUnitTest-module.json
@@ -1,22 +1,22 @@
{
- "skippableComposables": 142,
- "restartableComposables": 145,
+ "skippableComposables": 139,
+ "restartableComposables": 140,
"readonlyComposables": 0,
- "totalComposables": 145,
- "restartGroups": 145,
- "totalGroups": 146,
- "staticArguments": 451,
- "certainArguments": 4,
- "knownStableArguments": 1789,
- "knownUnstableArguments": 1,
+ "totalComposables": 140,
+ "restartGroups": 140,
+ "totalGroups": 140,
+ "staticArguments": 454,
+ "certainArguments": 0,
+ "knownStableArguments": 1767,
+ "knownUnstableArguments": 0,
"unknownStableArguments": 0,
- "totalArguments": 1790,
+ "totalArguments": 1767,
"markedStableClasses": 0,
- "inferredStableClasses": 3,
- "inferredUnstableClasses": 6,
- "inferredUncertainClasses": 5,
- "effectivelyStableClasses": 3,
- "totalClasses": 14,
+ "inferredStableClasses": 2,
+ "inferredUnstableClasses": 1,
+ "inferredUncertainClasses": 10,
+ "effectivelyStableClasses": 2,
+ "totalClasses": 13,
"memoizedLambdas": 221,
"singletonLambdas": 78,
"singletonComposableLambdas": 102,
diff --git a/ui/report/compose-reports/ui_debug-classes.txt b/ui/report/compose-reports/ui_debug-classes.txt
index f0c83eedd..788c6814a 100644
--- a/ui/report/compose-reports/ui_debug-classes.txt
+++ b/ui/report/compose-reports/ui_debug-classes.txt
@@ -80,21 +80,6 @@ stable class QuackSecondaryRoundSmallButtonDefaults {
stable var typography: QuackTypography
stable var disabledTypography: QuackTypography
}
-stable class QuackSwitchColors {
- stable val track: QuackColor
- stable val disableTrack: QuackColor
- stable val thumb: QuackColor
- stable val thumbStroke: QuackColor
- stable val disableThumb: QuackColor
- stable val disableThumbStroke: QuackColor
-}
-stable class QuackTabColors {
- stable val background: QuackColor
- stable val underline: QuackColor
- stable val indicate: QuackColor
- stable val contentColor: QuackColor
- stable val disableContentColor: QuackColor
-}
stable class QuackTagColors {
stable val backgroundColor: QuackColor
stable val unselectedBackgroundColor: QuackColor
@@ -142,75 +127,3 @@ stable class QuackGrayscaleOutlinedTagDefaults {
stable var typography: QuackTypography
stable var unselectedTypography: QuackTypography
}
-stable class Success {
- stable val label: String?
- = Stable
-}
-stable class Error {
- stable val label: String?
- = Stable
-}
-stable class Default {
- = Stable
-}
-stable class TextFieldValidationState {
-}
-stable class Invisible {
- stable val baselineLabel: String
- stable val baselineTypography: QuackTypography?
- = Stable
-}
-stable class Gone {
- = Stable
-}
-stable class TextFieldValidationLabelVisibilityStrategy {
-}
-stable class TextFieldColors {
- stable val backgroundColor: QuackColor
- stable val contentColor: QuackColor
- stable val placeholderColor: QuackColor
- stable val errorColor: QuackColor
- stable val successColor: QuackColor
- = Stable
-}
-stable class TextFieldColors {
- stable val backgroundColor: QuackColor?
- stable val backgroundColorGetter: Function2<@[ParameterName(name = 'text')] String, @[ParameterName(name = 'focusInteraction')] FocusInteraction?, QuackColor>?
- stable val contentColor: QuackColor
- stable val placeholderColor: QuackColor
- = Stable
-}
-stable class QuackDefaultTextFieldDefaults {
- stable var colors: TextFieldColors
- stable var contentPadding: PaddingValues
- stable var contentSpacedBy: Dp
- stable var validationLabelAndIndicatorSpacedBy: Dp
- stable var typography: QuackTypography
- stable val validationLabelTypography: QuackTypography
- = Stable
-}
-stable class QuackDefaultLargeTextFieldDefaults {
- stable var colors: TextFieldColors
- stable var contentPadding: PaddingValues
- stable var contentSpacedBy: Dp
- stable var validationLabelAndIndicatorSpacedBy: Dp
- stable var typography: QuackTypography
- stable val validationLabelTypography: QuackTypography
- = Stable
-}
-stable class QuackFilledLargeTextFieldDefaults {
- stable var radius: Dp
- stable var colors: TextFieldColors
- stable var contentPadding: PaddingValues
- stable var contentSpacedBy: Dp
- stable var typography: QuackTypography
- = Stable
-}
-stable class QuackFilledFlatTextFieldDefaults {
- stable var radius: Dp
- stable var colors: TextFieldColors
- stable var contentPadding: PaddingValues
- stable var contentSpacedBy: Dp
- stable var typography: QuackTypography
- = Stable
-}
diff --git a/ui/report/compose-reports/ui_debug-composables.csv b/ui/report/compose-reports/ui_debug-composables.csv
index b110b6d30..c98d0f702 100644
--- a/ui/report/compose-reports/ui_debug-composables.csv
+++ b/ui/report/compose-reports/ui_debug-composables.csv
@@ -1,44 +1,5 @@
package,name,composable,skippable,restartable,readonly,inline,isLambda,hasDefaults,defaultsGroup,groups,calls,
team.duckie.quackquack.ui.QuackButton,QuackButton,1,0,0,0,0,0,0,0,1,6,
team.duckie.quackquack.ui.QuackBaseButton,QuackBaseButton,1,1,1,0,0,0,0,0,1,2,
-team.duckie.quackquack.ui.QuackIcon,QuackIcon,1,0,0,0,0,0,0,0,2,3,
-team.duckie.quackquack.ui.QuackImage,QuackImage,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.QuackImage,QuackImage,1,0,0,0,0,0,0,0,2,3,
-team.duckie.quackquack.ui.QuackImage,QuackImage,1,0,0,0,0,0,0,0,2,6,
-team.duckie.quackquack.ui.sugar.QuackPrimaryLargeButton,QuackPrimaryLargeButton,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackSecondaryLargeButton,QuackSecondaryLargeButton,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackMediumButton,QuackMediumButton,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackPrimaryFilledSmallButton,QuackPrimaryFilledSmallButton,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackPrimaryOutlinedSmallButton,QuackPrimaryOutlinedSmallButton,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackPrimaryOutlinedRoundSmallButton,QuackPrimaryOutlinedRoundSmallButton,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackSecondarySmallButton,QuackSecondarySmallButton,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackSecondaryRoundSmallButton,QuackSecondaryRoundSmallButton,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackOutlinedTag,QuackOutlinedTag,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackFilledTag,QuackFilledTag,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackGrayscaleFlatTag,QuackGrayscaleFlatTag,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackGrayscaleOutlinedTag,QuackGrayscaleOutlinedTag,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackBody1,QuackBody1,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackBody2,QuackBody2,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackBody3,QuackBody3,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackHeadLine1,QuackHeadLine1,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackHeadLine2,QuackHeadLine2,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackLarge1,QuackLarge1,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackSubtitle,QuackSubtitle,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackSubtitle2,QuackSubtitle2,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackTitle1,QuackTitle1,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.sugar.QuackTitle2,QuackTitle2,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.QuackSwitch,QuackSwitch,1,1,1,0,0,0,0,0,1,7,
-team.duckie.quackquack.ui.QuackTab,QuackTab,1,1,1,0,0,0,1,0,3,10,
-team.duckie.quackquack.ui.QuackTag,QuackTag,1,0,0,0,0,0,0,0,1,7,
+team.duckie.quackquack.ui.QuackTag,QuackTag,1,0,0,0,0,0,0,0,1,6,
team.duckie.quackquack.ui.QuackBaseTag,QuackBaseTag,1,1,1,0,0,0,0,0,1,2,
-team.duckie.quackquack.ui.QuackText,QuackText,1,1,1,0,0,0,0,0,4,10,
-team.duckie.quackquack.ui.ClickableText,ClickableText,1,1,1,0,0,0,0,0,1,4,
-team.duckie.quackquack.ui.rememberSpanAnnotatedString,rememberSpanAnnotatedString,1,0,0,0,0,0,0,0,1,1,
-team.duckie.quackquack.ui.QuackDefaultTextField,QuackDefaultTextField,1,0,0,0,0,0,0,0,1,7,
-team.duckie.quackquack.ui.QuackDefaultTextField,QuackDefaultTextField,1,0,0,0,0,0,0,0,1,10,
-team.duckie.quackquack.ui.QuackFilledTextField,QuackFilledTextField,1,0,0,0,0,0,0,0,1,7,
-team.duckie.quackquack.ui.QuackFilledTextField,QuackFilledTextField,1,0,0,0,0,0,0,0,1,10,
-team.duckie.quackquack.ui.QuackBaseDefaultTextField,QuackBaseDefaultTextField,1,1,1,0,0,0,0,0,1,27,
-team.duckie.quackquack.ui.QuackOutlinedTextField,QuackOutlinedTextField,1,0,0,0,0,0,0,0,1,0,
-team.duckie.quackquack.ui.util.rememberLtrTextMeasurer,rememberLtrTextMeasurer,1,0,0,0,0,0,0,0,1,3,
-team.duckie.quackquack.ui.util.currentFontScale,currentFontScale,1,0,0,1,1,0,0,0,1,1,
diff --git a/ui/report/compose-reports/ui_debug-composables.txt b/ui/report/compose-reports/ui_debug-composables.txt
index 1afe56d80..75270c240 100644
--- a/ui/report/compose-reports/ui_debug-composables.txt
+++ b/ui/report/compose-reports/ui_debug-composables.txt
@@ -22,216 +22,6 @@ restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun QuackBase
stable trailingIcon: ImageVector?
stable onClick: Function0?
)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackIcon(
- stable icon: ImageVector
- stable modifier: Modifier? = @static Companion
- stable size: Dp = @static 24.dp
- stable tint: QuackColor = @static Companion.Unspecified
- stable contentScale: ContentScale? = @static Companion.Fit
- stable contentDescription: String? = @static null
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackImage(
- stable src: ImageVector
- stable modifier: Modifier? = @static Companion
- stable tint: QuackColor = @static Companion.Unspecified
- stable contentScale: ContentScale? = @static Companion.Fit
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackImage(
- stable src: Int
- stable modifier: Modifier? = @static Companion
- stable tint: QuackColor = @static Companion.Unspecified
- stable contentScale: ContentScale? = @static Companion.Fit
- stable contentDescription: String? = @static null
-)
-fun QuackImage(
- unstable src: Any?
- stable modifier: Modifier? = @static Companion
- stable tint: QuackColor = @static Companion.Unspecified
- stable contentScale: ContentScale? = @static Companion.Fit
- stable contentDescription: String? = @static null
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackPrimaryLargeButton(
- stable modifier: Modifier? = @static Companion
- stable enabled: Boolean = @static true
- stable text: String
- stable rippleEnabled: Boolean = @static true
- stable onClick: Function0
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackSecondaryLargeButton(
- stable modifier: Modifier? = @static Companion
- stable enabled: Boolean = @static true
- stable text: String
- stable rippleEnabled: Boolean = @static true
- stable onClick: Function0
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackMediumButton(
- stable modifier: Modifier? = @static Companion
- stable enabled: Boolean = @static true
- stable text: String
- stable rippleEnabled: Boolean = @static true
- stable onClick: Function0
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackPrimaryFilledSmallButton(
- stable modifier: Modifier? = @static Companion
- stable enabled: Boolean = @static true
- stable text: String
- stable rippleEnabled: Boolean = @static true
- stable onClick: Function0
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackPrimaryOutlinedSmallButton(
- stable modifier: Modifier? = @static Companion
- stable enabled: Boolean = @static true
- stable text: String
- stable rippleEnabled: Boolean = @static true
- stable onClick: Function0
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackPrimaryOutlinedRoundSmallButton(
- stable modifier: Modifier? = @static Companion
- stable enabled: Boolean = @static true
- stable text: String
- stable rippleEnabled: Boolean = @static true
- stable onClick: Function0
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackSecondarySmallButton(
- stable modifier: Modifier? = @static Companion
- stable enabled: Boolean = @static true
- stable text: String
- stable rippleEnabled: Boolean = @static true
- stable onClick: Function0
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackSecondaryRoundSmallButton(
- stable modifier: Modifier? = @static Companion
- stable enabled: Boolean = @static true
- stable text: String
- stable rippleEnabled: Boolean = @static true
- stable onClick: Function0
-)
-fun QuackOutlinedTag(
- stable text: String
- stable modifier: Modifier? = @static Companion
- stable selected: Boolean = @static true
- stable rippleEnabled: Boolean = @static true
- stable onClick: Function0
-)
-fun QuackFilledTag(
- stable text: String
- stable modifier: Modifier? = @static Companion
- stable selected: Boolean = @static true
- stable rippleEnabled: Boolean = @static true
- stable onClick: Function0
-)
-fun QuackGrayscaleFlatTag(
- stable text: String
- stable modifier: Modifier? = @static Companion
- stable selected: Boolean = @static true
- stable rippleEnabled: Boolean = @static true
- stable onClick: Function0
-)
-fun QuackGrayscaleOutlinedTag(
- stable text: String
- stable modifier: Modifier? = @static Companion
- stable selected: Boolean = @static true
- stable rippleEnabled: Boolean = @static true
- stable onClick: Function0
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackBody1(
- stable modifier: Modifier? = @static Companion
- stable text: String
- stable singleLine: Boolean = @static false
- stable softWrap: Boolean = @static true
- stable overflow: TextOverflow = @static Companion.Ellipsis
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackBody2(
- stable modifier: Modifier? = @static Companion
- stable text: String
- stable singleLine: Boolean = @static false
- stable softWrap: Boolean = @static true
- stable overflow: TextOverflow = @static Companion.Ellipsis
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackBody3(
- stable modifier: Modifier? = @static Companion
- stable text: String
- stable singleLine: Boolean = @static false
- stable softWrap: Boolean = @static true
- stable overflow: TextOverflow = @static Companion.Ellipsis
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackHeadLine1(
- stable modifier: Modifier? = @static Companion
- stable text: String
- stable singleLine: Boolean = @static false
- stable softWrap: Boolean = @static true
- stable overflow: TextOverflow = @static Companion.Ellipsis
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackHeadLine2(
- stable modifier: Modifier? = @static Companion
- stable text: String
- stable singleLine: Boolean = @static false
- stable softWrap: Boolean = @static true
- stable overflow: TextOverflow = @static Companion.Ellipsis
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackLarge1(
- stable modifier: Modifier? = @static Companion
- stable text: String
- stable singleLine: Boolean = @static false
- stable softWrap: Boolean = @static true
- stable overflow: TextOverflow = @static Companion.Ellipsis
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackSubtitle(
- stable modifier: Modifier? = @static Companion
- stable text: String
- stable singleLine: Boolean = @static false
- stable softWrap: Boolean = @static true
- stable overflow: TextOverflow = @static Companion.Ellipsis
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackSubtitle2(
- stable modifier: Modifier? = @static Companion
- stable text: String
- stable singleLine: Boolean = @static false
- stable softWrap: Boolean = @static true
- stable overflow: TextOverflow = @static Companion.Ellipsis
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackTitle1(
- stable modifier: Modifier? = @static Companion
- stable text: String
- stable singleLine: Boolean = @static false
- stable softWrap: Boolean = @static true
- stable overflow: TextOverflow = @static Companion.Ellipsis
-)
-scheme("[androidx.compose.ui.UiComposable]") fun QuackTitle2(
- stable modifier: Modifier? = @static Companion
- stable text: String
- stable singleLine: Boolean = @static false
- stable softWrap: Boolean = @static true
- stable overflow: TextOverflow = @static Companion.Ellipsis
-)
-restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun QuackSwitch(
- stable enabled: Boolean
- stable colors: QuackSwitchColors? = @static Companion.defaultSwitchColors
- stable modifier: Modifier? = @static Companion
- stable onClick: Function0
-)
-restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun QuackTab(
- stable index: Int
- stable colors: QuackTabColors? = @static Companion.defaultTabColors()
- indicatorStartXOffsetAnimatable: Animatable? = @dynamic remember({
- Animatable (
- initialValue = QuackTabIndicatorXOffsetInitialValue ,
- typeConverter = Companion . VectorConverter ,
- label = "QuackTabIndicatorStartXOffset"
- )
-}
-, $composer, 0)
- indicatorEndXOffsetAnimatable: Animatable? = @dynamic remember({
- Animatable (
- initialValue = QuackTabIndicatorXOffsetInitialValue ,
- typeConverter = Companion . VectorConverter ,
- label = "QuackTabIndicatorEndXOffset"
- )
-}
-, $composer, 0)
- stable modifier: Modifier? = @static Companion
- stable content: @[ExtensionFunctionType] Function1
-)
scheme("[androidx.compose.ui.UiComposable]") fun QuackTag(
stable text: String
stable style: QuackTagStyle
@@ -257,201 +47,3 @@ restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun QuackBase
stable trailingIconOnClick: Function0?
stable onClick: Function0?
)
-restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun QuackText(
- stable text: String
- stable typography: QuackTypography
- stable modifier: Modifier? = @static Companion
- stable singleLine: Boolean = @static false
- stable softWrap: Boolean = @static true
- stable overflow: TextOverflow = @static Companion.Ellipsis
-)
-restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun ClickableText(
- stable modifier: Modifier
- stable text: String
- stable highlightData: TextHighlightData
- stable style: TextStyle
- stable softWrap: Boolean
- stable overflow: TextOverflow
- stable maxLines: Int
-)
-fun rememberSpanAnnotatedString(
- stable text: String
- unstable spanTexts: List
- stable spanStyle: SpanStyle
- unstable annotationTexts: List
-): AnnotatedString
-fun QuackDefaultTextField(
- stable value: String
- stable onValueChange: Function1<@[ParameterName(name = 'value')] String, Unit>
- stable style: QuackTextFieldStyle