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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ Plugins/Gradle/gradle/wrapper/
Plugins/Gradle/gradlew
Plugins/Gradle/gradlew.bat
.intellijPlatform/
Plugins/IntelliJ/.kotlin
58 changes: 58 additions & 0 deletions Plugins/IntelliJ/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Android Testify IntelliJ Plugin - Developer Guide

This document provides an overview of the Android Testify IntelliJ Plugin project for AI agents and developers.

## Project Overview

This is an IntelliJ Platform Plugin designed to enhance the development experience for Android Testify screenshot testing within Android Studio. It provides features like line markers, navigation between tests and baseline images, and other utility actions.

## Key Technologies

* **Language:** Kotlin
* **Build System:** Gradle (Kotlin DSL)
* **Platform:** IntelliJ Platform (specifically targeting Android Studio)
* **Testing:** JUnit, OpenTest4J

## Project Structure

* `build.gradle.kts`: Main build configuration.
* `gradle.properties`: Project properties, including plugin version, platform version, and dependencies.
* `src/main/resources/META-INF/plugin.xml`: Plugin configuration file (manifest), defining actions, extensions, and dependencies.
* `src/main/kotlin/dev/testify`: Source code root.
* `actions`: Contains Action classes (e.g., `GoToSourceAction`, `GoToBaselineAction`).
* `extensions`: Contains IntelliJ extensions like `LineMarkerProvider` implementations.
* `TestFlavor.kt`, `FileUtilities.kt`, `PsiExtensions.kt`: Utility classes.

## Key Features & Components

### 1. Navigation
* **Go To Source (`GoToSourceAction`):** Navigates from a baseline image to its corresponding test source code.
* **Go To Baseline (`GoToBaselineAction`):** Navigates from a test method to its corresponding baseline image.

### 2. Line Markers
* **`ScreenshotInstrumentationLineMarkerProvider`:** Adds gutter icons to test methods annotated with `@ScreenshotInstrumentation`.
* **`ScreenshotClassMarkerProvider`:** Adds gutter icons to test classes.

### 3. Dependencies
* **`org.jetbrains.kotlin`**
* **`com.intellij.gradle`**
* **`org.jetbrains.android`**
* **`com.intellij.modules.androidstudio`**

## Build & Run

* **Build:** `./gradlew buildPlugin`
* **Run IDE:** `./gradlew runIde` (Starts a sandboxed Android Studio instance with the plugin installed)
* **Run Tests:** `./gradlew test`

## Configuration

* **Plugin Version:** Defined in `gradle.properties` (`pluginVersion`).
* **Platform Version:** Defined in `gradle.properties` (`platformVersion`). Currently targeting Android Studio Otter (2025.3.1.2).
* **Since/Until Build:** Defined in `gradle.properties` (`pluginSinceBuild`, `pluginUntilBuild`).

## Notes for Agents

* When modifying the plugin, ensure compatibility with the target Android Studio version specified in `gradle.properties`.
* The `plugin.xml` file is the central registry for all UI actions and extensions. Any new feature usually requires an entry here.
* The project uses the IntelliJ Platform Gradle Plugin (2.x) for building and verification.
12 changes: 12 additions & 0 deletions Plugins/IntelliJ/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

# Android Testify - IntelliJ Platform Plugin - Change Log

## [Unreleased]

## [4.0.0-alpha05]

- Added initial support for Paparazzi and Preview tests!
- Implemented record and test functionality for Paparazzi tests.
- Enhanced "Go To Baseline" and "Go To Source" navigation for baseline images.
- Added support for build variants.
- Enabled class-level menu actions.
- Disabled "Pull" action when no baseline image is found.
- Bumped minimum IDE support to 252.*. Added support for 253.*

## [3.0.0]

- Added support for Support Android Studio Narwhal | 2025.1.1 Canary 9 | 251.+
Expand Down
50 changes: 50 additions & 0 deletions Plugins/IntelliJ/GEMINI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Gemini Project Context: Android Testify IntelliJ Plugin

This document provides context for the Android Testify IntelliJ Plugin project.

## Project Overview

