Skip to content

Commit

Permalink
Merge branch 'main' into takahirom/extension-changeable/2024-10-31
Browse files Browse the repository at this point in the history
  • Loading branch information
takahirom authored Nov 6, 2024
2 parents ccb4154 + d1c65c0 commit a5f7d45
Show file tree
Hide file tree
Showing 42 changed files with 1,538 additions and 106 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/dependency-diff.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ jobs:
modules: |
roborazzi-compose-ios|commonMainImplementationDependenciesMetadata
roborazzi-compose-desktop|commonMainImplementationDependenciesMetadata
roborazzi-ai-gemini|commonMainImplementationDependenciesMetadata
roborazzi-ai-openai|commonMainImplementationDependenciesMetadata
roborazzi
roborazzi-compose
roborazzi-compose-preview-scanner-support
Expand Down
133 changes: 125 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1011,6 +1011,115 @@ Currently, we don't support all the annotation options provided by the Compose P
You can check the supported annotations in the [source code](https://github.com/takahirom/roborazzi/blob/main/roborazzi-compose-preview-scanner-support/src/main/java/com/github/takahirom/roborazzi/RobolectricPreviewInfosApplier.kt).
We are looking forward to your contributions to support more annotation options.
</div>
<div name="topic_ai_powered_image_assertion">
<!-- Generated by docs/topics/ai_powered_image_assertion.md. Do not edit this file. -->
# Experimental AI-Powered Image Assertion
Roborazzi supports AI-powered image assertion.
AI-powered image assertion is an experimental feature. Screenshot tests are a great way to verify your app's UI, but verifying the content of the images can be a tedious and time-consuming task. This manual effort reduces scalability. Roborazzi can help automate this process through AI-powered image assertion, making it more efficient and scalable.
There are two new library modules: `io.github.takahirom.roborazzi:roborazzi-ai-gemini` and `io.github.takahirom.roborazzi:roborazzi-ai-openai` for AI-powered image assertion.
`roborazzi-ai-gemini` leverages [Gemini](https://gemini.google.com/) and [generative-ai-kmp](https://github.com/PatilShreyas/generative-ai-kmp), while `roborazzi-ai-openai` utilizes the [OpenAI API](https://platform.openai.com/) through raw HTTP API calls implemented with Ktor and KotlinX Serialization
```kotlin
...
@get:Rule
val composeTestRule = createAndroidComposeRule<MainActivity>()
@get:Rule
val roborazziRule = RoborazziRule(
options = RoborazziRule.Options(
roborazziOptions = RoborazziOptions(
compareOptions = RoborazziOptions.CompareOptions(
aiAssertionOptions = AiAssertionOptions(
aiAssertionModel = GeminiAiAssertionModel(
// DO NOT HARDCODE your API key in your code.
// This is an example passing API Key through unitTests.all{ environment(key, value) }
apiKey = System.getenv("gemini_api_key") ?: ""
),
)
)
)
)
)
@Test
fun captureWithAi() {
ROBORAZZI_DEBUG = true
onView(ViewMatchers.isRoot())
.captureRoboImage(
roborazziOptions = provideRoborazziContext().options.addedAiAssertions(
AiAssertionOptions.AiAssertion(
assertionPrompt = "it should have PREVIOUS button",
requiredFulfillmentPercent = 90,
),
AiAssertionOptions.AiAssertion(
assertionPrompt = "it should show First Fragment",
requiredFulfillmentPercent = 90,
)
)
)
}
```
## Behavior of AI-Powered Image Assertion
AI-Powered Image Assertion runs only when the images are different. If the images are the same, AI-Powered Image Assertion is skipped.
This is because AI-Powered Image Assertion can be slow and expensive.
## Manual Image Assertion
You can use manual image assertion with Roborazzi. This allows you to utilize local LLMs or other LLMs. Manual Image Assertion doesn't require adding any dependencies other than Roborazzi itself.
You must provide the `AiAssertionModel` to `RoborazziOptions` to use manual image assertion.
```kotlin
interface AiAssertionModel {
fun assert(
referenceImageFilePath: String,
comparisonImageFilePath: String,
actualImageFilePath: String,
aiAssertionOptions: AiAssertionOptions
): AiAssertionResults
}
```
```kotlin
compareOptions = RoborazziOptions.CompareOptions(
aiAssertionOptions = AiAssertionOptions(
aiAssertionModel = object : AiAssertionOptions.AiAssertionModel {
override fun assert(
comparisonImageFilePath: String,
aiAssertionOptions: AiAssertionOptions
): AiAssertionResults {
// You can use any LLMs here to create AiAssertionResults
return AiAssertionResults(
aiAssertionResults = aiAssertionOptions.aiAssertions.map { assertion ->
AiAssertionResult(
assertionPrompt = assertion.assertionPrompt,
fulfillmentPercent = fulfillmentPercent,
requiredFulfillmentPercent = assertion.requiredFulfillmentPercent,
failIfNotFulfilled = assertion.failIfNotFulfilled,
explanation = "This is a manual test.",
)
}
)
}
},
aiAssertions = listOf(
AiAssertionOptions.AiAssertion(
assertionPrompt = "it should have PREVIOUS button",
requiredFulfillmentPercent = 90,
),
),
)
)
...
```
</div>
<div name="topic_idea_plugin">
Expand Down Expand Up @@ -1152,13 +1261,6 @@ kotlin {
}
}
...
// Roborazzi Desktop support uses Context Receivers
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
freeCompilerArgs += "-Xcontext-receivers"
}
}
```
Test target Composable function
Expand Down Expand Up @@ -1237,7 +1339,8 @@ The sample image
<!-- Generated by docs/topics/gradle_properties_options.md. Do not edit this file. -->
# Roborazzi gradle.properties Options and Recommendations
You can configure the following options in your `gradle.properties` file:
You can configure the following options in your `gradle.properties` file.
You can also use `-P` to set the options in the command line. For example, `./gradlew test -Proborazzi.test.record=true`.
## roborazzi.test
Expand Down Expand Up @@ -1280,6 +1383,15 @@ This option enables you to define the naming strategy for the recorded image. Th
roborazzi.record.namingStrategy=testClassAndMethod
```
## roborazzi.cleanupOldScreenshots
This option allows you to clean up old screenshots. By default, this option is set to false.
The reason why Roborazzi does not delete old screenshots by default is that Roborazzi doesn't know if you are running filtered tests or not. If you are running filtered tests, Roborazzi will delete the screenshots that are not related to the current test run.
```
roborazzi.cleanupOldScreenshots=true
```
## Robolectric Options
### robolectric.pixelCopyRenderMode
Expand Down Expand Up @@ -1430,6 +1542,11 @@ android {
It is discussed in [this issue](https://github.com/takahirom/roborazzi/issues/272).
Additionally, it might be worth trying to run your tests with VisualVM to monitor memory usage and identify potential leaks.
### Q: [IDEA Plugin] Roborazzi Gradle task is not displayed in Tool Window.
**A:** It is discussed in [this issue](https://github.com/takahirom/roborazzi/issues/493).
To enable the display of Roborazzi tasks, please enable ***Configure all Gradle tasks during Gradle Sync (this can make Gradle Sync slower)*** in the Settings | Experimental | Gradle.
<img src="https://github.com/user-attachments/assets/67fbb2a8-6b2a-458c-bb31-99025f1c1cab" width="800" />
</div>
### LICENSE
Expand Down
2 changes: 2 additions & 0 deletions README.template.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
</div>
<div name="topic_preview_support">
</div>
<div name="topic_ai_powered_image_assertion">
</div>
<div name="topic_idea_plugin">
</div>
<div name="topic_compose_multiplatform">
Expand Down
1 change: 1 addition & 0 deletions docs/roborazzi-docs.tree
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<toc-element topic="build_setup.md"/>
<toc-element topic="how_to_use.md"/>
<toc-element topic="preview_support.md"/>
<toc-element topic="ai_powered_image_assertion.md"/>
<toc-element topic="idea_plugin.md"/>
<toc-element topic="compose_multiplatform.md"/>
<toc-element topic="gradle_properties_options.md"/>
Expand Down
104 changes: 104 additions & 0 deletions docs/topics/ai_powered_image_assertion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Experimental AI-Powered Image Assertion

Roborazzi supports AI-powered image assertion.
AI-powered image assertion is an experimental feature. Screenshot tests are a great way to verify your app's UI, but verifying the content of the images can be a tedious and time-consuming task. This manual effort reduces scalability. Roborazzi can help automate this process through AI-powered image assertion, making it more efficient and scalable.

There are two new library modules: `io.github.takahirom.roborazzi:roborazzi-ai-gemini` and `io.github.takahirom.roborazzi:roborazzi-ai-openai` for AI-powered image assertion.

`roborazzi-ai-gemini` leverages [Gemini](https://gemini.google.com/) and [generative-ai-kmp](https://github.com/PatilShreyas/generative-ai-kmp), while `roborazzi-ai-openai` utilizes the [OpenAI API](https://platform.openai.com/) through raw HTTP API calls implemented with Ktor and KotlinX Serialization

```kotlin
...
@get:Rule
val composeTestRule = createAndroidComposeRule<MainActivity>()

@get:Rule
val roborazziRule = RoborazziRule(
options = RoborazziRule.Options(
roborazziOptions = RoborazziOptions(
compareOptions = RoborazziOptions.CompareOptions(
aiAssertionOptions = AiAssertionOptions(
aiAssertionModel = GeminiAiAssertionModel(
// DO NOT HARDCODE your API key in your code.
// This is an example passing API Key through unitTests.all{ environment(key, value) }
apiKey = System.getenv("gemini_api_key") ?: ""
),
)
)
)
)
)

@Test
fun captureWithAi() {
ROBORAZZI_DEBUG = true
onView(ViewMatchers.isRoot())
.captureRoboImage(
roborazziOptions = provideRoborazziContext().options.addedAiAssertions(
AiAssertionOptions.AiAssertion(
assertionPrompt = "it should have PREVIOUS button",
requiredFulfillmentPercent = 90,
),
AiAssertionOptions.AiAssertion(
assertionPrompt = "it should show First Fragment",
requiredFulfillmentPercent = 90,
)
)
)
}
```

## Behavior of AI-Powered Image Assertion

AI-Powered Image Assertion runs only when the images are different. If the images are the same, AI-Powered Image Assertion is skipped.
This is because AI-Powered Image Assertion can be slow and expensive.

## Manual Image Assertion

You can use manual image assertion with Roborazzi. This allows you to utilize local LLMs or other LLMs. Manual Image Assertion doesn't require adding any dependencies other than Roborazzi itself.

You must provide the `AiAssertionModel` to `RoborazziOptions` to use manual image assertion.

```kotlin
interface AiAssertionModel {
fun assert(
referenceImageFilePath: String,
comparisonImageFilePath: String,
actualImageFilePath: String,
aiAssertionOptions: AiAssertionOptions
): AiAssertionResults
}
```

```kotlin
compareOptions = RoborazziOptions.CompareOptions(
aiAssertionOptions = AiAssertionOptions(
aiAssertionModel = object : AiAssertionOptions.AiAssertionModel {
override fun assert(
comparisonImageFilePath: String,
aiAssertionOptions: AiAssertionOptions
): AiAssertionResults {
// You can use any LLMs here to create AiAssertionResults
return AiAssertionResults(
aiAssertionResults = aiAssertionOptions.aiAssertions.map { assertion ->
AiAssertionResult(
assertionPrompt = assertion.assertionPrompt,
fulfillmentPercent = fulfillmentPercent,
requiredFulfillmentPercent = assertion.requiredFulfillmentPercent,
failIfNotFulfilled = assertion.failIfNotFulfilled,
explanation = "This is a manual test.",
)
}
)
}
},
aiAssertions = listOf(
AiAssertionOptions.AiAssertion(
assertionPrompt = "it should have PREVIOUS button",
requiredFulfillmentPercent = 90,
),
),
)
)
...
```
7 changes: 0 additions & 7 deletions docs/topics/compose_multiplatform.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,6 @@ kotlin {
}
}
...

// Roborazzi Desktop support uses Context Receivers
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
freeCompilerArgs += "-Xcontext-receivers"
}
}
```

Test target Composable function
Expand Down
6 changes: 6 additions & 0 deletions docs/topics/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,9 @@ android {
```
It is discussed in [this issue](https://github.com/takahirom/roborazzi/issues/272).
Additionally, it might be worth trying to run your tests with VisualVM to monitor memory usage and identify potential leaks.

### Q: [IDEA Plugin] Roborazzi Gradle task is not displayed in Tool Window.

**A:** It is discussed in [this issue](https://github.com/takahirom/roborazzi/issues/493).
To enable the display of Roborazzi tasks, please enable ***Configure all Gradle tasks during Gradle Sync (this can make Gradle Sync slower)*** in the Settings | Experimental | Gradle.
<img src="https://github.com/user-attachments/assets/67fbb2a8-6b2a-458c-bb31-99025f1c1cab" width="800" />
12 changes: 11 additions & 1 deletion docs/topics/gradle_properties_options.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Roborazzi gradle.properties Options and Recommendations

You can configure the following options in your `gradle.properties` file:
You can configure the following options in your `gradle.properties` file.
You can also use `-P` to set the options in the command line. For example, `./gradlew test -Proborazzi.test.record=true`.

## roborazzi.test

Expand Down Expand Up @@ -43,6 +44,15 @@ This option enables you to define the naming strategy for the recorded image. Th
roborazzi.record.namingStrategy=testClassAndMethod
```

## roborazzi.cleanupOldScreenshots

This option allows you to clean up old screenshots. By default, this option is set to false.
The reason why Roborazzi does not delete old screenshots by default is that Roborazzi doesn't know if you are running filtered tests or not. If you are running filtered tests, Roborazzi will delete the screenshots that are not related to the current test run.

```
roborazzi.cleanupOldScreenshots=true
```

## Robolectric Options

### robolectric.pixelCopyRenderMode
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION_NAME=1.29.0
VERSION_NAME=1.31.0
GROUP=io.github.takahirom.roborazzi
# Project-wide Gradle settings.

Expand Down
Loading

0 comments on commit a5f7d45

Please sign in to comment.