From d45966fa91526f775cc4dd2f57551da9544aa521 Mon Sep 17 00:00:00 2001 From: Jodie Weldon Date: Sun, 4 Jan 2026 14:59:50 -0800 Subject: [PATCH 1/5] Add better ui testing --- .github/workflows/build.yml | 2 +- .github/workflows/run-ui-tests.yml | 50 ++++++----- .run/Run Tests.run.xml | 7 +- .run/Run UI Tests.run.xml | 27 ++++++ build.gradle.kts | 90 ++++++++++++++----- codecov.yml | 15 ++++ .../github/jodiew/sortlines/ui/PluginTest.kt | 83 +++++++++++++++++ 7 files changed, 227 insertions(+), 47 deletions(-) create mode 100644 .run/Run UI Tests.run.xml create mode 100644 codecov.yml create mode 100644 src/uiTest/kotlin/com/github/jodiew/sortlines/ui/PluginTest.kt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d89fa9b..a533100 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -109,7 +109,7 @@ jobs: # Run tests - name: Run Tests - run: ./gradlew check + run: ./gradlew check -x uiTest # Collect Tests Result of failed tests - name: Collect Tests Result diff --git a/.github/workflows/run-ui-tests.yml b/.github/workflows/run-ui-tests.yml index 4793385..e250c48 100644 --- a/.github/workflows/run-ui-tests.yml +++ b/.github/workflows/run-ui-tests.yml @@ -19,15 +19,12 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-latest - runIde: | - export DISPLAY=:99.0 - Xvfb -ac :99 -screen 0 1920x1080x16 & - gradle runIdeForUiTests & - - os: windows-latest - runIde: start gradlew.bat runIdeForUiTests - - os: macos-latest - runIde: ./gradlew runIdeForUiTests & + - runner: ubuntu-latest + osName: Linux + - runner: windows-latest + osName: Windows + - runner: macos-latest + osName: macOS steps: @@ -48,18 +45,25 @@ jobs: with: cache-read-only: true - # Run IDEA prepared for UI testing - - name: Run IDE - run: ${{ matrix.runIde }} + # Set up Xvfb for Linux headless mode + - name: Setup Xvfb for Linux + if: runner.os == 'Linux' + run: | + sudo apt-get update -y + sudo apt-get install -y xvfb x11-utils libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 + export DISPLAY=:99.0 + Xvfb -ac :99 -screen 0 1920x1080x16 & + sleep 3 + echo "DISPLAY=:99.0" >> $GITHUB_ENV - # Wait for IDEA to be started - - name: Health Check - uses: jtalk/url-health-check-action@v4 - with: - url: http://127.0.0.1:8082 - max-attempts: 15 - retry-delay: 30s - - # Run tests - - name: Tests - run: ./gradlew test + # Run UI test with IDE Starter framework + - name: Run UI Test + env: + CODECOV_API_TOKEN: ${{ secrets.CODECOV_API_TOKEN }} + LICENSE_KEY: ${{ secrets.LICENSE_KEY }} + UI_BRANCH_NAME: "${{ matrix.osName }}-${{ github.ref_name }}" + DISPLAY: ${{ runner.os == 'Linux' && ':99.0' || '' }} + run: | + echo "UI on ${{ matrix.osName }} run #: ${{ github.run_number }} and id: ${{ github.run_id }}" + ./gradlew uiTest + timeout-minutes: 30 diff --git a/.run/Run Tests.run.xml b/.run/Run Tests.run.xml index f281bdc..58674a1 100644 --- a/.run/Run Tests.run.xml +++ b/.run/Run Tests.run.xml @@ -5,7 +5,7 @@ @@ -18,8 +18,11 @@ true true + false false true + false + false - + \ No newline at end of file diff --git a/.run/Run UI Tests.run.xml b/.run/Run UI Tests.run.xml new file mode 100644 index 0000000..91151fc --- /dev/null +++ b/.run/Run UI Tests.run.xml @@ -0,0 +1,27 @@ + + + + + + + true + true + false + false + false + false + false + + + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index c5dfd20..081f7a6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,4 @@ +import org.gradle.kotlin.dsl.register import org.jetbrains.changelog.Changelog import org.jetbrains.changelog.markdownToHTML import org.jetbrains.intellij.platform.gradle.TestFrameworkType @@ -10,6 +11,7 @@ plugins { alias(libs.plugins.qodana) // Gradle Qodana Plugin alias(libs.plugins.kover) // Gradle Kover Plugin alias(libs.plugins.grammarkit) // Gradle Grammar-Kit Plugin + idea // IntelliJ IDEA support } group = providers.gradleProperty("pluginGroup").get() @@ -22,16 +24,41 @@ sourceSets { srcDirs("src/main/gen") } } + create("uiTest",Action{ + compileClasspath += sourceSets.main.get().output + sourceSets.test.get().output + runtimeClasspath += sourceSets.main.get().output + sourceSets.test.get().output + }) +} + +idea { + module { + generatedSourceDirs.add(file("src/main/gen")) + testSources.from(sourceSets.getByName("uiTest").kotlin.srcDirs) + testResources.from(sourceSets.getByName("uiTest").resources.srcDirs) + } +} + +val uiTestImplementation: Configuration by configurations.getting { + extendsFrom(configurations.testImplementation.get()) +} + +val uiTestRuntimeOnly: Configuration by configurations.getting { + extendsFrom(configurations.testRuntimeOnly.get()) } // Set the JVM language level used to build the project. kotlin { - jvmToolchain(21) + jvmToolchain { + languageVersion = JavaLanguageVersion.of(21) + @Suppress("UnstableApiUsage") + vendor = JvmVendorSpec.JETBRAINS + } } // Configure project's dependencies repositories { mavenCentral() + maven("https://cache-redirector.jetbrains.com/packages.jetbrains.team/maven/p/ij/intellij-ide-starter") // IntelliJ Platform Gradle Plugin Repositories Extension - read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-repositories-extension.html intellijPlatform { @@ -59,7 +86,16 @@ dependencies { testFramework(TestFrameworkType.Platform) testBundledPlugin("org.jetbrains.kotlin") + + // IntelliJ UI Tests framework - read more: https://github.com/JetBrains/intellij-ide-starter + testFramework(TestFrameworkType.Starter, configurationName = "uiTestImplementation") + testFramework(TestFrameworkType.JUnit5, configurationName = "uiTestImplementation") } + + uiTestImplementation("org.junit.jupiter:junit-jupiter:5.7.1") + uiTestImplementation("org.kodein.di:kodein-di-jvm:7.20.2") + uiTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.10.1") + uiTestRuntimeOnly("org.junit.platform:junit-platform-launcher") } // Configure IntelliJ Platform Gradle Plugin - read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-extension.html @@ -169,28 +205,40 @@ tasks { gradleVersion = providers.gradleProperty("gradleVersion").get() } + runIde { + systemProperties( + "ide.native.launcher" to true, + "ide.show.tips.on.startup.default.value" to false, + "jb.consents.confirmation.enabled" to false + ) + } + publishPlugin { dependsOn(patchChangelog) } -} -intellijPlatformTesting { -// runIde { -// register("runIdeForUiTests") { -// task { -// jvmArgumentProviders += CommandLineArgumentProvider { -// listOf( -// "-Drobot-server.port=8082", -// "-Dide.mac.message.dialogs.as.sheets=false", -// "-Djb.privacy.policy.text=", -// "-Djb.consents.confirmation.enabled=false", -// ) -// } -// } -// -// plugins { -// robotServerPlugin() -// } -// } -// } + register("uiTest") { + description = "Runs only the UI tests that start the IDE" + group = "verification" + val uiTestSourceSet = sourceSets["uiTest"] + testClassesDirs = uiTestSourceSet.output.classesDirs + classpath = uiTestSourceSet.runtimeClasspath + systemProperty("path.to.build.plugin", buildPlugin.get().archiveFile.get().asFile.absolutePath) + systemProperty("idea.home.path", prepareTestSandbox.get().getDestinationDir().parentFile.absolutePath) + systemProperty("platform.version", providers.gradleProperty("platformVersion").get()) + systemProperty( + "allure.results.directory", project.layout.buildDirectory.get().asFile.absolutePath + "/allure-results" + ) + useJUnitPlatform() + + // Add required JVM arguments + jvmArgumentProviders += CommandLineArgumentProvider { + mutableListOf( + "--add-opens=java.base/java.lang=ALL-UNNAMED", + "--add-opens=java.desktop/javax.swing=ALL-UNNAMED" + ) + } + + dependsOn(buildPlugin) + } } diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..c40fcc2 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,15 @@ +coverage: + status: + project: + default: + informational: true + threshold: 0% + base: auto + patch: + default: + informational: true + + +ignore: + - src/test/ + - src/uiTest/ diff --git a/src/uiTest/kotlin/com/github/jodiew/sortlines/ui/PluginTest.kt b/src/uiTest/kotlin/com/github/jodiew/sortlines/ui/PluginTest.kt new file mode 100644 index 0000000..1d2b25e --- /dev/null +++ b/src/uiTest/kotlin/com/github/jodiew/sortlines/ui/PluginTest.kt @@ -0,0 +1,83 @@ +package com.github.jodiew.sortlines.ui + +import com.intellij.driver.sdk.ui.components.common.welcomeScreen +import com.intellij.driver.sdk.ui.shouldBe +import com.intellij.ide.starter.driver.engine.runIdeWithDriver +import com.intellij.ide.starter.ide.IdeProductProvider +import com.intellij.ide.starter.models.TestCase +import com.intellij.ide.starter.plugins.PluginConfigurator +import com.intellij.ide.starter.project.NoProject +import com.intellij.ide.starter.runner.Starter +import com.intellij.openapi.util.SystemInfo +import org.junit.jupiter.api.Test +import kotlin.io.path.Path + +class PluginTest { + + /** + * Test to verify that the plugin is installed in the IDE. + */ + @Test + fun simpleTest() { + Starter.newContext("testInstalled", TestCase(IdeProductProvider.IU, NoProject).withVersion(System.getProperty("platform.version"))).apply { + val pathToPlugin = System.getProperty("path.to.build.plugin") + PluginConfigurator(this).installPluginFromPath(Path(pathToPlugin)) + }.applyVMOptionsPatch{ + // Required JVM arguments for module access + addSystemProperty("--add-opens", "java.base/java.lang=ALL-UNNAMED") + addSystemProperty("--add-opens", "java.desktop/javax.swing=ALL-UNNAMED") + + // Core IDE configuration + addSystemProperty("idea.trust.all.projects", true) // Trust all projects automatically + addSystemProperty("jb.consents.confirmation.enabled", false) // Disable consent dialogs + addSystemProperty("jb.privacy.policy.text", "") // Skip privacy policy + addSystemProperty("ide.show.tips.on.startup.default.value", false) // No tips on startup + + // Test framework configuration + addSystemProperty("junit.jupiter.extensions.autodetection.enabled", true) + addSystemProperty("shared.indexes.download.auto.consent", true) + + // UI testing specific + addSystemProperty("expose.ui.hierarchy.url", true) // Enable UI hierarchy inspection + + when { + SystemInfo.isMac -> { + // macOS specific settings + addSystemProperty("ide.mac.file.chooser.native", false) // Use Java file chooser + addSystemProperty("ide.mac.message.dialogs.as.sheets", false) // Use regular dialogs + addSystemProperty("jbScreenMenuBar.enabled", false) // Disable native menu bar + addSystemProperty("ide.native.launcher", true) // Use native launcher + } + + SystemInfo.isWindows -> { + // Windows specific settings + + } + + SystemInfo.isLinux -> { + // Linux specific settings + addSystemProperty("ide.browser.jcef.enabled", true) + addSystemProperty("ide.native.launcher", false) // Avoid launcher issues on Linux + + // X11/Wayland compatibility + addSystemProperty("sun.java2d.uiScale.enabled", false) + addSystemProperty("sun.java2d.xrender", false) + } + } + }.runIdeWithDriver().useDriverAndCloseIde { + welcomeScreen { + clickPlugins() + x { byAccessibleName("Installed") }.click() + keyboard { + typeText("Sort Lines", 50) + } + shouldBe("Plugin is installed") { + x { + byVisibleText("Sort Lines with Comment") + }.present() + } + + } + } + } +} \ No newline at end of file From d08cbdc7254d862d4053fcd5f2c3b11ba50a753f Mon Sep 17 00:00:00 2001 From: Jodie Weldon Date: Sun, 4 Jan 2026 15:05:45 -0800 Subject: [PATCH 2/5] update .gitignore to exclude allure-results and out directories --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index bc5e801..60a9844 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,10 @@ .idea .intellijPlatform .qodana +allure-results build idea-flex.skeleton java_pid*.hprof jflex-*.jar +out src/main/gen From 354b0e194a4edd568db65804fe2587cc8f9bfcf1 Mon Sep 17 00:00:00 2001 From: Jodie Weldon Date: Sun, 4 Jan 2026 16:14:17 -0800 Subject: [PATCH 3/5] Fix workflow bug for ui tests --- .github/workflows/run-ui-tests.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-ui-tests.yml b/.github/workflows/run-ui-tests.yml index e250c48..0e3e357 100644 --- a/.github/workflows/run-ui-tests.yml +++ b/.github/workflows/run-ui-tests.yml @@ -14,15 +14,16 @@ on: jobs: testUI: - runs-on: ${{ matrix.os }} + name: Run UI Tests on ${{ matrix.osName }} + runs-on: ${{ matrix.runner }} strategy: fail-fast: false matrix: include: - runner: ubuntu-latest osName: Linux - - runner: windows-latest - osName: Windows +# - runner: windows-latest +# osName: Windows - runner: macos-latest osName: macOS From a1f924d1a763fdd52c387ff84fde78d73a6226ec Mon Sep 17 00:00:00 2001 From: Jodie Weldon Date: Mon, 5 Jan 2026 13:34:17 -0800 Subject: [PATCH 4/5] Update run-ui-tests.yml --- .github/workflows/run-ui-tests.yml | 94 ++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 12 deletions(-) diff --git a/.github/workflows/run-ui-tests.yml b/.github/workflows/run-ui-tests.yml index 0e3e357..96bbc9b 100644 --- a/.github/workflows/run-ui-tests.yml +++ b/.github/workflows/run-ui-tests.yml @@ -1,6 +1,6 @@ -# GitHub Actions Workflow for launching UI tests on Linux, Windows, and Mac in the following steps: -# - Prepare and launch IDE with your plugin and robot-server plugin, which is needed to interact with the UI. -# - Wait for IDE to start. +# GitHub Actions Workflow for launching UI tests on Linux and Mac in the following steps: +# - Prepare and launch IDE with your plugin and robot-server plugin, which is necessary to interact with the UI. +# - Wait for IDE to start # - Run UI tests with a separate Gradle task. # # Please check https://github.com/JetBrains/intellij-ui-test-robot for information about UI tests with IntelliJ Platform. @@ -12,28 +12,29 @@ on: workflow_dispatch jobs: - testUI: - name: Run UI Tests on ${{ matrix.osName }} + name: Run UI Test on ${{ matrix.osName }} runs-on: ${{ matrix.runner }} + permissions: + contents: write + checks: write + pull-requests: write strategy: fail-fast: false matrix: include: - runner: ubuntu-latest osName: Linux -# - runner: windows-latest -# osName: Windows - runner: macos-latest osName: macOS +# - runner: windows-latest +# osName: Windows steps: - - # Check out the current repository - name: Fetch Sources uses: actions/checkout@v6 - # Set up Java environment for the next steps + # Set up the Java environment for the next steps - name: Setup Java uses: actions/setup-java@v5 with: @@ -60,11 +61,80 @@ jobs: # Run UI test with IDE Starter framework - name: Run UI Test env: - CODECOV_API_TOKEN: ${{ secrets.CODECOV_API_TOKEN }} - LICENSE_KEY: ${{ secrets.LICENSE_KEY }} UI_BRANCH_NAME: "${{ matrix.osName }}-${{ github.ref_name }}" DISPLAY: ${{ runner.os == 'Linux' && ':99.0' || '' }} run: | echo "UI on ${{ matrix.osName }} run #: ${{ github.run_number }} and id: ${{ github.run_id }}" ./gradlew uiTest timeout-minutes: 30 + + # List build directory for debugging + - name: List build directory structure + if: always() + shell: bash + run: | + echo "=== Build directory structure ===" + if [ -d "build" ]; then + find build -type d -name "log" -o -name "screenshots" -o -name "ui-hierarchy" 2>/dev/null | head -20 || true + echo "=== Looking for IDE test output ===" + find build -type d -path "*/ide-tests/*" 2>/dev/null | head -20 || true + else + echo "Build directory not found" + fi + + - name: Collect Test Results + if: always() + uses: actions/upload-artifact@v6 + with: + name: tests-result-${{ matrix.osName }} + path: | + build/reports/ + build/test-results/ + build/out/ + build/idea-sandbox/ + build/**/*.log + build/**/screenshots/ + build/**/ui-hierarchy/ + if-no-files-found: warn + continue-on-error: true + + - name: Collect Test Report XML + if: always() + uses: actions/upload-artifact@v6 + with: + name: test-report-${{ matrix.osName }} + path: ${{ github.workspace }}/build/test-results/uiTest/*.xml + + # Test report job using dorny/test-reporter + test-report: + name: UI Test Report + needs: testUI + runs-on: ubuntu-latest + if: always() + permissions: + contents: write + checks: write + pull-requests: write + actions: read + + steps: + - name: Fetch Sources + uses: actions/checkout@v6 + with: + token: '${{ secrets.GITHUB_TOKEN }}' + + - name: Download all test reports + uses: actions/download-artifact@v7 + with: + pattern: test-report-* + path: test-reports + merge-multiple: true + + - name: Generate Test Report + uses: dorny/test-reporter@v2.5.0 + if: success() || failure() + with: + name: UI Test Report Dashboard + reporter: java-junit + fail-on-error: 'true' + path: 'test-reports/*.xml' From e86023d2b12207118bfd53f6bfa1ece2f3e20cc3 Mon Sep 17 00:00:00 2001 From: Jodie Weldon Date: Mon, 5 Jan 2026 13:34:31 -0800 Subject: [PATCH 5/5] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c335868..f3f0751 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,16 @@ ## [Unreleased] +### Added + +- Simple UI test to verify the plugin is installed and a Gradle task to run it +- Codecov YAML file that was missed from the template + ### Fixed - Broken links in CHANGELOG.md caused by missing "v" in tag creation build by adding `versionPrefix` default +- Run UI tests GitHub Action workflow to use the added UI test +- Exclude the UI test from build GitHub Action workflow ### Changed