diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7255b778..fdccd188 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -55,9 +55,13 @@ jobs: ./gradlew listProductsReleases # prepare list of IDEs for Plugin Verifier # Run tests - - name: Run Tests + - name: Run JUnit 5 Tests run: ./gradlew test -Pidea.home.path=./intellij-community + # Run tests + - name: Run JUnit 3 Tests + run: ./gradlew testWithJunit3 -Pidea.home.path=./intellij-community + # Collect Tests Result of failed tests - name: Collect Tests Result if: ${{ failure() }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c8b2548..b05aa7d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ ## [Unreleased] +## [1.64.0] +### Changed +- New supported IDE version range: 2023.1.6-2024.2-EAP +- Did some optimizations on the unused step definition method inspection. Now it also marks the method name instead of +the entire method. +- Did some optimizations on the undefined step inspection. +- Removed duplicate reporting of steps that don't have step definition methods. +- Removed the class `StoryAnnotator` that didn't seem to do much useful. +- Improved the implementations of JBehave step reference handling. + ## [1.63.0] ### Changed - New supported IDE version range: 2022.2.5-2024.1-EAP diff --git a/build.gradle.kts b/build.gradle.kts index f13fc3e7..20e434ee 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -26,11 +26,13 @@ kotlin { dependencies { //https://kotlinlang.org/docs/reflection.html#jvm-dependency - implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.0") + implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.23") implementation("org.jbehave:jbehave-core:5.2.0") - testImplementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0") - testImplementation("org.assertj:assertj-core:3.25.2") - testImplementation("junit:junit:4.13.2") + testImplementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.23") + testImplementation("org.assertj:assertj-core:3.25.3") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.2") } // Configure Gradle IntelliJ Plugin - read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html @@ -87,14 +89,20 @@ tasks { } test { + useJUnitPlatform() //Required for running tests in 2021.3 due to it not finding test classes properly. //See https://app.slack.com/client/T5P9YATH9/C5U8BM1MK/thread/C5U8BM1MK-1639934273.054400 isScanForTestClasses = false - include("**/*Test.class") - exclude("**/StoryLocalizedLexer_FrenchTest.class") + include("**/codeInspector/*Test.class", "**/resolver/*Test.class", "**/utility/*Test.class", "**/service/*Test.class") + exclude("**/highlighter/*Test.class", "**/parser/*Test.class", "**/spellchecker/*Test.class", "**/structure/*Test.class") } // runPluginVerifier { // ideVersions.set(listOf("IC-232.7754.73")) // } } + +tasks.register("testWithJunit3") { + include("**/highlighter/*Test.class", "**/parser/*Test.class", "**/spellchecker/*Test.class", "**/structure/*Test.class") + exclude("**/highlighter/StoryLocalizedLexer_FrenchTest.class") +} diff --git a/gradle.properties b/gradle.properties index 362b00a2..c62647f9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,31 +4,28 @@ pluginGroup = com.github.kumaraman21.intellijbehave pluginName = JBehave Support pluginRepositoryUrl = https://github.com/witspirit/IntelliJBehave # SemVer format -> https://semver.org -pluginVersion = 1.63.0 +pluginVersion = 1.64.0 # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html -pluginSinceBuild = 222.4554.10 -pluginUntilBuild = 241.* +pluginSinceBuild = 231.9414.13 +pluginUntilBuild = 242.* # IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension platformType = IC -platformVersion = 2022.2.5 +platformVersion = 2023.1.6 # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html # Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22 platformPlugins =java,Kotlin # Gradle Releases -> https://github.com/gradle/gradle/releases -gradleVersion = 8.5 +gradleVersion = 8.6 # Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib kotlin.stdlib.default.dependency = false # Enable Gradle Configuration Cache -> https://docs.gradle.org/current/userguide/configuration_cache.html -org.gradle.unsafe.configuration-cache = false +org.gradle.configuration-cache = true # Enable Gradle Build Cache -> https://docs.gradle.org/current/userguide/build_cache.html org.gradle.caching = true - -# Enable Gradle Kotlin DSL Lazy Property Assignment -> https://docs.gradle.org/current/userguide/kotlin_dsl.html#kotdsl:assignment -systemProp.org.gradle.unsafe.kotlin.assignment = true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f9ecd57b..de27609c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,11 +1,11 @@ [versions] # libraries -annotations = "24.0.1" +annotations = "24.1.0" # plugins -kotlin = "1.9.0" -changelog = "2.1.2" -gradleIntelliJPlugin = "1.17.0" +kotlin = "1.9.23" +changelog = "2.2.0" +gradleIntelliJPlugin = "1.17.2" [libraries] annotations = { group = "org.jetbrains", name = "annotations", version.ref = "annotations" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c..d64cd491 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e093..a80b22ce 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/codeInspector/UndefinedStepInspection.java b/src/main/java/com/github/kumaraman21/intellijbehave/codeInspector/UndefinedStepInspection.java index 963e8513..886c34cc 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/codeInspector/UndefinedStepInspection.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/codeInspector/UndefinedStepInspection.java @@ -30,6 +30,9 @@ import com.intellij.psi.PsiReference; import org.jetbrains.annotations.NotNull; +/** + * Reports JBehave steps in Story files that have no Java step definition methods. + */ public class UndefinedStepInspection extends LocalInspectionTool { @NotNull @@ -44,27 +47,23 @@ public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, bool return new PsiElementVisitor() { @Override - public void visitElement(PsiElement psiElement) { + public void visitElement(@NotNull PsiElement psiElement) { super.visitElement(psiElement); - if (!(psiElement instanceof JBehaveStep)) { + if (!(psiElement instanceof JBehaveStep step)) { return; } - JBehaveStep step = (JBehaveStep) psiElement; PsiReference[] references = step.getReferences(); - if (references.length != 1 || !(references[0] instanceof StepPsiReference)) { - return; - } + if (references.length == 1 && references[0] instanceof StepPsiReference reference) { + JavaStepDefinition definition = reference.resolveToDefinition(); - StepPsiReference reference = (StepPsiReference) references[0]; - JavaStepDefinition definition = reference.resolveToDefinition(); - - if (definition == null) { - holder.registerProblem(step, "Step #ref is not defined"); - } else { - highlightParameters(step, definition, holder); + if (definition == null) { + holder.registerProblem(step, "Step #ref is not defined"); + } else { + highlightParameters(step, definition, holder); + } } } }; @@ -73,24 +72,22 @@ public void visitElement(PsiElement psiElement) { private void highlightParameters(JBehaveStep step, JavaStepDefinition javaStepDefinition, ProblemsHolder holder) { String stepText = step.getStepText(); - String annotationText = javaStepDefinition.getAnnotationTextFor(stepText); - ParametrizedString pString = new ParametrizedString(annotationText); int offset = step.getStepTextOffset(); - for (StringToken token : pString.tokenize(stepText)) { + for (StringToken token : new ParametrizedString(annotationText).tokenize(stepText)) { int length = token.getValue().length(); if (token.isIdentifier()) { - registerHiglighting(StorySyntaxHighlighter.TABLE_CELL, step, TextRange.from(offset, length), holder); + registerHighlighting(StorySyntaxHighlighter.TABLE_CELL, step, TextRange.from(offset, length), holder); } offset += length; } } - private static void registerHiglighting(TextAttributesKey attributesKey, - JBehaveStep step, - TextRange range, - ProblemsHolder holder) { + private static void registerHighlighting(TextAttributesKey attributesKey, + JBehaveStep step, + TextRange range, + ProblemsHolder holder) { final ProblemDescriptor descriptor = new ProblemDescriptorImpl( step, step, "", LocalQuickFix.EMPTY_ARRAY, ProblemHighlightType.INFORMATION, false, range, false, null, diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/codeInspector/UnusedStepDeclarationInspection.java b/src/main/java/com/github/kumaraman21/intellijbehave/codeInspector/UnusedStepDeclarationInspection.java index 8e680c8a..c94ae18e 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/codeInspector/UnusedStepDeclarationInspection.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/codeInspector/UnusedStepDeclarationInspection.java @@ -15,27 +15,32 @@ */ package com.github.kumaraman21.intellijbehave.codeInspector; +import static com.github.kumaraman21.intellijbehave.service.JBehaveUtil.isStepDefinition; + import com.github.kumaraman21.intellijbehave.parser.JBehaveStep; import com.github.kumaraman21.intellijbehave.parser.StoryFile; import com.github.kumaraman21.intellijbehave.resolver.StepPsiReference; import com.github.kumaraman21.intellijbehave.service.JavaStepDefinition; import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool; import com.intellij.codeInspection.ProblemsHolder; -import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ContentIterator; import com.intellij.openapi.roots.ProjectRootManager; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.*; +import com.intellij.psi.JavaElementVisitor; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiManager; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiReference; import org.jetbrains.annotations.NotNull; import java.util.HashSet; -import java.util.List; import java.util.Set; -import static com.github.kumaraman21.intellijbehave.service.JBehaveUtil.isStepDefinition; - +/** + * Reports Java JBehave step definition methods when they are not used in any Story file. + */ public class UnusedStepDeclarationInspection extends AbstractBaseJavaLocalInspectionTool { @NotNull @Override @@ -48,10 +53,8 @@ public String getShortName() { public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { return new JavaElementVisitor() { @Override - public void visitMethod(final PsiMethod method) { - Boolean isStepDefinition = ApplicationManager.getApplication().runReadAction((Computable) () -> isStepDefinition(method)); - - if (!isStepDefinition) { + public void visitMethod(final @NotNull PsiMethod method) { + if (method.getNameIdentifier() == null || !ReadAction.compute(() -> isStepDefinition(method))) { return; } @@ -63,26 +66,26 @@ public void visitMethod(final PsiMethod method) { for (JBehaveStep step : stepUsages) { PsiReference[] references = step.getReferences(); - if (references.length != 1 || !(references[0] instanceof StepPsiReference)) { - return; - } - - StepPsiReference reference = (StepPsiReference) references[0]; - JavaStepDefinition definition = reference.resolveToDefinition(); + if (references.length == 1 && references[0] instanceof StepPsiReference reference) { + JavaStepDefinition definition = reference.resolveToDefinition(); - if (definition != null && definition.getAnnotatedMethod() != null && definition.getAnnotatedMethod().isEquivalentTo(method)) { - return; + if (definition != null) { + PsiMethod annotatedMethod = definition.getAnnotatedMethod(); + if (annotatedMethod != null && annotatedMethod.isEquivalentTo(method)) { + return; + } + } } } - holder.registerProblem(method, "Step #ref is never used"); + holder.registerProblem(method.getNameIdentifier(), "Step #ref is never used"); } }; } private static class StepUsageFinder implements ContentIterator { - private Project project; - private Set stepUsages = new HashSet<>(); + private final Project project; + private final Set stepUsages = new HashSet<>(); private StepUsageFinder(Project project) { this.project = project; @@ -94,10 +97,8 @@ public boolean processFile(VirtualFile virtualFile) { return true; } - PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile); - if (psiFile instanceof StoryFile) { - List steps = ((StoryFile) psiFile).getSteps(); - stepUsages.addAll(steps); + if (PsiManager.getInstance(project).findFile(virtualFile) instanceof StoryFile storyFile) { + stepUsages.addAll(storyFile.getSteps()); } return true; } diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/completion/StoryCompletionContributor.java b/src/main/java/com/github/kumaraman21/intellijbehave/completion/StoryCompletionContributor.java index 535db7d4..c7dced96 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/completion/StoryCompletionContributor.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/completion/StoryCompletionContributor.java @@ -21,13 +21,12 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.lang.ASTNode; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiElement; import com.intellij.util.Consumer; +import org.jetbrains.annotations.NotNull; /** * @author @aloyer @@ -38,8 +37,8 @@ public StoryCompletionContributor() { } @Override - public void fillCompletionVariants(CompletionParameters parameters, final CompletionResultSet _result) { - if (parameters.getCompletionType() == CompletionType.BASIC && getIsStoryAutoCompletion()) { + public void fillCompletionVariants(CompletionParameters parameters, @NotNull final CompletionResultSet _result) { + if (parameters.getCompletionType() == CompletionType.BASIC && JBehaveSettings.getInstance().isStoryAutoCompletion()) { String prefix = CompletionUtil.findReferenceOrAlphanumericPrefix(parameters); CompletionResultSet result = _result.withPrefixMatcher(prefix); @@ -53,13 +52,6 @@ public void fillCompletionVariants(CompletionParameters parameters, final Comple } } - private boolean getIsStoryAutoCompletion() { - Application application = ApplicationManager.getApplication(); - JBehaveSettings component = application.getService(JBehaveSettings.class); - - return component.isStoryAutoCompletion(); - } - private LocalizedKeywords lookupLocalizedKeywords(CompletionParameters parameters) { String locale = "en"; ASTNode localeNode = parameters.getOriginalFile().getNode().findChildByType(StoryTokenType.COMMENT_WITH_LOCALE); @@ -133,12 +125,12 @@ private static boolean isStepTypeComplete(LocalizedKeywords keywords, String inp private static JBehaveStep getStepPsiElement(CompletionParameters parameters) { PsiElement position = parameters.getPosition(); PsiElement positionParent = position.getParent(); - if (positionParent instanceof JBehaveStep) { - return (JBehaveStep) positionParent; - } else if (position instanceof StepPsiReference) { - return ((StepPsiReference) position).getElement(); - } else if (position instanceof JBehaveStep) { - return (JBehaveStep) position; + if (positionParent instanceof JBehaveStep step) { + return step; + } else if (position instanceof StepPsiReference reference) { + return reference.getElement(); + } else if (position instanceof JBehaveStep step) { + return step; } else { return null; } @@ -166,20 +158,19 @@ private StepSuggester(PrefixMatcher prefixMatcher, @Override public boolean processStepDefinition(StepDefinitionAnnotation stepDefinitionAnnotation) { - StepType annotationStepType = stepDefinitionAnnotation.getStepType(); + StepType annotationStepType = stepDefinitionAnnotation.stepType(); if (annotationStepType != getStepType()) { return true; } - String annotationText = stepDefinitionAnnotation.getAnnotationText(); + String annotationText = stepDefinitionAnnotation.annotationText(); String adjustedAnnotationText = actualStepPrefix + " " + annotationText; - ParametrizedString pString = new ParametrizedString(adjustedAnnotationText); - String complete = pString.complete(textBeforeCaret); + String complete = new ParametrizedString(adjustedAnnotationText).complete(textBeforeCaret); if (StringUtil.isNotEmpty(complete)) { - PsiAnnotation matchingAnnotation = stepDefinitionAnnotation.getAnnotation(); + PsiAnnotation matchingAnnotation = stepDefinitionAnnotation.annotation(); consumer.consume(LookupElementBuilder.create(matchingAnnotation, textBeforeCaret + complete)); } else if (prefixMatcher.prefixMatches(adjustedAnnotationText)) { - PsiAnnotation matchingAnnotation = stepDefinitionAnnotation.getAnnotation(); + PsiAnnotation matchingAnnotation = stepDefinitionAnnotation.annotation(); consumer.consume(LookupElementBuilder.create(matchingAnnotation, adjustedAnnotationText)); } return true; diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/kotlin/KotlinConfig.kt b/src/main/java/com/github/kumaraman21/intellijbehave/kotlin/KotlinConfig.kt index 78ecca57..aaa03aaf 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/kotlin/KotlinConfig.kt +++ b/src/main/java/com/github/kumaraman21/intellijbehave/kotlin/KotlinConfig.kt @@ -6,6 +6,6 @@ import com.intellij.openapi.extensions.PluginId /** * Created by Rodrigo Quesada on 20/09/15. */ -private val kotlinPluginId = "org.jetbrains.kotlin" +private const val kotlinPluginId = "org.jetbrains.kotlin" val pluginIsEnabled = PluginManagerCore.getPlugin(PluginId.getId(kotlinPluginId))?.isEnabled ?: false \ No newline at end of file diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/kotlin/support/services/KotlinAnnotationsLoader.kt b/src/main/java/com/github/kumaraman21/intellijbehave/kotlin/support/services/KotlinAnnotationsLoader.kt index c67136ce..792fe91b 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/kotlin/support/services/KotlinAnnotationsLoader.kt +++ b/src/main/java/com/github/kumaraman21/intellijbehave/kotlin/support/services/KotlinAnnotationsLoader.kt @@ -15,28 +15,20 @@ import org.jetbrains.kotlin.psi.KtFunction class KotlinAnnotationsLoader private constructor() { companion object { - - val INSTANCE = KotlinAnnotationsLoader() - @JvmStatic - public fun getInstance() = INSTANCE - } - - fun getAnnotations(qualifiedName: QualifiedName, project: Project, scope: GlobalSearchScope): Collection { - val name = qualifiedName.lastComponent - return if (name != null) { - - KotlinAnnotationsIndex.getInstance().get(name, project, scope).asSequence() - .filterNotNull() - .map({ ktAnnotation -> - + fun getAnnotations(qualifiedName: QualifiedName, project: Project, scope: GlobalSearchScope): Collection { + val name = qualifiedName.lastComponent + return if (name != null) { + KotlinAnnotationsIndex.get(name, project, scope) + .asSequence() + .map { ktAnnotation -> val function = ktAnnotation.parent?.parent as? KtFunction function?.let { val psiAnnotation = LightClassUtil.getLightClassMethod(function)?.modifierList?.findAnnotation(qualifiedName.toString()) psiAnnotation?.let { NavigableKotlinPsiAnnotation(psiAnnotation, ktAnnotation) } } - - }).filterNotNull().toList() - } else emptyList() + }.filterNotNull().toList() + } else emptyList() + } } -} \ No newline at end of file +} diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/kotlin/support/services/KotlinPsiClassesHandler.kt b/src/main/java/com/github/kumaraman21/intellijbehave/kotlin/support/services/KotlinPsiClassesHandler.kt new file mode 100644 index 00000000..678fbce4 --- /dev/null +++ b/src/main/java/com/github/kumaraman21/intellijbehave/kotlin/support/services/KotlinPsiClassesHandler.kt @@ -0,0 +1,55 @@ +package com.github.kumaraman21.intellijbehave.kotlin.support.services + +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.idea.structuralsearch.visitor.KotlinRecursiveElementVisitor +import org.jetbrains.kotlin.idea.util.findAnnotation +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtFile +import kotlin.jvm.internal.Ref.BooleanRef + +/** + * Created by Rodrigo Quesada on 20/09/15. + */ +class KotlinPsiClassesHandler private constructor() { + + companion object { + @JvmStatic + fun getPsiClasses(psiFile: PsiFile): Array? = if (psiFile is KtFile) { + psiFile.classes + } else null + + @JvmStatic + fun isKotlinFile(psiFile: PsiFile): Boolean = psiFile is KtFile + + @JvmStatic + fun visitClasses(file: PsiFile): Boolean { + val hasJBehaveStepDefTestClass = BooleanRef() + if (file is KtFile) { + file.accept(object : KotlinRecursiveElementVisitor() { + override fun visitClass(aClass: KtClass) { + if (isKotlinJBehaveStepDefClass(aClass)) { + hasJBehaveStepDefTestClass.element = true + return + } + super.visitClass(aClass) + } + }) + } + + return hasJBehaveStepDefTestClass.element + } + + private fun isKotlinJBehaveStepDefClass(aClass: KtClass): Boolean { + return !aClass.isEnum() && !aClass.isInterface() && aClass.fqName != null && aClass.body?.functions?.any { + it.findAnnotation(FqName("org.jbehave.core.annotations.Given")) != null + || it.findAnnotation(FqName("org.jbehave.core.annotations.When")) != null + || it.findAnnotation(FqName("org.jbehave.core.annotations.Then")) != null + || it.findAnnotation(FqName("org.jbehave.core.annotations.Alias")) != null + || it.findAnnotation(FqName("org.jbehave.core.annotations.Aliases")) != null + || it.findAnnotation(FqName("org.jbehave.core.annotations.Composite")) != null + } == true + } + } +} diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/kotlin/support/services/KotlinPsiClassesLoader.kt b/src/main/java/com/github/kumaraman21/intellijbehave/kotlin/support/services/KotlinPsiClassesLoader.kt deleted file mode 100644 index 36606be9..00000000 --- a/src/main/java/com/github/kumaraman21/intellijbehave/kotlin/support/services/KotlinPsiClassesLoader.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.kumaraman21.intellijbehave.kotlin.support.services - -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiFile -import org.jetbrains.kotlin.psi.KtFile - -/** - * Created by Rodrigo Quesada on 20/09/15. - */ -class KotlinPsiClassesLoader private constructor() { - - companion object { - - val INSTANCE = KotlinPsiClassesLoader(); - - @JvmStatic - public fun getInstance() = INSTANCE - } - - fun getPsiClasses(psiFile: PsiFile): List? = if (psiFile is KtFile) { - psiFile.classes.toList() - } - else null -} \ No newline at end of file diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/resolver/JBehaveStepReferenceProvider.java b/src/main/java/com/github/kumaraman21/intellijbehave/resolver/JBehaveStepReferenceProvider.java index 25c5ef3a..2505a3ba 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/resolver/JBehaveStepReferenceProvider.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/resolver/JBehaveStepReferenceProvider.java @@ -8,16 +8,12 @@ import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; -public class JBehaveStepReferenceProvider extends PsiReferenceProvider { +public final class JBehaveStepReferenceProvider extends PsiReferenceProvider { @NotNull @Override - public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) { - if (element instanceof JBehaveStep) { - final JBehaveStep step = (JBehaveStep) element; - - return new PsiReference[]{new StepPsiReference(step, TextRange.from(0, element.getTextLength()))}; - } - - return PsiReference.EMPTY_ARRAY; + public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) { + return element instanceof JBehaveStep step + ? new PsiReference[]{new StepPsiReference(step, TextRange.from(0, element.getTextLength()))} + : PsiReference.EMPTY_ARRAY; } } diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotation.java b/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotation.java index e91ea441..6f44427d 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotation.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotation.java @@ -18,26 +18,5 @@ import com.intellij.psi.PsiAnnotation; import org.jbehave.core.steps.StepType; -public class StepDefinitionAnnotation { - private final StepType stepType; - private final String annotationText; - private final PsiAnnotation annotation; - - public StepDefinitionAnnotation(StepType stepType, String annotationText, PsiAnnotation annotation) { - this.stepType = stepType; - this.annotationText = annotationText; - this.annotation = annotation; - } - - public String getAnnotationText() { - return annotationText; - } - - public StepType getStepType() { - return stepType; - } - - public PsiAnnotation getAnnotation() { - return annotation; - } +public record StepDefinitionAnnotation(StepType stepType, String annotationText, PsiAnnotation annotation) { } diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotationConverter.java b/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotationConverter.java index 91ec72ba..14ed13fc 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotationConverter.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionAnnotationConverter.java @@ -29,9 +29,9 @@ import static com.github.kumaraman21.intellijbehave.utility.StepTypeMappings.ANNOTATION_TO_STEP_TYPE_MAPPING; import static org.apache.commons.lang.StringUtils.*; -public class StepDefinitionAnnotationConverter { +public final class StepDefinitionAnnotationConverter { - public Set convertFrom(PsiAnnotation[] annotations) { + public static Set convertFrom(PsiAnnotation[] annotations) { Set res = null; StepType stepType = null; @@ -43,10 +43,9 @@ public Set convertFrom(PsiAnnotation[] annotations) { // When there are no attributes for the annotation, we got nothing to do here if (attributes.length > 0) { - if (ANNOTATION_TO_STEP_TYPE_MAPPING.keySet().contains(annotationQualifiedName)) { + if (ANNOTATION_TO_STEP_TYPE_MAPPING.containsKey(annotationQualifiedName)) { stepType = ANNOTATION_TO_STEP_TYPE_MAPPING.get(annotationQualifiedName); String annotationText = getTextFromValue(attributes[0].getValue()); - if (res == null) { res = new HashSet<>(); } @@ -79,8 +78,7 @@ public Set convertFrom(PsiAnnotation[] annotations) { return res == null ? Collections.emptySet() : res; } - private Set getPatternVariants(final StepType stepType, String annotationText, final PsiAnnotation annotation) { - + private static Set getPatternVariants(final StepType stepType, String annotationText, final PsiAnnotation annotation) { return new PatternVariantBuilder(annotationText) .allVariants() .stream() diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionIterator.java b/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionIterator.java index a49d343c..c148f820 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionIterator.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionIterator.java @@ -16,24 +16,28 @@ package com.github.kumaraman21.intellijbehave.resolver; import com.github.kumaraman21.intellijbehave.kotlin.KotlinConfigKt; -import com.github.kumaraman21.intellijbehave.kotlin.support.services.KotlinPsiClassesLoader; +import com.github.kumaraman21.intellijbehave.kotlin.support.services.KotlinPsiClassesHandler; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ContentIterator; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.*; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiClassOwner; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.PsiMethod; import org.jbehave.core.steps.StepType; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.List; import java.util.Objects; -import java.util.Set; +/** + * Processes VirtualFiles to see if step annotations match the pre-configured step type. + */ public abstract class StepDefinitionIterator implements ContentIterator { - - private final StepDefinitionAnnotationConverter stepDefinitionAnnotationConverter = new StepDefinitionAnnotationConverter(); - private StepType stepType; - private Project project; + private final StepType stepType; + private final Project project; public StepDefinitionIterator(@Nullable StepType stepType, Project project) { this.stepType = stepType; @@ -45,34 +49,18 @@ public StepType getStepType() { } @Override - public boolean processFile(VirtualFile virtualFile) { - + public boolean processFile(@NotNull VirtualFile virtualFile) { PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile); - if (psiFile instanceof PsiClassOwner) { - // System.out.println("Virtual File that is a PsiClassOwner: "+virtualFile); - - List psiClasses = null; - if (KotlinConfigKt.getPluginIsEnabled()) { - psiClasses = KotlinPsiClassesLoader.getInstance().getPsiClasses(psiFile); - } - - if (psiClasses == null) psiClasses = Arrays.asList(((PsiClassOwner) psiFile).getClasses()); - - for (PsiClass psiClass : psiClasses) { - PsiMethod[] methods = psiClass.getMethods(); - - for (PsiMethod method : methods) { + if (psiFile instanceof PsiClassOwner psiClassOwner) { + for (PsiClass psiClass : getPsiClasses(psiFile, psiClassOwner)) { + for (PsiMethod method : psiClass.getMethods()) { PsiAnnotation[] annotations = method.getModifierList().getApplicableAnnotations(); - Set stepDefinitionAnnotations = stepDefinitionAnnotationConverter.convertFrom(annotations); - - for (StepDefinitionAnnotation stepDefinitionAnnotation : stepDefinitionAnnotations) { - if (stepType == null || Objects.equals(stepType, stepDefinitionAnnotation.getStepType())) { - boolean shouldContinue = processStepDefinition(stepDefinitionAnnotation); - if (!shouldContinue) { - return false; - } + for (StepDefinitionAnnotation stepDefinitionAnnotation : StepDefinitionAnnotationConverter.convertFrom(annotations)) { + if ((stepType == null || Objects.equals(stepType, stepDefinitionAnnotation.stepType())) + && !processStepDefinition(stepDefinitionAnnotation)) { + return false; } } } @@ -82,6 +70,22 @@ public boolean processFile(VirtualFile virtualFile) { return true; } + /** + * Returns the PSI classes from the provided file and class owner + * + * @param psiFile the Java or Kotlin step definitions file + * @param psiClassOwner {@code psiFile} as a {@link PsiClassOwner} + * @return the classes contained by the file + */ + private static PsiClass[] getPsiClasses(PsiFile psiFile, PsiClassOwner psiClassOwner) { + PsiClass[] psiClasses = null; + if (KotlinConfigKt.getPluginIsEnabled()) { + psiClasses = KotlinPsiClassesHandler.getPsiClasses(psiFile); + } + + return psiClasses != null ? psiClasses : psiClassOwner.getClasses(); + } + public abstract boolean processStepDefinition(StepDefinitionAnnotation stepDefinitionAnnotation); } diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepPsiReference.java b/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepPsiReference.java index ff4251ba..fbac3eb1 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepPsiReference.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepPsiReference.java @@ -23,6 +23,7 @@ import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.ResolveResult; +import com.intellij.psi.impl.PsiManagerEx; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; @@ -30,7 +31,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.List; public class StepPsiReference implements PsiPolyVariantReference { private final JBehaveStep myStep; @@ -42,12 +42,12 @@ public StepPsiReference(JBehaveStep element, TextRange range) { } @Override - public JBehaveStep getElement() { + public @NotNull JBehaveStep getElement() { return myStep; } @Override - public TextRange getRangeInElement() { + public @NotNull TextRange getRangeInElement() { return myRange; } @@ -64,7 +64,7 @@ public String getCanonicalText() { } @Override - public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { + public PsiElement handleElementRename(@NotNull String newElementName) throws IncorrectOperationException { return myStep; } @@ -74,21 +74,29 @@ public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOpe } @Override - public boolean isReferenceTo(PsiElement element) { - ResolveResult[] resolvedResults = multiResolve(false); + public boolean isReferenceTo(@NotNull PsiElement element) { + PsiManagerEx manager = null; - for (ResolveResult resolveResult : resolvedResults) { - if (getElement().getManager().areElementsEquivalent(resolveResult.getElement(), element)) { - return true; + //This part is a simplified version of 'multiResolve()', and using it instead of 'multiResolve()', + // so that unnecessary calls to 'getAnnotatedMethod()' and instantiation of ResolveResults can be avoided. + var resolvedElements = new ArrayList(4); + + for (JavaStepDefinition resolvedStepDefinition : resolveToDefinitions()) { + final PsiMethod method = resolvedStepDefinition.getAnnotatedMethod(); + if (method != null && !resolvedElements.contains(method)) { + if (manager == null) manager = getElement().getManager(); + if (manager.areElementsEquivalent(method, element)) { + return true; + } + resolvedElements.add(method); } } return false; } - @NotNull @Override - public Object[] getVariants() { + public Object @NotNull [] getVariants() { return ArrayUtil.EMPTY_OBJECT_ARRAY; } @@ -97,28 +105,26 @@ public boolean isSoft() { return false; } - @Nullable + /** + * Returns the first Java step definition found for this reference. + */ + @Nullable("When no definition could be resolved.") public JavaStepDefinition resolveToDefinition() { Collection definitions = resolveToDefinitions(); return definitions.isEmpty() ? null : definitions.iterator().next(); } @NotNull - public Collection resolveToDefinitions() { - JBehaveStepsIndex index = JBehaveStepsIndex.getInstance(myStep.getProject()); - return index.findStepDefinitions(myStep); + private Collection resolveToDefinitions() { + return JBehaveStepsIndex.getInstance(myStep.getProject()).findStepDefinitions(myStep); } - @NotNull @Override - public ResolveResult[] multiResolve(boolean incompleteCode) { - List result = new ArrayList<>(); - List resolvedElements = new ArrayList<>(); - - JBehaveStepsIndex index = JBehaveStepsIndex.getInstance(myStep.getProject()); - Collection resolvedStepDefinitions = index.findStepDefinitions(myStep); + public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) { + var result = new ArrayList(4); + var resolvedElements = new ArrayList(4); - for (JavaStepDefinition resolvedStepDefinition : resolvedStepDefinitions) { + for (JavaStepDefinition resolvedStepDefinition : resolveToDefinitions()) { final PsiMethod method = resolvedStepDefinition.getAnnotatedMethod(); if (method != null && !resolvedElements.contains(method)) { resolvedElements.add(method); @@ -134,6 +140,6 @@ public boolean isValidResult() { } } - return result.toArray(new ResolveResult[result.size()]); + return result.toArray(ResolveResult.EMPTY_ARRAY); } } diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepPsiReferenceContributor.java b/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepPsiReferenceContributor.java index dbd028ff..66d70e03 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepPsiReferenceContributor.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StepPsiReferenceContributor.java @@ -7,7 +7,13 @@ import static com.intellij.patterns.PlatformPatterns.psiElement; -public class StepPsiReferenceContributor extends PsiReferenceContributor { +/** + * Provides references for JBehave steps in Story files. + * + * @see JBehaveStepReferenceProvider + * @see StepPsiReference + */ +public final class StepPsiReferenceContributor extends PsiReferenceContributor { @Override public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StoryAnnotator.java b/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StoryAnnotator.java deleted file mode 100644 index 121ff71d..00000000 --- a/src/main/java/com/github/kumaraman21/intellijbehave/resolver/StoryAnnotator.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2011-12 Aman Kumar - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.github.kumaraman21.intellijbehave.resolver; - -import com.github.kumaraman21.intellijbehave.parser.JBehaveStep; -import com.github.kumaraman21.intellijbehave.service.JavaStepDefinition; -import com.github.kumaraman21.intellijbehave.utility.ParametrizedString; -import com.intellij.lang.annotation.AnnotationHolder; -import com.intellij.lang.annotation.Annotator; -import com.intellij.lang.annotation.HighlightSeverity; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiReference; -import org.jetbrains.annotations.NotNull; - -import static com.github.kumaraman21.intellijbehave.utility.ParametrizedString.StringToken; - -public class StoryAnnotator implements Annotator { - @Override - public void annotate(@NotNull PsiElement psiElement, @NotNull AnnotationHolder annotationHolder) { - if (!(psiElement instanceof JBehaveStep)) { - return; - } - - JBehaveStep step = (JBehaveStep) psiElement; - PsiReference[] references = step.getReferences(); - - if (references.length != 1 || !(references[0] instanceof StepPsiReference)) { - return; - } - - StepPsiReference reference = (StepPsiReference) references[0]; - JavaStepDefinition definition = reference.resolveToDefinition(); - - if (definition == null) { - annotationHolder.newAnnotation(HighlightSeverity.ERROR, "No definition found for the step") - .range(psiElement) - .create(); - } else { - annotateParameters(step, definition, annotationHolder); - } - } - - private void annotateParameters(JBehaveStep step, JavaStepDefinition javaStepDefinition, AnnotationHolder annotationHolder) { - String stepText = step.getStepText(); - String annotationText = javaStepDefinition.getAnnotationTextFor(stepText); - ParametrizedString pString = new ParametrizedString(annotationText); - - int offset = step.getTextOffset(); - for (StringToken token : pString.tokenize(stepText)) { - int length = token.getValue().length(); - if (token.isIdentifier()) { - annotationHolder.newAnnotation(HighlightSeverity.INFORMATION, "Parameter") - .range(TextRange.from(offset, length)) - .create(); - } - offset += length; - } - } -} diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveJavaMethodUsageSearcher.java b/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveJavaMethodUsageSearcher.java index 02ee7789..29c82bd1 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveJavaMethodUsageSearcher.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveJavaMethodUsageSearcher.java @@ -1,9 +1,12 @@ package com.github.kumaraman21.intellijbehave.service; +import static com.github.kumaraman21.intellijbehave.service.JBehaveUtil.getAnnotationTexts; +import static com.github.kumaraman21.intellijbehave.service.JBehaveUtil.getTheBiggestWordToSearchByIndex; +import static com.intellij.openapi.util.text.StringUtil.isNotEmpty; + import com.github.kumaraman21.intellijbehave.language.StoryFileType; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.QueryExecutorBase; -import com.intellij.openapi.util.Computable; +import com.intellij.openapi.application.ReadAction; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiReference; import com.intellij.psi.search.GlobalSearchScope; @@ -13,28 +16,23 @@ import com.intellij.util.Query; import org.jetbrains.annotations.NotNull; -import java.util.List; - -import static com.github.kumaraman21.intellijbehave.service.JBehaveUtil.getAnnotationTexts; -import static com.github.kumaraman21.intellijbehave.service.JBehaveUtil.getTheBiggestWordToSearchByIndex; -import static com.intellij.openapi.util.text.StringUtil.isNotEmpty; - public class JBehaveJavaMethodUsageSearcher extends QueryExecutorBase { @Override public void processQuery(@NotNull SearchParameters queryParameters, @NotNull Processor consumer) { - final PsiMethod method = queryParameters.getMethod(); + if (queryParameters.getScopeDeterminedByUser() instanceof GlobalSearchScope scopeByUser) { + final PsiMethod method = queryParameters.getMethod(); - List stepTexts = ApplicationManager.getApplication().runReadAction((Computable>) () -> getAnnotationTexts(method)); + boolean hasNonEmptyBiggestWord = ReadAction.compute(() -> getAnnotationTexts(method)) + .stream() + .anyMatch(stepText -> isNotEmpty(getTheBiggestWordToSearchByIndex(stepText))); - for (String stepText : stepTexts) { - String word = getTheBiggestWordToSearchByIndex(stepText); - if (isNotEmpty(word)) { - if (queryParameters.getScopeDeterminedByUser() instanceof GlobalSearchScope) { - GlobalSearchScope restrictedScope = GlobalSearchScope.getScopeRestrictedByFileTypes((GlobalSearchScope) queryParameters.getScopeDeterminedByUser(), StoryFileType.STORY_FILE_TYPE); - Query query = ReferencesSearch.search(new ReferencesSearch.SearchParameters(method, restrictedScope, false, queryParameters.getOptimizer())); - query.forEach(consumer); - } + //Originally, this was executed for each non-empty biggest word, but since it performed the same reference search query, it became deduplicated. + if (hasNonEmptyBiggestWord) { + var restrictedScope = GlobalSearchScope.getScopeRestrictedByFileTypes(scopeByUser, StoryFileType.STORY_FILE_TYPE); + Query query = ReferencesSearch.search(new ReferencesSearch.SearchParameters(method, restrictedScope, false, queryParameters.getOptimizer())); + //This, behind the scenes, will also call into JBehaveJavaStepDefinitionSearch#execute + query.forEach(consumer); } } } diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveJavaStepDefinitionSearch.java b/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveJavaStepDefinitionSearch.java index 19e6c7bf..f4fb283d 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveJavaStepDefinitionSearch.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveJavaStepDefinitionSearch.java @@ -4,13 +4,9 @@ import static com.github.kumaraman21.intellijbehave.service.JBehaveUtil.getAnnotationTexts; import static com.github.kumaraman21.intellijbehave.service.JBehaveUtil.isStepDefinition; -import java.util.List; - +import com.intellij.openapi.application.ReadAction; import org.jetbrains.annotations.NotNull; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.util.Computable; -import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiReference; import com.intellij.psi.search.SearchScope; @@ -18,35 +14,31 @@ import com.intellij.util.Processor; import com.intellij.util.QueryExecutor; +/** + * Provides Story step references for JBehave Java step definition methods. + */ public class JBehaveJavaStepDefinitionSearch implements QueryExecutor { @Override public boolean execute(@NotNull SearchParameters queryParameters, @NotNull Processor consumer) { - PsiElement myElement = queryParameters.getElementToSearch(); - - if (!(myElement instanceof PsiMethod)) { - return true; - } - - final PsiMethod method = (PsiMethod) myElement; - - Boolean isStepDefinition = ApplicationManager.getApplication().runReadAction((Computable) () -> isStepDefinition(method)); - - if (!isStepDefinition) { + if (!(queryParameters.getElementToSearch() instanceof PsiMethod method) || !ReadAction.compute(() -> isStepDefinition(method))) { return true; } - List stepTexts = ApplicationManager.getApplication().runReadAction((Computable>) () -> getAnnotationTexts(method)); - SearchScope searchScope = ApplicationManager.getApplication().runReadAction((Computable) queryParameters::getEffectiveSearchScope); - + SearchScope searchScope = null; boolean result = true; - for (String stepText : stepTexts) { + for (String stepText : ReadAction.compute(() -> getAnnotationTexts(method))) { if (stepText == null) { return true; } - result &= findJBehaveReferencesToElement(myElement, stepText, consumer, searchScope); + //Lazy-initializing the search scope in case the first step text is null + if (searchScope == null) { + searchScope = ReadAction.compute(queryParameters::getEffectiveSearchScope); + } + + result &= findJBehaveReferencesToElement(method, stepText, consumer, searchScope); } return result; diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveStepsIndex.java b/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveStepsIndex.java index df63f6e8..577f254a 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveStepsIndex.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveStepsIndex.java @@ -3,20 +3,24 @@ import static java.util.Arrays.asList; import static java.util.Collections.emptyList; +import com.github.kumaraman21.intellijbehave.JBehaveStepDefClassesModificationTracker; import com.github.kumaraman21.intellijbehave.kotlin.KotlinConfigKt; import com.github.kumaraman21.intellijbehave.kotlin.support.services.KotlinAnnotationsLoader; import com.github.kumaraman21.intellijbehave.parser.JBehaveStep; -import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.components.Service; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; +import com.intellij.openapi.roots.ProjectRootModificationTracker; import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiClass; import com.intellij.psi.impl.java.stubs.index.JavaAnnotationIndex; import com.intellij.psi.impl.java.stubs.index.JavaFullClassNameIndex; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; import com.intellij.psi.util.QualifiedName; import org.jbehave.core.annotations.Given; import org.jbehave.core.annotations.Then; @@ -34,7 +38,7 @@ * Project service that provides Java step definitions for JBehave Story steps. */ @Service(Service.Level.PROJECT) -public final class JBehaveStepsIndex { +public final class JBehaveStepsIndex implements Disposable { //Argument is necessary for project-level service creation public JBehaveStepsIndex(Project project) { @@ -52,11 +56,10 @@ public Collection findStepDefinitions(@NotNull JBehaveStep s return emptyList(); } - Map definitionsByClass = new HashMap<>(); - List stepDefinitions = loadStepsFor(module); + Map definitionsByClass = new HashMap<>(2); String stepText = step.getStepText(); - for (JavaStepDefinition stepDefinition : stepDefinitions) { + for (JavaStepDefinition stepDefinition : loadStepsFor(module)) { if (stepDefinition.matches(stepText) && stepDefinition.supportsStep(step)) { Integer currentHighestPriority = getPriorityByDefinition(definitionsByClass.get(stepDefinition.getClass())); Integer newPriority = getPriorityByDefinition(stepDefinition); @@ -75,17 +78,15 @@ private List loadStepsFor(@NotNull Module module) { GlobalSearchScope dependenciesScope = module.getModuleWithDependenciesAndLibrariesScope(true); PsiClass givenAnnotationClass = findStepAnnotation(Given.class.getName(), module, dependenciesScope); + if (givenAnnotationClass == null) return emptyList(); PsiClass whenAnnotationClass = findStepAnnotation(When.class.getName(), module, dependenciesScope); + if (whenAnnotationClass == null) return emptyList(); PsiClass thenAnnotationClass = findStepAnnotation(Then.class.getName(), module, dependenciesScope); - - if (givenAnnotationClass == null || whenAnnotationClass == null || thenAnnotationClass == null) { - return emptyList(); - } + if (thenAnnotationClass == null) return emptyList(); List result = new ArrayList<>(); - List stepAnnotations = asList(givenAnnotationClass, whenAnnotationClass, thenAnnotationClass); - for (PsiClass stepAnnotation : stepAnnotations) { + for (PsiClass stepAnnotation : asList(givenAnnotationClass, whenAnnotationClass, thenAnnotationClass)) { for (PsiAnnotation stepDefAnnotation : getAllStepAnnotations(stepAnnotation, dependenciesScope)) { result.add(new JavaStepDefinition(stepDefAnnotation)); } @@ -99,19 +100,33 @@ private static Integer getPriorityByDefinition(@Nullable JavaStepDefinition defi return definition != null ? definition.getAnnotationPriority() : -1; } + /** + * Collects all {@link PsiAnnotation}s in the given {@code scope} that reference the {@code annClass}. + * + * @param annClass the PsiClass representing the {@code @Given}, {@code @When} or {@code @Then} step annotations + * Since the annotation classes doesn't change much, unless e.g. updating the library version, + * the same PsiClass instance will be available for the same annotations throughout the IDE session, + * thus it should be safe to use it as the cache location + */ @NotNull private static Collection getAllStepAnnotations(@NotNull final PsiClass annClass, @NotNull final GlobalSearchScope scope) { - return ApplicationManager.getApplication().runReadAction((Computable>) () -> { - Project project = annClass.getProject(); - Collection psiAnnotations = new ArrayList<>(); - if (KotlinConfigKt.getPluginIsEnabled()) { - String qualifiedName = annClass.getQualifiedName(); - if (qualifiedName != null) { - psiAnnotations.addAll(KotlinAnnotationsLoader.getInstance().getAnnotations(QualifiedName.fromDottedString(qualifiedName), project, scope)); + return CachedValuesManager.getCachedValue(annClass, (CachedValueProvider>) () -> { + Collection annotations = ReadAction.compute(() -> { + Project project = annClass.getProject(); + Collection psiAnnotations = new ArrayList<>(); + if (KotlinConfigKt.getPluginIsEnabled()) { + String annotationFqn = annClass.getQualifiedName(); + if (annotationFqn != null) { + psiAnnotations.addAll(KotlinAnnotationsLoader.getAnnotations(QualifiedName.fromDottedString(annotationFqn), project, scope)); + } } - } - psiAnnotations.addAll(JavaAnnotationIndex.getInstance().get(annClass.getName(), project, scope)); - return psiAnnotations; + psiAnnotations.addAll(JavaAnnotationIndex.getInstance().get(annClass.getName(), project, scope)); + return psiAnnotations; + }); + + return new CachedValueProvider.Result<>(annotations, + JBehaveStepDefClassesModificationTracker.getInstance(annClass.getProject()), + ProjectRootModificationTracker.getInstance(annClass.getProject())); }); } @@ -125,4 +140,9 @@ private PsiClass findStepAnnotation(String stepClass, Module module, GlobalSearc } return null; } + + @Override + public void dispose() { + //No-op. Used for ProjectStartupActivity as parent Disposable + } } diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveUtil.java b/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveUtil.java index 3c46bc9b..5af49fc8 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveUtil.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/service/JBehaveUtil.java @@ -2,34 +2,12 @@ import static com.intellij.openapi.util.text.StringUtil.isEmptyOrSpaces; -import java.lang.annotation.Annotation; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.jbehave.core.annotations.Alias; -import org.jbehave.core.annotations.Aliases; -import org.jbehave.core.annotations.Given; -import org.jbehave.core.annotations.Then; -import org.jbehave.core.annotations.When; -import org.jbehave.core.steps.PatternVariantBuilder; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - import com.github.kumaraman21.intellijbehave.language.StoryFileType; import com.intellij.codeInsight.AnnotationUtil; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.util.Computable; +import com.intellij.openapi.application.ReadAction; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiAnnotationMemberValue; -import com.intellij.psi.PsiArrayInitializerMemberValue; -import com.intellij.psi.PsiConstantEvaluationHelper; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiReference; @@ -39,110 +17,139 @@ import com.intellij.psi.search.TextOccurenceProcessor; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.Processor; +import org.jbehave.core.annotations.Alias; +import org.jbehave.core.annotations.Aliases; +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Then; +import org.jbehave.core.annotations.When; +import org.jbehave.core.steps.PatternVariantBuilder; +import org.jetbrains.annotations.NotNull; + +import java.lang.annotation.Annotation; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; -public class JBehaveUtil { +public final class JBehaveUtil { - public static final Set JBEHAVE_ANNOTATIONS_SET = Set.of(Given.class.getName(), When.class.getName(), - Then.class.getName()); + private static final Set JBEHAVE_ANNOTATIONS_SET = Set.of(Given.class.getName(), When.class.getName(), + Then.class.getName()); + /** + * Returns if the provided annotation is one of {@link #JBEHAVE_ANNOTATIONS_SET}. + */ public static boolean isJBehaveStepAnnotation(@NotNull PsiAnnotation annotation) { - String annotationName = getAnnotationName(annotation); + String annotationName = annotation.getQualifiedName(); return annotationName != null && JBEHAVE_ANNOTATIONS_SET.contains(annotationName); } + /** + * Returns if the provided annotation is of type {@code annotationClass}. + *

