package com.google.jstestdriver.idea.assertFramework.jstd;
import com.google.jstestdriver.idea.assertFramework.AbstractTestFileStructureBuilder;
import com.google.jstestdriver.idea.util.CastUtils;
import com.google.jstestdriver.idea.util.JsPsiUtils;
import com.google.jstestdriver.idea.util.ObjectUtils;
import com.intellij.lang.javascript.JSTokenTypes;
import com.intellij.lang.javascript.psi.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.util.Processor;
import com.intellij.util.Query;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class JstdTestFileStructureBuilder extends AbstractTestFileStructureBuilder {
private static final JstdTestFileStructureBuilder INSTANCE = new JstdTestFileStructureBuilder();
private JstdTestFileStructureBuilder() {}
@NotNull
@Override
public JstdTestFileStructure buildTestFileStructure(@NotNull JSFile jsFile) {
JstdTestFileStructure jsTestFileStructure = new JstdTestFileStructure(jsFile);
List<JSElement> jsElements = JsPsiUtils.listJsElementsInExecutionOrder(jsFile);
for (JSElement jsElement : jsElements) {
fillJsTestFileStructure(jsTestFileStructure, jsElement);
}
return jsTestFileStructure;
}
private static void fillJsTestFileStructure(JstdTestFileStructure jsTestFileStructure, JSElement jsElement) {
{
JSExpressionStatement jsExpressionStatement = CastUtils.tryCast(jsElement, JSExpressionStatement.class);
if (jsExpressionStatement != null) {
{
JSCallExpression callExpression = CastUtils.tryCast(jsExpressionStatement.getExpression(), JSCallExpression.class);
if (callExpression != null) {
// TestCase("testCaseName", { test1: function() {} });
createTestCaseStructure(jsTestFileStructure, callExpression);
}
}
{
// testCase = TestCase("testCaseName");
JSAssignmentExpression jsAssignmentExpression = CastUtils.tryCast(jsExpressionStatement.getExpression(), JSAssignmentExpression.class);
if (jsAssignmentExpression != null) {
JSCallExpression jsCallExpression = CastUtils.tryCast(jsAssignmentExpression.getROperand(), JSCallExpression.class);
if (jsCallExpression != null) {
JstdTestCaseStructure testCaseStructure = createTestCaseStructure(jsTestFileStructure, jsCallExpression);
if (testCaseStructure != null) {
JSDefinitionExpression jsDefinitionExpression = CastUtils.tryCast(jsAssignmentExpression.getLOperand(), JSDefinitionExpression.class);
if (jsDefinitionExpression != null) {
JSReferenceExpression jsReferenceExpression = CastUtils.tryCast(jsDefinitionExpression.getExpression(), JSReferenceExpression.class);
if (jsReferenceExpression != null) {
PsiElement psiElement = JsPsiUtils.resolveUniquely(jsReferenceExpression);
JSVariable testCastJsVariable = CastUtils.tryCast(psiElement, JSVariable.class);
if (testCastJsVariable != null) {
addPrototypeTests(testCaseStructure, testCastJsVariable);
}
}
}
}
}
}
}
}
}
{
// var testCase = TestCase("testCaseName");
JSVarStatement jsVarStatement = CastUtils.tryCast(jsElement, JSVarStatement.class);
if (jsVarStatement != null) {
JSVariable[] jsVariables = ObjectUtils.notNull(jsVarStatement.getVariables(), JSVariable.EMPTY_ARRAY);
for (JSVariable jsVariable : jsVariables) {
JSCallExpression jsCallExpression = CastUtils.tryCast(jsVariable.getInitializer(), JSCallExpression.class);
if (jsCallExpression != null) {
JstdTestCaseStructure testCaseStructure = createTestCaseStructure(jsTestFileStructure, jsCallExpression);
if (testCaseStructure != null) {
addPrototypeTests(testCaseStructure, jsVariable);
}
}
}
}
}
}
private static void addPrototypeTests(@NotNull final JstdTestCaseStructure testCaseStructure, @NotNull final JSVariable jsVariable) {
Query<PsiReference> referenceQuery = ReferencesSearch.search(jsVariable);
referenceQuery.forEach(new Processor<PsiReference>() {
@Override
public boolean process(PsiReference psiReference) {
JSReferenceExpression testCaseJsReferenceExpression = CastUtils.tryCast(psiReference, JSReferenceExpression.class);
if (testCaseJsReferenceExpression != null) {
JSReferenceExpression prototypeJsReferenceExpression =
CastUtils.tryCast(testCaseJsReferenceExpression.getParent(), JSReferenceExpression.class);
if (prototypeJsReferenceExpression != null && "prototype".equals(prototypeJsReferenceExpression.getReferencedName())) {
JSReferenceExpression testOnPrototypeJsReferenceExpression =
CastUtils.tryCast(prototypeJsReferenceExpression.getParent(), JSReferenceExpression.class);
if (testOnPrototypeJsReferenceExpression != null) {
addPrototypeTest(testCaseStructure, testOnPrototypeJsReferenceExpression);
}
}
}
return true;
}
});
}
private static void addPrototypeTest(JstdTestCaseStructure testCaseStructure, JSReferenceExpression testOnPrototypeJsReferenceExpression) {
JSDefinitionExpression testJsDefinitionExpression =
CastUtils.tryCast(testOnPrototypeJsReferenceExpression.getParent(), JSDefinitionExpression.class);
if (testJsDefinitionExpression != null) {
JSAssignmentExpression testJsAssignmentExpression =
CastUtils.tryCast(testJsDefinitionExpression.getParent(), JSAssignmentExpression.class);
if (testJsAssignmentExpression != null) {
LeafPsiElement testMethodIdentifierPsiElement =
CastUtils.tryCast(testOnPrototypeJsReferenceExpression.getLastChild(), LeafPsiElement.class);
if (testMethodIdentifierPsiElement != null &&
testMethodIdentifierPsiElement.getElementType() == JSTokenTypes.IDENTIFIER) {
JSFunctionExpression jsFunctionExpression = JsPsiUtils.extractFunctionExpression(
testJsAssignmentExpression.getROperand()
);
JstdTestStructure jstdTestStructure = JstdTestStructure.newPrototypeBasedTestStructure(testMethodIdentifierPsiElement,
jsFunctionExpression);
testCaseStructure.addTestStructure(jstdTestStructure);
}
}
}
}
private static JstdTestCaseStructure createTestCaseStructure(@NotNull JstdTestFileStructure jsTestFileStructure,
@NotNull JSCallExpression testCaseCallExpression) {
JSReferenceExpression referenceExpression = CastUtils.tryCast(testCaseCallExpression.getMethodExpression(), JSReferenceExpression.class);
if (referenceExpression != null) {
String referenceName = referenceExpression.getReferencedName();
if ("TestCase".equals(referenceName) || "AsyncTestCase".equals(referenceName)) {
JSExpression[] arguments = JsPsiUtils.getArguments(testCaseCallExpression);
if (arguments.length >= 1) {
String testCaseName = JsPsiUtils.extractStringValue(arguments[0]);
if (testCaseName != null) {
JSObjectLiteralExpression testsObjectLiteral = null;
if (arguments.length >= 2) {
testsObjectLiteral = JsPsiUtils.extractObjectLiteralExpression(arguments[1]);
}
JstdTestCaseStructure testCaseStructure = new JstdTestCaseStructure(jsTestFileStructure, testCaseName, testCaseCallExpression, testsObjectLiteral);
jsTestFileStructure.addTestCaseStructure(testCaseStructure);
if (testsObjectLiteral != null) {
fillTestCaseStructureByObjectLiteral(testCaseStructure, testsObjectLiteral);
}
return testCaseStructure;
}
}
}
}
return null;
}
private static void fillTestCaseStructureByObjectLiteral(
@NotNull JstdTestCaseStructure testCaseStructure,
@NotNull JSObjectLiteralExpression testsObjectLiteral
) {
JSProperty[] properties = JsPsiUtils.getProperties(testsObjectLiteral);
for (JSProperty property : properties) {
JstdTestStructure testStructure = JstdTestStructure.newPropertyBasedTestStructure(property);
if (testStructure != null) {
testCaseStructure.addTestStructure(testStructure);
}
}
}
public static JstdTestFileStructureBuilder getInstance() {
return INSTANCE;
}
}