diff --git a/.agents/skills/oft-spec-driven-development/SKILL.md b/.agents/skills/oft-spec-driven-development/SKILL.md index 3dec71c..2352e40 100644 --- a/.agents/skills/oft-spec-driven-development/SKILL.md +++ b/.agents/skills/oft-spec-driven-development/SKILL.md @@ -39,7 +39,7 @@ When the user gives you a new issue link, derive the work plan from: 4. the quality requirements document 5. the current code and tests -Do not jump straight to code. First, determine whether the issue changes: +Do not jump straight to code. First determine whether the issue changes: - user-visible behavior - traced requirements or scenarios @@ -95,7 +95,6 @@ Use that mapping to decide what must change: - If architecture or technical behavior changes, update or add `dsn` items and relevant arc42 design sections. - If a lower layer adds no new information, prefer OFT forwarding instead of redundant new items. - Ensure each runtime design item covers only one scenario unless forwarding is the better fit. -- When you semantically change an existing requirement, increase the requirement revision number. ### 4. Treat Quality Requirements As Non-Negotiable @@ -158,14 +157,10 @@ Out of scope: ## Task List -- [ ] Create and checkout a new Git branch `/-` - ### Requirements And Design -- [ ] -- [ ] Stop and ask user for a review of the system requirements +- [ ] - [ ] -- [ ] Stop and ask user for a review of the design ### Implementation @@ -176,19 +171,6 @@ Out of scope: - [ ] - [ ] Keep the OpenFastTrace trace clean - [ ] Keep required build and plugin verification tasks green - -### Update user documentation - -- [ ] Update the end user documentation in README.md - -## Version and Changelog Update - -- [ ] Check if the current version mentioned in the build scripts and code parameters is the same as the latest GitHub release. -- [ ] Decide if and which part of the version needs to be incremented. Use semantic versioning rules (breaking change updates major, feature updates minor, documentation, fixes and refactoring update fix number) -- [ ] Raise the version to (this is a release) -- [ ] Write the changelog entry for -- [ ] Update release date to today -- [ ] Ensure that issue list contains the GitHub issue number and title ``` Keep verification items concrete. Do not leave them as generic "run tests" placeholders when the quality requirements demand more specific checks. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 861dd0e..3ab3736 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,11 @@ jobs: distribution: temurin java-version: 21 + - name: Install Pandoc + run: | + sudo apt-get update + sudo apt-get install --yes pandoc + - name: Set up Gradle uses: gradle/actions/setup-gradle@v6 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 751b0f6..6c402ef 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,6 +32,11 @@ jobs: distribution: temurin java-version: 21 + - name: Install Pandoc + run: | + sudo apt-get update + sudo apt-get install --yes pandoc + - name: Set up Gradle uses: gradle/actions/setup-gradle@v6 with: diff --git a/README.md b/README.md index ee41050..90819eb 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,8 @@ SonarCloud status: [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=org.itsallcode.openfasttrace%3Aopenfasttrace-intellij-plugin&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=org.itsallcode.openfasttrace%3Aopenfasttrace-intellij-plugin) **User Guides** -* [OpenFastTrace IntelliJ Plugin User Guide](doc/user_guide.md) * [OpenFastTrace User Guide](https://github.com/itsallcode/openfasttrace/blob/main/doc/user_guide.md) * [OpenFastTrace in IDE Help Action](src/main/java/org/itsallcode/openfasttrace/intellijplugin/help/OpenFastTraceUserGuide.java) -* [OpenFastTrace IntelliJ Plugin Demo](doc/demo/plugin-demo.md) **News and Discussions** * [Changelog](doc/changes/changelog.md) @@ -45,23 +43,16 @@ SonarCloud status: ## Using the Plugin Use an IntelliJ Platform IDE and install/build this plugin to get OFT support while editing project files. -For installation and workflow instructions, see the [OpenFastTrace IntelliJ Plugin User Guide](doc/user_guide.md). - The plugin currently focuses on: * syntax highlighting for OFT items in supported files * navigation support (`Go To Declaration`, symbol search, and related navigation paths) -* completion for existing specification item IDs while editing `Covers:` entries in supported specification documents and coverage-tag targets in files supported by the OpenFastTrace Tag Importer -* bundled OFT live templates for common specification items, including a scenario template -* project-wide or selected-resource OFT tracing from `Tools | OpenFastTrace | Trace Project` or through custom OpenFastTrace run configurations +* project-wide or selected-resource OFT tracing from `Tools | OpenFastTrace | Trace Project` * clickable specification item IDs directly from the trace output window, including generated IDs created from coverage tags -* IntelliJ Test Runner UI output for OpenFastTrace traces by default, grouped by source file, specification item, and trace link * quick access to the OpenFastTrace user guide from the IDE help menu For expected behavior and scope details, see: -* [User Guide](doc/user_guide.md) * [System Requirements](doc/system_requirements.md) * [Runtime View](doc/design/runtime_view.md) -* [Plugin Demo Script](doc/demo/plugin-demo.md) ## Getting the Project Clone this repository and build the plugin with Gradle: @@ -76,19 +67,9 @@ For manual IDE testing, launch a sandbox IDE with the plugin: ./gradlew manualTestIde ``` -## Trace Settings - After the sandbox IDE opens a project, configure trace scope under `Settings | Tools | OpenFastTrace`. By default, `Trace Project` scans the whole opened project. If you switch to selected-resource tracing, the plugin includes IntelliJ source roots, IntelliJ test roots, and one default additional project-relative path entry, `doc/`, until you change it. Additional files or directories are entered one per line. -## Run Configurations - -You can create and run a dedicated `OpenFastTrace` run configuration from the IDE's run/debug toolbar. The plugin traces the configured inputs in the background and shows the result in IntelliJ's Test Runner UI by default. The result tree groups source files, specification items, and trace links with pass/fail status and navigation back to source. - -Run configurations can select plain text output instead. In that mode, the plugin shows the text report in an IDE output tab with ANSI colors preserved. You can click specification item IDs in the report to jump to their declarations, including source-side items generated from coverage tags. - -## Live-Templates Bundled With the Plugin - -The plugin also bundles an `OpenFastTrace` live-template group under `Settings | Editor | Live Templates`. Use abbreviations such as `feat`, `req`, `dsn`, and `scn` in a supported editing context, then press `Tab` to insert an OFT item skeleton. The `scn` template inserts a scenario stub with placeholders for `Given`, `When`, `Then`, and the covered requirement. While the caret is still in a template's covered-item field, use basic completion to select an existing specification item ID from the project index. +Run an OFT trace from `Tools | OpenFastTrace | Trace Project` or use the default shortcut `Ctrl+Alt+Shift+O`. The plugin traces the configured inputs in the background and shows the plain text result in an IDE output tab with ANSI colors preserved. You can click specification item IDs in the report to jump to their declarations, including source-side items generated from coverage tags. ## Installation ### Runtime Dependencies @@ -97,10 +78,6 @@ You need a JetBrains IDE based on the IntelliJ Platform (for example, IntelliJ I ### Development Dependencies To build and test from source, use the project’s configured Gradle wrapper and a compatible JDK. -## Icon Assets - -The plugin logo asset `src/main/resources/META-INF/pluginIcon.svg` and the run-configuration icon asset `src/main/resources/icons/openfasttrace.svg` are derived from the OpenFastTrace logo artwork in this repository and use the same Apache 2.0 license as the project. - ## Development To understand product intent and implementation scope, start with: * [System Requirements](doc/system_requirements.md) @@ -113,21 +90,7 @@ To run the automated checks for the trace action and the project trace itself: ./gradlew check verifyPlugin ``` -To check for newer dependency, Gradle plugin, and Gradle wrapper versions: - -```sh -./gradlew --no-configuration-cache --no-parallel dependencyUpdates -Drevision=release -``` - -After intentionally changing dependency or Gradle plugin versions, refresh the -committed Gradle lock file: - -```sh -./gradlew --write-locks dependencies -``` - -Example OFT files for manual testing are available under `examples/` in this project. -For a guided live demonstration, use the script in [doc/demo/plugin-demo.md](doc/demo/plugin-demo.md) with the isolated example project in [doc/demo/example](doc/demo/example). +Example OFT files for manual testing are available under `examples\` in this project. ## License This project is licensed under the [Apache 2.0 license](LICENSE). diff --git a/build.gradle.kts b/build.gradle.kts index 1aacb0e..4a27f17 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,3 @@ -import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent import org.gradle.api.tasks.bundling.Zip @@ -6,27 +5,19 @@ import org.gradle.jvm.toolchain.JavaLanguageVersion import org.gradle.testing.jacoco.plugins.JacocoTaskExtension import org.jetbrains.intellij.platform.gradle.TestFrameworkType -fun isNonStableVersion(version: String): Boolean { - val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase().contains(it) } - val stableVersion = "^[0-9,.v-]+(-r)?$".toRegex().matches(version) - return !stableKeyword && !stableVersion -} - // [bld->dsn~plugin-build-uses-intellij-platform-gradle-plugin~1] plugins { id("java") id("jacoco") - id("com.github.ben-manes.versions") version "0.54.0" - id("com.diffplug.spotless") version "8.6.0" - id("org.itsallcode.openfasttrace") version "3.1.2" - id("org.jetbrains.intellij.platform") version "2.16.0" - id("org.sonarqube") version "7.3.1.8318" + id("com.diffplug.spotless") version "8.4.0" + id("org.itsallcode.openfasttrace") version "3.1.1" + id("org.jetbrains.intellij.platform") version "2.15.0" + id("org.sonarqube") version "7.2.3.7755" + id("org.sonatype.gradle.plugins.scan") version "3.1.5" } -val pluginVersion = providers.gradleProperty("version") - -group = "org.itsallcode.openfasttrace" -version = pluginVersion.get() +group = providers.gradleProperty("group").get() +version = providers.gradleProperty("version").get() java { toolchain { @@ -38,11 +29,6 @@ jacoco { toolVersion = "0.8.13" } -// [bld->dsn~gradle-dependency-maintenance-uses-locks-and-versions-plugin~1] -dependencyLocking { - lockAllConfigurations() -} - sonar { properties { property("sonar.organization", "itsallcode") @@ -54,14 +40,31 @@ sonar { } } +val ossIndexUsername = providers.gradleProperty("ossIndexUsername") + .orElse(providers.environmentVariable("OSSINDEX_USERNAME")) + .orNull +val ossIndexToken = providers.gradleProperty("ossIndexToken") + .orElse(providers.environmentVariable("OSSINDEX_TOKEN")) + .orNull + +ossIndexAudit { + ossIndexUsername?.let { username = it } + ossIndexToken?.let { password = it } + isUseCache = true + isPrintBanner = false + isColorEnabled = false + isFailOnDetection = true +} + requirementTracing { failBuild = true inputDirectories = files("doc", "src/main/java", "src/test/java") tags { tag { - paths = fileTree("./").include("build.gradle.kts") as FileCollection? - tagArtifactType = "bld" + paths = files("build.gradle.kts") coveredItemArtifactType = "dsn" + tagArtifactType = "bld" + coveredItemNamePrefix = "" } } } @@ -74,22 +77,21 @@ repositories { } dependencies { - implementation("org.itsallcode.openfasttrace:openfasttrace:4.5.0") + implementation("org.itsallcode.openfasttrace:openfasttrace:${providers.gradleProperty("openfasttraceVersion").get()}") intellijPlatform { - intellijIdea("2026.1.3") + intellijIdea(providers.gradleProperty("platformVersion")) bundledPlugin("com.intellij.java") testFramework(TestFrameworkType.Platform) pluginVerifier() zipSigner() } - testImplementation(platform("org.junit:junit-bom:6.1.0")) - testImplementation("junit:junit:4.13.2") + testImplementation(platform("org.junit:junit-bom:${providers.gradleProperty("junitBomVersion").get()}")) + testImplementation("junit:junit:${providers.gradleProperty("junit4Version").get()}") testImplementation("org.junit.jupiter:junit-jupiter") - testImplementation("org.junit.platform:junit-platform-launcher") - testImplementation("org.hamcrest:hamcrest:3.0") - testImplementation("org.opentest4j:opentest4j:1.3.0") + testImplementation("org.hamcrest:hamcrest:${providers.gradleProperty("hamcrestVersion").get()}") + testImplementation("org.opentest4j:opentest4j:${providers.gradleProperty("opentest4jVersion").get()}") testRuntimeOnly("org.junit.vintage:junit-vintage-engine") } @@ -97,18 +99,18 @@ intellijPlatform { buildSearchableOptions = false pluginConfiguration { - id = "org.itsallcode.openfasttrace-intellij-plugin" - name = "OpenFastTrace" - version = pluginVersion + id = providers.gradleProperty("pluginId") + name = providers.gradleProperty("pluginName") + version = providers.gradleProperty("version") vendor { - name = "Itsallcode.org" - email = "opensource@itsallcode.org" - url = "https://itsallcode.org/" + name = providers.gradleProperty("pluginVendor") + email = providers.gradleProperty("pluginVendorEmail") + url = providers.gradleProperty("pluginVendorUrl") } ideaVersion { - sinceBuild = "261" + sinceBuild = providers.gradleProperty("platformSinceBuild") } } @@ -138,17 +140,8 @@ tasks { options.encoding = "UTF-8" } - // [bld->dsn~packaged-plugin-logo-assets~1] named("buildPlugin") { - archiveBaseName.set("OpenFastTrace") - } - - withType().configureEach { - revision = "release" - gradleReleaseChannel = "current" - rejectVersionIf { - isNonStableVersion(candidate.version) && !isNonStableVersion(currentVersion) - } + archiveBaseName.set(providers.gradleProperty("pluginName")) } test { diff --git a/doc/changes/changelog.md b/doc/changes/changelog.md index 3977f78..9ad97b7 100644 --- a/doc/changes/changelog.md +++ b/doc/changes/changelog.md @@ -1,13 +1,5 @@ # Changelog -* [0.8.0](changes_0.8.0.md) -* [0.7.0](changes_0.7.0.md) -* [0.6.0](changes_0.6.0.md) -* [0.5.2](changes_0.5.2.md) -* [0.5.1](changes_0.5.1.md) -* [0.5.0](changes_0.5.0.md) -* [0.4.0](changes_0.4.0.md) -* [0.3.0](changes_0.3.0.md) * [0.2.1](changes_0.2.1.md) * [0.2.0](changes_0.2.0.md) * [0.1.0](changes_0.1.0.md) diff --git a/doc/changes/changes_0.2.1.md b/doc/changes/changes_0.2.1.md index f0728a8..cde8701 100644 --- a/doc/changes/changes_0.2.1.md +++ b/doc/changes/changes_0.2.1.md @@ -1,7 +1,5 @@ # OpenFastTrace IntelliJ Plugin 0.2.1, released 2026-04-28 -Code name: Fix for importer detection - ## Summary This bugfix release restores correct OpenFastTrace importer discovery when a trace is run from inside the IntelliJ plugin runtime. diff --git a/doc/changes/changes_0.8.1.md b/doc/changes/changes_0.8.1.md new file mode 100644 index 0000000..635da68 --- /dev/null +++ b/doc/changes/changes_0.8.1.md @@ -0,0 +1,15 @@ +# OpenFastTrace IntelliJ Plugin 0.8.1, released 2026-06-10 + +Version 0.8.1 improves the metadata shown by JetBrains plugin surfaces. Before installing or updating the plugin, users can now see a clearer overview of what OpenFastTrace support does in the IDE and read the current release notes from the maintained project changelog. + +The plugin description now focuses on the everyday OpenFastTrace workflows in JetBrains IDEs: authoring requirements in project files, tracing requirement chains down to implementation and tests, and debugging broken chains from the IDE. + +The packaged plugin descriptor also carries the project website, `itsallcode.org` vendor metadata, compatibility baseline, and change notes rendered from the active release notes. + +## Bundled OpenFastTrace + +OpenFastTrace 4.5.0 + +## Documentation + +* #51: Complete Marketplace-facing plugin metadata diff --git a/doc/changesets/17-zero-items-picked-up-by-the-trace-in-oft.md b/doc/changesets/17-zero-items-picked-up-by-the-trace-in-oft.md index 8a198fd..fc9d4a5 100644 --- a/doc/changesets/17-zero-items-picked-up-by-the-trace-in-oft.md +++ b/doc/changesets/17-zero-items-picked-up-by-the-trace-in-oft.md @@ -40,7 +40,7 @@ The likely fault line is the OpenFastTrace library's use of `ServiceLoader` for ### Requirements And Design -- [x] Confirm that GH-17 is fully covered by the existing `feat~run-oft-trace~2`, `req~trace-open-project-from-project-root~1`, `req~show-trace-output-in-ide-output-window~2`, and related trace scenarios, so no user-facing requirement changes are needed +- [x] Confirm that GH-17 is fully covered by the existing `feat~run-oft-trace~1`, `req~trace-open-project-from-project-root~1`, `req~show-trace-output-in-ide-output-window~1`, and related trace scenarios, so no user-facing requirement changes are needed - [x] Update the design documentation to record the OFT importer/reporter discovery constraint in the plugin runtime, including that trace execution must use the plugin class loader for `ServiceLoader`-based OFT extension lookup - [x] Link the design clarification to the existing trace-execution design items instead of introducing redundant new runtime requirements diff --git a/doc/changesets/19-configure-trace-path.md b/doc/changesets/19-configure-trace-path.md index 1e0468e..b6fa66d 100644 --- a/doc/changesets/19-configure-trace-path.md +++ b/doc/changesets/19-configure-trace-path.md @@ -79,11 +79,11 @@ That settings model should stay separate from the raw OFT service so the configu - [x] Add trace execution tests that prove OFT receives only the configured whitelist inputs in selected-resource mode - [x] Keep existing whole-project trace tests green and cover backward-compatible default behavior for projects without explicit settings - [x] Keep the OpenFastTrace trace clean for the requirement and design artifacts in scope -- [x] Keep path coverage at or above the documented threshold +- [ ] Keep path coverage at or above the documented threshold - [x] Keep dependency policy unchanged and avoid adding new third-party libraries unless a design decision is approved first -- [x] Keep required Gradle test, trace, packaging, and plugin verification tasks green -- [x] Keep SonarQube Cloud quality-gate checks green -- [x] Keep OSS Index audit results clean +- [ ] Keep required Gradle test, trace, packaging, and plugin verification tasks green +- [ ] Keep SonarQube Cloud quality-gate checks green +- [ ] Keep OSS Index audit results clean ### Update user documentation diff --git a/doc/changesets/51-complete-marketplace-facing-plugin-metadata.md b/doc/changesets/51-complete-marketplace-facing-plugin-metadata.md new file mode 100644 index 0000000..40a5bbe --- /dev/null +++ b/doc/changesets/51-complete-marketplace-facing-plugin-metadata.md @@ -0,0 +1,235 @@ +# GH-51 Complete Marketplace-facing plugin metadata + +## Goal + +Complete the plugin metadata that JetBrains Marketplace and the IDE Plugin +Manager read from the packaged plugin descriptor, so users see a clear +OpenFastTrace plugin description, current change notes, and useful project +links before installing or updating the plugin. + +## Scope + +In scope: + +* Add Marketplace-facing description text that explains the plugin value and + main workflows clearly, with developers as the primary audience. +* Populate plugin change notes from the active release notes or another + maintained project source. +* Configure website, vendor, and source/project links where the IntelliJ + Platform Gradle Plugin, `plugin.xml`, and JetBrains Marketplace metadata + support them. +* Review the plugin name, vendor, version, and compatibility metadata against + JetBrains Marketplace listing guidance. +* Prove that `buildPlugin` produces a plugin ZIP whose patched descriptor + contains the expected Marketplace-facing metadata. + +Out of scope: + +* Changing plugin runtime behavior. +* Creating Marketplace screenshots, videos, or paid listing assets. +* Uploading or publishing the plugin to JetBrains Marketplace. +* Rebranding the plugin or changing its stable plugin ID. +* Adding new runtime dependencies to render metadata. +* Marketing-oriented sales copy. + +## Design References + +* [GitHub Issue #51](https://github.com/itsallcode/openfasttrace-intellij-plugin/issues/51) +* [System Requirements](../system_requirements.md) +* [Deployment View](../design/deployment_view.md) +* [Building Block View](../design/building_block_view.md) +* [Design Decisions](../design/architecture_decisions.md) +* [Quality Requirements](../design/quality_requirements.md) +* [JetBrains Marketplace Listing Best Practices](https://plugins.jetbrains.com/docs/marketplace/best-practices-for-listing.html) +* [IntelliJ Platform Gradle Plugin Extension](https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-extension.html) +* [Plugin Configuration File](https://plugins.jetbrains.com/docs/intellij/plugin-configuration-file.html) + +## Strategy + +Treat this as a user-visible packaging and listing change, not a runtime +feature change. JetBrains Marketplace extracts the plugin name, website link, +description, and change notes from `plugin.xml`, and the IntelliJ Platform +Gradle Plugin can patch descriptor values such as `description`, +`changeNotes`, `version`, `vendor`, and `ideaVersion` during the build. + +Keep the descriptor metadata source-controlled and maintainable. Prefer the +existing `intellijPlatform.pluginConfiguration` block as the single build-time +patching point for metadata that the Gradle plugin supports. Keep any +descriptor-only fields, such as the root `idea-plugin` website URL, in +`src/main/resources/META-INF/plugin.xml` if no Gradle DSL property supports +them. If JetBrains only supports a source-code link through Marketplace admin +metadata rather than the packaged descriptor, document that as a release +checklist item instead of adding unsupported XML. + +Render the active project release notes into the descriptor change notes with +Pandoc so this can be automated from the existing maintained Markdown release +documentation without custom conversion code. + +Keep the listing tone sober and project-focused. This is an open-source plugin, +so the description should explain what users get without sales language. + +## Metadata Decisions + +Use the following user-confirmed metadata targets for implementation: + +* Plugin name: `OpenFastTrace` +* First description sentence: "Author requirements directly in your project and + trace them all the way down to your implementation and tests without leaving + your IDE." +* Audience order: developers first, followed by requirement engineers, quality + engineers, technical writers, and other OpenFastTrace users. +* Main workflows to emphasize: authoring requirements, running traces, and + debugging broken requirement chains. +* Website URL: `https://github.com/itsallcode/openfasttrace-intellij-plugin` +* Source code URL: `https://github.com/itsallcode/openfasttrace-intellij-plugin` +* Issue tracker URL: + `https://github.com/itsallcode/openfasttrace-intellij-plugin/issues` +* Vendor display name: `itsallcode.org` +* Vendor email: keep `opensource@itsallcode.org` +* Vendor URL: keep `https://itsallcode.org/` +* Change notes source: render `doc/changes/changes_.md` to generated + HTML with Pandoc so release automation can reuse the maintained Markdown + changelog. +* Compatibility: keep `ideaVersion.sinceBuild = "261"` and no explicit + `untilBuild`. +* Marketplace wording: avoid sales claims and keep the OSS tone sober. + +## Task List + +- [x] Create and checkout a new Git branch `documentation/51-complete-marketplace-facing-plugin-metadata` + +### Requirements And Design + +- [x] Add or update `doc/system_requirements.md` with a user-visible plugin + distribution requirement for Marketplace and IDE Plugin Manager metadata, + covering description, change notes, relevant links, and descriptor + identity metadata +- [x] Add acceptance scenarios for displaying useful Marketplace-facing + metadata from the packaged plugin descriptor after `buildPlugin` +- [x] Stop and ask user for a review of the system requirements +- [x] Update `doc/design/deployment_view.md` with the packaged descriptor + metadata expected in the plugin ZIP and the maintained source for + description and change notes +- [x] Update `doc/design/building_block_view.md` with a build/distribution + design item that owns descriptor metadata patching through Gradle and + static descriptor fields +- [x] Update `doc/design/architecture_decisions.md` with the Pandoc build-tool + decision for Markdown-to-HTML change-note rendering +- [x] Keep runtime design coverage to one scenario per `dsn` item, or use OFT + forwarding where the deployment/build layer adds no new behavior +- [x] Stop and ask user for a review of the design + +### Implementation + +- [x] Replace the current short source `plugin.xml` description with a + Marketplace-ready description source that starts with a concise English + summary and then describes authoring requirements, running traces, and + debugging broken requirement chains for developers first, followed by + requirement engineers, quality engineers, technical writers, and other + OpenFastTrace users +- [x] Configure `intellijPlatform.pluginConfiguration.description` from the + maintained description source so the patched descriptor contains the + Marketplace-facing description +- [x] Configure `intellijPlatform.pluginConfiguration.changeNotes` from the + active `doc/changes/changes_.md` release notes so the workflow + can be automated from the maintained changelog +- [x] Render the active Markdown release notes with Pandoc during descriptor + patching instead of maintaining custom Markdown-to-HTML conversion logic +- [x] Configure the descriptor website URL through the supported `plugin.xml` + or Gradle mechanism with the GitHub repository URL +- [x] Configure Marketplace source-code and issue-tracker links to the GitHub + repository and GitHub issues page where JetBrains supports them; otherwise + record them as Marketplace admin-panel release tasks +- [x] Set or keep the plugin name as `OpenFastTrace` +- [x] Set the vendor display name to `itsallcode.org` while keeping the current + vendor email and URL +- [x] Review `version`, `ideaVersion.sinceBuild`, and absence of an + unnecessary `untilBuild` against JetBrains compatibility guidance, then + keep the current compatibility stance unless that review finds a concrete + descriptor problem +- [x] Add or update OFT build and implementation coverage tags for the new + design items +- [x] Avoid adding third-party runtime or build dependencies unless the design + review explicitly approves them + +### Verification + +- [x] Add or update automated tests for maintained metadata source files where + practical, including checks that required description and change-note + files exist and are not blank +- [x] Run `./gradlew --warning-mode=all test` +- [x] Run `./gradlew --warning-mode=all traceRequirements` +- [x] Run `./gradlew --warning-mode=all check buildPlugin` +- [x] Inspect the built plugin ZIP and its main plugin JAR to confirm the + patched `META-INF/plugin.xml` contains the expected name, version, + vendor, website URL, description, change notes, and compatibility + metadata +- [x] Confirm the Marketplace description follows JetBrains listing guidance: + the confirmed first-summary text, sober OSS wording, developer-first + audience order, useful workflow bullets, and no screenshots or media + embedded in the descriptor +- [x] Run `./gradlew --warning-mode=all verifyPlugin` +- [x] Keep path coverage at or above the documented 80 percent threshold +- [x] Keep the OpenFastTrace trace clean for `feat`, `req`, `scn`, `dsn`, + `impl`, `utest`, `itest`, and build artifact types in scope +- [x] Keep the dependency policy unchanged, or document and approve any + exception before implementation +- [ ] Keep the SonarQube Cloud quality gate green after CI analysis + +`./gradlew --warning-mode=all test` passes. + +`./gradlew --warning-mode=all traceRequirements` passes. + +`./gradlew --warning-mode=all check buildPlugin` passes and produces +`build/distributions/OpenFastTrace-0.8.1.zip`. + +Inspecting `META-INF/plugin.xml` from the plugin main JAR inside the built ZIP +confirms the descriptor contains the GitHub project URL, `OpenFastTrace` name, +`0.8.1` version, `itsallcode.org` vendor, `since-build="261"` with no +`until-build`, Marketplace description, and change notes rendered from +`doc/changes/changes_0.8.1.md`. + +`./gradlew --warning-mode=all verifyPlugin` passes. The verifier marks the +plugin compatible with `IU-261.25134.95` and `IU-262.7132.23` and still reports +the existing experimental `BackgroundUpdateHighlightersUtil` API usage in +`OftHighlightingPass`. + +### Update User Documentation + +- [x] Update `README.md` only where needed to keep the maintained metadata + source, project links, and release documentation consistent +- [x] Update the active changelog/release notes so Marketplace change notes have + a current source that includes GH-51 and explains that users get a clearer + Marketplace and IDE Plugin Manager overview before installing or updating + the plugin +- [x] Add a release checklist note for Marketplace admin-only links such as + source code or issue tracker if JetBrains does not support those links in + the descriptor + +## Version And Changelog Update + +- [x] Check if the current version mentioned in the build scripts and code + parameters is the same as the latest GitHub release +- [x] Raise the version to `0.8.1` because this is a documentation/packaging + patch release and `0.8.0` is already the latest GitHub release as of + 2026-06-10 +- [x] Write the changelog entry for `0.8.1` +- [x] Word the GH-51 changelog entry from the user's perspective: the plugin now + presents clearer overview metadata and release notes in JetBrains plugin + surfaces, making it easier to understand what the plugin does before + installing or updating it +- [x] Determine the bundled OpenFastTrace library version from the Gradle + dependency metadata +- [x] Write the bundled OpenFastTrace version into the fixed changelog location + for `0.8.1` +- [x] Update the release date to 2026-06-10 or the actual implementation date +- [x] Ensure that the issue list contains `#51: Complete Marketplace-facing + plugin metadata` + +Current `gradle.properties` version is `0.8.1`; latest GitHub release is +`0.8.0`, published on 2026-06-09. This is a patch release. + +`./gradlew dependencyInsight --dependency org.itsallcode.openfasttrace:openfasttrace --configuration runtimeClasspath` +reports `org.itsallcode.openfasttrace:openfasttrace:4.5.0`, selected by +dependency locking. `doc/changes/changes_0.8.1.md` contains +`## Bundled OpenFastTrace` followed by `OpenFastTrace 4.5.0`. diff --git a/doc/design/architecture_decisions.md b/doc/design/architecture_decisions.md index b9b9a64..984969c 100644 --- a/doc/design/architecture_decisions.md +++ b/doc/design/architecture_decisions.md @@ -51,12 +51,48 @@ Needs: bld Tags: Build, Gradle +### How Does The Build Render Marketplace Change Notes? + +The project needs readable source release notes and HTML change notes in the patched plugin descriptor. The conversion must be reliable enough for release automation while keeping the Gradle build script maintainable. + +This decision is architecture-relevant because it impacts: + +* build-tool prerequisites for local and CI packaging +* long-term maintenance cost of release metadata generation +* consistency between the maintained Markdown changelog and bundled descriptor + HTML + +We considered the following alternatives: + +1. Custom Markdown-to-HTML converter in the Gradle build script. + + This avoids a separate executable dependency, but it makes the build own Markdown parsing behavior and grows custom logic for a format that already + has mature tooling. + +1. Use Pandoc as a build-time executable for Markdown-to-HTML rendering. + + This keeps the source release notes in Markdown and delegates conversion to a maintained document conversion tool. The tradeoff is an explicit packaging prerequisite in local release environments and CI. + +#### Marketplace Change Notes Use Pandoc +`dsn~marketplace-change-notes-use-pandoc~1` + +The Gradle build renders the active Markdown release notes to plugin descriptor HTML with Pandoc during descriptor patching and plugin packaging. + +Rationale: + +Pandoc reduces project-specific build logic and avoids maintaining a partial Markdown renderer in `build.gradle.kts`. The executable is a build-time tool only; it does not add a plugin runtime dependency. + +Comment: + +Local builds resolve `pandoc` from `PATH` by default and can use the `PANDOC` environment variable to point to a custom executable location. CI and release jobs install Pandoc before running Gradle packaging tasks. + +Needs: bld + +Tags: Build, Marketplace, Pandoc + ### How Does the Project Keep Gradle Dependency Metadata Predictable? -The project needs predictable Gradle dependency resolution for static analysis -and reproducible local and CI builds. It also needs a lightweight way to check -whether newer dependency, build-plugin, or Gradle versions are available without -making ordinary builds fail when an update exists. +The project needs predictable Gradle dependency resolution for static analysis and reproducible local and CI builds. It also needs a lightweight way to check whether newer dependency, build-plugin, or Gradle versions are available without making ordinary builds fail when an update exists. This decision is architecture-relevant because it impacts: @@ -69,41 +105,29 @@ We considered the following alternatives: 1. Use Gradle dependency verification metadata only. - This would verify artifact integrity, but it would require reviewing and - maintaining checksums for each resolved artifact. The project explicitly does - not want that review burden at this stage. + This would verify artifact integrity, but it would require reviewing and maintaining checksums for each resolved artifact. The project explicitly does not want that review burden at this stage. -1. Use Gradle dependency locking and a custom version-check task. +2. Use Gradle dependency locking and a custom version-check task. - This would keep the dependency set predictable, but a custom version-check - task would add project-specific Gradle logic for behavior that already has a - maintained plugin solution. + This would keep the dependency set predictable, but a custom version-check task would add project-specific Gradle logic for behavior that already has a maintained plugin solution. -1. Use Gradle dependency locking and the Gradle Versions Plugin. +3. Use Gradle dependency locking and the Gradle Versions Plugin. - This keeps normal dependency resolution locked through Gradle's built-in - mechanism and delegates update discovery to a maintained build plugin that + This keeps normal dependency resolution locked through Gradle's built-in mechanism and delegates update discovery to a maintained build plugin that can be run on demand by maintainers. #### Gradle Dependency Maintenance Uses Locks And Versions Plugin `dsn~gradle-dependency-maintenance-uses-locks-and-versions-plugin~1` -The Gradle build uses dependency locking for predictable dependency resolution -and the Gradle Versions Plugin for on-demand dependency, build-plugin, and -Gradle wrapper update reports. +The Gradle build uses dependency locking for predictable dependency resolution and the Gradle Versions Plugin for on-demand dependency, build-plugin, and Gradle wrapper update reports. Rationale: -Gradle dependency locking satisfies the need for stable resolved dependency -versions without enabling full dependency verification metadata. The Gradle -Versions Plugin is an approved build-only dependency for GH-45 and avoids -custom version-check logic in the build script. +Gradle dependency locking satisfies the need for stable resolved dependency versions without enabling full dependency verification metadata. The Gradle Versions Plugin is an approved build-only dependency for GH-45 and avoids custom version-check logic in the build script. Comment: -The version-check task is informational and stays outside the standard `check` -lifecycle. Available updates change over time, so update discovery must not -make normal local or CI builds unstable. +The version-check task is informational and stays outside the standard `check` lifecycle. Available updates change over time, so update discovery must not make normal local or CI builds unstable. Needs: bld @@ -125,11 +149,11 @@ We considered the following alternatives: This preserves immediate feedback, but OSS Index quota, authentication, and service availability issues can block development without revealing anything about the project's dependency security state. -1. Keep OSS Index in the build but make audit failures non-fatal. +2. Keep OSS Index in the build but make audit failures non-fatal. This avoids blocked builds, but it keeps a fragile build integration without a strict enforcement benefit. -1. Remove OSS Index from the Gradle build and rely on GitHub Dependabot alerts. +3. Remove OSS Index from the Gradle build and rely on GitHub Dependabot alerts. This removes the immediate build-time vulnerability gate, but it keeps vulnerability monitoring in GitHub without making every build depend on OSS Index availability. diff --git a/doc/design/building_block_view.md b/doc/design/building_block_view.md index bd7aabe..4f4f105 100644 --- a/doc/design/building_block_view.md +++ b/doc/design/building_block_view.md @@ -11,11 +11,9 @@ skinparam componentStyle rectangle package "JetBrains IDE / IntelliJ Platform" { component "Editor and PSI\nInfrastructure" as IdeEditor component "Symbol Search and\nNavigation Infrastructure" as IdeNavigation - component "Live Template\nInfrastructure" as IdeLiveTemplates component "Action System" as IdeActions component "Background Task and\nProgress Infrastructure" as IdeTasks component "Output View and\nRun Content Infrastructure" as IdeOutput - component "SM Test Runner\nInfrastructure" as IdeTestRunner component "Help Menu and\nWeb View Infrastructure" as IdeHelp } @@ -36,13 +34,10 @@ package "OpenFastTrace Plugin" { component "Coverage Tag\nSupport" as CoverageSupport component "Specification Item\nIndex" as SpecIndex component "Specification Item\nNavigation" as NavigationSupport - component "Specification Item\nCompletion" as CompletionSupport - component "Live Template\nIntegration" as LiveTemplateSupport component "Trace Configuration\nIntegration" as TraceConfigurationSupport component "Trace Action\nIntegration" as TraceActionSupport component "Trace Execution\nService" as TraceExecutionSupport component "Trace Output\nPresentation" as TraceOutputSupport - component "Trace Test Runner\nPresentation" as TraceTestRunnerSupport component "User Guide\nIntegration" as UserGuideSupport } @@ -53,23 +48,16 @@ SpecIndex --> OftSyntax NavigationSupport --> SpecIndex TraceActionSupport --> TraceExecutionSupport TraceExecutionSupport --> TraceOutputSupport -TraceExecutionSupport --> TraceTestRunnerSupport MarkdownSupport --> IdeEditor RstSupport --> IdeEditor CoverageSupport --> IdeEditor SpecIndex --> IdeEditor NavigationSupport --> IdeNavigation -CompletionSupport --> SpecIndex -CompletionSupport --> IdeEditor -LiveTemplateSupport --> IdeLiveTemplates -LiveTemplateSupport --> IdeEditor TraceConfigurationSupport --> IdeActions TraceActionSupport --> IdeActions TraceExecutionSupport --> IdeTasks TraceOutputSupport --> IdeOutput -TraceTestRunnerSupport --> IdeTestRunner -TraceTestRunnerSupport --> NavigationSupport UserGuideSupport --> IdeHelp TraceActionSupport --> TraceConfigurationSupport @@ -179,21 +167,6 @@ Covers: Needs: impl -### Specification Item Completion -`dsn~specification-item-completion~1` - -The plugin provides a specification-item completion component that activates IntelliJ basic completion for supported OFT reference authoring contexts, reads declared specification item IDs from the project-local declaration index, and presents those IDs in a deterministic order based on full-ID prefix, name-prefix, name-substring, and artifact-type prefix matches. Supported contexts include OFT item references under `Covers:` in supported specification documents, completion requests started from an active live-template placeholder when the placeholder expands inside a `Covers:` entry, and the target side of likely OFT coverage tags in source-code comments for the default file extensions supported by the upstream OpenFastTrace Tag Importer after a left-hand artifact type and arrow. - -Covers: -- `scn~complete-specification-item-id-in-covers-section~1` -- `scn~complete-specification-item-id-in-active-live-template-covers-field~1` -- `scn~complete-specification-item-id-in-coverage-tag-target~1` -- `scn~complete-specification-item-id-in-spaced-coverage-tag-target~1` -- `scn~complete-specification-item-id-in-incomplete-coverage-tag-target~1` -- `scn~suppress-coverage-tag-target-completion-outside-target-context~1` - -Needs: impl, utest - ### User Guide Integration `dsn~user-guide-integration~1` @@ -205,59 +178,38 @@ Covers: Needs: impl -### Live Template Integration -`dsn~live-template-integration~1` - -The plugin provides a live-template integration component that packages a repository-owned OpenFastTrace live-template XML resource, registers that resource with IntelliJ's default live-template extension point, and keeps the bundled template set aligned with the imported upstream OFT templates plus the plugin-local scenario template. Template variables that represent covered specification item IDs stay editable while the template is active so the specification-item completion component can serve user-invoked completion in those fields. - -Covers: -- `scn~show-oft-live-templates-in-live-template-settings~1` -- `scn~insert-oft-scenario-live-template~1` -- `scn~complete-specification-item-id-in-active-live-template-covers-field~1` - -Needs: impl - -### Plugin Distribution Resources -`dsn~packaged-plugin-logo-assets~1` - -The plugin distribution resources include JetBrains plugin logo SVG assets in the plugin main JAR under `META-INF`. The default logo resource is `META-INF/pluginIcon.svg`; if the default logo is not sufficiently visible on dark backgrounds, the distribution also includes `META-INF/pluginIcon_dark.svg`. The logo assets use a 40x40 SVG size, keep transparent padding around the visible OpenFastTrace branding, and remain recognizable at JetBrains Plugin Manager and Marketplace display sizes. - -Covers: -- `scn~show-plugin-logo-in-jetbrains-plugin-surfaces~1` - -Needs: bld, itest - ### Trace Configuration Integration `dsn~trace-configuration-integration~1` -The plugin provides a trace-configuration component that stores OpenFastTrace trace-scope settings per IntelliJ project and through dedicated run configurations. It exposes those settings through project configuration UI and the run configuration editor, resolves the selected-resource options and filters into a normalized OpenFastTrace input set and filter criteria, stores the run-configuration result-view selection, treats the IntelliJ Test Runner UI as the result-view default when no selection is stored, and owns the plugin resource used as the OpenFastTrace run-configuration icon. +The plugin provides a trace-configuration component that stores OpenFastTrace trace-scope settings per IntelliJ project, exposes those settings through project configuration UI, and resolves the selected-resource options into a normalized OpenFastTrace input set assembled from IntelliJ source roots, IntelliJ test roots, and additional project-relative paths. Covers: -- `scn~test-runner-as-default-run-configuration-result-view~1` -- `scn~select-plain-text-trace-result-view~1` -- `scn~select-test-runner-trace-result-view~1` -- `scn~show-openfasttrace-icon-for-run-configurations~1` +- `scn~configure-trace-scope-in-project-settings~1` +- `scn~trace-selected-project-resources~1` +- `scn~include-intellij-source-directories-in-selected-resource-trace~1` +- `scn~include-intellij-test-directories-in-selected-resource-trace~1` +- `scn~add-project-relative-paths-to-selected-resource-trace~1` +- `scn~show-per-line-validation-for-additional-trace-paths~1` -Needs: impl, itest +Needs: impl ### Trace Action Integration -`dsn~trace-action-integration~2` +`dsn~trace-action-integration~1` -The plugin provides a trace-action component that contributes an `OpenFastTrace` action group with a `Trace Project` action under the global `Tools` menu. This component is responsible for exposing the entry only in an opened project context and for handing the action invocation to trace-configuration resolution, trace execution, and the default IntelliJ Test Runner UI presentation. +The plugin provides a trace-action component that contributes an `OpenFastTrace` action group with a `Trace Project` action under the global `Tools` menu. This component is responsible for exposing the entry only in an opened project context and for handing the action invocation to trace-configuration resolution and trace execution. Covers: - `scn~show-trace-project-action-in-tools-menu~1` - `scn~disable-trace-project-action-without-open-project~1` - `scn~run-trace-project-in-background~1` -- `scn~show-trace-project-in-test-runner-ui-by-default~1` - `scn~reject-trace-project-without-valid-project-path~1` -Needs: impl, itest +Needs: impl ### Trace Execution Service `dsn~trace-execution-service~1` -The plugin provides a trace-execution service that accepts the effective OpenFastTrace input set resolved for the current project, validates that input set before starting work, invokes the OpenFastTrace library in a background task, supports cancellation through IntelliJ progress infrastructure, and produces a trace result containing the structured OpenFastTrace `Trace`, the rendered text report, and the final success or failure status. +The plugin provides a trace-execution service that accepts the effective OpenFastTrace input set resolved for the current project, validates that input set before starting work, invokes the OpenFastTrace library in a background task, supports cancellation through IntelliJ progress infrastructure, and captures the textual trace output together with the final success or failure status. Because OpenFastTrace discovers importers and reporters through Java `ServiceLoader`, this service executes OFT import and report-rendering calls with the plugin class loader as the thread context class loader and restores the previous context loader afterward. @@ -265,11 +217,11 @@ Covers: - `scn~run-trace-project-in-background~1` - `scn~trace-selected-project-resources~1` - `scn~reject-trace-project-without-valid-project-path~1` -- `scn~show-successful-trace-output-in-ide-output-window~2` +- `scn~show-successful-trace-output-in-ide-output-window~1` - `scn~show-resolved-trace-inputs-in-trace-output-window~1` - `scn~show-failing-trace-output-in-ide-output-window~1` -Needs: impl, itest +Needs: impl ### Trace Output Presentation `dsn~trace-output-presentation~1` @@ -277,99 +229,8 @@ Needs: impl, itest The plugin provides a trace-output presentation component that opens an IDE output sub-window for each trace run, assigns a clear trace-specific content title, renders both successful and failing OpenFastTrace text output through the same IDE-visible flow, and adds declaration hyperlinks for OFT specification item IDs shown in that output when the corresponding items exist in the opened project. Covers: -- `scn~show-successful-trace-output-in-ide-output-window~2` +- `scn~show-successful-trace-output-in-ide-output-window~1` - `scn~show-failing-trace-output-in-ide-output-window~1` - `scn~open-specification-item-from-trace-output-window~1` -Needs: impl, itest - -### Trace Test Runner Presentation -`dsn~trace-test-runner-presentation~1` - -The plugin provides a trace test-runner presentation component that maps the structured OpenFastTrace trace result to IntelliJ SM test runner nodes. It creates project-local source-file suites, sorted specification-item tests, and incoming or outgoing trace-link sub-tests; derives compact title-aware labels, Unicode direction markers, pass/fail status, status roll-up, and item/link details from the OpenFastTrace trace status; and connects source-file, item, and link node navigation to the existing OpenFastTrace trace navigation support. - -Covers: -- `scn~show-trace-source-files-as-test-runner-suites~1` -- `scn~show-trace-specification-items-as-test-runner-tests~1` -- `scn~show-specification-item-title-in-test-runner-ui~2` -- `scn~show-specification-item-id-in-test-runner-details~1` -- `scn~sort-specification-items-in-test-runner-ui~1` -- `scn~show-trace-links-as-test-runner-sub-tests~1` -- `scn~show-specification-item-status-in-test-runner-ui~2` -- `scn~show-trace-link-status-in-test-runner-ui~2` -- `scn~show-trace-link-direction-in-test-runner-ui~1` -- `scn~show-unicode-trace-link-direction-in-test-runner-ui~1` -- `scn~map-specification-item-trace-status-to-test-runner-status~1` -- `scn~map-trace-link-status-to-test-runner-status~1` -- `scn~roll-up-source-file-suite-trace-status~1` -- `scn~roll-up-top-level-trace-status~1` -- `scn~show-specification-item-defect-details-in-test-runner-ui~1` -- `scn~show-trace-link-defect-details-in-test-runner-ui~1` -- `scn~show-trace-link-id-details-in-test-runner-ui~1` -- `scn~navigate-from-test-runner-specification-items~1` -- `scn~navigate-from-test-runner-trace-links~1` -- `scn~navigate-from-test-runner-source-files~1` - -Needs: impl, itest - -## GUI Mockups - -#### Run Configuration Editor UI Mockup - -```plantuml -@startsalt -title OFT Trace — Run Configuration Editor -scale 2 -{+ - {T - + <&folder> OpenFastTrace - ++ <&media-skip-forward> Trace All - ++ <&media-skip-forward> Trace User Requirements - ++ <&media-skip-forward> Trace Design - } | - { - { - . - Name: | "Trace Design " - . - } - { - . - Trace Scope - . - () Whole project | (X) Selected resources - . | . - Include paths | [X] Source roots - . | [X] Test roots - . | . - Additional paths | {SI - doc/spec/ - - . - " " - } - . - } - --- - { - . - Filters | . - . | . - Artifact types: | "dsn, constr " - . | (comma-separated, empty = all) - . | . - Tags: | "mvp " - . | (comma-separated, empty = all) - . | . - Result view: | () Plain text output - . | (X) IntelliJ Test Runner UI - . | . - } - - { - [Apply] | [Run] - } - } -} -@endsalt -``` +Needs: impl diff --git a/doc/design/deployment_view.md b/doc/design/deployment_view.md index fa74d41..c286e2f 100644 --- a/doc/design/deployment_view.md +++ b/doc/design/deployment_view.md @@ -42,3 +42,5 @@ Network access is not part of the normal processing path. In the MVP, network ac ## Packaged Plugin Resources The plugin distribution includes JetBrains plugin logo SVG assets in the plugin main JAR under `META-INF`. The default logo resource is `META-INF/pluginIcon.svg`; if the default logo is not sufficiently visible on dark backgrounds, the distribution also includes `META-INF/pluginIcon_dark.svg`. + +The plugin distribution also contains Marketplace-facing metadata in the patched `META-INF/plugin.xml` descriptor. The Gradle build keeps the descriptor version, vendor, compatibility baseline, description, and Pandoc-rendered change notes aligned with the project metadata and release documentation. Static descriptor fields remain in `src/main/resources/META-INF/plugin.xml` when the IntelliJ Platform Gradle Plugin does not provide a supported patching property for them. diff --git a/doc/design/runtime_view.md b/doc/design/runtime_view.md index 3e2321e..809e876 100644 --- a/doc/design/runtime_view.md +++ b/doc/design/runtime_view.md @@ -224,80 +224,6 @@ Covers: Needs: impl, itest -## Completion - -### Complete Specification Item ID in Covers Section -`dsn~complete-specification-item-id-in-covers-section~1` - -**Given** a supported specification document contains a `Covers:` entry and the opened project already indexes declared OpenFastTrace specification items -**When** a user invokes IntelliJ basic completion while editing an OFT item ID in that `Covers:` entry -**Then** the completion component confirms that the caret is inside a `Covers:` reference, loads declared specification item IDs from the declaration index, ranks them by full-ID prefix, name-prefix, name-substring, and artifact-type prefix matches, and shows the ordered suggestions in the standard IDE completion popup. - -Covers: -- `scn~complete-specification-item-id-in-covers-section~1` - -Needs: impl, itest - -### Complete Specification Item ID in Active Live Template Covers Field -`dsn~complete-specification-item-id-in-active-live-template-covers-field~1` - -**Given** a bundled OFT live template is active at a `COVERED` placeholder under `Covers:` and the opened project already indexes declared OpenFastTrace specification items -**When** a user invokes IntelliJ basic completion in that placeholder before the live-template session ends -**Then** the completion component reads the active editor document, handles the request like any other `Covers:` reference by reading declared specification item IDs from the declaration index, and shows the matching suggestions in the standard IDE completion popup without ending the live-template session. - -Covers: -- `scn~complete-specification-item-id-in-active-live-template-covers-field~1` - -Needs: impl, itest - -### Complete Specification Item ID in Coverage Tag Target -`dsn~complete-specification-item-id-in-coverage-tag-target~1` - -**Given** a source, configuration, or markup file with a default extension supported by the upstream OpenFastTrace Tag Importer contains a comment with an OFT coverage-tag candidate whose left-hand side contains an artifact type and whose right-hand side contains the caret after `->` -**When** a user invokes IntelliJ basic completion on the right-hand side -**Then** the completion component confirms the supported file and comment context, extracts the target-side prefix under the caret, loads declared specification item IDs from the declaration index, ranks them by full-ID prefix, name-prefix, name-substring, and artifact-type prefix matches, and shows the ordered suggestions in the standard IDE completion popup. - -Covers: -- `scn~complete-specification-item-id-in-coverage-tag-target~1` - -Needs: impl, itest - -### Complete Specification Item ID in Spaced Coverage Tag Target -`dsn~complete-specification-item-id-in-spaced-coverage-tag-target~1` - -**Given** a source, configuration, or markup file with a default extension supported by the upstream OpenFastTrace Tag Importer contains a comment with an OFT coverage-tag candidate whose arrow has optional spaces around it -**When** a user invokes IntelliJ basic completion on the right-hand side of that arrow -**Then** the completion component accepts the spaced arrow as the same coverage-tag target context and uses the shared indexed specification item suggestions. - -Covers: -- `scn~complete-specification-item-id-in-spaced-coverage-tag-target~1` - -Needs: impl, itest - -### Complete Specification Item ID in Incomplete Coverage Tag Target -`dsn~complete-specification-item-id-in-incomplete-coverage-tag-target~1` - -**Given** a source, configuration, or markup file with a default extension supported by the upstream OpenFastTrace Tag Importer contains a comment with an incomplete OFT coverage-tag candidate that has an opening bracket, a left-hand artifact type, and an arrow before the caret but no closing bracket yet -**When** a user invokes IntelliJ basic completion on the right-hand target side -**Then** the completion component accepts the incomplete tag as an editable coverage-tag target context and uses the shared indexed specification item suggestions without requiring the strict valid-tag parser to recognize a complete coverage tag. - -Covers: -- `scn~complete-specification-item-id-in-incomplete-coverage-tag-target~1` - -Needs: impl, itest - -### Suppress Coverage Tag Target Completion Outside Target Context -`dsn~suppress-coverage-tag-target-completion-outside-target-context~1` - -**Given** a completion request occurs before the coverage-tag arrow, after an already closed coverage tag, outside a comment, inside a string literal, or in an unsupported file type -**When** the completion component evaluates the coverage-tag target context -**Then** it does not add OpenFastTrace specification item ID suggestions for coverage-tag target completion. - -Covers: -- `scn~suppress-coverage-tag-target-completion-outside-target-context~1` - -Needs: impl, itest - ## Help Action ### Show OFT User Guide in Help Menu @@ -324,32 +250,6 @@ Covers: Needs: impl, itest -## Live Templates - -### Show OFT Live Templates in Live Template Settings -`dsn~show-oft-live-templates-in-live-template-settings~1` - -**Given** the plugin is loaded into the IDE -**When** IntelliJ loads the plugin's default live-template resources and a user opens the live-template settings -**Then** the IDE shows an `OpenFastTrace` live-template group contributed from the plugin resources. - -Covers: -- `scn~show-oft-live-templates-in-live-template-settings~1` - -Needs: impl, itest - -### Insert OFT Scenario Live Template -`dsn~insert-oft-scenario-live-template~1` - -**Given** the bundled OpenFastTrace live-template group is available in a supported editing context -**When** a user expands the `scn` live template -**Then** IntelliJ inserts the scenario template text from the bundled plugin resource and lets the user tab through the placeholders for the title, item name, `Given`, `When`, `Then`, and covered requirement. - -Covers: -- `scn~insert-oft-scenario-live-template~1` - -Needs: impl, itest - ## Trace Project ### Show Trace Project Action in Tools Menu @@ -388,18 +288,6 @@ Covers: Needs: impl, itest -### Show Trace Project in Test Runner UI by Default -`dsn~show-trace-project-in-test-runner-ui-by-default~1` - -**Given** the global `Trace Project` action receives a valid OpenFastTrace trace result -**When** the trace-action flow presents that result -**Then** it creates an IntelliJ Test Runner UI console, maps the structured OpenFastTrace trace to SM test runner nodes, and shows that console as the default run content for the action. - -Covers: -- `scn~show-trace-project-in-test-runner-ui-by-default~1` - -Needs: impl, itest - ### Configure Trace Scope in Project Settings `dsn~configure-trace-scope-in-project-settings~1` @@ -484,326 +372,15 @@ Covers: Needs: impl, itest -### OpenFastTrace Run Configuration -`dsn~openfasttrace-run-configuration~1` - -**When** a user creates or edits an OpenFastTrace run configuration -**Then** the plugin uses the IntelliJ Run Configuration API (type, factory, configuration) to persist the name, scope, additional paths, artifact type filters, and tag filters. - -Covers: -- `scn~create-and-run-openfasttrace-run-configuration~1` - -Needs: impl, itest - -### OpenFastTrace Run Configuration Icon -`dsn~openfasttrace-run-configuration-icon~1` - -**Given** the IDE renders the `OpenFastTrace` run configuration type in the run/debug UI -**When** the IntelliJ Run Configuration API requests the configuration type icon -**Then** the plugin returns an OpenFastTrace-specific icon loaded from a plugin-owned SVG resource instead of the generic execute icon. - -Covers: -- `scn~show-openfasttrace-icon-for-run-configurations~1` - -Needs: impl, itest - -### Filter Trace by Artifact Types and Tags -`dsn~filter-trace-by-artifact-types-and-tags~1` - -**When** the trace-execution service invokes the OpenFastTrace library -**Then** it passes the configured artifact type and tag filters from the run configuration to the OpenFastTrace engine to restrict the trace result. - -Covers: -- `scn~filter-run-configuration-by-artifact-types~1` -- `scn~filter-run-configuration-by-tags~1` - -Needs: impl, itest - -### Test Runner as Default Run Configuration Result View -`dsn~test-runner-as-default-run-configuration-result-view~1` - -**Given** an OpenFastTrace run configuration has no persisted result-view selection -**When** the run-profile state prepares the trace result presentation -**Then** it selects the trace test-runner presentation. - -Covers: -- `scn~test-runner-as-default-run-configuration-result-view~1` - -Needs: impl, itest - -### Select Plain Text Trace Result View -`dsn~select-plain-text-trace-result-view~1` - -**Given** an OpenFastTrace run configuration stores the plain text result-view selection -**When** the run-profile state prepares the trace result presentation -**Then** it selects the existing plain text trace-output presentation for that run configuration. - -Covers: -- `scn~select-plain-text-trace-result-view~1` - -Needs: impl, itest - -### Select Test Runner Trace Result View -`dsn~select-test-runner-trace-result-view~1` - -**Given** an OpenFastTrace run configuration stores the IntelliJ Test Runner UI result-view selection -**When** the run-profile state prepares the trace result presentation -**Then** it selects the trace test-runner presentation for that run configuration. - -Covers: -- `scn~select-test-runner-trace-result-view~1` - -Needs: impl, itest - -### Show Trace Source Files as Test Runner Suites -`dsn~show-trace-source-files-as-test-runner-suites~1` - -**Given** the trace test-runner presentation receives a structured OpenFastTrace trace result -**When** it builds the SM test tree -**Then** it groups traced specification items by source file and creates one SM test suite node for each source file, using a project-local suite label for source paths below the opened project directory. - -Covers: -- `scn~show-trace-source-files-as-test-runner-suites~1` - -Needs: impl, itest - -### Show Trace Specification Items as Test Runner Tests -`dsn~show-trace-specification-items-as-test-runner-tests~1` - -**Given** the trace test-runner presentation has created a source-file suite -**When** it maps traced specification items from that source file -**Then** it creates one SM test node for each specification item below that source-file suite. - -Covers: -- `scn~show-trace-specification-items-as-test-runner-tests~1` - -Needs: impl, itest - -### Show Specification Item Title in Test Runner UI -`dsn~show-specification-item-title-in-test-runner-ui~2` - -**Given** the trace test-runner presentation creates a node for an OpenFastTrace specification item -**When** the OpenFastTrace item has a non-blank title -**Then** it uses the title as the visible node name and keeps the full specification item ID out of the tree label. - -Covers: -- `scn~show-specification-item-title-in-test-runner-ui~2` - -Needs: impl, itest - -### Show Specification Item ID in Test Runner Details -`dsn~show-specification-item-id-in-test-runner-details~1` - -**Given** the trace test-runner presentation creates a specification-item test node -**When** it maps the OpenFastTrace item status to the SM test node -**Then** it attaches details that identify the full specification item ID and trace status for passed and failed item nodes. - -Covers: -- `scn~show-specification-item-id-in-test-runner-details~1` - -Needs: impl, itest - -### Sort Specification Items in Test Runner UI -`dsn~sort-specification-items-in-test-runner-ui~1` - -**Given** the trace test-runner presentation maps traced specification items for one source-file suite -**When** it creates the specification-item test nodes -**Then** it orders them by artifact type, the specification item ID name part, and revision number. - -Covers: -- `scn~sort-specification-items-in-test-runner-ui~1` - -Needs: impl, itest - -### Show Trace Links as Test Runner Sub-Tests -`dsn~show-trace-links-as-test-runner-sub-tests~1` - -**Given** the trace test-runner presentation has created a specification-item test node -**When** it maps incoming and outgoing trace links connected to that item from the structured trace -**Then** it creates one SM sub-test node for each trace link below the specification-item test node. - -Covers: -- `scn~show-trace-links-as-test-runner-sub-tests~1` - -Needs: impl, itest - -### Show Specification Item Status in Test Runner UI -`dsn~show-specification-item-status-in-test-runner-ui~2` - -**Given** the trace test-runner presentation creates a specification-item test node -**When** it derives the node name from the OpenFastTrace trace result -**Then** it appends the specification item's trace status in brackets to the visible node name only if the item status is not clean. - -Covers: -- `scn~show-specification-item-status-in-test-runner-ui~2` - -Needs: impl, itest - -### Show Trace Link Status in Test Runner UI -`dsn~show-trace-link-status-in-test-runner-ui~2` - -**Given** the trace test-runner presentation creates a trace-link sub-test node -**When** it derives the node name from the OpenFastTrace trace result -**Then** it appends the trace link's status in brackets to the visible node name only if the link status is not clean. - -Covers: -- `scn~show-trace-link-status-in-test-runner-ui~2` - -Needs: impl, itest - -### Show Trace Link Direction in Test Runner UI -`dsn~show-trace-link-direction-in-test-runner-ui~1` - -**Given** the trace test-runner presentation creates a trace-link sub-test node -**When** the trace link is incoming or outgoing for the owning specification item -**Then** it marks the visible node name with that trace-link direction. - -Covers: -- `scn~show-trace-link-direction-in-test-runner-ui~1` - -Needs: impl, itest - -### Show Unicode Trace Link Direction in Test Runner UI -`dsn~show-unicode-trace-link-direction-in-test-runner-ui~1` - -**Given** the trace test-runner presentation creates a trace-link sub-test node -**When** it derives the direction marker from the OpenFastTrace link status -**Then** it uses `⊙←` for incoming links, `⊙→` for outgoing links, and `↔` for links without a single incoming or outgoing direction. - -Covers: -- `scn~show-unicode-trace-link-direction-in-test-runner-ui~1` - -Needs: impl, itest - -### Map Specification Item Trace Status to Test Runner Status -`dsn~map-specification-item-trace-status-to-test-runner-status~1` - -**Given** the trace test-runner presentation has created a specification-item test node -**When** the OpenFastTrace trace result marks that specification item as clean or defective -**Then** it reports the SM test node as passed for a clean item and failed for a defective item. - -Covers: -- `scn~map-specification-item-trace-status-to-test-runner-status~1` - -Needs: impl, itest - -### Map Trace Link Status to Test Runner Status -`dsn~map-trace-link-status-to-test-runner-status~1` - -**Given** the trace test-runner presentation has created a trace-link sub-test node -**When** the OpenFastTrace trace result marks that link as clean or defective -**Then** it reports the SM sub-test node as passed for a clean link and failed for a defective link. - -Covers: -- `scn~map-trace-link-status-to-test-runner-status~1` - -Needs: impl, itest - -### Roll Up Source File Suite Trace Status -`dsn~roll-up-source-file-suite-trace-status~1` - -**Given** the trace test-runner presentation has created a source-file suite -**When** at least one specification-item test or trace-link sub-test below that suite is failed -**Then** it marks the source-file suite as failed before finishing the suite node. - -Covers: -- `scn~roll-up-source-file-suite-trace-status~1` - -Needs: impl, itest - -### Roll Up Top-Level Trace Status -`dsn~roll-up-top-level-trace-status~1` - -**Given** the trace test-runner presentation has created the top-level trace suite -**When** at least one source-file suite contains a failed descendant -**Then** it marks the top-level trace suite as failed before finishing the trace result. - -Covers: -- `scn~roll-up-top-level-trace-status~1` - -Needs: impl, itest - -### Show Specification Item Defect Details in Test Runner UI -`dsn~show-specification-item-defect-details-in-test-runner-ui~1` - -**Given** the trace test-runner presentation creates a failed specification-item test node -**When** it maps the OpenFastTrace item status to the SM test node -**Then** it sets a concise failure message and detail text that identify the item and explain the defective status. - -Covers: -- `scn~show-specification-item-defect-details-in-test-runner-ui~1` - -Needs: impl, itest - -### Show Trace Link Defect Details in Test Runner UI -`dsn~show-trace-link-defect-details-in-test-runner-ui~1` - -**Given** the trace test-runner presentation creates a failed trace-link sub-test node -**When** it maps the OpenFastTrace link status to the SM test node -**Then** it combines node-specific IDs and direction with a static shared detail template keyed by the OpenFastTrace link status. - -Covers: -- `scn~show-trace-link-defect-details-in-test-runner-ui~1` - -Needs: impl, itest - -### Show Trace Link ID Details in Test Runner UI -`dsn~show-trace-link-id-details-in-test-runner-ui~1` - -**Given** the trace test-runner presentation creates a trace-link sub-test node -**When** it maps the OpenFastTrace link status to the SM test node -**Then** it attaches details that identify the owning specification item ID, linked specification item ID, link direction, and trace-link status for passed and failed link nodes. - -Covers: -- `scn~show-trace-link-id-details-in-test-runner-ui~1` - -Needs: impl, itest - -### Navigate from Test Runner Specification Items -`dsn~navigate-from-test-runner-specification-items~1` - -**Given** the trace test-runner presentation creates a specification-item test node -**When** it attaches source navigation to that node -**Then** it resolves the specification item ID through OpenFastTrace trace navigation and opens the corresponding declaration in the editor. - -Covers: -- `scn~navigate-from-test-runner-specification-items~1` - -Needs: impl, itest - -### Navigate from Test Runner Trace Links -`dsn~navigate-from-test-runner-trace-links~1` - -**Given** the trace test-runner presentation creates a trace-link sub-test node -**When** it attaches source navigation to that node -**Then** it resolves the link target through OpenFastTrace trace navigation and opens the corresponding declaration or source-side coverage tag in the editor. - -Covers: -- `scn~navigate-from-test-runner-trace-links~1` - -Needs: impl, itest - -### Navigate from Test Runner Source Files -`dsn~navigate-from-test-runner-source-files~1` - -**Given** the trace test-runner presentation creates a source-file suite node -**When** it attaches source navigation to that node -**Then** it opens the corresponding source file in the editor. - -Covers: -- `scn~navigate-from-test-runner-source-files~1` - -Needs: impl, itest - ### Show Successful Trace Output in IDE Output Window -`dsn~show-successful-trace-output-in-ide-output-window~2` +`dsn~show-successful-trace-output-in-ide-output-window~1` -**Given** a background OpenFastTrace trace run uses the plain text result view and completes successfully for the opened IntelliJ project +**Given** a background OpenFastTrace trace run completes successfully for the opened IntelliJ project **When** the trace-execution service temporarily switches the thread context class loader to the plugin class loader for OFT importer and reporter discovery, restores the previous context loader afterward, and hands the captured text report to trace-output presentation **Then** the plugin opens or updates an IDE output sub-window for that trace run and shows the OpenFastTrace text output under a clear trace-specific content title. Covers: -- `scn~show-successful-trace-output-in-ide-output-window~2` +- `scn~show-successful-trace-output-in-ide-output-window~1` Needs: impl, itest diff --git a/doc/images/intellij-window-with-plugin.png b/doc/images/intellij-window-with-plugin.png new file mode 100644 index 0000000..5087c4a Binary files /dev/null and b/doc/images/intellij-window-with-plugin.png differ diff --git a/doc/marketplace/description.html b/doc/marketplace/description.html new file mode 100644 index 0000000..07f5489 --- /dev/null +++ b/doc/marketplace/description.html @@ -0,0 +1,13 @@ +

