Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IDEA-113632 - Fix Geb Page content not being available #2862

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<String, PsiClass> supers = ClassUtil.getSuperClassesWithCache(ourPage);
String nameHint = ResolveUtil.getNameHint(processor);

for (PsiClass psiClass : supers.values()) {
Map<String, PsiMember> contentElements = getContentElements(psiClass);

if (nameHint == null) {
for (Map.Entry<String, PsiMember> 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<String, PsiMember> getContentElements(@NotNull PsiClass pageOrModuleClass) {
return CachedValuesManager.getCachedValue(pageOrModuleClass, () -> Result.create(
calculateContentElements(pageOrModuleClass), pageOrModuleClass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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 <warning descr="Cannot assign 'Navigator' to 'Integer'">notAnInteger</warning> = 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:
<caret>
}
}

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)
}
}