diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebUtil.java index fcfc379ee348f..867870088b519 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/geb/GebUtil.java @@ -2,21 +2,29 @@ package org.jetbrains.plugins.groovy.geb; import com.intellij.psi.*; +import com.intellij.psi.impl.source.PsiImmediateClassType; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.util.CachedValueProvider.Result; import com.intellij.psi.util.CachedValuesManager; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierFlags; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrLabeledStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.ClassUtil; +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrReferenceExpressionImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightField; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; +import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; import java.util.Collections; import java.util.HashMap; @@ -37,12 +45,81 @@ public static boolean contributeMembersInsideTest(PsiScopeProcessor processor, if (pageClass != null) { if (!pageClass.processDeclarations(processor, state, null, place)) return false; + contributePageContent(processor, state, place); } } return true; } + private static void contributePageContent(PsiScopeProcessor processor, ResolveState state, PsiElement place) { + if(place instanceof GrReferenceExpressionImpl expr + && !(expr.getParent() instanceof GrMethodCall)) { + + PsiClassType gebPageType = PsiType.getTypeByName("geb.Page", place.getProject(), place.getResolveScope()); + PsiClass ourPage = findPageChange(expr, gebPageType); + if (ourPage != null) { + Map supers = ClassUtil.getSuperClassesWithCache(ourPage); + String nameHint = ResolveUtil.getNameHint(processor); + + for (PsiClass psiClass : supers.values()) { + Map contentElements = getContentElements(psiClass); + + if (nameHint == null) { + for (Map.Entry entry : contentElements.entrySet()) { + processor.execute(entry.getValue(), state); + } + return; + } + else { + PsiVariable defElement = (PsiVariable)contentElements.get(nameHint); + if (defElement != null) { + processor.execute(defElement, state); + } + } + } + } + } + } + + + private static @Nullable PsiClass findPageChange(PsiElement place, PsiClassType pageType) { + PsiElement currentElement = place; + PsiClass ourPage = null; + while (currentElement != null) { + PsiElement examineThis = currentElement; + if (currentElement instanceof GrLabeledStatement labeled) { + examineThis = labeled.getStatement(); + } + if(examineThis instanceof GrVariableDeclaration) { + PsiElement findCall = examineThis.getLastChild(); + while(findCall != null) { + if(findCall instanceof GrExpression) { + examineThis = findCall; + break; + } + findCall = findCall.getLastChild(); + } + } + if (examineThis instanceof GrExpression call && !(examineThis instanceof GrReferenceExpression)) { + PsiType ret = call.getType(); + if (ret != null && pageType.isAssignableFrom(ret) && ret instanceof PsiImmediateClassType ct) { + ourPage = ct.resolve(); + break; + } + } + currentElement = currentElement.getPrevSibling(); + } + + if (ourPage == null) { + PsiElement parent = place.getParent(); + if (parent != null && !(parent instanceof GrMethod)) + return findPageChange(parent, pageType); + } + + return ourPage; + } + public static Map getContentElements(@NotNull PsiClass pageOrModuleClass) { return CachedValuesManager.getCachedValue(pageOrModuleClass, () -> Result.create( calculateContentElements(pageOrModuleClass), pageOrModuleClass diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/geb/GebTestsTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/geb/GebTestsTest.groovy index 4682a506c28b1..127d87fafb5ab 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/geb/GebTestsTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/geb/GebTestsTest.groovy @@ -15,7 +15,7 @@ import static org.jetbrains.plugins.groovy.GroovyProjectDescriptors.LIB_GROOVY_1 class GebTestsTest extends LightJavaCodeInsightFixtureTestCase { private static final TestLibrary LIB_GEB = new RepositoryTestLibrary( - 'org.codehaus.geb:geb-core:0.7.2', + 'org.gebish:geb-core:7.0', 'org.codehaus.geb:geb-junit4:0.7.2', 'org.codehaus.geb:geb-spock:0.7.2', 'org.codehaus.geb:geb-testng:0.7.2' @@ -283,4 +283,158 @@ class A extends geb.Page { } """) } + + void testPageContentIsInScope() { + myFixture.enableInspections(GroovyAssignabilityCheckInspection) + + myFixture.configureByText("SomeSpec.groovy", """ +class SomeSpec extends geb.spock.GebSpec { + void "test"() { + expect: + to PageOne + geb.navigator.Navigator aNavigator = header + String aString = headerText + Integer notAnInteger = header + + then: + header + headerText + } +} + +class PageOne extends geb.Page { + static content = { + header { \$("h1") } + headerText { header.text() } + } +} + +""") + + myFixture.checkHighlighting(true, false, true) + TestUtils.checkResolve(myFixture.file) + } + + void testPageContentIsCompletable() { + myFixture.enableInspections(GroovyAssignabilityCheckInspection) + + myFixture.configureByText("SomeSpec.groovy", """ +class SomeSpec extends geb.spock.GebSpec { + void "test"() { + when: + to PageOne + + then: + + } +} + +class PageOne extends geb.Page { + static content = { + header { \$("h1") } + headerText { header.text() } + } +} + +""") + + TestUtils.checkCompletionType(myFixture, "header", "geb.navigator.Navigator") + TestUtils.checkCompletionType(myFixture, "headerText", "java.lang.String") + } + + void testOnlyCurrentPageContentIsInScope() { + myFixture.enableInspections(GroovyAssignabilityCheckInspection) + + myFixture.configureByText("SomeSpec.groovy", """ +class SomeSpec extends geb.spock.GebSpec { + void "test"() { + when: + to PageOne + + then: + header + + when: + to PageTwo + + then: + pageTwoContent + headerText + } +} + +class PageOne extends geb.Page { + static content = { + header { \$("h1") } + headerText { header.text() } + } +} + +class PageTwo extends geb.Page { + static content = { + pageTwoContent { \$("h1") } + } +} + +""") + + myFixture.checkHighlighting(true, false, true) + TestUtils.checkResolve(myFixture.file, "headerText") + } + + void testChangingCurrentPage() { + myFixture.enableInspections(GroovyAssignabilityCheckInspection) + + myFixture.configureByText("SomeSpec.groovy", """ +class SomeSpec extends geb.spock.GebSpec { + void "to"() { + when: + to PageOne + + then: + header + } + + void "via"() { + when: + via PageOne + + then: + header + } + + void "at"() { + expect: + at PageOne + header + } + + void "explicit"() { + when: + page(PageOne) + + then: + header + } + + void "assignment"() { + when: + geb.Page newPage = to PageOne + + then: + header + } +} + +class PageOne extends geb.Page { + static content = { + header { \$("h1") } + headerText { header.text() } + } +} +""") + + myFixture.checkHighlighting(true, false, true) + TestUtils.checkResolve(myFixture.file) + } }