This is a Gradle-based IntelliJ Platform Plugin for [Android Testify](https://testify.dev/), an Android screenshot testing framework. The plugin is written in Kotlin and enhances the developer experience by integrating Testify commands directly into the IntelliJ IDE (including Android Studio).

Key features of the plugin include:
- Running Testify screenshot tests.
- Recording, pulling, revealing, and deleting baseline screenshot images.
- Navigating between test source code and their corresponding baseline images.

The plugin's functionality is defined in `src/main/resources/META-INF/plugin.xml`, and the implementation is in Kotlin under `src/main/kotlin/dev/testify/`.

## Building and Running

This project uses the Gradle wrapper (`gradlew`).

- **To build the plugin:**
```bash
./gradlew buildPlugin
```

- **To run the plugin in a development instance of the IDE:**
```bash
./gradlew runIde
```

- **To run the plugin's tests:**
```bash
./gradlew test
```

## Development Conventions

The project follows standard Kotlin coding conventions. The codebase is structured around the IntelliJ Platform's action and extension system.

- **Actions:** User-invoked commands (e.g., "Go to Baseline") are implemented as classes that extend `AnAction`. See files in `src/main/kotlin/dev/testify/actions/`.
- **Extensions:** IDE integrations, such as line markers next to screenshot tests, are implemented using extension points. See files in `src/main/kotlin/dev/testify/extensions/`.

Dependencies and project versions are managed in the `build.gradle.kts` file and the `gradle/libs.versions.toml` version catalog.

## Key Files

- `README.md`: Provides a high-level overview of the plugin for users.
- `build.gradle.kts`: The main Gradle build script that configures the IntelliJ Platform, Kotlin, and dependencies.
- `src/main/resources/META-INF/plugin.xml`: The plugin's manifest file, which declares its dependencies, extensions, and actions.
- `src/main/kotlin/dev/testify/`: The root directory for the plugin's Kotlin source code.
- `gradle/libs.versions.toml`: The Gradle version catalog, defining the project's dependencies and their versions.
4 changes: 3 additions & 1 deletion Plugins/IntelliJ/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ dependencies {

// IntelliJ Platform Gradle Plugin Dependencies Extension - read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html
intellijPlatform {
androidStudio(providers.gradleProperty("platformVersion"), useInstaller = true)
androidStudio(version = providers.gradleProperty("platformVersion")) {
this.useInstaller = true
}

// Plugin Dependencies. Uses `platformBundledPlugins` property from the gradle.properties file for bundled IntelliJ Platform plugins.
bundledPlugins(providers.gradleProperty("platformBundledPlugins").map { it.split(',') })
Expand Down
14 changes: 9 additions & 5 deletions Plugins/IntelliJ/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
# IntelliJ Platform Artifacts Repositories -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html
# Android Studio Plugin Development -> https://plugins.jetbrains.com/docs/intellij/android-studio.html#android-studio-releases-listing
# Samples and Examples -> https://github.com/balsikandar/Android-Studio-Plugins

pluginGroup = dev.testify
pluginName = Android Testify - Screenshot Instrumentation Tests
pluginRepositoryUrl = https://github.com/ndtp/android-testify/tree/main/Plugins/IntelliJ
# SemVer format -> https://semver.org
pluginVersion = 3.0.0
pluginVersion = 4.0.0-alpha05


# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 242
pluginUntilBuild = 252.*
pluginSinceBuild = 252
pluginUntilBuild = 253.*

# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
platformType = AI

# Narwhal 2025.1.1 Canary 9
platformVersion = 2025.1.1.9
# https://plugins.jetbrains.com/docs/intellij/android-studio-releases-list.html
# Otter 2025.2.1
platformVersion = 2025.3.1.2

# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
# Example: platformPlugins = com.jetbrains.php:203.4449.22, org.intellij.scala:2023.3.27@EAP
Expand Down
4 changes: 2 additions & 2 deletions Plugins/IntelliJ/gradle/libs.versions.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 72 additions & 0 deletions Plugins/IntelliJ/src/main/kotlin/dev/testify/FileUtilities.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package dev.testify

import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiMethod
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.PsiShortNamesCache

fun findClassByName(className: String, project: Project, packageName: String? = null): PsiClass? {
val psiShortNamesCache = PsiShortNamesCache.getInstance(project)
val classes = psiShortNamesCache.getClassesByName(className, GlobalSearchScope.projectScope(project))
return if (packageName != null) {
classes.firstOrNull { psiClass ->
val pkg = psiClass.qualifiedName?.substringBeforeLast(".")
packageName == pkg
}
} else {
classes.firstOrNull()
}
}

fun findMethod(methodName: String, psiClass: PsiClass): PsiMethod? {
return psiClass.findMethodsByName(methodName, false).firstOrNull()
}

fun findTestifyMethod(imageFile: VirtualFile, project: Project): PsiMethod? {
if (imageFile.path.contains("/androidTest").not()) return null
imageFile.nameWithoutExtension.let { imageName ->
val parts = imageName.split("_")
if (parts.size == 2) {
val (className, methodName) = parts
findClassByName(className, project)?.let { psiClass ->
return findMethod(methodName, psiClass)
}
}
}
return null
}

/**
* Input: /Users/danjette/dev/android-testify/Samples/Paparazzi/src/test/snapshots/images/dev.testify.samples.paparazzi.ui.common.composables_CastMemberScreenshotTest_b.png
*/
fun findPaparazziMethod(imageFile: VirtualFile, project: Project): PsiMethod? {
if (imageFile.path.contains("/test").not()) return null
imageFile.nameWithoutExtension.let { imageName ->
// imageName = composables_CastMemberScreenshotTest_default
val parts = imageName.split("_")
if (parts.size == 3) {
val (packageName, className, methodName) = parts
// _ = composables
// className = CastMemberScreenshotTest
// methodName = default
findClassByName(className, project, packageName)?.let { psiClass ->
return findMethod(methodName, psiClass)
}
}
}
return null
}

fun findPreviewMethod(imageFile: VirtualFile, project: Project): PsiMethod? {
if (imageFile.path.contains("/screenshotTest").not()) return null
val className = imageFile.parent.name
imageFile.nameWithoutExtension.let { imageName ->
val methodName = imageName.split("_").first()
findClassByName(className, project)?.let { psiClass ->
return findMethod(methodName, psiClass)
}
}
return null
}
Loading