+ * For example, if the {@code @org.jbehave.core.annotations.Given} annotation is of type {@link Given}. + */ public static boolean isAnnotationOfClass(@NotNull PsiAnnotation annotation, - @NotNull Class annotationClass) { - String annotationName = getAnnotationName(annotation); - - return annotationName != null && Objects.equals(annotationName, annotationClass.getName()); - } + @NotNull Class annotationClass) { + String annotationName = annotation.getQualifiedName(); - @Nullable - private static String getAnnotationName(@NotNull final PsiAnnotation annotation) { - return ApplicationManager.getApplication().runReadAction((Computable) () -> annotation.getQualifiedName()); + return annotationName != null && annotationName.equals(annotationClass.getName()); } + /** + * Returns all {@code @Given}, {@code @When} and {@code @Then} step annotations on the provided {@code method}. + */ @NotNull private static List getJBehaveStepAnnotations(@NotNull PsiMethod method) { - PsiAnnotation[] annotations = method.getModifierList().getAnnotations(); - - return Stream.of(annotations) - .filter(JBehaveUtil::isJBehaveStepAnnotation) - .collect(Collectors.toList()); - } - - private static Optional findAnnotation(PsiAnnotation @NotNull [] annotations, - @NotNull Class annotationClass) { - return Stream.of(annotations) - .filter(annotation -> isAnnotationOfClass(annotation, annotationClass)) - .findFirst(); + return Stream.of(method.getModifierList().getAnnotations()) + .filter(JBehaveUtil::isJBehaveStepAnnotation) + .collect(Collectors.toList()); } + /** + * Returns if the provided method is step definition, meaning has at least one annotation that + *

    + *
  • is annotated with one of {@link #JBEHAVE_ANNOTATIONS_SET},
  • + *
  • and its {@code value} attribute is not null.
  • + *
+ */ public static boolean isStepDefinition(@NotNull PsiMethod method) { return getJBehaveStepAnnotations(method).stream() - .map(stepAnnotation -> stepAnnotation.findAttributeValue("value")) - .anyMatch(Objects::nonNull); + .map(stepAnnotation -> stepAnnotation.findAttributeValue("value")) + .anyMatch(Objects::nonNull); } + /** + * Collects all possible step annotation pattern variants for {@code stepAnnotation}, + * as well as the {@link Alias} and {@link Aliases} annotations on the parent + * step definition method. + * + * @param stepAnnotation annotation for one of {@link #JBEHAVE_ANNOTATIONS_SET} + * @return the collection of step patterns + */ @NotNull public static Set getAnnotationTexts(@NotNull PsiAnnotation stepAnnotation) { - Set annotationTexts = new HashSet<>(); + Set annotationTexts = new HashSet<>(4); getAnnotationText(stepAnnotation).ifPresent(annotationTexts::add); PsiMethod method = PsiTreeUtil.getParentOfType(stepAnnotation, PsiMethod.class); if (method != null) { - - PsiAnnotation[] annotations = method.getModifierList().getAnnotations(); - - findAnnotation(annotations, Alias.class) - .flatMap(JBehaveUtil::getAnnotationText) - .ifPresent(annotationTexts::add); - - findAnnotation(annotations, Aliases.class) - .map(JBehaveUtil::getAliasesAnnotationTexts) - .ifPresent(annotationTexts::addAll); + for (PsiAnnotation annotation : method.getModifierList().getAnnotations()) { + if (isAnnotationOfClass(annotation, Alias.class)) { + getAnnotationText(annotation).ifPresent(annotationTexts::add); + } else if (isAnnotationOfClass(annotation, Aliases.class)) { + annotationTexts.addAll(getAliasesAnnotationTexts(annotation)); + } + } } return annotationTexts.stream() - .map(PatternVariantBuilder::new) - .map(PatternVariantBuilder::allVariants) - .flatMap(Set::stream) - .collect(Collectors.toSet()); + .map(PatternVariantBuilder::new) + .map(PatternVariantBuilder::allVariants) + .flatMap(Set::stream) + .collect(Collectors.toSet()); } + /** + * Returns the value of {@code annotation}'s {@code value} attribute, the step pattern. + * + * @param annotation a JBehave annotation: Given, When, Then, Alias or Aliases + */ private static Optional getAnnotationText(@NotNull PsiAnnotation annotation) { return Optional.ofNullable(AnnotationUtil.getStringAttributeValue(annotation, "value")); } @NotNull private static Set getAliasesAnnotationTexts(@NotNull PsiAnnotation aliasAnnotation) { - PsiAnnotationMemberValue values = aliasAnnotation.findAttributeValue("values"); - - if (!(values instanceof PsiArrayInitializerMemberValue)) { - return Collections.emptySet(); - } - - final PsiArrayInitializerMemberValue attrValue = (PsiArrayInitializerMemberValue) values; - - final PsiConstantEvaluationHelper constantEvaluationHelper = JavaPsiFacade.getInstance(aliasAnnotation.getProject()).getConstantEvaluationHelper(); - - return Stream.of(attrValue.getInitializers()) - .map(constantEvaluationHelper::computeConstantExpression) - .filter(String.class::isInstance) - .map(String.class::cast) - .collect(Collectors.toSet()); + return AnnotationUtil.arrayAttributeValues(aliasAnnotation.findAttributeValue("values")) + .stream() + .map(AnnotationUtil::getStringAttributeValue) + .collect(Collectors.toSet()); } + /** + * Collects all possible step annotation pattern variants for the annotations on {@code method}. + * + * @param method the step definition method + * @return the list of step patterns + */ @NotNull public static List getAnnotationTexts(@NotNull PsiMethod method) { return getJBehaveStepAnnotations(method) - .stream() - .map(JBehaveUtil::getAnnotationTexts) - .flatMap(Set::stream) - .collect(Collectors.toList()); + .stream() + .map(JBehaveUtil::getAnnotationTexts) + .flatMap(Set::stream) + .collect(Collectors.toList()); } + /** + * Returns the value of {@code stepAnnotation}'s {@code priority} attribute, or -1 if the + * priority is not an integer. + */ @NotNull public static Integer getAnnotationPriority(@NotNull PsiAnnotation stepAnnotation) { PsiAnnotationMemberValue attrValue = stepAnnotation.findAttributeValue("priority"); @@ -151,13 +158,19 @@ public static Integer getAnnotationPriority(@NotNull PsiAnnotation stepAnnotatio Object constValue = JavaPsiFacade.getInstance(stepAnnotation.getProject()).getConstantEvaluationHelper().computeConstantExpression(attrValue.getOriginalElement()); Integer priority = constValue instanceof Integer ? (Integer) constValue : null; - if (priority != null) { - return priority; - } - - return -1; + return priority != null ? priority : -1; } + /** + * Performs word based reference search for {@code stepDefinitionElement}. + * + * @param stepDefinitionElement a step definition method + * @param stepText the step pattern value of the step annotation + * @param consumer + * @param effectiveSearchScope the search scope to find references in + * @return true if the corresponding query execution in {@link JBehaveJavaStepDefinitionSearch} should continue, + * false if it should stop + */ public static boolean findJBehaveReferencesToElement(@NotNull PsiElement stepDefinitionElement, @NotNull String stepText, @NotNull Processor consumer, @NotNull final SearchScope effectiveSearchScope) { String word = getTheBiggestWordToSearchByIndex(stepText); @@ -165,24 +178,31 @@ public static boolean findJBehaveReferencesToElement(@NotNull PsiElement stepDef return true; } - SearchScope searchScope = restrictScopeToJBehaveFiles(() -> effectiveSearchScope); + SearchScope searchScope = restrictScopeToJBehaveFiles(effectiveSearchScope); - PsiSearchHelper instance = PsiSearchHelper.getInstance(stepDefinitionElement.getProject()); - return instance.processElementsWithWord(new MyReferenceCheckingProcessor(stepDefinitionElement, consumer), searchScope, word, (short) 5, true); + return PsiSearchHelper.getInstance(stepDefinitionElement.getProject()) + .processElementsWithWord(new MyReferenceCheckingProcessor(stepDefinitionElement, consumer), searchScope, word, (short) 5, true); } - public static SearchScope restrictScopeToJBehaveFiles(final Computable originalScopeComputation) { - return (SearchScope) ApplicationManager.getApplication().runReadAction((Computable) () -> { - SearchScope originalScope = originalScopeComputation.compute(); - if (originalScope instanceof GlobalSearchScope) { - return GlobalSearchScope.getScopeRestrictedByFileTypes((GlobalSearchScope) originalScope, StoryFileType.STORY_FILE_TYPE); - } else { - return originalScope; - } - }); + /** + * Returns a search scope that is based on the {@code originalScopeComputation} but that is restricted to JBehave Story file types. + */ + private static SearchScope restrictScopeToJBehaveFiles(final SearchScope originalScope) { + return ReadAction.compute(() -> + originalScope instanceof GlobalSearchScope globalSearchScope + ? GlobalSearchScope.getScopeRestrictedByFileTypes(globalSearchScope, StoryFileType.STORY_FILE_TYPE) + : originalScope); } + /** + * Returns the longest non-placeholder (e.g. {@code $price}) word from {@code stepText}. + * + * @param stepText the step pattern value from a step annotation + */ public static String getTheBiggestWordToSearchByIndex(@NotNull String stepText) { + //This helps avoid the creation of the variables below in case of empty and blank step text + if (stepText.isBlank()) return ""; + String result = ""; int par = 0; @@ -220,7 +240,7 @@ public static String getTheBiggestWordToSearchByIndex(@NotNull String stepText) return result; } - private static class MyReferenceCheckingProcessor implements TextOccurenceProcessor { + private static final class MyReferenceCheckingProcessor implements TextOccurenceProcessor { @NotNull private final PsiElement myElementToFind; @NotNull @@ -231,10 +251,15 @@ private MyReferenceCheckingProcessor(@NotNull PsiElement elementToFind, @NotNull myConsumer = consumer; } + @Override public boolean execute(@NotNull PsiElement element, int offsetInElement) { - PsiElement parent = element.getParent(); boolean result = executeInternal(element); - return result && parent != null ? executeInternal(parent) : result; + //This avoids calculating the parent element when result is false + if (result) { + PsiElement parent = element.getParent(); + return parent == null || executeInternal(parent); + } + return false; } private boolean executeInternal(@NotNull PsiElement referenceOwner) { @@ -247,4 +272,7 @@ private boolean executeInternal(@NotNull PsiElement referenceOwner) { return true; } } + + private JBehaveUtil() { + } } diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/service/JavaStepDefinition.java b/src/main/java/com/github/kumaraman21/intellijbehave/service/JavaStepDefinition.java index 68e10450..2164dbf3 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/service/JavaStepDefinition.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/service/JavaStepDefinition.java @@ -1,8 +1,9 @@ package com.github.kumaraman21.intellijbehave.service; +import static com.github.kumaraman21.intellijbehave.utility.StepTypeMappings.ANNOTATION_TO_STEP_TYPE_MAPPING; + import com.github.kumaraman21.intellijbehave.parser.JBehaveStep; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.util.Computable; +import com.intellij.openapi.application.ReadAction; import com.intellij.psi.PsiAnnotation; import com.intellij.psi.PsiMethod; import com.intellij.psi.SmartPointerManager; @@ -17,23 +18,43 @@ import java.util.Collections; import java.util.Objects; import java.util.Set; -import java.util.stream.Collectors; - -import static com.github.kumaraman21.intellijbehave.utility.StepTypeMappings.ANNOTATION_TO_STEP_TYPE_MAPPING; -public class JavaStepDefinition { - private final SmartPsiElementPointer myElementPointer; +/** + * Represents a Java step definition as a step annotation. + */ +public final class JavaStepDefinition { + private final SmartPsiElementPointer annotationPointer; private final StepPatternParser stepPatternParser = new RegexPrefixCapturingPatternParser(); public JavaStepDefinition(PsiAnnotation annotation) { - myElementPointer = SmartPointerManager.getInstance(annotation.getProject()).createSmartPsiElementPointer(annotation); + annotationPointer = SmartPointerManager.getInstance(annotation.getProject()).createSmartPsiElementPointer(annotation); } + /** + * Returns if any of the step annotation patterns match the provided Story step text. + * + * @param stepText the text of a step in a Story file + */ public boolean matches(String stepText) { - return getStepMatchers(getAnnotationTexts()).stream().anyMatch(stepMatcher -> stepMatcher.matches(stepText)); + final StepType annotationType = getAnnotationType(); + for (String annotationText : getAnnotationTexts()) { + OptimizedStepMatcher stepMatcher = new OptimizedStepMatcher(stepPatternParser.parseStep(annotationType, annotationText)); + if (stepMatcher.matches(stepText)) + return true; + } + + return false; } - @Nullable + /** + * Returns the step pattern from the current annotation if it matches the Story {@code stepText}. + *

+ * If there is only one step text without any text variant, it returns that pattern. + * Otherwise, it returns the first annotation text that matches {@code stepText}. + * + * @param stepText the text of a step in a Story file + */ + @Nullable("When there is no annotation text.") public String getAnnotationTextFor(String stepText) { Set annotationTexts = getAnnotationTexts(); @@ -41,9 +62,9 @@ public String getAnnotationTextFor(String stepText) { return annotationTexts.iterator().next(); } - Set stepMatchers = getStepMatchers(annotationTexts); - - for (OptimizedStepMatcher stepMatcher : stepMatchers) { + final StepType annotationType = getAnnotationType(); + for (String annotationText : annotationTexts) { + OptimizedStepMatcher stepMatcher = new OptimizedStepMatcher(stepPatternParser.parseStep(annotationType, annotationText)); if (stepMatcher.matches(stepText)) { return stepMatcher.pattern().annotated(); } @@ -54,70 +75,70 @@ public String getAnnotationTextFor(String stepText) { @Nullable private PsiAnnotation getAnnotation() { - return myElementPointer.getElement(); + return annotationPointer.getElement(); } + /** + * Returns the method that the current annotation is placed on. + */ @Nullable public PsiMethod getAnnotatedMethod() { return PsiTreeUtil.getParentOfType(getAnnotation(), PsiMethod.class); } - private Set getStepMatchers(Set annotationTextVariants) { - final StepType annotationType = getAnnotationType(); - - return annotationTextVariants.stream() - .map(annotationText -> stepPatternParser.parseStep(annotationType, annotationText)) - .map(OptimizedStepMatcher::new) - .collect(Collectors.toSet()); - } - @NotNull private Set getAnnotationTexts() { - PsiAnnotation element = getAnnotation(); - - return element == null ? Collections.emptySet() : JBehaveUtil.getAnnotationTexts(element); + PsiAnnotation annotation = getAnnotation(); + return annotation == null ? Collections.emptySet() : JBehaveUtil.getAnnotationTexts(annotation); } - @Nullable + /** + * Returns the {@link StepType} mapped to the current annotation if it is one of {@code @Given}, + * {@code @When} or {@code @Then}. + */ + @Nullable("When there is no step annotation to work with, or the annotation has no StepType mapped.") public StepType getAnnotationType() { - final PsiAnnotation element = getAnnotation(); - if (element == null) { - return null; - } - - String qualifiedName = ApplicationManager.getApplication().runReadAction((Computable) element::getQualifiedName); - return ANNOTATION_TO_STEP_TYPE_MAPPING.get(qualifiedName); + final PsiAnnotation annotation = getAnnotation(); + return annotation == null + ? null + : ANNOTATION_TO_STEP_TYPE_MAPPING.get(ReadAction.compute(annotation::getQualifiedName)); } + /** + * Returns the value of the {@code priority} attribute of the current annotation. + */ @NotNull public Integer getAnnotationPriority() { - PsiAnnotation element = getAnnotation(); + PsiAnnotation annotation = getAnnotation(); - return element != null ? JBehaveUtil.getAnnotationPriority(element) : -1; + return annotation != null ? JBehaveUtil.getAnnotationPriority(annotation) : -1; + } + + /** + * Returns if the step type of the provided Story step is the same as the step + * type of the current annotation. + * + * @param step a step from a Story file + */ + public boolean supportsStep(@NotNull JBehaveStep step) { + return Objects.equals(step.getStepType(), getAnnotationType()); } + //Equals and hashcode + public boolean equals(Object o) { if (this == o) { return true; - } - else if (o != null && getClass() == o.getClass()) { + } else if (o != null && getClass() == o.getClass()) { JavaStepDefinition that = (JavaStepDefinition) o; - return myElementPointer.equals(that.myElementPointer); - } - else { + return annotationPointer.equals(that.annotationPointer); + } else { return false; } } public int hashCode() { - return myElementPointer.hashCode(); - } - - public boolean supportsStep(@NotNull JBehaveStep step) { - StepType stepType = step.getStepType(); - StepType annotationType = getAnnotationType(); - - return Objects.equals(stepType, annotationType); + return annotationPointer.hashCode(); } } diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/service/OptimizedStepMatcher.kt b/src/main/java/com/github/kumaraman21/intellijbehave/service/OptimizedStepMatcher.kt index a28f3231..d9e759ab 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/service/OptimizedStepMatcher.kt +++ b/src/main/java/com/github/kumaraman21/intellijbehave/service/OptimizedStepMatcher.kt @@ -9,7 +9,7 @@ import org.jbehave.core.parsers.StepMatcher */ private object StepPatterns { object Plain { - val VARIABLE = "(.*)" + const val VARIABLE = "(.*)" } object Regex { diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/startup/ProjectStartupActivity.java b/src/main/java/com/github/kumaraman21/intellijbehave/startup/ProjectStartupActivity.java new file mode 100644 index 00000000..3354195e --- /dev/null +++ b/src/main/java/com/github/kumaraman21/intellijbehave/startup/ProjectStartupActivity.java @@ -0,0 +1,24 @@ +package com.github.kumaraman21.intellijbehave.startup; + +import com.github.kumaraman21.intellijbehave.JBehaveStepDefClassPsiChangeListener; +import com.github.kumaraman21.intellijbehave.service.JBehaveStepsIndex; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.ProjectActivity; +import com.intellij.psi.PsiManager; +import kotlin.Unit; +import kotlin.coroutines.Continuation; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Performs actions on project startup. + */ +public class ProjectStartupActivity implements ProjectActivity { + + @Nullable + @Override + public Object execute(@NotNull Project project, @NotNull Continuation continuation) { + PsiManager.getInstance(project).addPsiTreeChangeListener(new JBehaveStepDefClassPsiChangeListener(project), JBehaveStepsIndex.getInstance(project)); + return Unit.INSTANCE; + } +} diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/utility/ScanUtils.java b/src/main/java/com/github/kumaraman21/intellijbehave/utility/ScanUtils.java index cb8d8452..ba761d11 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/utility/ScanUtils.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/utility/ScanUtils.java @@ -23,7 +23,7 @@ import java.util.HashSet; -public class ScanUtils { +public final class ScanUtils { public static boolean iterateInContextOf(PsiElement storyRef, ContentIterator iterator) { Module module = ModuleUtil.findModuleForPsiElement(storyRef); @@ -45,5 +45,6 @@ public static boolean iterateInContextOf(PsiElement storyRef, ContentIterator it return shouldContinue; } - + private ScanUtils() { + } } diff --git a/src/main/java/com/github/kumaraman21/intellijbehave/utility/StepTypeMappings.java b/src/main/java/com/github/kumaraman21/intellijbehave/utility/StepTypeMappings.java index 070afa30..c2f3467e 100644 --- a/src/main/java/com/github/kumaraman21/intellijbehave/utility/StepTypeMappings.java +++ b/src/main/java/com/github/kumaraman21/intellijbehave/utility/StepTypeMappings.java @@ -20,23 +20,19 @@ import org.jbehave.core.annotations.When; import org.jbehave.core.steps.StepType; -import java.util.HashMap; import java.util.Map; -public class StepTypeMappings { +/** + * Utility to provide {@link StepType} and step annotation mappings. + */ +public final class StepTypeMappings { - public static final Map STEP_TYPE_TO_ANNOTATION_MAPPING = new HashMap<>(); - static { - STEP_TYPE_TO_ANNOTATION_MAPPING.put(StepType.GIVEN, Given.class.getName()); - STEP_TYPE_TO_ANNOTATION_MAPPING.put(StepType.WHEN, When.class.getName()); - STEP_TYPE_TO_ANNOTATION_MAPPING.put(StepType.THEN, Then.class.getName()); - } + public static final Map ANNOTATION_TO_STEP_TYPE_MAPPING = Map.of( + Given.class.getName(), StepType.GIVEN, + When.class.getName(), StepType.WHEN, + Then.class.getName(), StepType.THEN + ); - public static final Map ANNOTATION_TO_STEP_TYPE_MAPPING = new HashMap<>(); - static { - ANNOTATION_TO_STEP_TYPE_MAPPING.put(Given.class.getName(), StepType.GIVEN); - ANNOTATION_TO_STEP_TYPE_MAPPING.put(When.class.getName(), StepType.WHEN); - ANNOTATION_TO_STEP_TYPE_MAPPING.put(Then.class.getName(), StepType.THEN); + private StepTypeMappings() { } - } diff --git a/src/main/kotlin/com/github/kumaraman21/intellijbehave/JBehaveStepDefClassPsiChangeListener.kt b/src/main/kotlin/com/github/kumaraman21/intellijbehave/JBehaveStepDefClassPsiChangeListener.kt new file mode 100644 index 00000000..5ad289a1 --- /dev/null +++ b/src/main/kotlin/com/github/kumaraman21/intellijbehave/JBehaveStepDefClassPsiChangeListener.kt @@ -0,0 +1,82 @@ +package com.github.kumaraman21.intellijbehave + +import com.github.kumaraman21.intellijbehave.kotlin.pluginIsEnabled +import com.github.kumaraman21.intellijbehave.kotlin.support.services.KotlinPsiClassesHandler +import com.intellij.openapi.project.DumbService +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Ref +import com.intellij.psi.JavaRecursiveElementVisitor +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiJavaFile +import com.intellij.psi.PsiTreeChangeAdapter +import com.intellij.psi.PsiTreeChangeEvent + +/** + * Reacts to changes in JBehave Java step definition files. + */ +class JBehaveStepDefClassPsiChangeListener(val project: Project) : PsiTreeChangeAdapter() { + + override fun childrenChanged(event: PsiTreeChangeEvent) = updateJBehaveTestClassModificationTracker(event) + override fun childAdded(event: PsiTreeChangeEvent) = updateJBehaveTestClassModificationTracker(event) + override fun childMoved(event: PsiTreeChangeEvent) = updateJBehaveTestClassModificationTracker(event) + override fun childRemoved(event: PsiTreeChangeEvent) = updateJBehaveTestClassModificationTracker(event) + override fun childReplaced(event: PsiTreeChangeEvent) = updateJBehaveTestClassModificationTracker(event) + override fun propertyChanged(event: PsiTreeChangeEvent) = updateJBehaveTestClassModificationTracker(event) + + private fun updateJBehaveTestClassModificationTracker(event: PsiTreeChangeEvent) { + val file = event.file + if (file != null) { + updateModificationTrackerIfFileContainsJBehaveStepDefClass(file) + } + //file is null when the file has just been deleted + else { + val child = event.child + if (child is PsiJavaFile) { + updateModificationTrackerIfFileContainsJBehaveStepDefClass(child) + } + } + } + + /** + * Updates the [JBehaveStepDefClassesModificationTracker] if the provided file is a JBehave step definitions file. + * + * @param file the PsiFile being examined + */ + private fun updateModificationTrackerIfFileContainsJBehaveStepDefClass(file: PsiFile) { + if (!DumbService.isDumb(project)) { + val hasJBehaveStepDefTestClass = Ref() + if (file is PsiJavaFile) { + //If it finds a Citrus test class in the file, then modification tracker will be eligible for update + file.accept(object : JavaRecursiveElementVisitor() { + override fun visitClass(aClass: PsiClass) { + if (isJavaJBehaveStepDefClass(aClass)) { + hasJBehaveStepDefTestClass.set(true) + return + } + super.visitClass(aClass) + } + }) + } else if (pluginIsEnabled && KotlinPsiClassesHandler.isKotlinFile(file)) { + if (KotlinPsiClassesHandler.visitClasses(file)) + hasJBehaveStepDefTestClass.set(true) + } + + if (!hasJBehaveStepDefTestClass.isNull && hasJBehaveStepDefTestClass.get()) { + JBehaveStepDefClassesModificationTracker.getInstance(project).increaseModificationCount() + } + } + } + + private fun isJavaJBehaveStepDefClass(aClass: PsiClass): Boolean { + return !aClass.isEnum && !aClass.isRecord && !aClass.isInterface && aClass.qualifiedName != null + && aClass.allMethods.any { + it.hasAnnotation("org.jbehave.core.annotations.Given") + || it.hasAnnotation("org.jbehave.core.annotations.When") + || it.hasAnnotation("org.jbehave.core.annotations.Then") + || it.hasAnnotation("org.jbehave.core.annotations.Alias") + || it.hasAnnotation("org.jbehave.core.annotations.Aliases") + || it.hasAnnotation("org.jbehave.core.annotations.Composite") + } + } +} diff --git a/src/main/kotlin/com/github/kumaraman21/intellijbehave/jbehaveModificationTrackers.kt b/src/main/kotlin/com/github/kumaraman21/intellijbehave/jbehaveModificationTrackers.kt new file mode 100644 index 00000000..02518066 --- /dev/null +++ b/src/main/kotlin/com/github/kumaraman21/intellijbehave/jbehaveModificationTrackers.kt @@ -0,0 +1,26 @@ +package com.github.kumaraman21.intellijbehave + +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.service +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.ModificationTracker +import com.intellij.openapi.util.SimpleModificationTracker + +/** + * Tracks the modification count of JBehave step definition files in the given project. + */ +@Service(Service.Level.PROJECT) +class JBehaveStepDefClassesModificationTracker(val project: Project) : ModificationTrackerBase() { + companion object { + @JvmStatic + fun getInstance(project: Project) = project.service() + } +} + +abstract class ModificationTrackerBase : ModificationTracker { + private val modificationTracker = SimpleModificationTracker() + + override fun getModificationCount() = modificationTracker.modificationCount + + fun increaseModificationCount() = modificationTracker.incModificationCount() +} \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index a707625d..89acd64f 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -43,7 +43,7 @@ ]]> Bert Van Vlerken, Victor Rosenberg - + com.intellij.modules.java org.jetbrains.kotlin @@ -70,8 +70,6 @@ fieldName="STORY_FILE_TYPE" language="Story" extensions="story"/> - @@ -103,6 +101,8 @@ Also, since multiple Run Configurations with the same name are not allowed to be saved by IntelliJ, this won't interfere with the default JBehave Story Runner.--> + + diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/ContentEntryProjectDescriptor.java b/src/test/java/com/github/kumaraman21/intellijbehave/ContentEntryProjectDescriptor.java new file mode 100644 index 00000000..63b2652e --- /dev/null +++ b/src/test/java/com/github/kumaraman21/intellijbehave/ContentEntryProjectDescriptor.java @@ -0,0 +1,35 @@ +package com.github.kumaraman21.intellijbehave; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.projectRoots.JavaSdk; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ContentEntry; +import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.model.java.JavaResourceRootType; +import org.jetbrains.jps.model.java.JavaSourceRootType; + +/** + * Project descriptor that recognizes different project content entries. + */ +public final class ContentEntryProjectDescriptor extends DefaultLightProjectDescriptor { + + @Override + public Sdk getSdk() { + return JavaSdk.getInstance().createJdk("Real JDK", System.getenv("JAVA_HOME"), false); + } + + @Override + public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) { + super.configureModule(module, model, contentEntry); + contentEntry.clearSourceFolders(); + + String contentEntryUrl = contentEntry.getUrl(); + contentEntry.addSourceFolder(contentEntryUrl + "/main/java", JavaSourceRootType.SOURCE); + contentEntry.addSourceFolder(contentEntryUrl + "/main/kotlin", JavaSourceRootType.SOURCE); + contentEntry.addSourceFolder(contentEntryUrl + "/main/resources", JavaResourceRootType.RESOURCE); + contentEntry.addSourceFolder(contentEntryUrl + "/test/java", JavaSourceRootType.TEST_SOURCE); + contentEntry.addSourceFolder(contentEntryUrl + "/test/resources", JavaResourceRootType.TEST_RESOURCE); + } +} diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/ContentEntryTestBase.java b/src/test/java/com/github/kumaraman21/intellijbehave/ContentEntryTestBase.java new file mode 100644 index 00000000..d435c284 --- /dev/null +++ b/src/test/java/com/github/kumaraman21/intellijbehave/ContentEntryTestBase.java @@ -0,0 +1,20 @@ +package com.github.kumaraman21.intellijbehave; + +import org.junit.jupiter.api.BeforeEach; + +/** + * Base class for tests using content entries. + */ +public abstract class ContentEntryTestBase extends JBehaveSupportTestBase { + + public ContentEntryTestBase() { + super(new ContentEntryProjectDescriptor()); + } + + @Override + @BeforeEach + protected void setUp() { + super.setUp(); + getFixture().copyDirectoryToProject("src", ""); + } +} diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/JBehaveSupportTestBase.java b/src/test/java/com/github/kumaraman21/intellijbehave/JBehaveSupportTestBase.java new file mode 100644 index 00000000..670c0dc7 --- /dev/null +++ b/src/test/java/com/github/kumaraman21/intellijbehave/JBehaveSupportTestBase.java @@ -0,0 +1,43 @@ +package com.github.kumaraman21.intellijbehave; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.projectRoots.JavaSdk; +import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess; +import com.intellij.testFramework.PsiTestUtil; +import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor; +import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase5; +import com.intellij.util.PathUtil; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.BeforeEach; + +import java.io.File; + +/** + * Base test class for this plugin. + */ +public abstract class JBehaveSupportTestBase extends LightJavaCodeInsightFixtureTestCase5 { + + protected JBehaveSupportTestBase() { + super(new DefaultLightProjectDescriptor(() -> JavaSdk.getInstance().createJdk("Real JDK", System.getenv("JAVA_HOME"), false))); + } + + protected JBehaveSupportTestBase(DefaultLightProjectDescriptor projectDescriptor) { + super(projectDescriptor); + } + + @BeforeEach + protected void setUp() { + loadLibraries(); + } + + protected void loadLibraries() { + loadLibrary(getFixture().getProjectDisposable(), getFixture().getModule(), "JBehave Core", "jbehave-core-5.1.1.jar"); + } + + private static void loadLibrary(@NotNull Disposable projectDisposable, @NotNull Module module, String libraryName, String libraryJarName) { + String libPath = PathUtil.toSystemIndependentName(new File("lib").getAbsolutePath()); + VfsRootAccess.allowRootAccess(projectDisposable, libPath); + PsiTestUtil.addLibrary(projectDisposable, module, libraryName, libPath, libraryJarName); + } +} diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/codeInspector/ContentEntryInspectionTestBase.java b/src/test/java/com/github/kumaraman21/intellijbehave/codeInspector/ContentEntryInspectionTestBase.java new file mode 100644 index 00000000..bb9721ef --- /dev/null +++ b/src/test/java/com/github/kumaraman21/intellijbehave/codeInspector/ContentEntryInspectionTestBase.java @@ -0,0 +1,40 @@ +package com.github.kumaraman21.intellijbehave.codeInspector; + +import com.github.kumaraman21.intellijbehave.ContentEntryProjectDescriptor; +import com.github.kumaraman21.intellijbehave.JBehaveSupportTestBase; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.intellij.testFramework.TestDataFile; +import org.junit.jupiter.api.BeforeEach; + +/** + * Base test class for inspections using content entries. + */ +abstract class ContentEntryInspectionTestBase extends JBehaveSupportTestBase { + + public ContentEntryInspectionTestBase() { + super(new ContentEntryProjectDescriptor()); + } + + /** + * Configures the inspection to be tested. + */ + protected abstract InspectionProfileEntry getInspection(); + + @Override + @BeforeEach + protected void setUp() { + super.setUp(); + getFixture().copyDirectoryToProject("src", ""); + } + + /** + * Tests inspection highlighting in the file located at the specified path. + * + * @param filePath the path to the file relative to the path returned by {@link #getTestDataPath()} + */ + protected void doTest(@TestDataFile String filePath) { + getFixture().configureByFile(filePath); + getFixture().enableInspections(getInspection()); + getFixture().testHighlighting(true, false, true); + } +} diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/codeInspector/UndefinedStepInspectionTest.java b/src/test/java/com/github/kumaraman21/intellijbehave/codeInspector/UndefinedStepInspectionTest.java new file mode 100644 index 00000000..88fa659c --- /dev/null +++ b/src/test/java/com/github/kumaraman21/intellijbehave/codeInspector/UndefinedStepInspectionTest.java @@ -0,0 +1,31 @@ +package com.github.kumaraman21.intellijbehave.codeInspector; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.intellij.testFramework.junit5.RunInEdt; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Test; + +/** + * Integration test for {@link UndefinedStepInspection}. + */ +@RunInEdt +class UndefinedStepInspectionTest extends ContentEntryInspectionTestBase { + + @Nullable + @Override + protected String getTestDataPath() { + return "src/test/testData/codeinspector/undefinedstep"; + } + + @Override + protected InspectionProfileEntry getInspection() { + return new UndefinedStepInspection(); + } + + @Test + void highlightingUndefinedSteps() { + getFixture().copyFileToProject("main/java/OtherStepDefs.java"); + getFixture().copyFileToProject("main/java/StepDefs.java"); + doTest("src/test/resources/undefined_steps.story"); + } +} diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/codeInspector/UnusedStepDeclarationInspectionTest.java b/src/test/java/com/github/kumaraman21/intellijbehave/codeInspector/UnusedStepDeclarationInspectionTest.java new file mode 100644 index 00000000..54cde843 --- /dev/null +++ b/src/test/java/com/github/kumaraman21/intellijbehave/codeInspector/UnusedStepDeclarationInspectionTest.java @@ -0,0 +1,31 @@ +package com.github.kumaraman21.intellijbehave.codeInspector; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.intellij.testFramework.junit5.RunInEdt; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Test; + +/** + * Integration test for {@link UnusedStepDeclarationInspection}. + */ +@RunInEdt +class UnusedStepDeclarationInspectionTest extends ContentEntryInspectionTestBase { + + @Nullable + @Override + protected String getTestDataPath() { + return "src/test/testData/codeinspector/unusedstepdeclarations"; + } + + @Override + protected InspectionProfileEntry getInspection() { + return new UnusedStepDeclarationInspection(); + } + + @Test + void highlightingUnusedStepDeclaration() { + getFixture().copyFileToProject("main/java/OtherStepDefs.java"); + getFixture().copyFileToProject("test/resources/unused_step_declarations.story"); + doTest("main/java/StepDefs.java"); + } +} diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionIteratorTest.java b/src/test/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionIteratorTest.java new file mode 100644 index 00000000..c48f00bc --- /dev/null +++ b/src/test/java/com/github/kumaraman21/intellijbehave/resolver/StepDefinitionIteratorTest.java @@ -0,0 +1,91 @@ +package com.github.kumaraman21.intellijbehave.resolver; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.github.kumaraman21.intellijbehave.ContentEntryTestBase; +import com.intellij.openapi.project.Project; +import com.intellij.testFramework.junit5.RunInEdt; +import org.jbehave.core.steps.StepType; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Test; + +import java.util.function.Predicate; + +/** + * Integration test for {@link StepDefinitionIterator}. + */ +@RunInEdt +class StepDefinitionIteratorTest extends ContentEntryTestBase { + + @Nullable + @Override + protected String getTestDataPath() { + return "src/test/testData/resolver/stepdefiterator"; + } + + @Test + void processesEverything() { + getFixture().copyFileToProject("main/java/OtherStepDefs.java"); + getFixture().copyFileToProject("main/kotlin/AnotherStepDefs.kt"); + getFixture().copyFileToProject("src/test/resources/iterator.story"); + var stepDefFile = getFixture().configureByFile("main/java/StepDefs.java"); + + var iterator = new DummyIterator(StepType.GIVEN, getFixture().getProject(), __ -> true); + boolean isProcessed = iterator.processFile(stepDefFile.getVirtualFile()); + + assertThat(isProcessed).isTrue(); + } + + @Test + void processesNothing() { + getFixture().copyFileToProject("main/java/OtherStepDefs.java"); + getFixture().copyFileToProject("main/kotlin/AnotherStepDefs.kt"); + getFixture().copyFileToProject("src/test/resources/iterator.story"); + var stepDefFile = getFixture().configureByFile("main/java/StepDefs.java"); + + var iterator = new DummyIterator(StepType.WHEN, getFixture().getProject(), __ -> false); + boolean isProcessed = iterator.processFile(stepDefFile.getVirtualFile()); + + assertThat(isProcessed).isFalse(); + } + + @Test + void processesFiltered() { + getFixture().copyFileToProject("main/java/OtherStepDefs.java"); + getFixture().copyFileToProject("main/kotlin/AnotherStepDefs.kt"); + getFixture().copyFileToProject("src/test/resources/iterator.story"); + var stepDefFile = getFixture().configureByFile("main/java/StepDefs.java"); + + var iterator = new DummyIterator(StepType.THEN, getFixture().getProject(), ann -> ann.annotationText().contains("size")); + boolean isProcessed = iterator.processFile(stepDefFile.getVirtualFile()); + + assertThat(isProcessed).isTrue(); + } + + @Test + void processesFilteredKotlin() { + getFixture().copyFileToProject("main/java/OtherStepDefs.java"); + getFixture().copyFileToProject("main/java/StepDefs.java"); + getFixture().copyFileToProject("src/test/resources/iterator.story"); + var stepDefFile = getFixture().configureByFile("main/kotlin/AnotherStepDefs.kt"); + + var iterator = new DummyIterator(StepType.THEN, getFixture().getProject(), ann -> ann.annotationText().contains("size")); + boolean isProcessed = iterator.processFile(stepDefFile.getVirtualFile()); + + assertThat(isProcessed).isFalse(); //False because there is a step that doesn't match the condition + } + + private static final class DummyIterator extends StepDefinitionIterator { + private final Predicate annotationPredicate; + + public DummyIterator(@Nullable StepType stepType, Project project, Predicate annotationPredicate) { + super(stepType, project); + this.annotationPredicate = annotationPredicate; + } + + @Override + public boolean processStepDefinition(StepDefinitionAnnotation stepDefinitionAnnotation) { + return annotationPredicate.test(stepDefinitionAnnotation); + } + } +} diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/resolver/StepPsiReferenceContributorTest.java b/src/test/java/com/github/kumaraman21/intellijbehave/resolver/StepPsiReferenceContributorTest.java new file mode 100644 index 00000000..36abcbc3 --- /dev/null +++ b/src/test/java/com/github/kumaraman21/intellijbehave/resolver/StepPsiReferenceContributorTest.java @@ -0,0 +1,48 @@ +package com.github.kumaraman21.intellijbehave.resolver; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.github.kumaraman21.intellijbehave.ContentEntryTestBase; +import com.intellij.psi.PsiReference; +import com.intellij.testFramework.junit5.RunInEdt; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Test; + +/** + * Integration test for {@link StepPsiReferenceContributor}. + */ +@RunInEdt +class StepPsiReferenceContributorTest extends ContentEntryTestBase { + + @Nullable + @Override + protected String getTestDataPath() { + return "src/test/testData/reference/storystep"; + } + + @Test + void shouldProviderReferenceForStep() { + var storyFile = getFixture().configureByFile("test/resources/step_reference.story"); + getFixture().copyFileToProject("main/java/StepDefs.java"); + var step = storyFile.findElementAt(getFixture().getCaretOffset()).getParent(); + + PsiReference[] references = step.getReferences(); + assertThat(references).hasSize(1); + assertThat(references[0]) + .isInstanceOf(StepPsiReference.class) + .extracting(ref -> ref.resolve().getText()) + .isEqualTo(""" + @When("search for $string") + public void searchForText(@Named("string") String string) { + }"""); + } + + @Test + void shouldNotProvideReferenceForNonStepElement() { + var storyFile = getFixture().configureByFile("test/resources/non_step_reference.story"); + getFixture().copyFileToProject("main/java/StepDefs.java"); + var step = storyFile.findElementAt(getFixture().getCaretOffset()).getParent(); + + assertThat(step.getReferences()).isEmpty(); + } +} diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/resolver/StepPsiReferenceTest.java b/src/test/java/com/github/kumaraman21/intellijbehave/resolver/StepPsiReferenceTest.java new file mode 100644 index 00000000..256da39c --- /dev/null +++ b/src/test/java/com/github/kumaraman21/intellijbehave/resolver/StepPsiReferenceTest.java @@ -0,0 +1,55 @@ +package com.github.kumaraman21.intellijbehave.resolver; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.github.kumaraman21.intellijbehave.ContentEntryTestBase; +import com.intellij.psi.PsiJavaFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiReference; +import com.intellij.testFramework.junit5.RunInEdt; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Test; + +/** + * Integration test for {@link StepPsiReference}. + */ +@RunInEdt +class StepPsiReferenceTest extends ContentEntryTestBase { + + @Nullable + @Override + protected String getTestDataPath() { + return "src/test/testData/reference/steppsi"; + } + + //isReferenceTo + + @Test + void shouldBeReferenceToPsiMethod() { + var storyFile = getFixture().configureByFile("test/resources/step_reference.story"); + getFixture().copyFileToProject("main/java/StepDefs.java"); + var step = storyFile.findElementAt(getFixture().getCaretOffset()).getParent(); + + PsiReference[] references = step.getReferences(); + assertThat(references).hasSize(1); + + var resolvedMethod = (PsiMethod) references[0].resolve(); + + assertThat(references[0].isReferenceTo(resolvedMethod)).isTrue(); + } + + @Test + void shouldNotBeReferenceToElement() { + var storyFile = getFixture().configureByFile("test/resources/step_reference.story"); + var stepDefVirtualFile = getFixture().copyFileToProject("main/java/StepDefs.java"); + var step = storyFile.findElementAt(getFixture().getCaretOffset()).getParent(); + var stepDefFile = PsiManager.getInstance(getFixture().getProject()).findFile(stepDefVirtualFile); + var notReferencedStepDefMethod = ((PsiJavaFile) stepDefFile).getClasses()[0].findMethodsByName("openAUrl", false)[0]; + + PsiReference[] references = step.getReferences(); + assertThat(references).hasSize(1); + + assertThat(references[0].isReferenceTo(notReferencedStepDefMethod)).isFalse(); + } +} diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveJavaMethodUsageSearcherTest.java b/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveJavaMethodUsageSearcherTest.java new file mode 100644 index 00000000..7e148dc6 --- /dev/null +++ b/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveJavaMethodUsageSearcherTest.java @@ -0,0 +1,94 @@ +package com.github.kumaraman21.intellijbehave.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.github.kumaraman21.intellijbehave.ContentEntryTestBase; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.SearchScope; +import com.intellij.psi.search.searches.MethodReferencesSearch; +import com.intellij.testFramework.junit5.RunInEdt; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Test; + +/** + * Integration test for {@link JBehaveJavaMethodUsageSearcher}. + */ +@RunInEdt +class JBehaveJavaMethodUsageSearcherTest extends ContentEntryTestBase { + + @Nullable + @Override + protected String getTestDataPath() { + return "src/test/testData/service/javamethodusagesearch"; + } + + @Test + void shouldReturnTrueIfThereIsNoStepText() { + var stepDefFile = getFixture().configureByFile("main/java/StepDefs.java"); + var method = (PsiMethod) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + var queryParameters = new MethodReferencesSearch.SearchParameters(method, GlobalSearchScope.projectScope(getFixture().getProject()), false); + + var ref = new Ref(); + new JBehaveJavaMethodUsageSearcher().processQuery(queryParameters, reference -> { + ref.set(true); + return true; + }); + + assertThat(ref.get()).isNull(); + } + + @Test + void shouldNotProcessQueryIfStepTextIsEmpty() { + var stepDefFile = getFixture().configureByFile("main/java/MoreStepDefs.java"); + var method = (PsiMethod) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + var queryParameters = new MethodReferencesSearch.SearchParameters(method, GlobalSearchScope.projectScope(getFixture().getProject()), false); + + var ref = new Ref(); + new JBehaveJavaMethodUsageSearcher().processQuery(queryParameters, reference -> { + ref.set(true); + return true; + }); + + assertThat(ref.get()).isNull(); + } + + @Test + void shouldNotProcessQueryIfSearchScopeIsNotGlobal() { + var stepDefFile = getFixture().configureByFile("main/java/OtherStepDefs.java"); + var method = (PsiMethod) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + var queryParameters = new MethodReferencesSearch.SearchParameters(method, new DummyScope(), false); + + var ref = new Ref(); + new JBehaveJavaMethodUsageSearcher().processQuery(queryParameters, reference -> { + ref.set(true); + return true; + }); + + assertThat(ref.get()).isNull(); + } + +// @Test +// void shouldProcessQuery() { +// } + + private static final class DummyScope extends SearchScope { + @Override + public @NotNull SearchScope intersectWith(@NotNull SearchScope scope2) { + return scope2; + } + + @Override + public @NotNull SearchScope union(@NotNull SearchScope scope) { + return scope; + } + + @Override + public boolean contains(@NotNull VirtualFile file) { + return false; + } + } +} diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveJavaStepDefinitionSearchTest.java b/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveJavaStepDefinitionSearchTest.java new file mode 100644 index 00000000..1e27ead8 --- /dev/null +++ b/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveJavaStepDefinitionSearchTest.java @@ -0,0 +1,74 @@ +package com.github.kumaraman21.intellijbehave.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.github.kumaraman21.intellijbehave.ContentEntryTestBase; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.testFramework.junit5.RunInEdt; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Test; + +/** + * Integration test for {@link JBehaveJavaStepDefinitionSearch}. + */ +@RunInEdt +class JBehaveJavaStepDefinitionSearchTest extends ContentEntryTestBase { + + @Nullable + @Override + protected String getTestDataPath() { + return "src/test/testData/service/stepdefsearch"; + } + + @Test + void shouldReturnTrueForNonPsiMethodElement() { + var stepDefFile = getFixture().configureByFile("main/java/OtherStepDefs.java"); + var element = stepDefFile.findElementAt(getFixture().getCaretOffset()); + var queryParameters = new ReferencesSearch.SearchParameters(element, GlobalSearchScope.projectScope(getFixture().getProject()), false); + + boolean shouldContinue = new JBehaveJavaStepDefinitionSearch().execute(queryParameters, __ -> true); + + assertThat(shouldContinue).isTrue(); + } + + @Test + void shouldReturnTrueForNonStepDefinitionMethod() { + var stepDefFile = getFixture().configureByFile("main/java/StepDefs.java"); + var method = (PsiMethod) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + var queryParameters = new ReferencesSearch.SearchParameters(method, GlobalSearchScope.projectScope(getFixture().getProject()), false); + + boolean shouldContinue = new JBehaveJavaStepDefinitionSearch().execute(queryParameters, __ -> true); + + assertThat(shouldContinue).isTrue(); + } + +// @Test +// void shouldReturnTrueIfThereIsNoStepText() { } + +// @Test +// void shouldReturnTrueIfThereIsANullStepTextForNonStepDefinitionMethod() { } + + @Test + void shouldReturnTrueForValidStepDefinitionMethod() { + var stepDefFile = getFixture().configureByFile("main/java/MoreStepDefs.java"); + var method = (PsiMethod) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + var queryParameters = new ReferencesSearch.SearchParameters(method, GlobalSearchScope.projectScope(getFixture().getProject()), false); + + boolean shouldContinue = new JBehaveJavaStepDefinitionSearch().execute(queryParameters, ref -> true); + + assertThat(shouldContinue).isTrue(); + } + + @Test + void shouldReturnFalseForValidStepDefinitionMethodAndFalseConsumer() { + var stepDefFile = getFixture().configureByFile("main/java/MoreStepDefs.java"); + var method = (PsiMethod) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + var queryParameters = new ReferencesSearch.SearchParameters(method, GlobalSearchScope.projectScope(getFixture().getProject()), false); + + boolean shouldContinue = new JBehaveJavaStepDefinitionSearch().execute(queryParameters, ref -> false); + + assertThat(shouldContinue).isFalse(); + } +} diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveStepsIndexTest.java b/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveStepsIndexTest.java index 37ea6bd2..05aff1dc 100644 --- a/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveStepsIndexTest.java +++ b/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveStepsIndexTest.java @@ -2,115 +2,69 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.github.kumaraman21.intellijbehave.ContentEntryTestBase; import com.github.kumaraman21.intellijbehave.parser.JBehaveStep; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.projectRoots.JavaSdk; -import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.roots.ContentEntry; -import com.intellij.openapi.roots.ModifiableRootModel; -import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess; -import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.PsiSubstitutor; -import com.intellij.testFramework.LightProjectDescriptor; -import com.intellij.testFramework.PsiTestUtil; -import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; -import com.intellij.util.PathUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jps.model.java.JavaResourceRootType; -import org.jetbrains.jps.model.java.JavaSourceRootType; - -import java.io.File; +import com.intellij.testFramework.junit5.RunInEdt; +import org.junit.jupiter.api.Test; /** * Functional test for {@link JBehaveStepsIndex}. */ -public class JBehaveStepsIndexTest extends LightJavaCodeInsightFixtureTestCase { - - @Override - public void setUp() throws Exception { - super.setUp(); - loadLibrary(myFixture.getProjectDisposable(), getModule(), "JBehave Core", "jbehave-core-5.1.1.jar"); - myFixture.copyDirectoryToProject("src", ""); - myFixture.copyFileToProject("main/java/StepDefs.java"); - myFixture.copyFileToProject("main/java/OtherStepDefs.java"); - } +@RunInEdt +class JBehaveStepsIndexTest extends ContentEntryTestBase { @Override protected String getTestDataPath() { return "src/test/testData/stepsindex"; } - @Override - protected @NotNull LightProjectDescriptor getProjectDescriptor() { - return new ContentRootsProjectDescriptor(); - } - // findStepDefinitions - public void testFindsSingleStepDefinition() { - myFixture.configureByFile("test/resources/has_java_step_def.story"); + @Test + void shouldFindsSingleStepDefinition() { + getFixture().copyFileToProject("main/java/StepDefs.java"); + getFixture().copyFileToProject("main/java/OtherStepDefs.java"); + + getFixture().configureByFile("test/resources/has_java_step_def.story"); - var stepDefinitions = JBehaveStepsIndex.getInstance(getProject()).findStepDefinitions(getStep()); + var stepDefinitions = JBehaveStepsIndex.getInstance(getFixture().getProject()).findStepDefinitions(getStep()); assertThat(stepDefinitions).hasSize(1); assertThat(stepDefinitions.iterator().next().getAnnotatedMethod().getContainingClass().getQualifiedName()).isEqualTo("StepDefs"); assertThat(stepDefinitions.iterator().next().getAnnotatedMethod().getSignature(PsiSubstitutor.EMPTY)) - .hasToString("MethodSignatureBackedByPsiMethod: openAUrl([PsiType:String])"); + .hasToString("MethodSignatureBackedByPsiMethod: openAUrl([PsiType:String])"); } //NOTE: at the moment, this only returns the first found step definition, regardless of the step pattern // existing for multiple different step types, e.g. for a @Given and a @Then step def, and regardless of step priority. //Supporting returning multiple matching step definitions should be revisited. - public void testFindsMultipleStepDefinitions() { - myFixture.configureByFile("test/resources/has_multiple_java_step_def.story"); + @Test + void shouldFindsMultipleStepDefinitions() { + getFixture().copyFileToProject("main/java/StepDefs.java"); + getFixture().copyFileToProject("main/java/OtherStepDefs.java"); + getFixture().configureByFile("test/resources/has_multiple_java_step_def.story"); - var stepDefinitions = JBehaveStepsIndex.getInstance(getProject()).findStepDefinitions(getStep()); + var stepDefinitions = JBehaveStepsIndex.getInstance(getFixture().getProject()).findStepDefinitions(getStep()); assertThat(stepDefinitions).hasSize(1); assertThat(stepDefinitions.iterator().next().getAnnotatedMethod().getContainingClass().getQualifiedName()).isEqualTo("OtherStepDefs"); assertThat(stepDefinitions.iterator().next().getAnnotatedMethod().getSignature(PsiSubstitutor.EMPTY)) - .hasToString("MethodSignatureBackedByPsiMethod: checkResultListSize([PsiType:int])"); + .hasToString("MethodSignatureBackedByPsiMethod: checkResultListSize([PsiType:int])"); } - public void testFindsNoStepDefinition() { - myFixture.configureByFile("test/resources/has_no_java_step_def.story"); + @Test + void shouldFindsNoStepDefinition() { + getFixture().copyFileToProject("main/java/StepDefs.java"); + getFixture().copyFileToProject("main/java/OtherStepDefs.java"); + getFixture().configureByFile("test/resources/has_no_java_step_def.story"); - var stepDefinitions = JBehaveStepsIndex.getInstance(getProject()).findStepDefinitions(getStep()); + var stepDefinitions = JBehaveStepsIndex.getInstance(getFixture().getProject()).findStepDefinitions(getStep()); assertThat(stepDefinitions).isEmpty(); } private JBehaveStep getStep() { - return (JBehaveStep) myFixture.getFile().findElementAt(myFixture.getCaretOffset()).getParent(); - } - - private static void loadLibrary(@NotNull Disposable projectDisposable, @NotNull Module module, String libraryName, String libraryJarName) { - String libPath = PathUtil.toSystemIndependentName(new File("lib").getAbsolutePath()); - VfsRootAccess.allowRootAccess(projectDisposable, libPath); - PsiTestUtil.addLibrary(projectDisposable, module, libraryName, libPath, libraryJarName); - } - - public static class ContentRootsProjectDescriptor extends ProjectDescriptor { - public ContentRootsProjectDescriptor() { - super(LanguageLevel.JDK_11); - } - - @Override - public Sdk getSdk() { - return JavaSdk.getInstance().createJdk("Real JDK", System.getenv("JAVA_HOME"), false); - } - - @Override - public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) { - super.configureModule(module, model, contentEntry); - contentEntry.clearSourceFolders(); - - String contentEntryUrl = contentEntry.getUrl(); - contentEntry.addSourceFolder(contentEntryUrl + "/main/java", JavaSourceRootType.SOURCE); - contentEntry.addSourceFolder(contentEntryUrl + "/main/resources", JavaResourceRootType.RESOURCE); - contentEntry.addSourceFolder(contentEntryUrl + "/test/java", JavaSourceRootType.TEST_SOURCE); - contentEntry.addSourceFolder(contentEntryUrl + "/test/resources", JavaResourceRootType.TEST_RESOURCE); - } + return (JBehaveStep) getFixture().getFile().findElementAt(getFixture().getCaretOffset()).getParent(); } } diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveStepsIndexWithNoDependencyTest.java b/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveStepsIndexWithNoDependencyTest.java index 0f2b9257..72ef6e56 100644 --- a/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveStepsIndexWithNoDependencyTest.java +++ b/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveStepsIndexWithNoDependencyTest.java @@ -2,23 +2,17 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.github.kumaraman21.intellijbehave.ContentEntryTestBase; import com.github.kumaraman21.intellijbehave.parser.JBehaveStep; -import com.intellij.testFramework.LightProjectDescriptor; -import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; -import org.jetbrains.annotations.NotNull; +import com.intellij.testFramework.junit5.RunInEdt; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * Functional test for {@link JBehaveStepsIndex}. */ -public class JBehaveStepsIndexWithNoDependencyTest extends LightJavaCodeInsightFixtureTestCase { - - @Override - public void setUp() throws Exception { - super.setUp(); - myFixture.copyDirectoryToProject("src", ""); - myFixture.copyFileToProject("main/java/StepDefs.java"); - myFixture.copyFileToProject("main/java/OtherStepDefs.java"); - } +@RunInEdt +class JBehaveStepsIndexWithNoDependencyTest extends ContentEntryTestBase { @Override protected String getTestDataPath() { @@ -26,15 +20,18 @@ protected String getTestDataPath() { } @Override - protected @NotNull LightProjectDescriptor getProjectDescriptor() { - return new JBehaveStepsIndexTest.ContentRootsProjectDescriptor(); + protected void loadLibraries() { + //Doesn't load anything to emulate the missing JBehave } - public void testFindsNoStepDefinitionDueToNoJBehaveAnnotationsAvailable() { - myFixture.configureByFile("test/resources/has_java_step_def.story"); + @Test + void shouldFindNoStepDefinitionDueToNoJBehaveAnnotationsAvailable() { + getFixture().copyFileToProject("main/java/StepDefs.java"); + getFixture().copyFileToProject("main/java/OtherStepDefs.java"); + getFixture().configureByFile("test/resources/has_java_step_def.story"); - JBehaveStep step = (JBehaveStep) myFixture.getFile().findElementAt(myFixture.getCaretOffset()).getParent(); - var stepDefinitions = JBehaveStepsIndex.getInstance(getProject()).findStepDefinitions(step); + JBehaveStep step = (JBehaveStep) getFixture().getFile().findElementAt(getFixture().getCaretOffset()).getParent(); + var stepDefinitions = JBehaveStepsIndex.getInstance(getFixture().getProject()).findStepDefinitions(step); assertThat(stepDefinitions).isEmpty(); } diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveUtilContentsTest.java b/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveUtilContentsTest.java new file mode 100644 index 00000000..d60bf9ca --- /dev/null +++ b/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveUtilContentsTest.java @@ -0,0 +1,83 @@ +package com.github.kumaraman21.intellijbehave.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.github.kumaraman21.intellijbehave.ContentEntryTestBase; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.testFramework.junit5.RunInEdt; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Test; + +/** + * Integration test for {@link JBehaveUtil}. + */ +@RunInEdt +class JBehaveUtilContentsTest extends ContentEntryTestBase { + + @Nullable + @Override + protected String getTestDataPath() { + return "src/test/testData/service/jbehaveutil"; + } + + //findJBehaveReferencesToElement + + @Test + void shouldContinueReferenceSearchForEmptyBiggestWord() { + var stepDefFile = getFixture().configureByFile("main/java/StepDefs.java"); + + var stepDefMethod = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + + boolean findRef = JBehaveUtil.findJBehaveReferencesToElement( + stepDefMethod, + "", + __ -> true, + GlobalSearchScope.projectScope(getFixture().getProject())); + assertThat(findRef).isTrue(); + } + + @Test + void shouldContinueReferenceSearchForValidMethod() { + getFixture().copyFileToProject("src/test/resources/reference.story"); + var stepDefFile = getFixture().configureByFile("main/java/StepDefs.java"); + + var stepDefMethod = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + + boolean findRef = JBehaveUtil.findJBehaveReferencesToElement( + stepDefMethod, + "search for $string", + __ -> true, + GlobalSearchScope.projectScope(getFixture().getProject())); + assertThat(findRef).isTrue(); + } + + @Test + void shouldNotContinueReferenceSearchForValidMethodWithFalseConsumer() { + getFixture().copyFileToProject("src/test/resources/reference.story"); + var stepDefFile = getFixture().configureByFile("main/java/StepDefs.java"); + + var stepDefMethod = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + + boolean findRef = JBehaveUtil.findJBehaveReferencesToElement( + stepDefMethod, + "search for $string", + __ -> false, + GlobalSearchScope.projectScope(getFixture().getProject())); + assertThat(findRef).isFalse(); + } + + @Test + void shouldContinueReferenceSearchForMethodWithNoReference() { + getFixture().copyFileToProject("src/test/resources/reference.story"); + var stepDefFile = getFixture().configureByFile("main/java/OtherStepDefs.java"); + + var stepDefMethod = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + + boolean findRef = JBehaveUtil.findJBehaveReferencesToElement( + stepDefMethod, + "result list size is $size", + __ -> true, + GlobalSearchScope.projectScope(getFixture().getProject())); + assertThat(findRef).isTrue(); + } +} diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveUtilTest.java b/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveUtilTest.java new file mode 100644 index 00000000..cc8a5881 --- /dev/null +++ b/src/test/java/com/github/kumaraman21/intellijbehave/service/JBehaveUtilTest.java @@ -0,0 +1,485 @@ +package com.github.kumaraman21.intellijbehave.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.github.kumaraman21.intellijbehave.JBehaveSupportTestBase; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiMethod; +import com.intellij.testFramework.junit5.RunInEdt; +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.When; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +/** + * Integration test for {@link JBehaveUtil}. + */ +@RunInEdt +class JBehaveUtilTest extends JBehaveSupportTestBase { + @Nullable + @Override + protected String getTestDataPath() { + return ""; //empty because it is not needed, but without overriding the method, the test setup fails + } + + //isJBehaveStepAnnotation + + @Test + void shouldBeJBehaveStepAnnotation() { + var stepDefFile = getFixture().configureByText("JBehaveStepAnnotation.java", """ + import org.jbehave.core.annotations.Given; + + class JBehaveStepAnnotation { + @Given("") + void stepDefMethod() { + } + } + """); + + var annotation = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + + assertThat(annotation).isInstanceOf(PsiAnnotation.class); + assertThat(JBehaveUtil.isJBehaveStepAnnotation((PsiAnnotation) annotation)).isTrue(); + } + + @Test + void shouldNotBeJBehaveStepAnnotationForNonStepAnnotation() { + var stepDefFile = getFixture().configureByText("JBehaveStepAnnotation.java", """ + import org.jbehave.core.annotations.Alias; + + class JBehaveStepAnnotation { + @Alias("") + void stepDefMethod() { + } + } + """); + + var annotation = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + + assertThat(annotation).isInstanceOf(PsiAnnotation.class); + assertThat(JBehaveUtil.isJBehaveStepAnnotation((PsiAnnotation) annotation)).isFalse(); + } + + @Test + void shouldNotBeJBehaveStepAnnotationForUnresolvedAnnotation() { + var stepDefFile = getFixture().configureByText("JBehaveStepAnnotation.java", """ + class JBehaveStepAnnotation { + @("") + void stepDefMethod() { + } + } + """); + + var annotation = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + + assertThat(annotation).isInstanceOf(PsiAnnotation.class); + assertThat(JBehaveUtil.isJBehaveStepAnnotation((PsiAnnotation) annotation)).isFalse(); + } + + //isAnnotationOfClass + + @Test + void shouldBeAnnotationOfClass() { + var stepDefFile = getFixture().configureByText("JBehaveAnnotationOfClass.java", """ + import org.jbehave.core.annotations.Given; + + class JBehaveAnnotationOfClass { + @Given("") + void stepDefMethod() { + } + } + """); + + var annotation = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(annotation).isInstanceOf(PsiAnnotation.class); + assertThat(JBehaveUtil.isAnnotationOfClass((PsiAnnotation) annotation, Given.class)).isTrue(); + } + + @Test + void shouldNotBeAnnotationOfClass() { + var stepDefFile = getFixture().configureByText("JBehaveAnnotationOfClass.java", """ + import org.jbehave.core.annotations.Given; + + class JBehaveAnnotationOfClass { + @Given("") + void stepDefMethod() { + } + } + """); + + var annotation = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(annotation).isInstanceOf(PsiAnnotation.class); + assertThat(JBehaveUtil.isAnnotationOfClass((PsiAnnotation) annotation, When.class)).isFalse(); + } + + @Test + void shouldNotBeAnnotationOfClassForUnresolvedAnnotation() { + var stepDefFile = getFixture().configureByText("JBehaveAnnotationOfClass.java", """ + class JBehaveAnnotationOfClass { + @("") + void stepDefMethod() { + } + } + """); + + var annotation = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(annotation).isInstanceOf(PsiAnnotation.class); + assertThat(JBehaveUtil.isAnnotationOfClass((PsiAnnotation) annotation, When.class)).isFalse(); + } + + //isStepDefinition + + @Test + void shouldBeStepDefinitionMethod() { + var stepDefFile = getFixture().configureByText("StepDefinitionMethod.java", """ + import org.jbehave.core.annotations.Given; + + class StepDefinitionMethod { + @Given("") + void stepDefMethod() { + } + } + """); + + var method = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + assertThat(method).isInstanceOf(PsiMethod.class); + assertThat(JBehaveUtil.isStepDefinition((PsiMethod) method)).isTrue(); + } + + @Test + void shouldNotBeStepDefinitionMethodWithoutStepAnnotation() { + var stepDefFile = getFixture().configureByText("StepDefinitionMethod.java", """ + class StepDefinitionMethod { + void stepDefMethod() { + } + } + """); + + var method = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + assertThat(method).isInstanceOf(PsiMethod.class); + assertThat(JBehaveUtil.isStepDefinition((PsiMethod) method)).isFalse(); + } + + @Test + void shouldNotBeStepDefinitionMethodWithAllStepAnnotationsWithNullValue() { + var stepDefFile = getFixture().configureByText("StepDefinitionMethod.java", """ + import org.jbehave.core.annotations.Given; + + class StepDefinitionMethod { + @Given + void stepDefMethod() { + } + } + """); + + var method = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + assertThat(method).isInstanceOf(PsiMethod.class); + assertThat(JBehaveUtil.isStepDefinition((PsiMethod) method)).isFalse(); + } + + //getAnnotationTexts(PsiAnnotation) + + @Test + void shouldGetNoTextForInvalidAnnotation() { + var stepDefFile = getFixture().configureByText("AnnotationTexts.java", """ + import org.jbehave.core.annotations.Then; + + class AnnotationTexts { + @Then() + void stepDefMethod(int price) { + } + } + """); + + var annotation = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(annotation).isInstanceOf(PsiAnnotation.class); + assertThat(JBehaveUtil.getAnnotationTexts((PsiAnnotation) annotation)).isEmpty(); + } + + @Test + void shouldGetTextsWithoutAliases() { + var stepDefFile = getFixture().configureByText("AnnotationTexts.java", """ + import org.jbehave.core.annotations.Then; + + class AnnotationTexts { + @Then("the {price|cost} of the product should be $price") + void stepDefMethod(int price) { + } + } + """); + + var annotation = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(annotation).isInstanceOf(PsiAnnotation.class); + assertThat(JBehaveUtil.getAnnotationTexts((PsiAnnotation) annotation)).containsExactlyInAnyOrder( + "the price of the product should be $price", + "the cost of the product should be $price"); + } + + @Test + void shouldGetTextsWithAlias() { + var stepDefFile = getFixture().configureByText("AnnotationTexts.java", """ + import org.jbehave.core.annotations.Then; + import org.jbehave.core.annotations.Alias; + + class AnnotationTexts { + @Then("the {price|cost} of the product should be $price") + @Alias("the product should {cost|be sold for} $price") + void stepDefMethod(int price) { + } + } + """); + + var annotation = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(annotation).isInstanceOf(PsiAnnotation.class); + assertThat(JBehaveUtil.getAnnotationTexts((PsiAnnotation) annotation)).containsExactlyInAnyOrder( + "the price of the product should be $price", + "the product should cost $price", + "the cost of the product should be $price", + "the product should be sold for $price" + ); + } + + @Test + void shouldGetTextsWithAliases() { + var stepDefFile = getFixture().configureByText("AnnotationTexts.java", """ + import org.jbehave.core.annotations.Then; + import org.jbehave.core.annotations.Aliases; + + class AnnotationTexts { + @Then("the {price|cost} of the product should be $price") + @Aliases(values = {"the product should cost $price", + "the product should be sold for $price"}) + void stepDefMethod(int price) { + } + } + """); + + var annotation = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(annotation).isInstanceOf(PsiAnnotation.class); + assertThat(JBehaveUtil.getAnnotationTexts((PsiAnnotation) annotation)).containsExactlyInAnyOrder( + "the price of the product should be $price", + "the product should cost $price", + "the cost of the product should be $price", + "the product should be sold for $price" + ); + } + + @Test + void shouldGetTextsWithAliasAndAliases() { + var stepDefFile = getFixture().configureByText("AnnotationTexts.java", """ + import org.jbehave.core.annotations.Then; + import org.jbehave.core.annotations.Alias; + import org.jbehave.core.annotations.Aliases; + + class AnnotationTexts { + @Then("the {price|cost} of the product should be $price") + @Alias("the product should {cost|be sold for} $price") + @Aliases(values = {"the product should worth $price", + "the product should be sold for $price"}) + void stepDefMethod(int price) { + } + } + """); + + var annotation = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(annotation).isInstanceOf(PsiAnnotation.class); + assertThat(JBehaveUtil.getAnnotationTexts((PsiAnnotation) annotation)).containsExactlyInAnyOrder( + "the product should worth $price", + "the price of the product should be $price", + "the product should cost $price", + "the cost of the product should be $price", + "the product should be sold for $price" + ); + } + + //getAnnotationTexts(PsiMethod) + + @Test + void shouldGetNoTextForMethodWithInvalidStepAnnotation() { + var stepDefFile = getFixture().configureByText("AnnotationTexts.java", """ + import org.jbehave.core.annotations.Then; + + class AnnotationTexts { + @Then() + void stepDefMethod(int price) { + } + } + """); + + var method = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + assertThat(method).isInstanceOf(PsiMethod.class); + assertThat(JBehaveUtil.getAnnotationTexts((PsiMethod) method)).isEmpty(); + } + + @Test + void shouldGetTextsForMethodWithoutAliases() { + var stepDefFile = getFixture().configureByText("AnnotationTexts.java", """ + import org.jbehave.core.annotations.Then; + + class AnnotationTexts { + @Then("the {price|cost} of the product should be $price") + void stepDefMethod(int price) { + } + } + """); + + var method = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + assertThat(method).isInstanceOf(PsiMethod.class); + assertThat(JBehaveUtil.getAnnotationTexts((PsiMethod) method)).containsExactlyInAnyOrder( + "the price of the product should be $price", + "the cost of the product should be $price"); + } + + @Test + void shouldGetTextsForMethodWithAlias() { + var stepDefFile = getFixture().configureByText("AnnotationTexts.java", """ + import org.jbehave.core.annotations.Then; + import org.jbehave.core.annotations.Alias; + + class AnnotationTexts { + @Then("the {price|cost} of the product should be $price") + @Alias("the product should {cost|be sold for} $price") + void stepDefMethod(int price) { + } + } + """); + + var method = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + assertThat(method).isInstanceOf(PsiMethod.class); + assertThat(JBehaveUtil.getAnnotationTexts((PsiMethod) method)).containsExactlyInAnyOrder( + "the price of the product should be $price", + "the product should cost $price", + "the cost of the product should be $price", + "the product should be sold for $price" + ); + } + + @Test + void shouldGetTextsForMethodWithAliases() { + var stepDefFile = getFixture().configureByText("AnnotationTexts.java", """ + import org.jbehave.core.annotations.Then; + import org.jbehave.core.annotations.Aliases; + + class AnnotationTexts { + @Then("the {price|cost} of the product should be $price") + @Aliases(values = {"the product should cost $price", + "the product should be sold for $price"}) + void stepDefMethod(int price) { + } + } + """); + + var method = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + assertThat(method).isInstanceOf(PsiMethod.class); + assertThat(JBehaveUtil.getAnnotationTexts((PsiMethod) method)).containsExactlyInAnyOrder( + "the price of the product should be $price", + "the product should cost $price", + "the cost of the product should be $price", + "the product should be sold for $price" + ); + } + + @Test + void shouldGetTextsForMethodWithAliasAndAliases() { + var stepDefFile = getFixture().configureByText("AnnotationTexts.java", """ + import org.jbehave.core.annotations.Then; + import org.jbehave.core.annotations.Alias; + import org.jbehave.core.annotations.Aliases; + + class AnnotationTexts { + @Then("the {price|cost} of the product should be $price") + @Alias("the product should {cost|be sold for} $price") + @Aliases(values = {"the product should worth $price", + "the product should be sold for $price"}) + void stepDefMethod(int price) { + } + } + """); + + var method = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent(); + assertThat(method).isInstanceOf(PsiMethod.class); + assertThat(JBehaveUtil.getAnnotationTexts((PsiMethod) method)).containsExactlyInAnyOrder( + "the product should worth $price", + "the price of the product should be $price", + "the product should cost $price", + "the cost of the product should be $price", + "the product should be sold for $price" + ); + } + + //getAnnotationPriority + + @Test + void shouldGetDefaultPriority() { + var stepDefFile = getFixture().configureByText("AnnotationPriority.java", """ + import org.jbehave.core.annotations.Then; + + class AnnotationPriority { + @Then("the {price|cost} of the product should be $price") + void stepDefMethod(int price) { + } + } + """); + + var annotation = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(annotation).isInstanceOf(PsiAnnotation.class); + assertThat(JBehaveUtil.getAnnotationPriority((PsiAnnotation) annotation)).isEqualTo(0); + } + + @Test + void shouldGetCustomPriority() { + var stepDefFile = getFixture().configureByText("AnnotationPriority.java", """ + import org.jbehave.core.annotations.Then; + + class AnnotationPriority { + @Then(value = "the {price|cost} of the product should be $price", priority = 30) + void stepDefMethod(int price) { + } + } + """); + + var annotation = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(annotation).isInstanceOf(PsiAnnotation.class); + assertThat(JBehaveUtil.getAnnotationPriority((PsiAnnotation) annotation)).isEqualTo(30); + } + + @Test + void shouldGetFallbackPriorityWhenConfiguredWithInvalidValue() { + var stepDefFile = getFixture().configureByText("AnnotationPriority.java", """ + import org.jbehave.core.annotations.Then; + + class AnnotationPriority { + @Then(value = "the {price|cost} of the product should be $price", priority = "invalid") + void stepDefMethod(int price) { + } + } + """); + + var annotation = stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(annotation).isInstanceOf(PsiAnnotation.class); + assertThat(JBehaveUtil.getAnnotationPriority((PsiAnnotation) annotation)).isEqualTo(-1); + } + + //getTheBiggestWordToSearchByIndex + + @ParameterizedTest + @CsvSource({ + //Empty string for empty string + "'',''", + //Empty string for blank string + "' ',''", + //Without placeholder + "the {price|cost} of the product should be 100, product", + //With placeholder + "the {price|cost} of the product should be $price, product", + //First of multiple of the same-length words + "the {price|cost} of the product shouldd be $price, product", + //Empty string for placeholder-only, + "$price $cost, ''" + }) + void shouldReturnBiggestWord(String stepText, String biggestWord) { + assertThat(JBehaveUtil.getTheBiggestWordToSearchByIndex(stepText)).isEqualTo(biggestWord); + } +} diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/service/JavaStepDefinitionTest.java b/src/test/java/com/github/kumaraman21/intellijbehave/service/JavaStepDefinitionTest.java new file mode 100644 index 00000000..847dbc1a --- /dev/null +++ b/src/test/java/com/github/kumaraman21/intellijbehave/service/JavaStepDefinitionTest.java @@ -0,0 +1,265 @@ +package com.github.kumaraman21.intellijbehave.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.github.kumaraman21.intellijbehave.JBehaveSupportTestBase; +import com.github.kumaraman21.intellijbehave.parser.JBehaveStep; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiMethod; +import com.intellij.testFramework.junit5.RunInEdt; +import org.jbehave.core.steps.StepType; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Test; + +/** + * Integration test for {@link JavaStepDefinition}. + */ +@RunInEdt +class JavaStepDefinitionTest extends JBehaveSupportTestBase { + + @Nullable + @Override + protected String getTestDataPath() { + return ""; + } + + //matches + + @Test + void shouldMatchStepText() { + var stepDefFile = getFixture().configureByText("JavaStepDefinition.java", """ + import org.jbehave.core.annotations.Aliases; + + class JavaStepDefinition { + @Aliases(values = { + "the price should be $price", + "the cost should be $price" + }) + void steDefMethod(int price) { + } + } + """); + + var annotation = (PsiAnnotation) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(new JavaStepDefinition(annotation).matches("the cost should be 200")).isTrue(); + } + + @Test + void shouldNotMatchStepText() { + var stepDefFile = getFixture().configureByText("JavaStepDefinition.java", """ + import org.jbehave.core.annotations.Aliases; + + class JavaStepDefinition { + @Aliases(values = { + "the price should be $price", + "the cost should be $price" + }) + void steDefMethod(int price) { + } + } + """); + + var annotation = (PsiAnnotation) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(new JavaStepDefinition(annotation).matches("the price is 200")).isFalse(); + } + + //getAnnotationTextFor + + @Test + void shouldGetAnnotationTextWhenThereIsOneSuchText() { + var stepDefFile = getFixture().configureByText("JavaStepDefinition.java", """ + import org.jbehave.core.annotations.Alias; + + class JavaStepDefinition { + @Alias("the price should be $price") + void steDefMethod(int price) { + } + } + """); + + var annotation = (PsiAnnotation) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(new JavaStepDefinition(annotation).getAnnotationTextFor("the price should be 200")) + .isEqualTo("the price should be $price"); + } + + @Test + void shouldGetFirstMatchingAnnotationTextFromMultiple() { + var stepDefFile = getFixture().configureByText("JavaStepDefinition.java", """ + import org.jbehave.core.annotations.Aliases; + + class JavaStepDefinition { + @Aliases(values = { + "the price should be $price", + "the cost should be $price" + }) + void steDefMethod(int price) { + } + } + """); + + var annotation = (PsiAnnotation) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(new JavaStepDefinition(annotation).getAnnotationTextFor("the cost should be 200")) + .isEqualTo("the cost should be $price"); + } + + @Test + void shouldReturnNoAnnotationTextWhenNotMatching() { + var stepDefFile = getFixture().configureByText("JavaStepDefinition.java", """ + import org.jbehave.core.annotations.Alias; + + class JavaStepDefinition { + @Aliases(values = { + "the price should be $price", + "the cost should be $price" + }) + void steDefMethod(int price) { + } + } + """); + + var annotation = (PsiAnnotation) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(new JavaStepDefinition(annotation).getAnnotationTextFor("non matching text")).isNull(); + } + + //getAnnotatedMethod + + @Test + void shouldReturnTheAnnotatedMethod() { + var stepDefFile = getFixture().configureByText("JavaStepDefinition.java", """ + import org.jbehave.core.annotations.Then; + + class JavaStepDefinition { + @Then("the price should be $price") + void steDefMethod(int price) { + } + } + """); + + var annotation = (PsiAnnotation) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(new JavaStepDefinition(annotation).getAnnotatedMethod()).extracting(PsiMethod::getName).isEqualTo("steDefMethod"); + } + + @Test + void shouldReturnNullWhenThereIsNoParentMethod() { + var stepDefFile = getFixture().configureByText("JavaStepDefinition.java", """ + import org.jbehave.core.annotations.Then; + + class JavaStepDefinition { + @Then("the price should be $price") + } + """); + + var annotation = (PsiAnnotation) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(new JavaStepDefinition(annotation).getAnnotatedMethod()).isNull(); + } + + //getAnnotationType + + @Test + void shouldGetAnnotationType() { + var stepDefFile = getFixture().configureByText("JavaStepDefinition.java", """ + import org.jbehave.core.annotations.Then; + + class JavaStepDefinition { + @Then("the price should be $price") + void steDefMethod(int price) { + } + } + """); + + var annotation = (PsiAnnotation) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(new JavaStepDefinition(annotation).getAnnotationType()).isEqualTo(StepType.THEN); + } + + @Test + void shouldReturnNullForNonMappedAnnotationType() { + var stepDefFile = getFixture().configureByText("JavaStepDefinition.java", """ + import org.jbehave.core.annotations.Alias; + + class JavaStepDefinition { + @Alias("the price should be $price") + void steDefMethod(int price) { + } + } + """); + + var annotation = (PsiAnnotation) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(new JavaStepDefinition(annotation).getAnnotationType()).isNull(); + } + +// @Test +// void shouldReturnNullAnnotationTypeForNoAnnotation() { +// } + + //getAnnotationPriority + + @Test + void shouldGetAnnotationPriority() { + var stepDefFile = getFixture().configureByText("JavaStepDefinition.java", """ + import org.jbehave.core.annotations.Then; + + class JavaStepDefinition { + @Then(value = "the price should be $price", priority = 500) + void steDefMethod(int price) { + } + } + """); + + var annotation = (PsiAnnotation) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + assertThat(new JavaStepDefinition(annotation).getAnnotationPriority()).isEqualTo(500); + } + +// @Test +// void shouldReturnFallbackValueForNoAnnotation() { +// } + + //supportsStep + + @Test + void shouldSupportStep() { + var stepDefFile = getFixture().configureByText("JavaStepDefinition.java", """ + import org.jbehave.core.annotations.Then; + + class JavaStepDefinition { + @Then(value = "the price should be $price", priority = 500) + void steDefMethod(int price) { + } + } + """); + + var annotation = (PsiAnnotation) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + + var storyFile = getFixture().configureByText("supports_step.story", """ + Scenario: Product price + + Then the price should be 200 + """); + + var step = (JBehaveStep) storyFile.findElementAt(getFixture().getCaretOffset()).getParent(); + assertThat(new JavaStepDefinition(annotation).supportsStep(step)).isTrue(); + } + + @Test + void shouldNotSupportStep() { + var stepDefFile = getFixture().configureByText("JavaStepDefinition.java", """ + import org.jbehave.core.annotations.Given; + + class JavaStepDefinition { + @Given(value = "the price should be $price", priority = 500) + void steDefMethod(int price) { + } + } + """); + + var annotation = (PsiAnnotation) stepDefFile.findElementAt(getFixture().getCaretOffset()).getParent().getParent(); + + var storyFile = getFixture().configureByText("supports_step.story", """ + Scenario: Product price + + Then the price should be 200 + """); + + var step = (JBehaveStep) storyFile.findElementAt(getFixture().getCaretOffset()).getParent(); + assertThat(new JavaStepDefinition(annotation).supportsStep(step)).isFalse(); + } +} diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/utility/CharTreeTest.java b/src/test/java/com/github/kumaraman21/intellijbehave/utility/CharTreeTest.java index 5e4ae411..1ec00309 100644 --- a/src/test/java/com/github/kumaraman21/intellijbehave/utility/CharTreeTest.java +++ b/src/test/java/com/github/kumaraman21/intellijbehave/utility/CharTreeTest.java @@ -1,8 +1,8 @@ package com.github.kumaraman21.intellijbehave.utility; import org.jbehave.core.i18n.LocalizedKeywords; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static com.github.kumaraman21.intellijbehave.utility.JBKeyword.*; import static org.assertj.core.api.Assertions.assertThat; @@ -14,7 +14,7 @@ public class CharTreeTest { private CharTree charTree; - @Before + @BeforeEach public void setUp () { LocalizedKeywords keywords = new LocalizedKeywords(); charTree = new CharTree<>('/', null); diff --git a/src/test/java/com/github/kumaraman21/intellijbehave/utility/LocalizedStorySupportTest.java b/src/test/java/com/github/kumaraman21/intellijbehave/utility/LocalizedStorySupportTest.java index d7c4c48e..789f5bdf 100644 --- a/src/test/java/com/github/kumaraman21/intellijbehave/utility/LocalizedStorySupportTest.java +++ b/src/test/java/com/github/kumaraman21/intellijbehave/utility/LocalizedStorySupportTest.java @@ -1,9 +1,9 @@ package com.github.kumaraman21.intellijbehave.utility; -import org.junit.Test; - import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + /** * @author @aloyer */ @@ -16,6 +16,7 @@ public void checkForLanguageDefinition_validCases() { assertThat(LocalizedStorySupport.checkForLanguageDefinition(" !-- language: fr ")).isEqualTo("fr"); assertThat(LocalizedStorySupport.checkForLanguageDefinition(" !-- language: fr ")).isEqualTo("fr"); } + @Test public void checkForLanguageDefinition_invalidCases() { assertThat(LocalizedStorySupport.checkForLanguageDefinition(" !-- languge:fr")).isNull(); diff --git a/src/test/testData/codeinspector/undefinedstep/src/main/java/OtherStepDefs.java b/src/test/testData/codeinspector/undefinedstep/src/main/java/OtherStepDefs.java new file mode 100644 index 00000000..ebaa1d47 --- /dev/null +++ b/src/test/testData/codeinspector/undefinedstep/src/main/java/OtherStepDefs.java @@ -0,0 +1,10 @@ +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.Then; +import org.jbehave.core.steps.Steps; + +public class OtherStepDefs extends Steps { + + @Then("result list size is $size") + public void checkResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/codeinspector/undefinedstep/src/main/java/StepDefs.java b/src/test/testData/codeinspector/undefinedstep/src/main/java/StepDefs.java new file mode 100644 index 00000000..b3707a08 --- /dev/null +++ b/src/test/testData/codeinspector/undefinedstep/src/main/java/StepDefs.java @@ -0,0 +1,19 @@ +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.When; +import org.jbehave.core.steps.Steps; + +public class StepDefs extends Steps { + + @Given("Open url '$url'") + public void openAUrl(@Named("url") String url) { + } + + @When("search for $string") + public void searchForText(@Named("string") String string) { + } + + @Given(value = "result list size is $size", priority = 2) + public void setResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/codeinspector/undefinedstep/src/test/resources/undefined_steps.story b/src/test/testData/codeinspector/undefinedstep/src/test/resources/undefined_steps.story new file mode 100644 index 00000000..eb457a7c --- /dev/null +++ b/src/test/testData/codeinspector/undefinedstep/src/test/resources/undefined_steps.story @@ -0,0 +1,12 @@ +Narrative: +Testing a search result size + +Meta: +@Suite smoke testing +Scenario: open a url + +Given Open url 'http://some.url/path' +When search for 'something' +Then result list size is 10 +Then check result list size is 10 +Then check that result contains "an item" diff --git a/src/test/testData/codeinspector/unusedstepdeclarations/src/main/java/OtherStepDefs.java b/src/test/testData/codeinspector/unusedstepdeclarations/src/main/java/OtherStepDefs.java new file mode 100644 index 00000000..ebaa1d47 --- /dev/null +++ b/src/test/testData/codeinspector/unusedstepdeclarations/src/main/java/OtherStepDefs.java @@ -0,0 +1,10 @@ +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.Then; +import org.jbehave.core.steps.Steps; + +public class OtherStepDefs extends Steps { + + @Then("result list size is $size") + public void checkResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/codeinspector/unusedstepdeclarations/src/main/java/StepDefs.java b/src/test/testData/codeinspector/unusedstepdeclarations/src/main/java/StepDefs.java new file mode 100644 index 00000000..0c767412 --- /dev/null +++ b/src/test/testData/codeinspector/unusedstepdeclarations/src/main/java/StepDefs.java @@ -0,0 +1,19 @@ +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.When; +import org.jbehave.core.steps.Steps; + +public class StepDefs extends Steps { + + @Given("Open url '$url'") + public void openAUrl(@Named("url") String url) { + } + + @When("search for $string") + public void searchForText(@Named("string") String string) { + } + + @Given(value = "result list size is $size", priority = 2) + public void setResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/codeinspector/unusedstepdeclarations/src/test/resources/unused_step_declarations.story b/src/test/testData/codeinspector/unusedstepdeclarations/src/test/resources/unused_step_declarations.story new file mode 100644 index 00000000..e38d05aa --- /dev/null +++ b/src/test/testData/codeinspector/unusedstepdeclarations/src/test/resources/unused_step_declarations.story @@ -0,0 +1,10 @@ +Narrative: +Testing a search result size + +Meta: +@Suite smoke testing +Scenario: open a url + +Given Open url 'http://some.url/path' +!-- When search for 'something' +Then result list size is 10 diff --git a/src/test/testData/reference/steppsi/src/main/java/StepDefs.java b/src/test/testData/reference/steppsi/src/main/java/StepDefs.java new file mode 100644 index 00000000..783714c5 --- /dev/null +++ b/src/test/testData/reference/steppsi/src/main/java/StepDefs.java @@ -0,0 +1,20 @@ +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.Then; +import org.jbehave.core.annotations.When; +import org.jbehave.core.steps.Steps; + +public class StepDefs extends Steps { + + @Given("Open url '$url'") + public void openAUrl(@Named("url") String url) { + } + + @When("search for $string") + public void searchForText(@Named("string") String string) { + } + + @Then(value = "result list size is $size", priority = 2) + public void setResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/reference/steppsi/src/test/resources/step_reference.story b/src/test/testData/reference/steppsi/src/test/resources/step_reference.story new file mode 100644 index 00000000..87400f44 --- /dev/null +++ b/src/test/testData/reference/steppsi/src/test/resources/step_reference.story @@ -0,0 +1,12 @@ +Narrative: +Testing a search result size + +Meta: +@Suite smoke testing +Scenario: open a url + +Given Open url 'http://some.url/path' +When search for 'something' +Then result list size is 30 +When search for 'something else' +Then result list size is 25 \ No newline at end of file diff --git a/src/test/testData/reference/storystep/src/main/java/StepDefs.java b/src/test/testData/reference/storystep/src/main/java/StepDefs.java new file mode 100644 index 00000000..783714c5 --- /dev/null +++ b/src/test/testData/reference/storystep/src/main/java/StepDefs.java @@ -0,0 +1,20 @@ +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.Then; +import org.jbehave.core.annotations.When; +import org.jbehave.core.steps.Steps; + +public class StepDefs extends Steps { + + @Given("Open url '$url'") + public void openAUrl(@Named("url") String url) { + } + + @When("search for $string") + public void searchForText(@Named("string") String string) { + } + + @Then(value = "result list size is $size", priority = 2) + public void setResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/reference/storystep/src/test/resources/non_step_reference.story b/src/test/testData/reference/storystep/src/test/resources/non_step_reference.story new file mode 100644 index 00000000..aa847dc0 --- /dev/null +++ b/src/test/testData/reference/storystep/src/test/resources/non_step_reference.story @@ -0,0 +1,12 @@ +Narrative: +Testing a search result size + +Meta: +@Suite smoke testing +Scenario: open a url + +Given Open url 'http://some.url/path' +When search for 'something' +Then result list size is 30 +When search for 'something else' +Then result list size is 25 \ No newline at end of file diff --git a/src/test/testData/reference/storystep/src/test/resources/step_reference.story b/src/test/testData/reference/storystep/src/test/resources/step_reference.story new file mode 100644 index 00000000..87400f44 --- /dev/null +++ b/src/test/testData/reference/storystep/src/test/resources/step_reference.story @@ -0,0 +1,12 @@ +Narrative: +Testing a search result size + +Meta: +@Suite smoke testing +Scenario: open a url + +Given Open url 'http://some.url/path' +When search for 'something' +Then result list size is 30 +When search for 'something else' +Then result list size is 25 \ No newline at end of file diff --git a/src/test/testData/resolver/stepdefiterator/src/main/java/OtherStepDefs.java b/src/test/testData/resolver/stepdefiterator/src/main/java/OtherStepDefs.java new file mode 100644 index 00000000..ebaa1d47 --- /dev/null +++ b/src/test/testData/resolver/stepdefiterator/src/main/java/OtherStepDefs.java @@ -0,0 +1,10 @@ +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.Then; +import org.jbehave.core.steps.Steps; + +public class OtherStepDefs extends Steps { + + @Then("result list size is $size") + public void checkResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/resolver/stepdefiterator/src/main/java/StepDefs.java b/src/test/testData/resolver/stepdefiterator/src/main/java/StepDefs.java new file mode 100644 index 00000000..b3707a08 --- /dev/null +++ b/src/test/testData/resolver/stepdefiterator/src/main/java/StepDefs.java @@ -0,0 +1,19 @@ +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.When; +import org.jbehave.core.steps.Steps; + +public class StepDefs extends Steps { + + @Given("Open url '$url'") + public void openAUrl(@Named("url") String url) { + } + + @When("search for $string") + public void searchForText(@Named("string") String string) { + } + + @Given(value = "result list size is $size", priority = 2) + public void setResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/resolver/stepdefiterator/src/main/kotlin/AnotherStepDefs.kt b/src/test/testData/resolver/stepdefiterator/src/main/kotlin/AnotherStepDefs.kt new file mode 100644 index 00000000..5c7d010e --- /dev/null +++ b/src/test/testData/resolver/stepdefiterator/src/main/kotlin/AnotherStepDefs.kt @@ -0,0 +1,12 @@ +import org.jbehave.core.annotations.Then +import org.jbehave.core.annotations.Named + +class AnotherStepDefs { + @Then("check result size is \$size") + fun checkSize(@Named("size") size: Int) { + } + + @Then("result ends with \$text") + fun checkEnding(@Named("text") text: Int) { + } +} \ No newline at end of file diff --git a/src/test/testData/resolver/stepdefiterator/src/test/resources/iterator.story b/src/test/testData/resolver/stepdefiterator/src/test/resources/iterator.story new file mode 100644 index 00000000..6483ba1f --- /dev/null +++ b/src/test/testData/resolver/stepdefiterator/src/test/resources/iterator.story @@ -0,0 +1,12 @@ +Narrative: +Testing a search result size + +Meta: +@Suite smoke testing +Scenario: open a url + +Given Open url 'http://some.url/path' +When search for 'something' +Then result list size is 10 +Then check result list size is 10 +Then check that result contains "an item" diff --git a/src/test/testData/service/javamethodusagesearch/src/main/java/MoreStepDefs.java b/src/test/testData/service/javamethodusagesearch/src/main/java/MoreStepDefs.java new file mode 100644 index 00000000..dffd1fca --- /dev/null +++ b/src/test/testData/service/javamethodusagesearch/src/main/java/MoreStepDefs.java @@ -0,0 +1,19 @@ +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.When; +import org.jbehave.core.steps.Steps; + +public class MoreStepDefs extends Steps { + + @Given("Open url '$url'") + public void openAUrl(@Named("url") String url) { + } + + @When("") + public void searchForText(@Named("string") String string) { + } + + @Given(value = "result list size is $size", priority = 2) + public void setResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/service/javamethodusagesearch/src/main/java/OtherStepDefs.java b/src/test/testData/service/javamethodusagesearch/src/main/java/OtherStepDefs.java new file mode 100644 index 00000000..98f98ff4 --- /dev/null +++ b/src/test/testData/service/javamethodusagesearch/src/main/java/OtherStepDefs.java @@ -0,0 +1,10 @@ +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.Then; +import org.jbehave.core.steps.Steps; + +public class OtherStepDefs extends Steps { + + @Then("result list size is $size") + public void checkResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/service/javamethodusagesearch/src/main/java/StepDefs.java b/src/test/testData/service/javamethodusagesearch/src/main/java/StepDefs.java new file mode 100644 index 00000000..c019d1c9 --- /dev/null +++ b/src/test/testData/service/javamethodusagesearch/src/main/java/StepDefs.java @@ -0,0 +1,18 @@ +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.When; +import org.jbehave.core.steps.Steps; + +public class StepDefs extends Steps { + + @Given("Open url '$url'") + public void openAUrl(@Named("url") String url) { + } + + public void searchForText(@Named("string") String string) { + } + + @Given(value = "result list size is $size", priority = 2) + public void setResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/service/javamethodusagesearch/src/test/resources/reference.story b/src/test/testData/service/javamethodusagesearch/src/test/resources/reference.story new file mode 100644 index 00000000..728270d2 --- /dev/null +++ b/src/test/testData/service/javamethodusagesearch/src/test/resources/reference.story @@ -0,0 +1,12 @@ +Narrative: +Testing a search result size + +Meta: +@Suite smoke testing +Scenario: open a url + +Given Open url 'http://some.url/path' +When search for 'something' +Then the result list size is 30 +When search for 'something else' +Then the result list size is 25 \ No newline at end of file diff --git a/src/test/testData/service/jbehaveutil/src/main/java/OtherStepDefs.java b/src/test/testData/service/jbehaveutil/src/main/java/OtherStepDefs.java new file mode 100644 index 00000000..e6b51aa7 --- /dev/null +++ b/src/test/testData/service/jbehaveutil/src/main/java/OtherStepDefs.java @@ -0,0 +1,10 @@ +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.Then; +import org.jbehave.core.steps.Steps; + +public class OtherStepDefs extends Steps { + + @Then("result list size is $size") + public void checkResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/service/jbehaveutil/src/main/java/StepDefs.java b/src/test/testData/service/jbehaveutil/src/main/java/StepDefs.java new file mode 100644 index 00000000..ae9d0573 --- /dev/null +++ b/src/test/testData/service/jbehaveutil/src/main/java/StepDefs.java @@ -0,0 +1,19 @@ +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.When; +import org.jbehave.core.steps.Steps; + +public class StepDefs extends Steps { + + @Given("Open url '$url'") + public void openAUrl(@Named("url") String url) { + } + + @When("search for $string") + public void searchForText(@Named("string") String string) { + } + + @Given(value = "result list size is $size", priority = 2) + public void setResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/service/jbehaveutil/src/test/resources/reference.story b/src/test/testData/service/jbehaveutil/src/test/resources/reference.story new file mode 100644 index 00000000..d54b467f --- /dev/null +++ b/src/test/testData/service/jbehaveutil/src/test/resources/reference.story @@ -0,0 +1,9 @@ +Narrative: +Testing a search result size + +Meta: +@Suite smoke testing +Scenario: open a url + +Given Open url 'http://some.url/path' +When search for 'something' diff --git a/src/test/testData/service/stepdefsearch/src/main/java/MoreStepDefs.java b/src/test/testData/service/stepdefsearch/src/main/java/MoreStepDefs.java new file mode 100644 index 00000000..855f52d1 --- /dev/null +++ b/src/test/testData/service/stepdefsearch/src/main/java/MoreStepDefs.java @@ -0,0 +1,19 @@ +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.When; +import org.jbehave.core.steps.Steps; + +public class MoreStepDefs extends Steps { + + @Given("Open url '$url'") + public void openAUrl(@Named("url") String url) { + } + + @When("search for $string") + public void searchForText(@Named("string") String string) { + } + + @Given(value = "result list size is $size", priority = 2) + public void setResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/service/stepdefsearch/src/main/java/OtherStepDefs.java b/src/test/testData/service/stepdefsearch/src/main/java/OtherStepDefs.java new file mode 100644 index 00000000..98f98ff4 --- /dev/null +++ b/src/test/testData/service/stepdefsearch/src/main/java/OtherStepDefs.java @@ -0,0 +1,10 @@ +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.Then; +import org.jbehave.core.steps.Steps; + +public class OtherStepDefs extends Steps { + + @Then("result list size is $size") + public void checkResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/service/stepdefsearch/src/main/java/StepDefs.java b/src/test/testData/service/stepdefsearch/src/main/java/StepDefs.java new file mode 100644 index 00000000..c019d1c9 --- /dev/null +++ b/src/test/testData/service/stepdefsearch/src/main/java/StepDefs.java @@ -0,0 +1,18 @@ +import org.jbehave.core.annotations.Given; +import org.jbehave.core.annotations.Named; +import org.jbehave.core.annotations.When; +import org.jbehave.core.steps.Steps; + +public class StepDefs extends Steps { + + @Given("Open url '$url'") + public void openAUrl(@Named("url") String url) { + } + + public void searchForText(@Named("string") String string) { + } + + @Given(value = "result list size is $size", priority = 2) + public void setResultListSize(@Named("size") int size) { + } +} diff --git a/src/test/testData/service/stepdefsearch/src/test/resources/reference.story b/src/test/testData/service/stepdefsearch/src/test/resources/reference.story new file mode 100644 index 00000000..d54b467f --- /dev/null +++ b/src/test/testData/service/stepdefsearch/src/test/resources/reference.story @@ -0,0 +1,9 @@ +Narrative: +Testing a search result size + +Meta: +@Suite smoke testing +Scenario: open a url + +Given Open url 'http://some.url/path' +When search for 'something'