Author requirements directly in your project and trace them all the way down to your implementation and tests without leaving your IDE.

+ +

OpenFastTrace adds IDE support for developers who maintain OpenFastTrace specifications next to source code. It also supports requirement engineers, quality engineers, technical writers, and other OpenFastTrace users working in the same project.

+ +
    +
  • Author OpenFastTrace requirement, scenario, design, implementation, and test items with syntax highlighting and live templates.
  • +
  • Navigate between specification item declarations, Covers: references, and source-side coverage tags.
  • +
  • Complete existing specification item IDs while editing requirement chains.
  • +
  • Run OpenFastTrace checks for the whole project or for configured run configurations.
  • +
  • Inspect trace results in IntelliJ's Test Runner UI and navigate to the source of broken requirement chains.
  • +
+ +

The plugin uses the project files already opened in the IDE and does not require a separate OpenFastTrace command-line installation for normal tracing workflows.

diff --git a/doc/marketplace/release-checklist.md b/doc/marketplace/release-checklist.md new file mode 100644 index 0000000..bce5889 --- /dev/null +++ b/doc/marketplace/release-checklist.md @@ -0,0 +1,12 @@ +# JetBrains Marketplace Release Checklist + +The packaged plugin descriptor owns metadata that JetBrains can read from `META-INF/plugin.xml`, including the plugin name, version, vendor, compatibility baseline, website URL, description, and change notes. + +When publishing or updating the JetBrains Marketplace listing, keep these Marketplace admin fields aligned with the repository: + +* Website URL: `https://github.com/itsallcode/openfasttrace-intellij-plugin` +* Source code URL: `https://github.com/itsallcode/openfasttrace-intellij-plugin` +* Issue tracker URL: `https://github.com/itsallcode/openfasttrace-intellij-plugin/issues` +* Vendor: `itsallcode.org` + +The Marketplace description is maintained in [description.html](description.html). The Marketplace change notes are rendered from the active Markdown release notes under [../changes](../changes/changelog.md) with Pandoc during the Gradle descriptor patching step. Keep Pandoc available on `PATH` in local release environments and CI packaging jobs, or set `PANDOC` to the executable path. diff --git a/doc/system_requirements.md b/doc/system_requirements.md index bac51ca..b1bb230 100644 --- a/doc/system_requirements.md +++ b/doc/system_requirements.md @@ -20,10 +20,6 @@ Informative text explains background, scope, and intent. Specification items def An integrated development environment based on the IntelliJ Platform. In this document, this includes IntelliJ IDEA and other compatible JetBrains IDEs that can host the plugin. -### IntelliJ Test Runner UI - -The IntelliJ Platform view that shows hierarchical test execution results with suites, test entries, pass/fail status, progress, and source navigation. - ### MVP Minimum Viable Product. The smallest useful product scope that delivers the essential user value intended for the first release. @@ -95,38 +91,10 @@ The plugin lets users open the OpenFastTrace user guide from within the IDE. Use Needs: req -### OFT Live Templates -`feat~oft-live-templates~1` - -The plugin bundles OpenFastTrace live templates for common specification items. Users can insert OFT item skeletons directly in the IDE instead of copying and adapting existing text manually. - -Needs: req - -### OFT Reference Completion -`feat~oft-reference-completion~1` - -The plugin suggests existing OpenFastTrace specification item IDs while users fill `Covers:` entries in supported specification documents and coverage-tag targets in supported source comments. Users can complete references from indexed declarations instead of memorizing or manually searching for IDs. - -Needs: req - ### Run OFT Trace -`feat~run-oft-trace~2` +`feat~run-oft-trace~1` -The plugin lets users run an OpenFastTrace trace for the currently opened IntelliJ project and inspect the result inside the IDE using the IntelliJ Test Runner UI as the default view for the global project trace. - -Needs: req - -### OFT Run Configurations -`feat~oft-run-configurations~1` - -The plugin lets users define multiple OpenFastTrace run configurations with different scan parameters and filters. - -Needs: req - -### OFT Test Runner Trace Results -`feat~oft-test-runner-trace-results~1` - -The plugin lets users inspect OpenFastTrace run-configuration results in IntelliJ's built-in test runner UI. Users can review source-file grouped specification items and trace links with pass/fail status and navigate from the structured result tree to the corresponding source locations. +The plugin lets users run an OpenFastTrace trace for the currently opened IntelliJ project and inspect the textual result inside the IDE. Needs: req @@ -282,54 +250,6 @@ Covers: Needs: scn -### OFT Live Templates - -The following requirements refine the OFT live-template feature into user-visible capabilities. - -### Bundle OFT Live Templates -`req~bundle-oft-live-templates~1` - -The plugin bundles an OpenFastTrace live-template group into the IDE installation. Users can access OFT live templates without importing template files manually. - -Covers: -- `feat~oft-live-templates~1` - -Needs: scn - -### Provide OFT Scenario Live Template -`req~provide-oft-scenario-live-template~1` - -The bundled OpenFastTrace live-template group includes a scenario template for `scn` items. Users can insert a scenario skeleton with placeholders for the scenario title, item name, given-when-then text, and covered requirement. - -Covers: -- `feat~oft-live-templates~1` - -Needs: scn - -### OFT Reference Completion - -The following requirements refine the OFT reference-completion feature into user-visible capabilities. - -### Complete Specification Item IDs in Covers Section -`req~complete-specification-item-ids-in-covers-section~1` - -The plugin suggests existing OpenFastTrace specification item IDs when a user invokes completion while editing an OFT item ID under `Covers:` in a supported specification document. The suggestion list is ranked first by exact prefix match against the full ID, then by prefix match against the item name, then by substring match against the item name, and finally by prefix match against the artifact type. - -Covers: -- `feat~oft-reference-completion~1` - -Needs: scn - -### Complete Specification Item IDs in Coverage Tag Target -`req~complete-specification-item-ids-in-coverage-tag-target~1` - -The plugin suggests existing OpenFastTrace specification item IDs when a user invokes completion while editing the target side of a likely OFT coverage tag in a supported source-code comment. Coverage-tag target completion is available for the default file extensions supported by the upstream OpenFastTrace Tag Importer. The completion context requires a left-hand artifact type and an arrow before the caret so suggestions appear for coverage-tag targets instead of ordinary comment text. The suggestion list is ranked first by exact prefix match against the full ID, then by prefix match against the item name, then by substring match against the item name, and finally by prefix match against the artifact type. - -Covers: -- `feat~oft-reference-completion~1` - -Needs: scn - ### Run OFT Trace The following requirements refine the Run OFT Trace feature into user-visible capabilities. @@ -340,7 +260,7 @@ The following requirements refine the Run OFT Trace feature into user-visible ca The plugin adds an `OpenFastTrace` group with a `Trace Project` action to the global `Tools` menu. Users can find the trace entry in the established IDE location for project-level tooling actions. Covers: -- `feat~run-oft-trace~2` +- `feat~run-oft-trace~1` Needs: scn @@ -354,7 +274,7 @@ Rationale: Users cannot invoke the trace action when the IDE has no project context to trace. Covers: -- `feat~run-oft-trace~2` +- `feat~run-oft-trace~1` Needs: scn @@ -364,7 +284,7 @@ Needs: scn The plugin traces the currently opened IntelliJ project by using the opened project directory as the default OpenFastTrace input root. Users can run a whole-project trace without manually selecting files and directories first. Covers: -- `feat~run-oft-trace~2` +- `feat~run-oft-trace~1` Needs: scn @@ -374,7 +294,7 @@ Needs: scn The plugin integrates OpenFastTrace trace-scope settings into IntelliJ project configuration. Users can configure whether the `Trace Project` action traces the whole opened project or only selected resources and can edit the selected-resource paths directly in the IDE settings workflow. Covers: -- `feat~run-oft-trace~2` +- `feat~run-oft-trace~1` Needs: scn @@ -384,7 +304,7 @@ Needs: scn The plugin can trace selected project resources instead of the whole opened project directory. Users can restrict the OpenFastTrace scan to the resources they intend to include in the trace. Covers: -- `feat~run-oft-trace~2` +- `feat~run-oft-trace~1` Needs: scn @@ -394,7 +314,7 @@ Needs: scn When selected-resource tracing is active, the plugin can include source directories known to IntelliJ in the effective OpenFastTrace input set. Users do not need to discover and configure ordinary source roots by hand. Covers: -- `feat~run-oft-trace~2` +- `feat~run-oft-trace~1` Needs: scn @@ -404,7 +324,7 @@ Needs: scn When selected-resource tracing is active, the plugin can include test directories known to IntelliJ in the effective OpenFastTrace input set. Users do not need to discover and configure ordinary test roots by hand. Covers: -- `feat~run-oft-trace~2` +- `feat~run-oft-trace~1` Needs: scn @@ -414,7 +334,7 @@ Needs: scn When selected-resource tracing is active, the plugin lets users add additional trace inputs through a multi-line text field in the project settings. Each non-empty line specifies one file or directory path relative to the opened project directory that OpenFastTrace should scan. If the user has not changed that setting, the field contains exactly one default entry: `doc/`. Covers: -- `feat~run-oft-trace~2` +- `feat~run-oft-trace~1` Needs: scn @@ -424,29 +344,17 @@ Needs: scn The plugin runs the OpenFastTrace project trace in a background task with IDE progress reporting. Users can start a trace without blocking the editor UI while the trace is running. Covers: -- `feat~run-oft-trace~2` - -Needs: scn - -### Show Trace Project in Test Runner UI by Default -`req~show-trace-project-in-test-runner-ui-by-default~1` - -When a user starts the global `Trace Project` action from the `Tools` menu, the plugin shows the default trace result in IntelliJ's Test Runner UI. Users can inspect the default project trace as structured source-file suites, specification-item tests, and trace-link sub-tests without creating a run configuration first. - -Covers: -- `feat~run-oft-trace~2` -- `feat~oft-test-runner-trace-results~1` +- `feat~run-oft-trace~1` Needs: scn ### Show Trace Output in IDE Output Window -`req~show-trace-output-in-ide-output-window~2` +`req~show-trace-output-in-ide-output-window~1` -The plugin can show the OpenFastTrace text trace output in an IDE output sub-window and keep that output available after the trace finishes. Users who select the plain text result view can inspect the plain text result inside the IDE without looking at log files or an external terminal. +The plugin shows the OpenFastTrace text trace output in an IDE output sub-window and keeps that output available after the trace finishes. Users can inspect the plain text result inside the IDE without looking at log files or an external terminal. Covers: -- `feat~run-oft-trace~2` -- `feat~oft-run-configurations~1` +- `feat~run-oft-trace~1` Needs: scn @@ -456,301 +364,27 @@ Needs: scn The plugin makes OpenFastTrace specification item IDs in the trace output window navigable to their declarations in the opened project. Users can jump from a reported item in the trace output directly to the defining specification item without searching manually. Covers: -- `feat~run-oft-trace~2` - -Needs: scn - -### OpenFastTrace Run Configurations -`req~openfasttrace-run-configurations~1` - -The plugin provides a dedicated run configuration type for OpenFastTrace. Users can create, name, and save multiple trace configurations to switch quickly between different scan scopes and filters. - -Covers: -- `feat~oft-run-configurations~1` - -Needs: scn - -### Filter Trace by Artifact Types -`req~filter-trace-by-artifact-types~1` - -When using an OpenFastTrace run configuration, the plugin lets users filter the trace results by artifact types. Users can specify a comma-separated list of types (e.g., `req, dsn`) to focus the trace on specific document layers. - -Covers: -- `feat~oft-run-configurations~1` - -Needs: scn - -### Filter Trace by Tags -`req~filter-trace-by-tags~1` - -When using an OpenFastTrace run configuration, the plugin lets users filter the trace results by tags. Users can specify a comma-separated list of tags to focus the trace on tagged specification items. - -Covers: -- `feat~oft-run-configurations~1` - -Needs: scn - -### Select Trace Result View in Run Configuration -`req~select-trace-result-view-in-run-configuration~2` - -The OpenFastTrace run configuration includes a result-view option for choosing plain text output or the IntelliJ Test Runner UI. - -Rationale: - -Users get structured trace inspection by default, while users who prefer the existing text report can opt into plain text output per run configuration. - -Covers: -- `feat~oft-run-configurations~1` -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Plugin Distribution - -The following requirements refine how the plugin distribution appears in JetBrains plugin management surfaces. - -### Plugin Logo for JetBrains Plugin Surfaces -`req~plugin-logo-for-jetbrains-plugin-surfaces~1` - -The plugin distribution includes JetBrains-compliant OpenFastTrace plugin logo assets that IntelliJ Platform IDEs and JetBrains Marketplace can display for the plugin. Users can recognize the plugin in plugin-management surfaces. - -Rationale: - -JetBrains plugin surfaces use packaged plugin logo resources to distinguish plugins in lists and detail views. Providing suitable OpenFastTrace logo assets improves recognition without changing plugin behavior. - -Needs: scn - -### Run Configuration Presentation - -The following requirements refine how OpenFastTrace run configurations appear in the IDE run/debug UI. - -### OpenFastTrace Run Configuration Icon -`req~openfasttrace-run-configuration-icon~1` - -The plugin shows an OpenFastTrace-specific icon for the `OpenFastTrace` run configuration type. Users can distinguish OpenFastTrace trace configurations from generic run actions in the run/debug UI. - -Covers: -- `feat~oft-run-configurations~1` - -Needs: scn - -### Show Trace Source Files as Test Runner Suites -`req~show-trace-source-files-as-test-runner-suites~1` - -The IntelliJ Test Runner UI result view shows each traced source file as a test runner suite. For traced source files below the opened project directory, the suite label uses the project-local path, for example `doc/system_requirements.md`. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Show Trace Specification Items as Test Runner Tests -`req~show-trace-specification-items-as-test-runner-tests~1` - -The IntelliJ Test Runner UI result view shows each traced specification item as a test entry below the suite for the source file that contains that item. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Show Specification Item Title in Test Runner UI -`req~show-specification-item-title-in-test-runner-ui~2` - -The IntelliJ Test Runner UI result view uses the item's title as the visible specification-item name when the imported OpenFastTrace item provides a non-blank title. Items without a title show the full specification item ID as the visible name. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Show Specification Item ID in Test Runner Details -`req~show-specification-item-id-in-test-runner-details~1` - -The IntelliJ Test Runner UI result view shows the full specification item ID and trace status in the details for specification-item entries, even when the entry is passed. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Sort Specification Items in Test Runner UI -`req~sort-specification-items-in-test-runner-ui~1` - -Within each source-file suite, the IntelliJ Test Runner UI result view sorts specification item entries by artifact type, then by the name part of the specification item ID, then by revision number. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Show Trace Links as Test Runner Sub-Tests -`req~show-trace-links-as-test-runner-sub-tests~1` - -The IntelliJ Test Runner UI result view shows each incoming or outgoing trace link as a sub-test below the connected specification item for which the link is shown. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Show Specification Item Status in Test Runner UI -`req~show-specification-item-status-in-test-runner-ui~2` - -The IntelliJ Test Runner UI result view includes a specification item's trace status in brackets in the item entry only when the item status is not clean, for example `(uncovered)` or `(defective)`. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Show Trace Link Status in Test Runner UI -`req~show-trace-link-status-in-test-runner-ui~2` - -The IntelliJ Test Runner UI result view includes a trace link's status in brackets in the link entry only when the link status is not clean, for example `(orphaned)`. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Show Trace Link Direction in Test Runner UI -`req~show-trace-link-direction-in-test-runner-ui~1` - -The IntelliJ Test Runner UI result view marks each trace link entry as incoming or outgoing. Users can distinguish links that cover the specification item from links that the specification item covers. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Show Unicode Trace Link Direction in Test Runner UI -`req~show-unicode-trace-link-direction-in-test-runner-ui~1` - -The IntelliJ Test Runner UI result view uses Unicode arrows in trace-link entries. Incoming links use `←`, outgoing links use `→`, and links without a single incoming or outgoing direction use `↔`. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Map Specification Item Trace Status to Test Runner Status -`req~map-specification-item-trace-status-to-test-runner-status~1` - -The IntelliJ Test Runner UI result view treats a clean specification item as a passed test and a defective specification item as a failed test. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Map Trace Link Status to Test Runner Status -`req~map-trace-link-status-to-test-runner-status~1` - -The IntelliJ Test Runner UI result view treats a clean incoming or outgoing trace link as a passed sub-test and a defective incoming or outgoing trace link as a failed sub-test. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Roll Up Source File Suite Trace Status -`req~roll-up-source-file-suite-trace-status~1` - -The IntelliJ Test Runner UI result view marks a source-file suite as failed if any specification item or trace-link sub-test below that source file is failed. It marks the source-file suite as passed only if all descendants are clean. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Roll Up Top-Level Trace Status -`req~roll-up-top-level-trace-status~1` - -The IntelliJ Test Runner UI result view marks the top-level trace suite as failed if any source-file suite contains a failed descendant. It marks the top-level trace suite as passed only if all source-file suites are clean. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Show Specification Item Defect Details in Test Runner UI -`req~show-specification-item-defect-details-in-test-runner-ui~1` - -The IntelliJ Test Runner UI result view shows a clear details text for failed specification-item entries. The details identify the specification item, its trace status, and the reason the item is defective. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Show Trace Link Defect Details in Test Runner UI -`req~show-trace-link-defect-details-in-test-runner-ui~1` - -The IntelliJ Test Runner UI result view shows a clear details text for failed trace-link entries. The details identify the owning specification item, the linked item, the link direction, the trace-link status, and the reason that status is defective. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Show Trace Link ID Details in Test Runner UI -`req~show-trace-link-id-details-in-test-runner-ui~1` - -The IntelliJ Test Runner UI result view shows the owning specification item ID, linked specification item ID, link direction, and trace-link status in the details for trace-link entries, even when the entry is passed. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Navigate from Test Runner Specification Items -`req~navigate-from-test-runner-specification-items~1` - -Users can navigate from a specification item entry in the IntelliJ Test Runner UI result view to the corresponding specification item declaration in the opened project. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Navigate from Test Runner Trace Links -`req~navigate-from-test-runner-trace-links~1` - -Users can navigate from a trace-link entry in the IntelliJ Test Runner UI result view to the corresponding specification item declaration or source-side coverage tag in the opened project. - -Covers: -- `feat~oft-test-runner-trace-results~1` - -Needs: scn - -### Navigate from Test Runner Source Files -`req~navigate-from-test-runner-source-files~1` - -Users can navigate from a source-file suite entry in the IntelliJ Test Runner UI result view to the corresponding file in the opened project. - -Covers: -- `feat~oft-test-runner-trace-results~1` +- `feat~run-oft-trace~1` Needs: scn ### Show Scanned Base Directory in Trace Output Window `req~show-scanned-base-directory-in-trace-output-window~1` -When a plain text project trace starts, the plugin writes the resolved project base directory that OpenFastTrace scans into the IDE output sub-window. Users can confirm the actual trace input root directly from the trace output. +When a project trace starts, the plugin writes the resolved project base directory that OpenFastTrace scans into the IDE output sub-window. Users can confirm the actual trace input root directly from the trace output. Covers: -- `feat~run-oft-trace~2` +- `feat~run-oft-trace~1` Needs: scn ### Show Resolved Trace Inputs in Trace Output Window `req~show-resolved-trace-inputs-in-trace-output-window~1` -When a plain text project trace starts with selected-resource tracing, the plugin writes the resolved files and directories that it passes to OpenFastTrace into the IDE output sub-window. Users can confirm the actual configured trace scope directly from the trace output. +When a project trace starts with selected-resource tracing, the plugin writes the resolved files and directories that it passes to OpenFastTrace into the IDE output sub-window. Users can confirm the actual configured trace scope directly from the trace output. Covers: -- `feat~run-oft-trace~2` +- `feat~run-oft-trace~1` Needs: scn @@ -760,17 +394,17 @@ Needs: scn The plugin reports when the opened project does not resolve to a valid local path before starting the trace. Users can see why the trace cannot start without inspecting plugin logs. Covers: -- `feat~run-oft-trace~2` +- `feat~run-oft-trace~1` Needs: scn ### Show Failing Trace Result in IDE Output Window `req~show-failing-trace-result-in-ide-output-window~1` -When plain text output is selected, the plugin shows failing OpenFastTrace executions through the same IDE output flow as successful traces, including the text output and the failing result. Users can inspect trace failures without switching to plugin logs or an external terminal. +The plugin shows failing OpenFastTrace executions through the same IDE output flow as successful traces, including the text output and the failing result. Users can inspect trace failures without switching to plugin logs or an external terminal. Covers: -- `feat~run-oft-trace~2` +- `feat~run-oft-trace~1` Needs: scn @@ -780,7 +414,7 @@ Needs: scn The plugin preserves the OpenFastTrace plain-text defect summary for an unclean trace chain in the IDE output window, including the reported total-item count and defect count. Users can verify how many trace issues OpenFastTrace found directly from the text result. Covers: -- `feat~run-oft-trace~2` +- `feat~run-oft-trace~1` Needs: scn @@ -1011,82 +645,6 @@ Covers: Needs: dsn -### OFT Reference Completion - -The following scenarios describe completion support while editing OFT references in `Covers:` sections and coverage-tag targets. - -### Complete Specification Item ID in Covers Section -`scn~complete-specification-item-id-in-covers-section~1` - -**Given** a project contains declared OpenFastTrace specification items and a user edits a `Covers:` entry in a supported specification document -**When** the user types a partial specification item ID and invokes completion -**Then** the IDE suggests existing declared specification item IDs from the project index and orders the suggestion list by full-ID prefix match, then name-prefix match, then name-substring match, and finally artifact-type prefix match - -Covers: -- `req~complete-specification-item-ids-in-covers-section~1` - -Needs: dsn - -### Complete Specification Item ID in Coverage Tag Target -`scn~complete-specification-item-id-in-coverage-tag-target~1` - -**Given** a project contains declared OpenFastTrace specification items and a user edits the target side of an OFT coverage tag candidate such as `[impl->dsn~openfasttrace]` in a comment of a file whose extension is supported by the upstream OpenFastTrace Tag Importer -**When** the user invokes completion -**Then** the IDE suggests existing declared specification item IDs from the project index and orders the suggestion list by full-ID prefix match, then name-prefix match, then name-substring match, and finally artifact-type prefix match - -Covers: -- `req~complete-specification-item-ids-in-coverage-tag-target~1` - -Needs: dsn - -### Complete Specification Item ID in Spaced Coverage Tag Target -`scn~complete-specification-item-id-in-spaced-coverage-tag-target~1` - -**Given** a project contains declared OpenFastTrace specification items and a user edits the target side of an OFT coverage tag candidate with optional spaces around the arrow such as `[impl -> dsn~openfasttrace]` in a comment of a file whose extension is supported by the upstream OpenFastTrace Tag Importer -**When** the user invokes completion -**Then** the IDE suggests existing declared specification item IDs from the project index as it does for the compact arrow spelling - -Covers: -- `req~complete-specification-item-ids-in-coverage-tag-target~1` - -Needs: dsn - -### Complete Specification Item ID in Incomplete Coverage Tag Target -`scn~complete-specification-item-id-in-incomplete-coverage-tag-target~1` - -**Given** a project contains declared OpenFastTrace specification items and a user edits the target side of an incomplete OFT coverage tag candidate such as `[impl->dsn~openfasttrace` in a comment of a file whose extension is supported by the upstream OpenFastTrace Tag Importer -**When** the user invokes completion before typing the closing bracket -**Then** the IDE suggests existing declared specification item IDs from the project index while leaving the incomplete tag editable - -Covers: -- `req~complete-specification-item-ids-in-coverage-tag-target~1` - -Needs: dsn - -### Suppress Coverage Tag Target Completion Outside Target Context -`scn~suppress-coverage-tag-target-completion-outside-target-context~1` - -**Given** a user invokes completion before the coverage-tag arrow, after an already closed coverage tag, outside a comment, inside a string literal, or in an unsupported file type -**When** the completion context is evaluated -**Then** the plugin does not add OpenFastTrace specification item ID suggestions for coverage-tag target completion - -Covers: -- `req~complete-specification-item-ids-in-coverage-tag-target~1` - -Needs: dsn - -### Complete Specification Item ID in Active Live Template Covers Field -`scn~complete-specification-item-id-in-active-live-template-covers-field~1` - -**Given** a project contains declared OpenFastTrace specification items and a user expands a bundled OFT live template whose active `COVERED` placeholder is under `Covers:` -**When** the user types a partial specification item ID in that placeholder and invokes completion before leaving live-template mode -**Then** the IDE suggests existing declared specification item IDs from the project index in the standard completion popup. - -Covers: -- `req~complete-specification-item-ids-in-covers-section~1` - -Needs: dsn - ### Open OFT User Guide The following scenarios describe the happy path for opening the OpenFastTrace user guide from the IDE. @@ -1115,34 +673,6 @@ Covers: Needs: dsn -### OFT Live Templates - -The following scenarios describe how bundled OFT live templates become available and how the scenario template is inserted. - -### Show OFT Live Templates in Live Template Settings -`scn~show-oft-live-templates-in-live-template-settings~1` - -**Given** the OpenFastTrace plugin is installed in the IDE -**When** a user opens the IDE live-template settings -**Then** the settings contain an `OpenFastTrace` live-template group with the bundled OFT templates - -Covers: -- `req~bundle-oft-live-templates~1` - -Needs: dsn - -### Insert OFT Scenario Live Template -`scn~insert-oft-scenario-live-template~1` - -**Given** the OpenFastTrace plugin is installed and a user edits an OFT specification document in a live-template context -**When** the user expands the `scn` live template -**Then** the IDE inserts an OFT scenario skeleton with placeholders for the title, item name, `Given`, `When`, `Then`, and covered requirement - -Covers: -- `req~provide-oft-scenario-live-template~1` - -Needs: dsn - ### Run OFT Trace The following scenarios describe the trace action flow for the opened IntelliJ project. @@ -1184,18 +714,6 @@ Covers: Needs: dsn -### Show Trace Project in Test Runner UI by Default -`scn~show-trace-project-in-test-runner-ui-by-default~1` - -**Given** an IntelliJ project is open, its configured trace inputs are valid, and the OFT trace completes -**When** a user invokes `Tools | OpenFastTrace | Trace Project` -**Then** the IDE shows the trace result in the IntelliJ Test Runner UI - -Covers: -- `req~show-trace-project-in-test-runner-ui-by-default~1` - -Needs: dsn - ### Configure Trace Scope in Project Settings `scn~configure-trace-scope-in-project-settings~1` @@ -1282,14 +800,14 @@ Covers: Needs: dsn ### Show Successful Trace Output in IDE Output Window -`scn~show-successful-trace-output-in-ide-output-window~2` +`scn~show-successful-trace-output-in-ide-output-window~1` -**Given** an IntelliJ project is open, its configured trace inputs are valid, an `OpenFastTrace` run configuration uses the plain text result view, and the OFT trace completes successfully -**When** a user runs that configuration +**Given** an IntelliJ project is open, its project directory is a valid OFT trace input, and the OFT trace completes successfully +**When** a user invokes `Tools | OpenFastTrace | Trace Project` **Then** the IDE shows the resulting OpenFastTrace text report in an output sub-window that remains available after the trace completes Covers: -- `req~show-trace-output-in-ide-output-window~2` +- `req~show-trace-output-in-ide-output-window~1` Needs: dsn @@ -1309,7 +827,7 @@ Needs: dsn `scn~show-scanned-base-directory-in-trace-output-window~1` **Given** an IntelliJ project is open and its project directory resolves to a valid local OFT trace input -**When** a user runs a trace using the plain text result view +**When** a user invokes `Tools | OpenFastTrace | Trace Project` **Then** the IDE output for that trace starts with the resolved base directory that the plugin passes to OpenFastTrace Covers: @@ -1321,7 +839,7 @@ Needs: dsn `scn~show-resolved-trace-inputs-in-trace-output-window~1` **Given** an IntelliJ project is open, selected-resource tracing is active, and the plugin resolved the configured trace files and directories successfully -**When** a user runs a trace using the plain text result view +**When** a user invokes `Tools | OpenFastTrace | Trace Project` **Then** the IDE output for that trace lists the resolved files and directories that the plugin passes to OpenFastTrace Covers: @@ -1332,8 +850,8 @@ Needs: dsn ### Show Failing Trace Output in IDE Output Window `scn~show-failing-trace-output-in-ide-output-window~1` -**Given** an IntelliJ project is open, its configured trace inputs are valid, a trace uses the plain text result view, and the OFT trace reports a failure -**When** the trace completes +**Given** an IntelliJ project is open, its project directory is a valid OFT trace input, and the OFT trace reports a failure +**When** a user invokes `Tools | OpenFastTrace | Trace Project` **Then** the IDE shows the OpenFastTrace text output together with the failing result through the same trace output flow Covers: @@ -1344,348 +862,11 @@ Needs: dsn ### Show Defect Count for Unclean Trace Chain in IDE Output Window `scn~show-defect-count-for-unclean-trace-chain-in-output-window~1` -**Given** an IntelliJ project is open, its configured trace inputs are valid, a trace uses the plain text result view, and the OFT trace finds an unclean feature-to-requirement-to-design chain with missing implementation coverage -**When** the trace completes +**Given** an IntelliJ project is open, its project directory is a valid OFT trace input, and the OFT trace finds an unclean feature-to-requirement-to-design chain with missing implementation coverage +**When** a user invokes `Tools | OpenFastTrace | Trace Project` **Then** the IDE output contains the OpenFastTrace plain-text summary line with the reported total-item count and defect count for that unclean chain Covers: - `req~show-defect-count-for-unclean-trace-chain-in-output-window~1` Needs: dsn - -### Create and Run OpenFastTrace Run Configuration -`scn~create-and-run-openfasttrace-run-configuration~1` - -**Given** an IntelliJ project is open -**When** a user creates a new `OpenFastTrace` run configuration, names it "Trace Design", selects "Selected resources", enables "Source roots", and enters "doc/spec/" as an additional path -**Then** the IDE saves the configuration and, when the user runs it, the plugin executes the trace with the configured inputs and shows the result in the output window. - -Covers: -- `req~openfasttrace-run-configurations~1` - -Needs: dsn - -### Filter Run Configuration by Artifact Types -`scn~filter-run-configuration-by-artifact-types~1` - -**Given** an IntelliJ project is open and an `OpenFastTrace` run configuration is configured with artifact type filters "dsn, constr" -**When** a user runs that configuration -**Then** the plugin passes the artifact type filters to OpenFastTrace and the resulting trace output contains only the filtered item types. - -Covers: -- `req~filter-trace-by-artifact-types~1` - -Needs: dsn - -### Filter Run Configuration by Tags -`scn~filter-run-configuration-by-tags~1` - -**Given** an IntelliJ project is open and an `OpenFastTrace` run configuration is configured with tag filters "mvp" -**When** a user runs that configuration -**Then** the plugin passes the tag filter to OpenFastTrace and the resulting trace output contains only items matching that tag. - -Covers: -- `req~filter-trace-by-tags~1` - -Needs: dsn - -### Test Runner as Default Run Configuration Result View -`scn~test-runner-as-default-run-configuration-result-view~1` - -**Given** an IntelliJ project is open and an `OpenFastTrace` run configuration has no explicit result-view selection -**When** a user runs that configuration -**Then** the plugin shows the trace result in the IntelliJ Test Runner UI - -Covers: -- `req~select-trace-result-view-in-run-configuration~2` - -Needs: dsn - -### Select Plain Text Trace Result View -`scn~select-plain-text-trace-result-view~1` - -**Given** an IntelliJ project is open and a user edits an `OpenFastTrace` run configuration -**When** the user selects plain text output as the result view and runs the configuration -**Then** the plugin saves that selection and shows the trace result in the existing plain text output view - -Covers: -- `req~select-trace-result-view-in-run-configuration~2` -- `req~show-trace-output-in-ide-output-window~2` - -Needs: dsn - -### Select Test Runner Trace Result View -`scn~select-test-runner-trace-result-view~1` - -**Given** an IntelliJ project is open and a user edits an `OpenFastTrace` run configuration -**When** the user selects the IntelliJ Test Runner UI as the result view and runs the configuration -**Then** the plugin saves that selection and shows the trace result in the IntelliJ Test Runner UI - -Covers: -- `req~select-trace-result-view-in-run-configuration~2` - -Needs: dsn - -### Show Plugin Logo in JetBrains Plugin Surfaces -`scn~show-plugin-logo-in-jetbrains-plugin-surfaces~1` - -**Given** a user installs the OpenFastTrace plugin distribution in an IntelliJ Platform IDE -**When** the IDE displays the plugin in `Settings | Plugins`, or JetBrains Marketplace displays the plugin after publication -**Then** the surface can load an OpenFastTrace plugin logo from packaged `META-INF` SVG assets that are designed for JetBrains plugin-logo sizes and light/dark backgrounds - -Covers: -- `req~plugin-logo-for-jetbrains-plugin-surfaces~1` - -Needs: dsn - -### Show OpenFastTrace Icon for Run Configurations -`scn~show-openfasttrace-icon-for-run-configurations~1` - -**Given** an IntelliJ project is open -**When** a user creates or views an `OpenFastTrace` run configuration in the run/debug UI -**Then** the IDE shows an OpenFastTrace-specific icon for that configuration type instead of the generic execute icon - -Covers: -- `req~openfasttrace-run-configuration-icon~1` - -Needs: dsn - -### Show Trace Source Files as Test Runner Suites -`scn~show-trace-source-files-as-test-runner-suites~1` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and the trace result contains specification items from supported source files -**When** the trace completes -**Then** the test runner tree contains one suite per source file and labels source files below the opened project directory with project-local paths - -Covers: -- `req~show-trace-source-files-as-test-runner-suites~1` - -Needs: dsn - -### Show Trace Specification Items as Test Runner Tests -`scn~show-trace-specification-items-as-test-runner-tests~1` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and the trace result contains specification items from supported source files -**When** the trace completes -**Then** the test runner tree contains one test entry per specification item below the suite for the source file that contains that item - -Covers: -- `req~show-trace-specification-items-as-test-runner-tests~1` - -Needs: dsn - -### Show Specification Item Title in Test Runner UI -`scn~show-specification-item-title-in-test-runner-ui~2` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and the trace result contains a specification item with a title -**When** the trace completes -**Then** the test runner tree shows the title as the visible specification-item name and omits the full specification item ID from the tree label - -Covers: -- `req~show-specification-item-title-in-test-runner-ui~2` - -Needs: dsn - -### Show Specification Item ID in Test Runner Details -`scn~show-specification-item-id-in-test-runner-details~1` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and the trace result contains a specification item -**When** the user selects that specification item in the test runner tree -**Then** the detail view shows the full specification item ID and trace status - -Covers: -- `req~show-specification-item-id-in-test-runner-details~1` - -Needs: dsn - -### Sort Specification Items in Test Runner UI -`scn~sort-specification-items-in-test-runner-ui~1` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and one source-file suite contains multiple specification items -**When** the trace completes -**Then** the specification item entries in that source-file suite are ordered by artifact type, ID name part, and revision number - -Covers: -- `req~sort-specification-items-in-test-runner-ui~1` - -Needs: dsn - -### Show Trace Links as Test Runner Sub-Tests -`scn~show-trace-links-as-test-runner-sub-tests~1` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and a traced specification item has incoming or outgoing trace links -**When** the trace completes -**Then** the test runner tree shows each incoming or outgoing trace link as a sub-test below that specification item - -Covers: -- `req~show-trace-links-as-test-runner-sub-tests~1` - -Needs: dsn - -### Show Specification Item Status in Test Runner UI -`scn~show-specification-item-status-in-test-runner-ui~2` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and the trace result contains a specification item -**When** the trace completes -**Then** the specification item entry shows the item's trace status in brackets only when the item status is not clean - -Covers: -- `req~show-specification-item-status-in-test-runner-ui~2` - -Needs: dsn - -### Show Trace Link Status in Test Runner UI -`scn~show-trace-link-status-in-test-runner-ui~2` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and the trace result contains an incoming or outgoing trace link -**When** the trace completes -**Then** the trace-link sub-test shows the link status in brackets only when the link status is not clean - -Covers: -- `req~show-trace-link-status-in-test-runner-ui~2` - -Needs: dsn - -### Show Trace Link Direction in Test Runner UI -`scn~show-trace-link-direction-in-test-runner-ui~1` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and the trace result contains specification items with incoming or outgoing trace links -**When** the trace completes -**Then** each trace-link sub-test shows whether the link is incoming or outgoing - -Covers: -- `req~show-trace-link-direction-in-test-runner-ui~1` - -Needs: dsn - -### Show Unicode Trace Link Direction in Test Runner UI -`scn~show-unicode-trace-link-direction-in-test-runner-ui~1` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and the trace result contains incoming and outgoing trace links -**When** the trace completes -**Then** the trace-link sub-tests use Unicode arrows for the visible link direction - -Covers: -- `req~show-unicode-trace-link-direction-in-test-runner-ui~1` - -Needs: dsn - -### Map Specification Item Trace Status to Test Runner Status -`scn~map-specification-item-trace-status-to-test-runner-status~1` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and the trace result contains clean and defective specification items -**When** the trace completes -**Then** clean specification items are shown as passed tests and defective specification items are shown as failed tests - -Covers: -- `req~map-specification-item-trace-status-to-test-runner-status~1` - -Needs: dsn - -### Map Trace Link Status to Test Runner Status -`scn~map-trace-link-status-to-test-runner-status~1` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and the trace result contains clean and defective incoming or outgoing trace links -**When** the trace completes -**Then** clean trace links are shown as passed sub-tests and defective trace links are shown as failed sub-tests - -Covers: -- `req~map-trace-link-status-to-test-runner-status~1` - -Needs: dsn - -### Roll Up Source File Suite Trace Status -`scn~roll-up-source-file-suite-trace-status~1` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and a source-file suite contains at least one failed specification item or trace-link sub-test -**When** the trace completes -**Then** the source-file suite is shown as failed - -Covers: -- `req~roll-up-source-file-suite-trace-status~1` - -Needs: dsn - -### Roll Up Top-Level Trace Status -`scn~roll-up-top-level-trace-status~1` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and at least one source-file suite contains a failed descendant -**When** the trace completes -**Then** the top-level trace suite is shown as failed - -Covers: -- `req~roll-up-top-level-trace-status~1` - -Needs: dsn - -### Show Specification Item Defect Details in Test Runner UI -`scn~show-specification-item-defect-details-in-test-runner-ui~1` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and the trace result contains a defective specification item -**When** the user selects that specification item in the test runner tree -**Then** the detail view explains why the specification item is defective - -Covers: -- `req~show-specification-item-defect-details-in-test-runner-ui~1` - -Needs: dsn - -### Show Trace Link Defect Details in Test Runner UI -`scn~show-trace-link-defect-details-in-test-runner-ui~1` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and the trace result contains a defective trace link -**When** the user selects that trace link in the test runner tree -**Then** the detail view explains why the trace link is defective - -Covers: -- `req~show-trace-link-defect-details-in-test-runner-ui~1` - -Needs: dsn - -### Show Trace Link ID Details in Test Runner UI -`scn~show-trace-link-id-details-in-test-runner-ui~1` - -**Given** an `OpenFastTrace` run configuration uses the IntelliJ Test Runner UI result view and the trace result contains a trace link -**When** the user selects that trace link in the test runner tree -**Then** the detail view shows the owning specification item ID, linked specification item ID, link direction, and trace-link status - -Covers: -- `req~show-trace-link-id-details-in-test-runner-ui~1` - -Needs: dsn - -### Navigate from Test Runner Specification Items -`scn~navigate-from-test-runner-specification-items~1` - -**Given** an IntelliJ project is open and the test runner tree shows an OpenFastTrace specification item entry from that project -**When** a user activates source navigation for that specification item entry -**Then** the IDE opens the corresponding specification item declaration in the editor - -Covers: -- `req~navigate-from-test-runner-specification-items~1` - -Needs: dsn - -### Navigate from Test Runner Trace Links -`scn~navigate-from-test-runner-trace-links~1` - -**Given** an IntelliJ project is open and the test runner tree shows an OpenFastTrace trace-link entry from that project -**When** a user activates source navigation for that trace-link entry -**Then** the IDE opens the corresponding specification item declaration or source-side coverage tag in the editor - -Covers: -- `req~navigate-from-test-runner-trace-links~1` - -Needs: dsn - -### Navigate from Test Runner Source Files -`scn~navigate-from-test-runner-source-files~1` - -**Given** an IntelliJ project is open and the test runner tree shows an OpenFastTrace source-file suite entry from that project -**When** a user activates source navigation for that source-file suite entry -**Then** the IDE opens the corresponding source file in the editor - -Covers: -- `req~navigate-from-test-runner-source-files~1` - -Needs: dsn diff --git a/gradle.properties b/gradle.properties index c9bd9c6..e83966e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,22 @@ -version=0.8.0 +group=org.itsallcode.openfasttrace +version=0.2.1 + +platformType=IC +platformVersion=2026.1 +platformSinceBuild=261 +openfasttraceVersion=4.2.1 +junitBomVersion=5.13.1 +junit4Version=4.13.2 +hamcrestVersion=3.0 +opentest4jVersion=1.3.0 + +pluginId=org.itsallcode.openfasttrace-intellij-plugin +pluginName=OpenFastTrace +pluginVendor=Itsallcode.org +pluginVendorEmail=opensource@itsallcode.org +pluginVendorUrl=https://itsallcode.org/ + +userGuideUrl=https://github.com/itsallcode/openfasttrace/blob/main/doc/user_guide.md org.gradle.configuration-cache=true org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 diff --git a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftAdditionalTracePathValidation.java b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftAdditionalTracePathValidation.java index 6943ded..ac35c12 100644 --- a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftAdditionalTracePathValidation.java +++ b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftAdditionalTracePathValidation.java @@ -5,10 +5,8 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; -import java.util.regex.Pattern; final class OftAdditionalTracePathValidation { - private static final Pattern TRACE_INPUT_LIST_DIVIDER = Pattern.compile("\\R"); private final String resolvedRelativeToText; private final List messages; @@ -19,7 +17,7 @@ private OftAdditionalTracePathValidation(final String resolvedRelativeToText, fi static OftAdditionalTracePathValidation validate(final Path projectRoot, final String additionalPathsText) { final List messages = new ArrayList<>(); - final String[] lines = TRACE_INPUT_LIST_DIVIDER.split(additionalPathsText, -1); + final String[] lines = additionalPathsText.split("\\R", -1); for (int lineIndex = 0; lineIndex < lines.length; lineIndex++) { final String line = lines[lineIndex].trim(); if (line.isEmpty()) { @@ -33,6 +31,10 @@ static OftAdditionalTracePathValidation validate(final Path projectRoot, final S ); } + static OftAdditionalTracePathValidation unavailable() { + return new OftAdditionalTracePathValidation("", List.of()); + } + String resolvedRelativeToText() { return resolvedRelativeToText; } @@ -41,7 +43,6 @@ List messages() { return messages; } - @SuppressWarnings("java:S1166") private static void validateLine( final Path projectRoot, final String line, @@ -52,26 +53,20 @@ private static void validateLine( try { relativePath = Path.of(line); } catch (final InvalidPathException exception) { - // The UI intentionally shows only the validation result, not parser internals from InvalidPathException. - addValidationErrorMessage(messages, line, lineNumber, "' is not a valid path"); + messages.add("Line " + lineNumber + ": '" + line + "' is not a valid path"); return; } if (relativePath.isAbsolute()) { - addValidationErrorMessage(messages, line, lineNumber, "' must be project-relative"); + messages.add("Line " + lineNumber + ": '" + line + "' must be project-relative"); return; } final Path resolvedPath = projectRoot.resolve(relativePath).normalize(); if (!Files.exists(resolvedPath)) { - addValidationErrorMessage(messages, line, lineNumber, "' not found"); + messages.add("Line " + lineNumber + ": '" + line + "' not found"); return; } if (!Files.isRegularFile(resolvedPath) && !Files.isDirectory(resolvedPath)) { - addValidationErrorMessage(messages, line, lineNumber, "' is neither a file nor a directory"); + messages.add("Line " + lineNumber + ": '" + line + "' is neither a file nor a directory"); } } - - private static void addValidationErrorMessage(final List messages, final String line, final int lineNumber, - final String validationError) { - messages.add("Line " + lineNumber + ": '" + line + validationError); - } } diff --git a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceBackgroundRunner.java b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceBackgroundRunner.java index cfca8f8..a338223 100644 --- a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceBackgroundRunner.java +++ b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceBackgroundRunner.java @@ -1,6 +1,5 @@ package org.itsallcode.openfasttrace.intellijplugin.trace; -import com.intellij.execution.process.ProcessHandler; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; @@ -10,20 +9,13 @@ import java.io.PrintWriter; import java.io.StringWriter; - -public final class OftTraceBackgroundRunner implements OftTraceRunner { +final class OftTraceBackgroundRunner implements OftTraceRunner { private final OftTraceService traceService; private final OftTraceOutputPresenter outputPresenter; - private final ProcessHandler processHandler; - public OftTraceBackgroundRunner( - final OftTraceService traceService, - final OftTraceOutputPresenter outputPresenter, - final ProcessHandler processHandler - ) { + OftTraceBackgroundRunner(final OftTraceService traceService, final OftTraceOutputPresenter outputPresenter) { this.traceService = traceService; this.outputPresenter = outputPresenter; - this.processHandler = processHandler; } // [impl->dsn~run-trace-project-in-background~1] @@ -61,33 +53,21 @@ public void run(final ProgressIndicator indicator) { @Override public void onSuccess() { - try { - outputPresenter.show(getProject(), contentTitle, result); - } finally { - processHandler.detachProcess(); - } + outputPresenter.show(getProject(), contentTitle, result); } @Override public void onCancel() { - try { - outputPresenter.show(getProject(), contentTitle, OftTraceResult.cancelled()); - } finally { - processHandler.detachProcess(); - } + outputPresenter.show(getProject(), contentTitle, OftTraceResult.cancelled()); } @Override public void onThrowable(final @NonNull Throwable error) { - try { - if (error instanceof ProcessCanceledException) { - outputPresenter.show(getProject(), contentTitle, OftTraceResult.cancelled()); - return; - } - outputPresenter.show(getProject(), contentTitle, OftTraceResult.error(formatThrowable(error))); - } finally { - processHandler.detachProcess(); + if (error instanceof ProcessCanceledException) { + outputPresenter.show(getProject(), contentTitle, OftTraceResult.cancelled()); + return; } + outputPresenter.show(getProject(), contentTitle, OftTraceResult.error(formatThrowable(error))); } } diff --git a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolution.java b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolution.java index cae87cb..3f0af33 100644 --- a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolution.java +++ b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolution.java @@ -1,6 +1,6 @@ package org.itsallcode.openfasttrace.intellijplugin.trace; -public final class OftTraceInputResolution { +final class OftTraceInputResolution { private final OftTraceInputs inputs; private final String errorMessage; @@ -9,23 +9,23 @@ private OftTraceInputResolution(final OftTraceInputs inputs, final String errorM this.errorMessage = errorMessage; } - public static OftTraceInputResolution valid(final OftTraceInputs inputs) { + static OftTraceInputResolution valid(final OftTraceInputs inputs) { return new OftTraceInputResolution(inputs, null); } - public static OftTraceInputResolution invalid(final String errorMessage) { + static OftTraceInputResolution invalid(final String errorMessage) { return new OftTraceInputResolution(null, errorMessage); } - public boolean isValid() { + boolean isValid() { return inputs != null; } - public OftTraceInputs inputs() { + OftTraceInputs inputs() { return inputs; } - public String errorMessage() { + String errorMessage() { return errorMessage; } } diff --git a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolver.java b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolver.java index 52304c5..f999886 100644 --- a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolver.java +++ b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolver.java @@ -10,75 +10,49 @@ import com.intellij.openapi.roots.SourceFolder; import com.intellij.openapi.vfs.VirtualFile; +import java.util.LinkedHashSet; +import java.util.List; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Optional; -public final class OftTraceInputResolver { +final class OftTraceInputResolver { private static final Logger LOG = Logger.getInstance(OftTraceInputResolver.class); private OftTraceInputResolver() { // Prevent constructor invocation. } - public static OftTraceInputResolution resolve(final Project project) { + static OftTraceInputResolution resolve(final Project project) { return resolve(project, OftTraceProjectSettings.getInstance(project).snapshot()); } - public static OftTraceInputResolution resolve(final Project project, final OftTraceSettingsSnapshot settings) { + static OftTraceInputResolution resolve(final Project project, final OftTraceSettingsSnapshot settings) { final OftTraceInputResolution projectRootResolution = resolveProjectRoot( - normalizeProjectPath(project.getBasePath()), - settings + normalizeProjectPath(project.getBasePath()) ); if (projectRootResolution.isValid()) { - return resolveFromProjectRoot( - project, - projectRootResolution.inputs().inputPaths().getFirst(), - settings - ); + return resolveFromProjectRoot(project, projectRootResolution.inputs().inputPaths().getFirst(), settings); } final OftTraceInputResolution projectFileResolution = resolveProjectRoot( - normalizeProjectPath(project.getProjectFilePath()), - settings + normalizeProjectPath(project.getProjectFilePath()) ); if (projectFileResolution.isValid()) { - return resolveFromProjectRoot( - project, - projectFileResolution.inputs().inputPaths().getFirst(), - settings + return resolveFromProjectRoot(project, projectFileResolution.inputs().inputPaths().getFirst(), settings); + } + final VirtualFile projectDirectory = ProjectUtil.guessProjectDir(project); + if (projectDirectory != null) { + final OftTraceInputResolution guessedDirectoryResolution = resolveProjectRoot( + normalizeProjectPath(projectDirectory.getPath()) ); + if (guessedDirectoryResolution.isValid()) { + return resolveFromProjectRoot(project, guessedDirectoryResolution.inputs().inputPaths().getFirst(), settings); + } } - return resolveFromGuessedProjectDirectory(project, settings).orElse(projectRootResolution); + return projectRootResolution; } - private static Optional resolveFromGuessedProjectDirectory( - final Project project, - final OftTraceSettingsSnapshot settings - ) { - return resolveFromGuessedProjectDirectory(project, ProjectUtil.guessProjectDir(project), settings); - } - - private static Optional resolveFromGuessedProjectDirectory( - final Project project, - final VirtualFile guessedProjectDirectory, - final OftTraceSettingsSnapshot settings - ) { - return Optional.ofNullable(guessedProjectDirectory) - .map(VirtualFile::getPath) - .map(OftTraceInputResolver::normalizeProjectPath) - .map(basePath -> resolveProjectRoot(basePath, settings)) - .filter(OftTraceInputResolution::isValid) - .map(resolution -> resolveFromProjectRoot( - project, - resolution.inputs().inputPaths().getFirst(), - settings - )); - } - - static OftTraceInputResolution resolveProjectRoot(final String basePath, final OftTraceSettingsSnapshot settings) { + static OftTraceInputResolution resolveProjectRoot(final String basePath) { if (basePath == null || basePath.isBlank()) { return OftTraceInputResolution.invalid( "The current project does not expose a local base path for tracing." @@ -103,11 +77,7 @@ static OftTraceInputResolution resolveProjectRoot(final String basePath, final O "The current project base path is not a directory: " + inputPath ); } - return OftTraceInputResolution.valid(OftTraceInputs.wholeProject( - inputPath, - settings.artifactTypes(), - settings.tags() - )); + return OftTraceInputResolution.valid(OftTraceInputs.wholeProject(inputPath)); } // [impl->dsn~trace-selected-project-resources~1] @@ -120,11 +90,7 @@ private static OftTraceInputResolution resolveFromProjectRoot( final OftTraceSettingsSnapshot settings ) { if (settings.scopeMode() == OftTraceScopeMode.WHOLE_PROJECT) { - return OftTraceInputResolution.valid(OftTraceInputs.wholeProject( - projectRoot, - settings.artifactTypes(), - settings.tags() - )); + return OftTraceInputResolution.valid(OftTraceInputs.wholeProject(projectRoot)); } final LinkedHashSet inputs = new LinkedHashSet<>(); if (settings.includeSourceRoots()) { @@ -134,11 +100,7 @@ private static OftTraceInputResolution resolveFromProjectRoot( inputs.addAll(resolveModuleSourceFolders(project, true)); } for (final String additionalPath : settings.additionalPaths()) { - final OftTraceInputResolution additionalPathResolution = resolveConfiguredAdditionalPath( - projectRoot, - additionalPath, - settings - ); + final OftTraceInputResolution additionalPathResolution = resolveConfiguredAdditionalPath(projectRoot, additionalPath); if (!additionalPathResolution.isValid()) { return additionalPathResolution; } @@ -149,11 +111,7 @@ private static OftTraceInputResolution resolveFromProjectRoot( "The current trace configuration does not resolve to any files or directories." ); } - return OftTraceInputResolution.valid(OftTraceInputs.selectedResources( - List.copyOf(inputs), - settings.artifactTypes(), - settings.tags() - )); + return OftTraceInputResolution.valid(OftTraceInputs.selectedResources(List.copyOf(inputs))); } private static List resolveModuleSourceFolders(final Project project, final boolean testSource) { @@ -161,40 +119,25 @@ private static List resolveModuleSourceFolders(final Project project, fina for (final Module module : ModuleManager.getInstance(project).getModules()) { for (final ContentEntry contentEntry : ModuleRootManager.getInstance(module).getContentEntries()) { for (final SourceFolder sourceFolder : contentEntry.getSourceFolders()) { - addMatchingSourceFolderPath(paths, sourceFolder, testSource); + if (sourceFolder.isTestSource() != testSource) { + continue; + } + final VirtualFile sourceDirectory = sourceFolder.getFile(); + if (sourceDirectory != null) { + paths.add(Path.of(sourceDirectory.getPath())); + } } } } return List.copyOf(paths); } - private static void addMatchingSourceFolderPath( - final LinkedHashSet paths, - final SourceFolder sourceFolder, - final boolean testSource - ) { - if (sourceFolder.isTestSource() == testSource) { - sourceFolderPath(sourceFolder).ifPresent(paths::add); - } - } - - private static Optional sourceFolderPath(final SourceFolder sourceFolder) { - return Optional.ofNullable(sourceFolder.getFile()) - .map(VirtualFile::getPath) - .map(Path::of); - } - // [impl->dsn~add-project-relative-paths-to-selected-resource-trace~1] - private static OftTraceInputResolution resolveConfiguredAdditionalPath( - final Path projectRoot, - final String additionalPath, - final OftTraceSettingsSnapshot settings - ) { + private static OftTraceInputResolution resolveConfiguredAdditionalPath(final Path projectRoot, final String additionalPath) { final Path relativePath; try { relativePath = Path.of(additionalPath); } catch (final InvalidPathException exception) { - LOG.debug("Ignoring invalid configured OpenFastTrace input path: " + additionalPath, exception); return OftTraceInputResolution.invalid( "The configured additional trace path is invalid: " + exception.getInput() ); @@ -215,11 +158,7 @@ private static OftTraceInputResolution resolveConfiguredAdditionalPath( "The configured additional trace path is neither a file nor a directory: " + resolvedPath ); } - return OftTraceInputResolution.valid(OftTraceInputs.selectedResources( - List.of(resolvedPath), - settings.artifactTypes(), - settings.tags() - )); + return OftTraceInputResolution.valid(OftTraceInputs.selectedResources(List.of(resolvedPath))); } private static String normalizeProjectPath(final String projectPath) { @@ -258,11 +197,11 @@ private static boolean isIdeaDirectory(final Path path) { return fileName != null && ".idea".equals(fileName.toString()); } - private static Optional parentDirectoryIfRegularFile(final Path path) { + private static java.util.Optional parentDirectoryIfRegularFile(final Path path) { final Path parent = path.getParent(); if (parent != null && Files.isRegularFile(path)) { - return Optional.of(parent); + return java.util.Optional.of(parent); } - return Optional.empty(); + return java.util.Optional.empty(); } } diff --git a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputs.java b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputs.java index 3f2d686..de1964e 100644 --- a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputs.java +++ b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputs.java @@ -3,56 +3,31 @@ import java.nio.file.Path; import java.util.List; -public final class OftTraceInputs { +final class OftTraceInputs { private final boolean wholeProject; private final List inputPaths; - private final List artifactTypes; - private final List tags; - - private OftTraceInputs( - final boolean wholeProject, - final List inputPaths, - final List artifactTypes, - final List tags - ) { + + private OftTraceInputs(final boolean wholeProject, final List inputPaths) { this.wholeProject = wholeProject; this.inputPaths = List.copyOf(inputPaths); - this.artifactTypes = List.copyOf(artifactTypes); - this.tags = List.copyOf(tags); } - public static OftTraceInputs wholeProject( - final Path projectRoot, - final List artifactTypes, - final List tags - ) { - return new OftTraceInputs(true, List.of(projectRoot), artifactTypes, tags); + static OftTraceInputs wholeProject(final Path projectRoot) { + return new OftTraceInputs(true, List.of(projectRoot)); } - public static OftTraceInputs selectedResources( - final List inputPaths, - final List artifactTypes, - final List tags - ) { - return new OftTraceInputs(false, inputPaths, artifactTypes, tags); + static OftTraceInputs selectedResources(final List inputPaths) { + return new OftTraceInputs(false, inputPaths); } - public boolean isWholeProject() { + boolean isWholeProject() { return wholeProject; } - public List inputPaths() { + List inputPaths() { return inputPaths; } - public List artifactTypes() { - return artifactTypes; - } - - public List tags() { - return tags; - } - String progressText() { if (wholeProject) { return inputPaths.getFirst().toString(); diff --git a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectConfigurable.java b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectConfigurable.java index dd89ec1..0f5a109 100644 --- a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectConfigurable.java +++ b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectConfigurable.java @@ -1,11 +1,9 @@ package org.itsallcode.openfasttrace.intellijplugin.trace; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.project.Project; -import org.jspecify.annotations.NonNull; import javax.swing.JComponent; import java.nio.file.InvalidPathException; @@ -14,7 +12,6 @@ // [impl->dsn~configure-trace-scope-in-project-settings~1] public final class OftTraceProjectConfigurable implements SearchableConfigurable, Configurable.NoScroll { - private static final Logger LOG = Logger.getInstance(OftTraceProjectConfigurable.class); static final String ID = "org.itsallcode.openfasttrace.intellijplugin.trace.OftTraceProjectConfigurable"; private final Project project; @@ -25,7 +22,7 @@ public OftTraceProjectConfigurable(final Project project) { } @Override - public @NonNull String getId() { + public String getId() { return ID; } @@ -48,7 +45,6 @@ public boolean isModified() { } @Override - @SuppressWarnings("java:S1162") // IntelliJ settings validation reports rejected values through this API type. public void apply() throws ConfigurationException { validate(component.getSettings()); OftTraceProjectSettings.getInstance(project).updateFrom(component.getSettings()); @@ -64,15 +60,12 @@ public void disposeUIResources() { component = null; } - // IntelliJ Configurable callers expect ConfigurationException for validation errors. - @SuppressWarnings("java:S1162") private static void validate(final OftTraceSettingsSnapshot settings) throws ConfigurationException { for (final String additionalPath : settings.additionalPaths()) { final Path path; try { path = Path.of(additionalPath); } catch (final InvalidPathException exception) { - LOG.debug("Rejecting invalid OpenFastTrace additional trace path: " + additionalPath, exception); throw new ConfigurationException( "Additional trace path is invalid: " + exception.getInput() ); @@ -93,7 +86,6 @@ private Path projectRoot() { try { return Path.of(basePath); } catch (final InvalidPathException exception) { - LOG.debug("Ignoring invalid OpenFastTrace project base path: " + basePath, exception); return null; } } diff --git a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectSettings.java b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectSettings.java index ceb669b..abce423 100644 --- a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectSettings.java +++ b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectSettings.java @@ -13,6 +13,8 @@ ) // [impl->dsn~trace-configuration-integration~1] final class OftTraceProjectSettings implements PersistentStateComponent { + static final String DEFAULT_ADDITIONAL_PATH = "doc/"; + private State state = new State(); static OftTraceProjectSettings getInstance(final Project project) { @@ -24,9 +26,7 @@ OftTraceSettingsSnapshot snapshot() { parseScopeMode(state.traceScopeMode), state.includeSourceRoots, state.includeTestRoots, - state.additionalPathsText, - state.artifactTypesText, - state.tagsText + state.additionalPathsText ); } @@ -35,8 +35,6 @@ void updateFrom(final OftTraceSettingsSnapshot snapshot) { state.includeSourceRoots = snapshot.includeSourceRoots(); state.includeTestRoots = snapshot.includeTestRoots(); state.additionalPathsText = snapshot.additionalPathsText(); - state.artifactTypesText = snapshot.artifactTypesText(); - state.tagsText = snapshot.tagsText(); } @Override @@ -49,33 +47,25 @@ public void loadState(final State state) { this.state = state == null ? new State() : state; this.state.traceScopeMode = parseScopeMode(this.state.traceScopeMode).name(); if (this.state.additionalPathsText == null) { - this.state.additionalPathsText = OftTraceSettingsSnapshot.DEFAULT.additionalPathsText(); - } - if (this.state.artifactTypesText == null) { - this.state.artifactTypesText = ""; - } - if (this.state.tagsText == null) { - this.state.tagsText = ""; + this.state.additionalPathsText = DEFAULT_ADDITIONAL_PATH; } } private static OftTraceScopeMode parseScopeMode(final String value) { if (value == null || value.isBlank()) { - return OftTraceSettingsSnapshot.DEFAULT.scopeMode(); + return OftTraceScopeMode.WHOLE_PROJECT; } try { return OftTraceScopeMode.valueOf(value); } catch (final IllegalArgumentException ignored) { - return OftTraceSettingsSnapshot.DEFAULT.scopeMode(); + return OftTraceScopeMode.WHOLE_PROJECT; } } static final class State { - String traceScopeMode = OftTraceSettingsSnapshot.DEFAULT.scopeMode().name(); - boolean includeSourceRoots = OftTraceSettingsSnapshot.DEFAULT.includeSourceRoots(); - boolean includeTestRoots = OftTraceSettingsSnapshot.DEFAULT.includeTestRoots(); - String additionalPathsText = OftTraceSettingsSnapshot.DEFAULT.additionalPathsText(); - String artifactTypesText = OftTraceSettingsSnapshot.DEFAULT.artifactTypesText(); - String tagsText = OftTraceSettingsSnapshot.DEFAULT.tagsText(); + String traceScopeMode = OftTraceScopeMode.WHOLE_PROJECT.name(); + boolean includeSourceRoots = true; + boolean includeTestRoots = true; + String additionalPathsText = DEFAULT_ADDITIONAL_PATH; } } diff --git a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceRunContentOutputPresenter.java b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceRunContentOutputPresenter.java index 01ffbd0..90d47cd 100644 --- a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceRunContentOutputPresenter.java +++ b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceRunContentOutputPresenter.java @@ -1,5 +1,6 @@ package org.itsallcode.openfasttrace.intellijplugin.trace; +import com.intellij.execution.RunContentDescriptorId; import com.intellij.execution.executors.DefaultRunExecutor; import com.intellij.execution.impl.ConsoleViewImpl; import com.intellij.execution.ui.ConsoleView; @@ -14,14 +15,14 @@ import java.util.function.BiConsumer; import java.util.function.Function; -public final class OftTraceRunContentOutputPresenter implements OftTraceOutputPresenter { +final class OftTraceRunContentOutputPresenter implements OftTraceOutputPresenter { public static final String IDEA_CYCLE_BUFFER_SIZE = "idea.cycle.buffer.size"; private static final AtomicInteger NEXT_DESCRIPTOR_ID = new AtomicInteger(1); private final Function consoleFactory; private final BiConsumer runContentShower; private final OftAnsiConsoleOutput ansiConsoleOutput = new OftAnsiConsoleOutput(); - public OftTraceRunContentOutputPresenter() { + OftTraceRunContentOutputPresenter() { this( OftTraceRunContentOutputPresenter::createTraceConsole, (project, descriptor) -> RunContentManager.getInstance(project) @@ -29,7 +30,7 @@ public OftTraceRunContentOutputPresenter() { ); } - public OftTraceRunContentOutputPresenter( + OftTraceRunContentOutputPresenter( final Function consoleFactory, final BiConsumer runContentShower ) { @@ -50,7 +51,7 @@ public void show(final Project project, final String contentTitle, final OftTrac } final RunContentDescriptor descriptor = new RunContentDescriptor(console, null, console.getComponent(), contentTitle); - descriptor.setExecutionId(NEXT_DESCRIPTOR_ID.getAndIncrement()); + descriptor.setId(new OftRunContentDescriptorId(NEXT_DESCRIPTOR_ID.getAndIncrement())); Disposer.register(descriptor, console); runContentShower.accept(project, descriptor); } @@ -62,7 +63,7 @@ private static void print(final ConsoleView console, final String text, final bo ); } - public static ConsoleView createTraceConsole(final Project project) { + static ConsoleView createTraceConsole(final Project project) { final String previousBufferSize = System.getProperty(IDEA_CYCLE_BUFFER_SIZE); try { System.setProperty(IDEA_CYCLE_BUFFER_SIZE, "disabled"); @@ -82,4 +83,11 @@ private static void restoreCycleBufferSize(final String previousBufferSize) { System.setProperty(IDEA_CYCLE_BUFFER_SIZE, previousBufferSize); } } + + private record OftRunContentDescriptorId(int uid) implements RunContentDescriptorId { + @Override + public int getUid() { + return uid; + } + } } diff --git a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceRunner.java b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceRunner.java index da3bda8..fa8df2a 100644 --- a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceRunner.java +++ b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceRunner.java @@ -2,6 +2,6 @@ import com.intellij.openapi.project.Project; -public interface OftTraceRunner { +interface OftTraceRunner { void run(final Project project, final OftTraceInputs inputs, final String contentTitle); } diff --git a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceScopeMode.java b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceScopeMode.java index def2884..70858d5 100644 --- a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceScopeMode.java +++ b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceScopeMode.java @@ -1,6 +1,6 @@ package org.itsallcode.openfasttrace.intellijplugin.trace; -public enum OftTraceScopeMode { +enum OftTraceScopeMode { WHOLE_PROJECT, SELECTED_RESOURCES } diff --git a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceService.java b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceService.java index c8376e7..fb6d9a2 100644 --- a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceService.java +++ b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceService.java @@ -3,7 +3,6 @@ import com.intellij.openapi.progress.ProcessCanceledException; import org.itsallcode.openfasttrace.api.ColorScheme; import org.itsallcode.openfasttrace.api.DetailsSectionDisplay; -import org.itsallcode.openfasttrace.api.FilterSettings; import org.itsallcode.openfasttrace.api.ReportSettings; import org.itsallcode.openfasttrace.api.core.LinkedSpecificationItem; import org.itsallcode.openfasttrace.api.core.SpecificationItem; @@ -15,22 +14,18 @@ import java.io.PrintWriter; import java.io.StringWriter; -import java.nio.file.Path; import java.util.List; -import java.util.Set; import java.util.concurrent.Callable; import java.util.stream.Collectors; -public final class OftTraceService { - @SuppressWarnings("java:S3032") - // OFT ServiceLoader discovery must use the plugin class loader, not an arbitrary caller context loader. +final class OftTraceService { private static final ClassLoader PLUGIN_CLASS_LOADER = OftTraceService.class.getClassLoader(); private final Oft oft; private final OftTraceReportRenderer reportRenderer; // [impl->dsn~trace-execution-service~1] - public OftTraceService() { + OftTraceService() { this(Oft.create(), new OftPlainTextTraceReportRenderer()); } @@ -39,20 +34,15 @@ public OftTraceService() { this.reportRenderer = reportRenderer; } - // [impl->dsn~show-successful-trace-output-in-ide-output-window~2] + // [impl->dsn~show-successful-trace-output-in-ide-output-window~1] // [impl->dsn~show-scanned-base-directory-in-trace-output-window~1] // [impl->dsn~show-failing-trace-output-in-ide-output-window~1] // [impl->dsn~preserve-defect-count-for-unclean-trace-chain-in-output-window~1] - // [impl->dsn~filter-trace-by-artifact-types-and-tags~1] - public OftTraceResult traceProject(final OftTraceInputs inputs, final OftTraceProgress progress) { + OftTraceResult traceProject(final OftTraceInputs inputs, final OftTraceProgress progress) { try { progress.phase("Importing OpenFastTrace items...", 0.15D); progress.checkCanceled(); - final FilterSettings filterSettings = FilterSettings.builder() - .artifactTypes(Set.copyOf(inputs.artifactTypes())) - .tags(Set.copyOf(inputs.tags())) - .build(); - final List items = importItems(inputs.inputPaths(), filterSettings); + final List items = importItems(inputs); progress.phase("Linking OpenFastTrace items...", 0.4D); progress.checkCanceled(); @@ -66,7 +56,7 @@ public OftTraceResult traceProject(final OftTraceInputs inputs, final OftTracePr progress.checkCanceled(); final String output = buildTraceOutput(inputs, trace); progress.phase("Finished OpenFastTrace trace.", 1.0D); - return trace.hasNoDefects() ? OftTraceResult.success(output, trace) : OftTraceResult.failure(output, trace); + return trace.hasNoDefects() ? OftTraceResult.success(output) : OftTraceResult.failure(output); } catch (final ProcessCanceledException exception) { throw exception; } catch (final RuntimeException exception) { @@ -74,11 +64,9 @@ public OftTraceResult traceProject(final OftTraceInputs inputs, final OftTracePr } } - - private List importItems(final List inputs, final FilterSettings filterSettings) { + private List importItems(final OftTraceInputs inputs) { final ImportSettings settings = ImportSettings.builder() - .addInputs(inputs) - .filter(filterSettings) + .addInputs(inputs.inputPaths()) .build(); return runWithPluginClassLoader(() -> oft.importItems(settings)); } @@ -87,7 +75,7 @@ private String buildTraceOutput(final OftTraceInputs inputs, final Trace trace) return buildInputHeader(inputs) + renderTrace(trace); } - private static String buildInputHeader(final OftTraceInputs inputs) { + private String buildInputHeader(final OftTraceInputs inputs) { if (inputs.isWholeProject()) { return "Scanning base directory: " + inputs.inputPaths().getFirst().toAbsolutePath().normalize() + System.lineSeparator() diff --git a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceSettingsComponent.java b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceSettingsComponent.java index d8c0074..21ec399 100644 --- a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceSettingsComponent.java +++ b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceSettingsComponent.java @@ -6,7 +6,6 @@ import com.intellij.ui.components.JBRadioButton; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.components.JBTextArea; -import com.intellij.ui.components.JBTextField; import com.intellij.util.ui.FormBuilder; import javax.swing.ButtonGroup; @@ -18,7 +17,7 @@ import java.awt.Dimension; import java.nio.file.Path; -public final class OftTraceSettingsComponent { +final class OftTraceSettingsComponent { private final JBRadioButton wholeProjectRadioButton = new JBRadioButton("Trace the whole project"); private final JBRadioButton selectedResourcesRadioButton = @@ -28,31 +27,16 @@ public final class OftTraceSettingsComponent { private final JBCheckBox includeTestRootsCheckBox = new JBCheckBox("Include IntelliJ test directories"); private final JBTextArea additionalPathsTextArea = new JBTextArea(); - private final JBTextField artifactTypesField = new JBTextField(); - private final JBTextField tagsField = new JBTextField(); - private final JBRadioButton plainTextResultViewRadioButton = - new JBRadioButton("Plain text output"); - private final JBRadioButton testRunnerResultViewRadioButton = - new JBRadioButton("IntelliJ Test Runner UI"); private final JBLabel resolvedRelativeToLabel = new JBLabel(); private final JBTextArea validationMessagesArea = new JBTextArea(); private final Path projectRoot; - private final boolean showResultViewSelection; private final JPanel panel; - public OftTraceSettingsComponent(final Path projectRoot) { - this(projectRoot, false); - } - - public OftTraceSettingsComponent(final Path projectRoot, final boolean showResultViewSelection) { + OftTraceSettingsComponent(final Path projectRoot) { this.projectRoot = projectRoot; - this.showResultViewSelection = showResultViewSelection; final ButtonGroup traceScopeGroup = new ButtonGroup(); traceScopeGroup.add(wholeProjectRadioButton); traceScopeGroup.add(selectedResourcesRadioButton); - final ButtonGroup resultViewGroup = new ButtonGroup(); - resultViewGroup.add(plainTextResultViewRadioButton); - resultViewGroup.add(testRunnerResultViewRadioButton); selectedResourcesRadioButton.addActionListener(event -> updateSelectedResourcesEnabledState()); wholeProjectRadioButton.addActionListener(event -> updateSelectedResourcesEnabledState()); additionalPathsTextArea.setLineWrap(false); @@ -91,72 +75,57 @@ public void changedUpdate(final DocumentEvent event) { additionalPathsFeedbackPanel.add(resolvedRelativeToLabel, BorderLayout.NORTH); additionalPathsFeedbackPanel.add(validationMessagesArea, BorderLayout.CENTER); additionalPathsPanel.add(additionalPathsFeedbackPanel, BorderLayout.SOUTH); - final FormBuilder formBuilder = FormBuilder.createFormBuilder() + panel = FormBuilder.createFormBuilder() .addComponent(wholeProjectRadioButton) .addComponent(selectedResourcesRadioButton) .addComponent(includeSourceRootsCheckBox, 1) .addComponent(includeTestRootsCheckBox, 1) .addComponent(additionalPathsPanel, 1) - .addSeparator() - .addLabeledComponent("Artifact types:", artifactTypesField) - .addTooltip("comma-separated, empty = all") - .addLabeledComponent("Tags:", tagsField) - .addTooltip("comma-separated, empty = all"); - if (showResultViewSelection) { - formBuilder - .addSeparator() - .addComponent(new JBLabel("Result view")) - .addComponent(plainTextResultViewRadioButton, 1) - .addComponent(testRunnerResultViewRadioButton, 1); - } - panel = formBuilder .addComponentFillVertically(new JPanel(), 0) .getPanel(); - setSettings(OftTraceSettingsSnapshot.DEFAULT); + setSettings(new OftTraceSettingsSnapshot( + OftTraceScopeMode.WHOLE_PROJECT, + true, + true, + OftTraceProjectSettings.DEFAULT_ADDITIONAL_PATH + )); } - public JComponent getPanel() { + JComponent getPanel() { return panel; } - public OftTraceSettingsSnapshot getSettings() { + OftTraceSettingsSnapshot getSettings() { return new OftTraceSettingsSnapshot( selectedResourcesRadioButton.isSelected() ? OftTraceScopeMode.SELECTED_RESOURCES : OftTraceScopeMode.WHOLE_PROJECT, includeSourceRootsCheckBox.isSelected(), includeTestRootsCheckBox.isSelected(), - additionalPathsTextArea.getText(), - artifactTypesField.getText(), - tagsField.getText(), - selectedResultView() + additionalPathsTextArea.getText() ); } - public void setSettings(final OftTraceSettingsSnapshot settings) { + void setSettings(final OftTraceSettingsSnapshot settings) { wholeProjectRadioButton.setSelected(settings.scopeMode() == OftTraceScopeMode.WHOLE_PROJECT); selectedResourcesRadioButton.setSelected(settings.scopeMode() == OftTraceScopeMode.SELECTED_RESOURCES); includeSourceRootsCheckBox.setSelected(settings.includeSourceRoots()); includeTestRootsCheckBox.setSelected(settings.includeTestRoots()); additionalPathsTextArea.setText(settings.additionalPathsText()); - artifactTypesField.setText(settings.artifactTypesText()); - tagsField.setText(settings.tagsText()); - plainTextResultViewRadioButton.setSelected(settings.resultView() == OftTraceResultView.PLAIN_TEXT); - testRunnerResultViewRadioButton.setSelected(settings.resultView() == OftTraceResultView.TEST_RUNNER); updateSelectedResourcesEnabledState(); } - public boolean isSelectedResourcesEnabled() { + boolean isSelectedResourcesEnabled() { return includeSourceRootsCheckBox.isEnabled() && includeTestRootsCheckBox.isEnabled() && additionalPathsTextArea.isEnabled(); } - public String resolvedRelativeToText() { + String resolvedRelativeToText() { return resolvedRelativeToLabel.getText(); } - public String validationMessagesText() { + String validationMessagesText() { return validationMessagesArea.getText(); } @@ -170,16 +139,6 @@ private void updateSelectedResourcesEnabledState() { updateValidationFeedback(); } - private OftTraceResultView selectedResultView() { - if (!showResultViewSelection) { - return OftTraceSettingsSnapshot.DEFAULT.resultView(); - } - if (plainTextResultViewRadioButton.isSelected()) { - return OftTraceResultView.PLAIN_TEXT; - } - return OftTraceResultView.TEST_RUNNER; - } - // [impl->dsn~show-per-line-validation-for-additional-trace-paths~1] private void updateValidationFeedback() { if (!selectedResourcesRadioButton.isSelected() || projectRoot == null) { diff --git a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceSettingsSnapshot.java b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceSettingsSnapshot.java index dcac0ec..d5063fd 100644 --- a/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceSettingsSnapshot.java +++ b/src/main/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceSettingsSnapshot.java @@ -1,71 +1,18 @@ package org.itsallcode.openfasttrace.intellijplugin.trace; +import java.util.Arrays; import java.util.List; -import java.util.regex.Pattern; -public record OftTraceSettingsSnapshot( +record OftTraceSettingsSnapshot( OftTraceScopeMode scopeMode, boolean includeSourceRoots, boolean includeTestRoots, - String additionalPathsText, - String artifactTypesText, - String tagsText, - OftTraceResultView resultView + String additionalPathsText ) { - public static final OftTraceSettingsSnapshot DEFAULT = new OftTraceSettingsSnapshot( - OftTraceScopeMode.WHOLE_PROJECT, - true, - true, - "doc/", - "", - "", - OftTraceResultView.TEST_RUNNER - ); - - private static final Pattern LINE_SEPARATOR = Pattern.compile("\\R"); - public static final Pattern COMMA = Pattern.compile(","); - - public OftTraceSettingsSnapshot( - final OftTraceScopeMode scopeMode, - final boolean includeSourceRoots, - final boolean includeTestRoots, - final String additionalPathsText, - final String artifactTypesText, - final String tagsText - ) { - this( - scopeMode, - includeSourceRoots, - includeTestRoots, - additionalPathsText, - artifactTypesText, - tagsText, - DEFAULT.resultView() - ); - } - - public List additionalPaths() { - return LINE_SEPARATOR.splitAsStream(additionalPathsText) + List additionalPaths() { + return Arrays.stream(additionalPathsText.split("\\R", -1)) .map(String::trim) .filter(line -> !line.isEmpty()) .toList(); } - - public List artifactTypes() { - return splitCommaSeparated(artifactTypesText); - } - - public List tags() { - return splitCommaSeparated(tagsText); - } - - private static List splitCommaSeparated(final String text) { - if (text == null || text.isBlank()) { - return List.of(); - } - return COMMA.splitAsStream(text) - .map(String::trim) - .filter(item -> !item.isEmpty()) - .toList(); - } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 6564fd3..2ec8442 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -6,121 +6,10 @@ ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/OftMarketplaceMetadataTest.java b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/OftMarketplaceMetadataTest.java new file mode 100644 index 0000000..5d119b1 --- /dev/null +++ b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/OftMarketplaceMetadataTest.java @@ -0,0 +1,92 @@ +package org.itsallcode.openfasttrace.intellijplugin; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.StringReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +class OftMarketplaceMetadataTest { + private static final String PROJECT_URL = "https://github.com/itsallcode/openfasttrace-intellij-plugin"; + private static final String FIRST_DESCRIPTION_SENTENCE = + "Author requirements directly in your project and trace them all the way down to your implementation " + + "and tests without leaving your IDE."; + + // [itest->dsn~marketplace-plugin-metadata~1] + @Test + void givenMarketplaceDescriptionWhenReadingThenItContainsConfirmedMetadata() throws Exception { + final String description = Files.readString(Path.of("doc/marketplace/description.html")); + + Assertions.assertAll( + () -> assertThat(description, containsString(FIRST_DESCRIPTION_SENTENCE)), + () -> assertThat(description, containsString("developers")), + () -> assertThat(description, containsString("requirement engineers")), + () -> assertThat(description, containsString("quality engineers")), + () -> assertThat(description, containsString("technical writers")), + () -> assertThat(description, containsString("broken requirement chains")) + ); + } + + // [itest->dsn~marketplace-plugin-metadata~1] + @Test + void givenActiveReleaseNotesWhenReadingThenItContainsMarketplaceMetadataEntry() throws Exception { + final String version = projectVersion(); + final String bundledOpenFastTraceVersion = bundledOpenFastTraceVersion(); + final String releaseNotes = Files.readString(Path.of("doc/changes/changes_" + version + ".md")); + + Assertions.assertAll( + () -> assertThat(releaseNotes, containsString("# OpenFastTrace IntelliJ Plugin " + version)), + () -> assertThat(releaseNotes, containsString("#51: Complete Marketplace-facing plugin metadata")), + () -> assertThat(releaseNotes, containsString("clearer overview")), + () -> assertThat(releaseNotes, containsString("installing or updating")), + () -> assertThat(releaseNotes, containsString("## Bundled OpenFastTrace")), + () -> assertThat(releaseNotes, containsString("OpenFastTrace " + bundledOpenFastTraceVersion)) + ); + } + + // [itest->dsn~marketplace-plugin-metadata~1] + @Test + void givenSourcePluginDescriptorWhenReadingThenItContainsProjectWebsiteUrl() throws Exception { + final Document document = loadXml(Files.readString(Path.of("src/main/resources/META-INF/plugin.xml"))); + + assertThat(document.getDocumentElement().getAttribute("url"), is(PROJECT_URL)); + } + + private static String projectVersion() throws Exception { + final Properties properties = new Properties(); + try (var input = Files.newInputStream(Path.of("gradle.properties"))) { + properties.load(input); + } + return properties.getProperty("version"); + } + + private static String bundledOpenFastTraceVersion() throws Exception { + final String prefix = "org.itsallcode.openfasttrace:openfasttrace:"; + try (var lines = Files.lines(Path.of("gradle.lockfile"))) { + return lines + .filter(line -> line.startsWith(prefix)) + .findFirst() + .map(line -> line.substring(prefix.length(), line.indexOf('='))) + .orElseThrow(); + } + } + + private static Document loadXml(final String xml) throws Exception { + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + factory.setNamespaceAware(true); + factory.setExpandEntityReferences(false); + return factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml))); + } +} diff --git a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceBackgroundRunnerTest.java b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceBackgroundRunnerTest.java index c36d30f..eb143f0 100644 --- a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceBackgroundRunnerTest.java +++ b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceBackgroundRunnerTest.java @@ -1,6 +1,5 @@ package org.itsallcode.openfasttrace.intellijplugin.trace; -import com.intellij.execution.process.NopProcessHandler; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; @@ -32,10 +31,9 @@ void testGivenTraceTaskWhenRunningThenItUpdatesProgressAndPresentsSuccessfulResu final AtomicReference presenterCall = new AtomicReference<>(); final OftTraceBackgroundRunner runner = new OftTraceBackgroundRunner( new OftTraceService(), - (project, contentTitle, result) -> presenterCall.set(new PresenterCall(project, contentTitle, result)), - new NopProcessHandler() + (project, contentTitle, result) -> presenterCall.set(new PresenterCall(project, contentTitle, result)) ); - final Task.Backgroundable task = newTraceTask(runner, OftTraceInputs.wholeProject(temporaryDirectory, List.of(), List.of()), "Trace"); + final Task.Backgroundable task = newTraceTask(runner, OftTraceInputs.wholeProject(temporaryDirectory), "Trace"); final List progressEvents = new ArrayList<>(); final ProgressIndicator indicator = progressIndicator(progressEvents); @@ -76,8 +74,8 @@ void testGivenTraceTaskWhenRunningThenItUpdatesProgressAndPresentsSuccessfulResu void testGivenTraceTaskWhenCancelledThenItPresentsCancelledResult() throws Exception { final AtomicReference presenterCall = new AtomicReference<>(); final Task.Backgroundable task = newTraceTask( - new OftTraceBackgroundRunner(new OftTraceService(), capturePresenter(presenterCall), new NopProcessHandler()), - OftTraceInputs.wholeProject(Path.of("."), List.of(), List.of()), + new OftTraceBackgroundRunner(new OftTraceService(), capturePresenter(presenterCall)), + OftTraceInputs.wholeProject(Path.of(".")), "Trace" ); @@ -95,8 +93,8 @@ void testGivenTraceTaskWhenCancelledThenItPresentsCancelledResult() throws Excep void testGivenProcessCanceledExceptionWhenHandlingThrowableThenItPresentsCancelledResult() throws Exception { final AtomicReference presenterCall = new AtomicReference<>(); final Task.Backgroundable task = newTraceTask( - new OftTraceBackgroundRunner(new OftTraceService(), capturePresenter(presenterCall), new NopProcessHandler()), - OftTraceInputs.wholeProject(Path.of("."), List.of(), List.of()), + new OftTraceBackgroundRunner(new OftTraceService(), capturePresenter(presenterCall)), + OftTraceInputs.wholeProject(Path.of(".")), "Trace" ); @@ -112,8 +110,8 @@ void testGivenProcessCanceledExceptionWhenHandlingThrowableThenItPresentsCancell void testGivenUnexpectedThrowableWhenHandlingThrowableThenItPresentsFormattedError() throws Exception { final AtomicReference presenterCall = new AtomicReference<>(); final Task.Backgroundable task = newTraceTask( - new OftTraceBackgroundRunner(new OftTraceService(), capturePresenter(presenterCall), new NopProcessHandler()), - OftTraceInputs.wholeProject(Path.of("."), List.of(), List.of()), + new OftTraceBackgroundRunner(new OftTraceService(), capturePresenter(presenterCall)), + OftTraceInputs.wholeProject(Path.of(".")), "Trace" ); diff --git a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolverPlatformTest.java b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolverPlatformTest.java index 773f153..ab4d2d5 100644 --- a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolverPlatformTest.java +++ b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolverPlatformTest.java @@ -1,7 +1,6 @@ package org.itsallcode.openfasttrace.intellijplugin.trace; import com.intellij.openapi.application.WriteAction; -import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; @@ -9,10 +8,10 @@ import com.intellij.openapi.vfs.VirtualFile; import org.itsallcode.openfasttrace.intellijplugin.AbstractOftPlatformTestCase; +import java.io.IOException; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Optional; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; @@ -34,12 +33,10 @@ public void testGivenSelectedResourceSettingsWhenResolvingThenItUsesIntellijSour getProject(), contentRoot, new OftTraceSettingsSnapshot( - OftTraceScopeMode.SELECTED_RESOURCES, - OftTraceSettingsSnapshot.DEFAULT.includeSourceRoots(), - OftTraceSettingsSnapshot.DEFAULT.includeTestRoots(), - OftTraceSettingsSnapshot.DEFAULT.additionalPathsText(), - OftTraceSettingsSnapshot.DEFAULT.artifactTypesText(), - OftTraceSettingsSnapshot.DEFAULT.tagsText() + OftTraceScopeMode.SELECTED_RESOURCES, + true, + true, + "doc/" ) ); @@ -53,28 +50,9 @@ public void testGivenSelectedResourceSettingsWhenResolvingThenItUsesIntellijSour } } - public void testWhenResolvingFromGuessedProjectDirectoryThenItReturnsAResolutionOptional() throws Exception { - final Optional resolution = resolveFromGuessedProjectDirectory( - getProject(), - OftTraceSettingsSnapshot.DEFAULT - ); - - assertThat(resolution.map(OftTraceInputResolution::isValid).orElse(true), is(true)); - } - - public void testWhenResolvingProjectThenItUsesProjectSettings() { - OftTraceProjectSettings.getInstance(getProject()).loadState(new OftTraceProjectSettings.State()); - - final OftTraceInputResolution resolution = OftTraceInputResolver.resolve(getProject()); - - assertNotNull(resolution); - } - private void configureModuleRoots(final Path contentRoot, final Path sourceDirectory, final Path testDirectory) { final VirtualFile contentRootFile = LocalFileSystem.getInstance().refreshAndFindFileByNioFile(contentRoot); - final VirtualFile sourceDirectoryFile = LocalFileSystem.getInstance().refreshAndFindFileByNioFile( - sourceDirectory - ); + final VirtualFile sourceDirectoryFile = LocalFileSystem.getInstance().refreshAndFindFileByNioFile(sourceDirectory); final VirtualFile testDirectoryFile = LocalFileSystem.getInstance().refreshAndFindFileByNioFile(testDirectory); assertNotNull(contentRootFile); assertNotNull(sourceDirectoryFile); @@ -105,31 +83,17 @@ private void restoreContentRoots(final VirtualFile[] contentRoots) { } private static OftTraceInputResolution resolveFromProjectRoot( - final Project project, + final com.intellij.openapi.project.Project project, final Path projectRoot, final OftTraceSettingsSnapshot settings ) throws ReflectiveOperationException { final Method method = OftTraceInputResolver.class.getDeclaredMethod( "resolveFromProjectRoot", - Project.class, + com.intellij.openapi.project.Project.class, Path.class, OftTraceSettingsSnapshot.class ); method.setAccessible(true); return (OftTraceInputResolution) method.invoke(null, project, projectRoot, settings); } - - @SuppressWarnings("unchecked") - private static Optional resolveFromGuessedProjectDirectory( - final Project project, - final OftTraceSettingsSnapshot settings - ) throws ReflectiveOperationException { - final Method method = OftTraceInputResolver.class.getDeclaredMethod( - "resolveFromGuessedProjectDirectory", - Project.class, - OftTraceSettingsSnapshot.class - ); - method.setAccessible(true); - return (Optional) method.invoke(null, project, settings); - } } diff --git a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolverTest.java b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolverTest.java index 2d22d43..45ce410 100644 --- a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolverTest.java +++ b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceInputResolverTest.java @@ -1,17 +1,10 @@ package org.itsallcode.openfasttrace.intellijplugin.trace; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.SourceFolder; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.testFramework.LightVirtualFile; import org.hamcrest.Matchers; -import org.jspecify.annotations.NonNull; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; import java.io.IOException; import java.lang.reflect.Method; @@ -19,20 +12,15 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Optional; -import java.util.stream.Stream; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; class OftTraceInputResolverTest { - private static final String PROJECT_ROOT_ADDITIONAL_PATH = ""; - private static final OftTraceSettingsSnapshot DEFAULT_SETTINGS = OftTraceSettingsSnapshot.DEFAULT; - @Test void testGivenNullBasePathWhenResolvingProjectRootThenItReturnsAnInvalidResolution() { - final OftTraceInputResolution resolution = OftTraceInputResolver.resolveProjectRoot(null, OftTraceSettingsSnapshot.DEFAULT); + final OftTraceInputResolution resolution = OftTraceInputResolver.resolveProjectRoot(null); Assertions.assertAll( () -> assertThat(resolution.isValid(), is(false)), @@ -45,7 +33,7 @@ void testGivenNullBasePathWhenResolvingProjectRootThenItReturnsAnInvalidResoluti @Test void testGivenBlankBasePathWhenResolvingProjectRootThenItReturnsAnInvalidResolution() { - final OftTraceInputResolution resolution = OftTraceInputResolver.resolveProjectRoot(" ", OftTraceSettingsSnapshot.DEFAULT); + final OftTraceInputResolution resolution = OftTraceInputResolver.resolveProjectRoot(" "); Assertions.assertAll( () -> assertThat(resolution.isValid(), is(false)), @@ -61,7 +49,7 @@ void testGivenMissingDirectoryWhenResolvingProjectRootThenItReturnsAnInvalidReso @TempDir final Path temporaryDirectory ) { final OftTraceInputResolution resolution = - OftTraceInputResolver.resolveProjectRoot(temporaryDirectory.resolve("missing").toString(), OftTraceSettingsSnapshot.DEFAULT); + OftTraceInputResolver.resolveProjectRoot(temporaryDirectory.resolve("missing").toString()); Assertions.assertAll( () -> assertThat(resolution.isValid(), is(false)), @@ -75,7 +63,7 @@ void testGivenFilePathWhenResolvingProjectRootThenItReturnsAnInvalidResolution( ) throws IOException { final Path file = Files.writeString(temporaryDirectory.resolve("build.gradle.kts"), "plugins {}"); - final OftTraceInputResolution resolution = OftTraceInputResolver.resolveProjectRoot(file.toString(), DEFAULT_SETTINGS); + final OftTraceInputResolution resolution = OftTraceInputResolver.resolveProjectRoot(file.toString()); Assertions.assertAll( () -> assertThat(resolution.isValid(), is(false)), @@ -87,10 +75,7 @@ void testGivenFilePathWhenResolvingProjectRootThenItReturnsAnInvalidResolution( void testGivenDirectoryWhenResolvingProjectRootThenItReturnsAWholeProjectResolution( @TempDir final Path temporaryDirectory ) { - final OftTraceInputResolution resolution = OftTraceInputResolver.resolveProjectRoot( - temporaryDirectory.toString(), - DEFAULT_SETTINGS - ); + final OftTraceInputResolution resolution = OftTraceInputResolver.resolveProjectRoot(temporaryDirectory.toString()); Assertions.assertAll( () -> assertThat(resolution.isValid(), is(true)), @@ -101,7 +86,7 @@ void testGivenDirectoryWhenResolvingProjectRootThenItReturnsAWholeProjectResolut @Test void testGivenInvalidBasePathStringWhenResolvingProjectRootThenItReturnsAnInvalidResolution() { - final OftTraceInputResolution resolution = OftTraceInputResolver.resolveProjectRoot("\0", DEFAULT_SETTINGS); + final OftTraceInputResolution resolution = OftTraceInputResolver.resolveProjectRoot("\0"); Assertions.assertAll( () -> assertThat(resolution.isValid(), is(false)), @@ -110,14 +95,12 @@ void testGivenInvalidBasePathStringWhenResolvingProjectRootThenItReturnsAnInvali } @Test - void testGivenWholeProjectSettingsWhenResolvingThenItUsesTheProjectBasePath( - @TempDir final Path temporaryDirectory - ) { + void testGivenWholeProjectSettingsWhenResolvingThenItUsesTheProjectBasePath(@TempDir final Path temporaryDirectory) { final Project project = projectProxy(temporaryDirectory.toString(), null, "trace-project"); final OftTraceInputResolution resolution = OftTraceInputResolver.resolve( project, - OftTraceSettingsSnapshot.DEFAULT + new OftTraceSettingsSnapshot(OftTraceScopeMode.WHOLE_PROJECT, true, true, "doc/") ); Assertions.assertAll( @@ -128,30 +111,35 @@ void testGivenWholeProjectSettingsWhenResolvingThenItUsesTheProjectBasePath( } @Test - void testGivenInvalidBasePathAndValidProjectFilePathWhenResolvingThenItUsesTheProjectFileParent( + // [itest->dsn~add-project-relative-paths-to-selected-resource-trace~1] + void testGivenSelectedResourceSettingsWhenResolvingThenItUsesAdditionalProjectRelativePaths( @TempDir final Path temporaryDirectory ) throws IOException { - final Path projectFile = Files.writeString(temporaryDirectory.resolve("trace-project.ipr"), ""); - final Project project = projectProxy("\0", projectFile.toString(), "trace-project"); + final Path docDirectory = Files.createDirectories(temporaryDirectory.resolve("doc")); + final Path configFile = Files.writeString(temporaryDirectory.resolve("trace.conf"), "value"); + final Project project = projectProxy(temporaryDirectory.toString(), null, "trace-project"); final OftTraceInputResolution resolution = OftTraceInputResolver.resolve( project, - OftTraceSettingsSnapshot.DEFAULT + new OftTraceSettingsSnapshot( + OftTraceScopeMode.SELECTED_RESOURCES, + false, + false, + "doc/\ntrace.conf" + ) ); Assertions.assertAll( () -> assertThat(resolution.isValid(), is(true)), - () -> assertThat(resolution.inputs().inputPaths(), contains(temporaryDirectory)) + () -> assertThat(resolution.inputs().isWholeProject(), is(false)), + () -> assertThat(resolution.inputs().inputPaths(), contains(docDirectory, configFile)) ); } @Test - // [itest->dsn~add-project-relative-paths-to-selected-resource-trace~1] - void testGivenSelectedResourceSettingsWhenResolvingThenItUsesAdditionalProjectRelativePaths( + void testGivenSelectedResourceSettingsWithMissingAdditionalPathWhenResolvingThenItReturnsAnInvalidResolution( @TempDir final Path temporaryDirectory - ) throws IOException { - final Path docDirectory = Files.createDirectories(temporaryDirectory.resolve("doc")); - final Path configFile = Files.writeString(temporaryDirectory.resolve("trace.conf"), "value"); + ) { final Project project = projectProxy(temporaryDirectory.toString(), null, "trace-project"); final OftTraceInputResolution resolution = OftTraceInputResolver.resolve( @@ -160,30 +148,43 @@ void testGivenSelectedResourceSettingsWhenResolvingThenItUsesAdditionalProjectRe OftTraceScopeMode.SELECTED_RESOURCES, false, false, - "doc/\ntrace.conf", - "", - "" + "missing" ) ); Assertions.assertAll( - () -> assertThat(resolution.isValid(), is(true)), - () -> assertThat(resolution.inputs().isWholeProject(), is(false)), - () -> assertThat(resolution.inputs().inputPaths(), contains(docDirectory, configFile)) + () -> assertThat(resolution.isValid(), is(false)), + () -> assertThat(resolution.errorMessage(), Matchers.containsString("does not exist")) + ); + } + + @Test + void testGivenSelectedResourceSettingsWithAbsoluteAdditionalPathWhenResolvingThenItReturnsAnInvalidResolution( + @TempDir final Path temporaryDirectory + ) { + final Project project = projectProxy(temporaryDirectory.toString(), null, "trace-project"); + + final OftTraceInputResolution resolution = OftTraceInputResolver.resolve( + project, + new OftTraceSettingsSnapshot( + OftTraceScopeMode.SELECTED_RESOURCES, + false, + false, + temporaryDirectory.toString() + ) + ); + + Assertions.assertAll( + () -> assertThat(resolution.isValid(), is(false)), + () -> assertThat(resolution.errorMessage(), Matchers.containsString("must be project-relative")) ); } - @ParameterizedTest - @MethodSource("invalidSelectedResourceSettings") - void testGivenSelectedResourceSettingsWithInvalidInputsWhenResolvingThenItReturnsAnInvalidResolution( - final String configuredAdditionalPaths, - final String expectedErrorMessageFragment, + @Test + void testGivenSelectedResourceSettingsWithoutAnyInputsWhenResolvingThenItReturnsAnInvalidResolution( @TempDir final Path temporaryDirectory ) { final Project project = projectProxy(temporaryDirectory.toString(), null, "trace-project"); - final String additionalPathsText = PROJECT_ROOT_ADDITIONAL_PATH.equals(configuredAdditionalPaths) - ? temporaryDirectory.toString() - : configuredAdditionalPaths; final OftTraceInputResolution resolution = OftTraceInputResolver.resolve( project, @@ -191,15 +192,16 @@ void testGivenSelectedResourceSettingsWithInvalidInputsWhenResolvingThenItReturn OftTraceScopeMode.SELECTED_RESOURCES, false, false, - additionalPathsText, - "", "" ) ); Assertions.assertAll( () -> assertThat(resolution.isValid(), is(false)), - () -> assertThat(resolution.errorMessage(), Matchers.containsString(expectedErrorMessageFragment)) + () -> assertThat( + resolution.errorMessage(), + Matchers.containsString("does not resolve to any files or directories") + ) ); } @@ -212,7 +214,7 @@ void testGivenIdeaDirectoryBasePathWhenResolvingThenItUsesTheProjectRoot(@TempDi final OftTraceInputResolution resolution = OftTraceInputResolver.resolve( project, - OftTraceSettingsSnapshot.DEFAULT + new OftTraceSettingsSnapshot(OftTraceScopeMode.WHOLE_PROJECT, true, true, "doc/") ); Assertions.assertAll( @@ -230,22 +232,6 @@ void testGivenInvalidProjectPathWhenNormalizingThenItReturnsTheOriginalValue() ); } - @Test - @SuppressWarnings("unchecked") - void testGivenGuessedProjectDirectoryWhenResolvingFromGuessedDirectoryThenItUsesTheGuessedDirectory( - @TempDir final Path temporaryDirectory - ) throws ReflectiveOperationException { - final Optional resolution = (Optional) invokeStatic( - "resolveFromGuessedProjectDirectory", - new Class[]{Project.class, VirtualFile.class, OftTraceSettingsSnapshot.class}, - projectProxy(null, null, "trace-project"), - virtualFileAt(temporaryDirectory), - OftTraceSettingsSnapshot.DEFAULT - ); - - assertThat(resolution.orElseThrow().inputs().inputPaths(), contains(temporaryDirectory)); - } - @Test void testGivenBlankProjectPathWhenNormalizingThenItReturnsItUnchanged() throws ReflectiveOperationException { assertThat( @@ -281,29 +267,6 @@ void testGivenProjectFileInsideIdeaDirectoryWhenLookingUpItsParentThenItReturnsT ); } - @Test - void testGivenRegularProjectFileWhenNormalizingThenItReturnsTheParentDirectory( - @TempDir final Path temporaryDirectory - ) throws ReflectiveOperationException, IOException { - final Path projectFile = Files.writeString(temporaryDirectory.resolve("project.ipr"), ""); - - assertThat( - invokeStatic("normalizeProjectPath", new Class[]{String.class}, projectFile.toString()), - is(temporaryDirectory.toString()) - ); - } - - @Test - void testGivenSourceFolderWithoutVirtualFileWhenResolvingSourceFolderPathThenItReturnsEmpty() - throws ReflectiveOperationException { - final SourceFolder sourceFolder = sourceFolderProxy(null); - - assertThat( - invokeStatic("sourceFolderPath", new Class[]{SourceFolder.class}, sourceFolder), - is(Optional.empty()) - ); - } - @Test void testGivenRootPathWhenCheckingIdeaDirectoryThenItReturnsFalse() throws ReflectiveOperationException { final Path rootPath = FileSystems.getDefault().getRootDirectories().iterator().next(); @@ -314,15 +277,6 @@ void testGivenRootPathWhenCheckingIdeaDirectoryThenItReturnsFalse() throws Refle ); } - private static Stream invalidSelectedResourceSettings() { - return Stream.of( - Arguments.of("missing", "does not exist"), - Arguments.of(PROJECT_ROOT_ADDITIONAL_PATH, "must be project-relative"), - Arguments.of("bad\0path", "path is invalid"), - Arguments.of("", "does not resolve to any files or directories") - ); - } - private Project projectProxy(final String basePath, final String projectFilePath, final String name) { return (Project) Proxy.newProxyInstance( Project.class.getClassLoader(), @@ -341,35 +295,7 @@ private Project projectProxy(final String basePath, final String projectFilePath ); } - private static SourceFolder sourceFolderProxy(final VirtualFile file) { - return (SourceFolder) Proxy.newProxyInstance( - SourceFolder.class.getClassLoader(), - new Class[]{SourceFolder.class}, - (proxy, method, args) -> switch (method.getName()) { - case "getFile" -> file; - case "isTestSource" -> false; - case "equals" -> proxy == args[0]; - case "hashCode" -> System.identityHashCode(proxy); - case "toString" -> "SourceFolder[" + file + "]"; - default -> throw new UnsupportedOperationException(method.getName()); - } - ); - } - - private static VirtualFile virtualFileAt(final Path path) { - return new LightVirtualFile(path.getFileName().toString()) { - @Override - public @NonNull String getPath() { - return path.toString(); - } - }; - } - - private static Object invokeStatic( - final String methodName, - final Class[] parameterTypes, - final Object... arguments - ) + private static Object invokeStatic(final String methodName, final Class[] parameterTypes, final Object... arguments) throws ReflectiveOperationException { final Method method = OftTraceInputResolver.class.getDeclaredMethod(methodName, parameterTypes); method.setAccessible(true); diff --git a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectActionTest.java b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectActionTest.java index 718fd8b..ea9c252 100644 --- a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectActionTest.java +++ b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectActionTest.java @@ -21,7 +21,6 @@ import java.lang.reflect.Proxy; import java.nio.file.Path; import java.util.Arrays; -import java.util.List; import java.util.concurrent.atomic.AtomicReference; import static org.hamcrest.MatcherAssert.assertThat; @@ -33,7 +32,6 @@ import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; -// [itest->dsn~trace-action-integration~2] public class OftTraceProjectActionTest extends AbstractOftPlatformTestCase { // [itest->dsn~show-trace-project-action-in-tools-menu~1] public void testGivenPluginIsLoadedWhenToolsMenuActionIsQueriedThenTheTraceActionAndGroupAreRegistered() { @@ -76,14 +74,6 @@ public void testGivenProjectWhenActionUpdatesThenPresentationIsEnabled() { assertThat(presentation.isEnabled(), is(true)); } - // [itest->dsn~show-trace-project-in-test-runner-ui-by-default~1] - public void testGivenDefaultActionWhenCreatingDefaultPresenterThenItUsesTestRunnerRunContentPresenter() { - assertThat( - OftTraceProjectAction.createDefaultOutputPresenter(), - instanceOf(OftTraceTestRunnerRunContentOutputPresenter.class) - ); - } - // [itest->dsn~disable-trace-project-action-without-open-project~1] public void testGivenNoProjectWhenActionUpdatesThenPresentationIsDisabled() { final OftTraceProjectAction action = new OftTraceProjectAction(); @@ -101,7 +91,7 @@ public void testGivenProjectWhenActionPerformsThenItStartsTheTraceRunnerWithTheP final Path projectRoot = createManagedTestArtifactDirectory("trace-project-action-input"); final Project project = projectProxy(projectRoot.toString(), "valid-project"); final OftTraceProjectAction action = new OftTraceProjectAction( - testProject -> OftTraceInputResolution.valid(OftTraceInputs.wholeProject(projectRoot, List.of(), List.of())), + testProject -> OftTraceInputResolution.valid(OftTraceInputs.wholeProject(projectRoot)), (runnerProject, inputs, contentTitle) -> runnerCall.set(new RunnerCall(runnerProject, inputs, contentTitle)), (presentedProject, contentTitle, result) -> diff --git a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectConfigurableTest.java b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectConfigurableTest.java index 4b6d50d..b6e806c 100644 --- a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectConfigurableTest.java +++ b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectConfigurableTest.java @@ -5,8 +5,6 @@ import org.itsallcode.openfasttrace.intellijplugin.AbstractOftPlatformTestCase; import org.junit.jupiter.api.Assertions; -import javax.swing.JComponent; - public class OftTraceProjectConfigurableTest extends AbstractOftPlatformTestCase { @Override protected void setUp() throws Exception { @@ -22,10 +20,10 @@ public void testGivenDefaultSettingsWhenResettingConfigurableThenItShowsTheDocum configurable.reset(); final OftTraceSettingsSnapshot settings = configurableSettings(configurable); - assertEquals(OftTraceSettingsSnapshot.DEFAULT.scopeMode(), settings.scopeMode()); - assertEquals(OftTraceSettingsSnapshot.DEFAULT.includeSourceRoots(), settings.includeSourceRoots()); - assertEquals(OftTraceSettingsSnapshot.DEFAULT.includeTestRoots(), settings.includeTestRoots()); - assertEquals(OftTraceSettingsSnapshot.DEFAULT.additionalPathsText(), settings.additionalPathsText()); + assertEquals(OftTraceScopeMode.WHOLE_PROJECT, settings.scopeMode()); + assertEquals(true, settings.includeSourceRoots()); + assertEquals(true, settings.includeTestRoots()); + assertEquals("doc/", settings.additionalPathsText()); } public void testGivenDefaultSettingsWhenResettingConfigurableThenSelectedResourceControlsAreDisabled() { @@ -45,9 +43,7 @@ public void testGivenSelectedResourceSettingsWhenApplyingConfigurableThenItUpdat OftTraceScopeMode.SELECTED_RESOURCES, true, false, - "doc/\nspec/", - "", - "" + "doc/\nspec/" )); assertTrue(configurable.isModified()); @@ -55,8 +51,8 @@ public void testGivenSelectedResourceSettingsWhenApplyingConfigurableThenItUpdat final OftTraceSettingsSnapshot snapshot = OftTraceProjectSettings.getInstance(getProject()).snapshot(); assertEquals(OftTraceScopeMode.SELECTED_RESOURCES, snapshot.scopeMode()); - assertTrue(snapshot.includeSourceRoots()); - assertFalse(snapshot.includeTestRoots()); + assertEquals(true, snapshot.includeSourceRoots()); + assertEquals(false, snapshot.includeTestRoots()); assertEquals("doc/\nspec/", snapshot.additionalPathsText()); } @@ -67,9 +63,7 @@ public void testGivenAbsoluteAdditionalPathWhenApplyingConfigurableThenItRejects OftTraceScopeMode.SELECTED_RESOURCES, true, true, - "/absolute/path", - "", - "" + "/absolute/path" )); final ConfigurationException exception = @@ -78,41 +72,6 @@ public void testGivenAbsoluteAdditionalPathWhenApplyingConfigurableThenItRejects assertTrue(exception.getLocalizedMessage().contains("must be project-relative")); } - public void testGivenInvalidAdditionalPathWhenApplyingConfigurableThenItRejectsTheSettings() { - final OftTraceProjectConfigurable configurable = new OftTraceProjectConfigurable(getProject()); - configurable.createComponent(); - component(configurable).setSettings(new OftTraceSettingsSnapshot( - OftTraceScopeMode.SELECTED_RESOURCES, - true, - true, - "bad\0path", - "", - "" - )); - - final ConfigurationException exception = - Assertions.assertThrows(ConfigurationException.class, configurable::apply); - - assertTrue(exception.getLocalizedMessage().contains("Additional trace path is invalid")); - } - - public void testWhenReadingConfigurableMetadataThenItReturnsTheRegisteredName() { - final OftTraceProjectConfigurable configurable = new OftTraceProjectConfigurable(getProject()); - - assertEquals(OftTraceProjectConfigurable.ID, configurable.getId()); - assertEquals("OpenFastTrace", configurable.getDisplayName()); - } - - public void testWhenDisposingConfigurableThenItReleasesTheComponent() { - final OftTraceProjectConfigurable configurable = new OftTraceProjectConfigurable(getProject()); - final JComponent firstComponent = configurable.createComponent(); - - configurable.disposeUIResources(); - final JComponent secondComponent = configurable.createComponent(); - - assertNotSame(firstComponent, secondComponent); - } - // [itest->dsn~show-per-line-validation-for-additional-trace-paths~1] public void testGivenSelectedResourceSettingsWithMissingPathWhenResettingConfigurableThenItShowsPerLineValidation() throws Exception { @@ -122,9 +81,7 @@ public void testGivenSelectedResourceSettingsWithMissingPathWhenResettingConfigu OftTraceScopeMode.SELECTED_RESOURCES, true, true, - "doc/\nmissing", - "", - "" + "doc/\nmissing" )); final OftTraceProjectConfigurable configurable = new OftTraceProjectConfigurable(project); configurable.createComponent(); @@ -149,42 +106,6 @@ public void testGivenWholeProjectModeWhenResettingConfigurableThenItDoesNotShowP assertEquals("", component(configurable).validationMessagesText()); } - public void testGivenBlankProjectBasePathWhenShowingSelectedResourceSettingsThenItDoesNotShowValidationRoot() { - final Project project = projectProxy(" ", "blank-root-project"); - final OftTraceProjectConfigurable configurable = new OftTraceProjectConfigurable(project); - configurable.createComponent(); - - component(configurable).setSettings(new OftTraceSettingsSnapshot( - OftTraceScopeMode.SELECTED_RESOURCES, - OftTraceSettingsSnapshot.DEFAULT.includeSourceRoots(), - OftTraceSettingsSnapshot.DEFAULT.includeTestRoots(), - OftTraceSettingsSnapshot.DEFAULT.additionalPathsText(), - OftTraceSettingsSnapshot.DEFAULT.artifactTypesText(), - OftTraceSettingsSnapshot.DEFAULT.tagsText() - )); - - assertEquals("", component(configurable).resolvedRelativeToText()); - assertEquals("", component(configurable).validationMessagesText()); - } - - public void testGivenInvalidProjectBasePathWhenShowingSelectedResourceSettingsThenItDoesNotShowValidationRoot() { - final Project project = projectProxy("bad\0path", "invalid-root-project"); - final OftTraceProjectConfigurable configurable = new OftTraceProjectConfigurable(project); - configurable.createComponent(); - - component(configurable).setSettings(new OftTraceSettingsSnapshot( - OftTraceScopeMode.SELECTED_RESOURCES, - OftTraceSettingsSnapshot.DEFAULT.includeSourceRoots(), - OftTraceSettingsSnapshot.DEFAULT.includeTestRoots(), - OftTraceSettingsSnapshot.DEFAULT.additionalPathsText(), - OftTraceSettingsSnapshot.DEFAULT.artifactTypesText(), - OftTraceSettingsSnapshot.DEFAULT.tagsText() - )); - - assertEquals("", component(configurable).resolvedRelativeToText()); - assertEquals("", component(configurable).validationMessagesText()); - } - private static OftTraceSettingsSnapshot configurableSettings(final OftTraceProjectConfigurable configurable) { return component(configurable).getSettings(); } diff --git a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectSettingsTest.java b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectSettingsTest.java index 903378d..c2ccf3a 100644 --- a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectSettingsTest.java +++ b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceProjectSettingsTest.java @@ -15,11 +15,10 @@ protected void setUp() throws Exception { public void testGivenFreshProjectSettingsWhenReadingThenItReturnsTheDocumentedDefaults() { final OftTraceSettingsSnapshot snapshot = OftTraceProjectSettings.getInstance(getProject()).snapshot(); - assertThat(snapshot.scopeMode(), is(OftTraceSettingsSnapshot.DEFAULT.scopeMode())); - assertThat(snapshot.includeSourceRoots(), is(OftTraceSettingsSnapshot.DEFAULT.includeSourceRoots())); - assertThat(snapshot.includeTestRoots(), is(OftTraceSettingsSnapshot.DEFAULT.includeTestRoots())); - assertThat(snapshot.additionalPathsText(), is(OftTraceSettingsSnapshot.DEFAULT.additionalPathsText())); - assertThat(snapshot.resultView(), is(OftTraceSettingsSnapshot.DEFAULT.resultView())); + assertThat(snapshot.scopeMode(), is(OftTraceScopeMode.WHOLE_PROJECT)); + assertThat(snapshot.includeSourceRoots(), is(true)); + assertThat(snapshot.includeTestRoots(), is(true)); + assertThat(snapshot.additionalPathsText(), is("doc/")); } public void testGivenStoredStateWhenLoadingThenItNormalizesTheSnapshot() { diff --git a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceRunContentOutputPresenterTest.java b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceRunContentOutputPresenterTest.java index 90b0275..e754167 100644 --- a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceRunContentOutputPresenterTest.java +++ b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceRunContentOutputPresenterTest.java @@ -15,7 +15,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.charset.StandardCharsets; -import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; @@ -26,7 +25,6 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -// [itest->dsn~trace-output-presentation~1] public class OftTraceRunContentOutputPresenterTest extends AbstractOftPlatformTestCase { private static final Pattern ANSI_ESCAPE_SEQUENCE = Pattern.compile("\u001B\\[[;\\d]*m"); private static final String FIRST_ITEM_ID = "req~long_requirement_00000~1"; @@ -37,7 +35,7 @@ public void testGivenTwoThousandUncoveredRequirementsWhenPresentedThenTheIdeCons final Path temporaryDirectory = createManagedTestArtifactDirectory("trace-run-content-output-presenter-input"); writeLongFailingTraceProject(temporaryDirectory, 2000); final OftTraceResult result = new OftTraceService().traceProject( - OftTraceInputs.wholeProject(temporaryDirectory, List.of(), List.of()), + OftTraceInputs.wholeProject(temporaryDirectory), OftTraceProgress.NONE ); final String renderedOutput = stripAnsi(result.output()); @@ -178,7 +176,7 @@ abstract class AbstractOftPlatformTestCase { } } - public void testGivenTraceOutputWhenPresentedThenRunContentDescriptorGetsAnExecutionId() { + public void testGivenTraceOutputWhenPresentedThenRunContentDescriptorGetsAnId() { final AtomicReference descriptorRef = new AtomicReference<>(); final OftTraceRunContentOutputPresenter presenter = new OftTraceRunContentOutputPresenter( OftTraceRunContentOutputPresenter::createTraceConsole, @@ -195,7 +193,8 @@ public void testGivenTraceOutputWhenPresentedThenRunContentDescriptorGetsAnExecu )); assertThat(descriptorRef.get(), notNullValue()); - assertThat(descriptorRef.get().getExecutionId(), Matchers.greaterThan(0L)); + assertThat(descriptorRef.get().getId(), notNullValue()); + assertThat(descriptorRef.get().getId().getUid(), Matchers.greaterThan(0)); } private String readConsoleText(final ConsoleViewImpl console) { diff --git a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceServiceTest.java b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceServiceTest.java index 9f2696e..cf3bcc2 100644 --- a/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceServiceTest.java +++ b/src/test/java/org/itsallcode/openfasttrace/intellijplugin/trace/OftTraceServiceTest.java @@ -3,7 +3,6 @@ import org.itsallcode.openfasttrace.core.Oft; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -12,24 +11,22 @@ import java.nio.file.Files; import java.nio.file.Path; import java.net.URLClassLoader; -import java.util.List; import java.util.regex.Pattern; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -// [itest->dsn~trace-execution-service~1] class OftTraceServiceTest { private static final Pattern ANSI_ESCAPE_SEQUENCE = Pattern.compile("\u001B\\[[;\\d]*m"); - // [itest->dsn~show-successful-trace-output-in-ide-output-window~2] + // [itest->dsn~show-successful-trace-output-in-ide-output-window~1] @Test void testGivenCleanTraceInputWhenTracingThenItReturnsSuccessfulPlainTextOutput(@TempDir final Path temporaryDirectory) throws IOException { writeSuccessfulTraceProject(temporaryDirectory); final OftTraceResult result = new OftTraceService().traceProject( - OftTraceInputs.wholeProject(temporaryDirectory, List.of(), List.of()), + OftTraceInputs.wholeProject(temporaryDirectory), OftTraceProgress.NONE ); final String renderedOutput = stripAnsi(result.output()); @@ -44,9 +41,7 @@ void testGivenCleanTraceInputWhenTracingThenItReturnsSuccessfulPlainTextOutput(@ ), () -> assertThat(result.output(), Matchers.containsString("\u001B[")), () -> assertThat(renderedOutput, Matchers.containsString("ok -")), - () -> assertThat(renderedOutput, Matchers.not(Matchers.containsString("not ok"))), - // [itest->dsn~trace-test-runner-presentation~1] - () -> assertThat(result.trace().isPresent(), is(true)) + () -> assertThat(renderedOutput, Matchers.not(Matchers.containsString("not ok"))) ); } @@ -63,7 +58,7 @@ void testGivenForeignThreadContextClassLoaderWhenTracingThenItStillImportsProjec currentThread.setContextClassLoader(foreignClassLoader); final OftTraceResult result = new OftTraceService().traceProject( - OftTraceInputs.wholeProject(temporaryDirectory, List.of(), List.of()), + OftTraceInputs.wholeProject(temporaryDirectory), OftTraceProgress.NONE ); final String renderedOutput = stripAnsi(result.output()); @@ -86,7 +81,7 @@ void testGivenDefectiveTraceInputWhenTracingThenItReturnsFailingPlainTextOutput( writeFailingTraceProject(temporaryDirectory); final OftTraceResult result = new OftTraceService().traceProject( - OftTraceInputs.wholeProject(temporaryDirectory, List.of(), List.of()), + OftTraceInputs.wholeProject(temporaryDirectory), OftTraceProgress.NONE ); final String renderedOutput = stripAnsi(result.output()); @@ -101,9 +96,7 @@ void testGivenDefectiveTraceInputWhenTracingThenItReturnsFailingPlainTextOutput( ), () -> assertThat(renderedOutput, Matchers.containsString("not ok")), () -> assertThat(renderedOutput, Matchers.containsString("req~trace_output_requirement~1")), - () -> assertThat(result.output(), Matchers.containsString("\u001B[")), - // [itest->dsn~trace-test-runner-presentation~1] - () -> assertThat(result.trace().isPresent(), is(true)) + () -> assertThat(result.output(), Matchers.containsString("\u001B[")) ); } @@ -116,7 +109,7 @@ void testGivenTraceInputWhenTracingThenItWritesTheScannedBaseDirectoryIntoTheTex writeSuccessfulTraceProject(temporaryDirectory); final OftTraceResult result = new OftTraceService().traceProject( - OftTraceInputs.wholeProject(temporaryDirectory, List.of(), List.of()), + OftTraceInputs.wholeProject(temporaryDirectory), OftTraceProgress.NONE ); final String renderedOutput = stripAnsi(result.output()); @@ -143,7 +136,7 @@ void testGivenUncleanTraceChainWhenTracingThenItReportsTheExpectedDefectCountInT writeUncleanTraceChainProject(temporaryDirectory); final OftTraceResult result = new OftTraceService().traceProject( - OftTraceInputs.wholeProject(temporaryDirectory, List.of(), List.of()), + OftTraceInputs.wholeProject(temporaryDirectory), OftTraceProgress.NONE ); final String renderedOutput = stripAnsi(result.output()); @@ -176,7 +169,7 @@ void testGivenRuntimeExceptionWhenTracingThenItReturnsAnErrorResult(@TempDir fin }; final OftTraceResult result = new OftTraceService(oft, renderer).traceProject( - OftTraceInputs.wholeProject(temporaryDirectory, List.of(), List.of()), + OftTraceInputs.wholeProject(temporaryDirectory), OftTraceProgress.NONE ); @@ -184,77 +177,8 @@ void testGivenRuntimeExceptionWhenTracingThenItReturnsAnErrorResult(@TempDir fin () -> assertThat(result.isSuccessful(), is(false)), () -> assertThat(result.statusMessage(), is("OpenFastTrace trace failed unexpectedly.")), () -> assertThat(result.output(), Matchers.containsString("OpenFastTrace trace failed for input path")), - () -> assertThat(result.output(), Matchers.containsString("IllegalStateException: boom")), - // [itest->dsn~trace-test-runner-presentation~1] - () -> assertThat(result.trace().isEmpty(), is(true)) - ); - } - - // [itest->dsn~filter-trace-by-artifact-types-and-tags~1] - @Test - void testGivenArtifactTypeFilterMatchingRequirementWhenTracingThenItIncludesTheRequirement( - @TempDir final Path temporaryDirectory - ) throws IOException { - writeSuccessfulTraceProject(temporaryDirectory); - - final OftTraceResult result = new OftTraceService().traceProject( - OftTraceInputs.selectedResources( - java.util.List.of(temporaryDirectory), - java.util.List.of("feat", "req"), - java.util.List.of() - ), - OftTraceProgress.NONE + () -> assertThat(result.output(), Matchers.containsString("IllegalStateException: boom")) ); - final String renderedOutput = stripAnsi(result.output()); - System.out.println(renderedOutput); - Assertions.assertAll( - () -> assertThat(result.isSuccessful(), is(true)), - () -> assertThat(renderedOutput, Matchers.containsString("ok - 2 total")) - ); - } - - @Disabled("Reanable after https://github.com/itsallcode/openfasttrace/issues/505 is fixed") - @Test - void testGivenTagFilterMatchingArtifactWhenTracingThenItIncludesTheArtifact( - @TempDir final Path temporaryDirectory - ) throws IOException { - final Path docDirectory = Files.createDirectories(temporaryDirectory.resolve("doc")); - Files.writeString( - docDirectory.resolve("tags.md"), - """ - ### Tagged Requirement - `req~tagged_requirement~1` - - Tags: tagged - - Needs: impl - - ### Untagged Requirement - `req~untagged_requirement~1` - - Needs: impl - - ### Tagged Coverage - `impl~tagged_coverage~1` - - Covers: - - `req~tagged_requirement~1` - - Tags: tagged - """ - ); - - final OftTraceResult result = new OftTraceService().traceProject( - OftTraceInputs.selectedResources( - java.util.List.of(temporaryDirectory), - java.util.List.of(), - java.util.List.of("tagged") - ), - OftTraceProgress.NONE - ); - final String renderedOutput = stripAnsi(result.output()); - - assertThat(renderedOutput, Matchers.containsString("ok - 2 total")); } private String stripAnsi(final String output) { @@ -273,7 +197,7 @@ void testGivenSelectedResourceInputsWhenTracingThenItListsConfiguredInputsAndIgn final Path docDirectory = temporaryDirectory.resolve("doc"); final Path sourceDirectory = temporaryDirectory.resolve("src"); final OftTraceResult result = new OftTraceService().traceProject( - OftTraceInputs.selectedResources(java.util.List.of(docDirectory, sourceDirectory), java.util.List.of(), java.util.List.of()), + OftTraceInputs.selectedResources(java.util.List.of(docDirectory, sourceDirectory)), OftTraceProgress.NONE ); final String renderedOutput = stripAnsi(result.output());