package fr.adrienbrault.idea.symfony2plugin.tests.templating.util;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.PsiRecursiveElementVisitor;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.php.lang.psi.elements.Function;
import com.jetbrains.twig.TwigFile;
import com.jetbrains.twig.TwigFileType;
import com.jetbrains.twig.TwigLanguage;
import com.jetbrains.twig.TwigTokenTypes;
import com.jetbrains.twig.elements.TwigElementFactory;
import com.jetbrains.twig.elements.TwigElementTypes;
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigMacro;
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigSet;
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
public class TwigUtilTest extends SymfonyLightCodeInsightFixtureTestCase {
public void setUp() throws Exception {
super.setUp();
createDummyFiles(
"app/Resources/TwigUtilIntegrationBundle/views/layout.html.twig",
"app/Resources/TwigUtilIntegrationBundle/views/Foo/layout.html.twig",
"app/Resources/TwigUtilIntegrationBundle/views/Foo/Bar/layout.html.twig"
);
}
public String getTestDataPath() {
return new File(this.getClass().getResource("fixtures").getFile()).getAbsolutePath();
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getTemplateNameByOverwrite
*/
public void testTemplateOverwriteNameGeneration() {
if(System.getenv("PHPSTORM_ENV") != null) return;
assertEquals(
"TwigUtilIntegrationBundle:layout.html.twig",
TwigUtil.getTemplateNameByOverwrite(getProject(), VfsUtil.findRelativeFile(getProject().getBaseDir(), "app/Resources/TwigUtilIntegrationBundle/views/layout.html.twig".split("/")))
);
assertEquals(
"TwigUtilIntegrationBundle:Foo/layout.html.twig",
TwigUtil.getTemplateNameByOverwrite(getProject(), VfsUtil.findRelativeFile(getProject().getBaseDir(), "app/Resources/TwigUtilIntegrationBundle/views/Foo/layout.html.twig".split("/")))
);
assertEquals(
"TwigUtilIntegrationBundle:Foo/Bar/layout.html.twig",
TwigUtil.getTemplateNameByOverwrite(getProject(), VfsUtil.findRelativeFile(getProject().getBaseDir(), "app/Resources/TwigUtilIntegrationBundle/views/Foo/Bar/layout.html.twig".split("/")))
);
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getTemplateNameByOverwrite
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getTemplateName
*/
public void testTemplateOverwriteNavigation() {
if(System.getenv("PHPSTORM_ENV") != null) return;
assertNavigationContainsFile(TwigFileType.INSTANCE, "{% extends '<caret>TwigUtilIntegrationBundle:layout.html.twig' %}", "/views/layout.html.twig");
assertNavigationContainsFile(TwigFileType.INSTANCE, "{% extends '<caret>TwigUtilIntegrationBundle:Foo/layout.html.twig' %}", "/views/Foo/layout.html.twig");
assertNavigationContainsFile(TwigFileType.INSTANCE, "{% extends '<caret>TwigUtilIntegrationBundle:Foo/Bar/layout.html.twig' %}", "/views/Foo/Bar/layout.html.twig");
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#isValidTemplateString
*/
public void testIsValidTemplateString() {
assertFalse(TwigUtil.isValidTemplateString(createPsiElementAndFindString("{% include \"foo/#{segment.typeKey}.html.twig\" %}", TwigElementTypes.INCLUDE_TAG)));
assertFalse(TwigUtil.isValidTemplateString(createPsiElementAndFindString("{% include \"foo/#{1 + 2}.html.twig\" %}", TwigElementTypes.INCLUDE_TAG)));
assertFalse(TwigUtil.isValidTemplateString(createPsiElementAndFindString("{% include ~ \"foo.html.twig\" ~ %}", TwigElementTypes.INCLUDE_TAG)));
assertFalse(TwigUtil.isValidTemplateString(createPsiElementAndFindString("{% include \"foo.html.twig\" ~ %}", TwigElementTypes.INCLUDE_TAG)));
assertFalse(TwigUtil.isValidTemplateString(createPsiElementAndFindString("{% include ~ \"foo.html.twig\" %}", TwigElementTypes.INCLUDE_TAG)));
assertTrue(TwigUtil.isValidTemplateString(createPsiElementAndFindString("{% include \"foo.html.twig\" %}", TwigElementTypes.INCLUDE_TAG)));
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getDomainTrans
*/
public void testGetDomainTrans() {
String[] blocks = {
"{{ '<caret>'|transchoice(3, {}, 'foo') }}",
"{{ '<caret>'|transchoice(3, [], 'foo') }}",
"{{ '<caret>'|trans({}, 'foo') }}",
"{{ '<caret>' | trans( {} , 'foo' ) }}",
"{{ '<caret>'|trans([], 'foo') }}",
"{{ '<caret>'|trans(, 'foo') }}",
"{{ '<caret>'|trans(null, 'foo') }}",
"{{ '<caret>'|trans({'foo': 'foo', 'foo'}, 'foo') }}",
"{{ '<caret>' | transchoice(count, {'%var%': value}, 'foo') }}",
"{{ '<caret>' | transchoice(c, {'%var%': value}, 'foo') }}",
"{{ '<caret>' | transchoice(, {'%var%': value}, 'foo') }}",
"{{ '<caret>' | transchoice(foo.bar, {'%var%': value}, 'foo') }}",
"{{ '<caret>' | transchoice(foo.bar ~ bar, {'%var%': value}, 'foo') }}",
"{{ '<caret>' | transchoice(foo.bar + bar, {'%var%': value}, 'foo') }}",
"{{ '<caret>' | transchoice(foo.bar - bar, {'%var%': value}, 'foo') }}",
"{{ '<caret>'|trans({'%some%': \"button.reserve\"|trans,}, 'foo') }}",
"{{ 'bar'|trans({'%some%': \"<caret>\"|trans({}, 'foo'),}) }}",
};
for (String s : blocks) {
myFixture.configureByText(TwigFileType.INSTANCE, s);
PsiElement element = myFixture.getFile().findElementAt(myFixture.getCaretOffset());
assertNotNull(element);
assertEquals("foo", TwigUtil.getDomainTrans(element));
}
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getCreateAbleTemplatePaths
*/
public void testGetCreateAbleTemplatePaths() {
myFixture.copyFileToProject("ide-twig.json", "ide-twig.json");
myFixture.copyFileToProject("dummy.html.twig", "res/dummy.html.twig");
myFixture.copyFileToProject("dummy.html.twig", "res/foo/dummy.html.twig");
assertContainsElements(TwigUtil.getCreateAbleTemplatePaths(getProject(), "@foo/bar.html.twig"), "src/res/bar.html.twig");
assertContainsElements(TwigUtil.getCreateAbleTemplatePaths(getProject(), "bar.html.twig"), "src/res/bar.html.twig");
assertContainsElements(TwigUtil.getCreateAbleTemplatePaths(getProject(), "FooBundle:Bar:dummy.html.twig"), "src/res/Bar/dummy.html.twig");
assertContainsElements(TwigUtil.getCreateAbleTemplatePaths(getProject(), "FooBundle:Bar\\Foo:dummy.html.twig"), "src/res/Bar/Foo/dummy.html.twig");
assertContainsElements(TwigUtil.getCreateAbleTemplatePaths(getProject(), "FooBundle:Bar:Foo\\dummy.html.twig"), "src/res/Bar/Foo/dummy.html.twig");
assertContainsElements(TwigUtil.getCreateAbleTemplatePaths(getProject(), "@FooBundle/Bar/dummy.html.twig"), "src/res/Bar/dummy.html.twig");
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getTransDefaultDomainOnScope
*/
public void testGetTwigFileTransDefaultDomainForFileScope() {
PsiFile psiFile = myFixture.configureByText("foo.html.twig", "{% trans_default_domain \"foo\" %}{{ <caret> }}");
PsiElement psiElement = psiFile.findElementAt(myFixture.getCaretOffset());
assertNotNull(psiElement);
assertEquals("foo", TwigUtil.getTransDefaultDomainOnScope(psiElement));
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getTransDefaultDomainOnScope
*/
public void testGetTwigFileTransDefaultDomainForEmbedScope() {
PsiFile psiFile = myFixture.configureByText("foo.html.twig", "" +
"{% trans_default_domain \"foo\" %}\n" +
"{% embed 'default/e.html.twig' %}\n" +
" {% trans_default_domain \"foobar\" %}\n" +
" {{ <caret> }}\n" +
"{% endembed %}\n"
);
PsiElement psiElement = psiFile.findElementAt(myFixture.getCaretOffset());
assertNotNull(psiElement);
assertEquals("foobar", TwigUtil.getTransDefaultDomainOnScope(psiElement));
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getInjectedTwigElement
*/
public void testGetTransDefaultDomainOnInjectedElement() {
PsiFile psiFile = myFixture.configureByText("foo.html.twig", "" +
"{% trans_default_domain \"foo\" %}\n" +
"<a href=\"#\">FOO<caret>BAR</a>"
);
assertTrue(
TwigUtil.getInjectedTwigElement(psiFile, myFixture.getCaretOffset()).getContainingFile().getFileType() == TwigFileType.INSTANCE
);
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getTransDefaultDomainOnScopeOrInjectedElement
*/
public void testGetTransDefaultDomainOnScopeOrInjectedElement() {
PsiFile psiFile = myFixture.configureByText("foo.html.twig", "" +
"{% trans_default_domain \"foo\" %}\n" +
"<a href=\"#\">FOO<caret>BAR</a>"
);
assertEquals("foo", TwigUtil.getTransDefaultDomainOnScopeOrInjectedElement(psiFile, myFixture.getCaretOffset()));
psiFile = myFixture.configureByText("foo.html.twig", "" +
"{% trans_default_domain \"foo\" %}\n" +
"{% embed 'default/e.html.twig' %}\n" +
" {% trans_default_domain \"foobar\" %}\n" +
" <ht<caret>ml>\n" +
"{% endembed %}\n"
);
assertEquals("foobar", TwigUtil.getTransDefaultDomainOnScopeOrInjectedElement(psiFile, myFixture.getCaretOffset()));
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getInjectedTwigElement
*/
public void testGetTransDefaultDomainOnInjectedElementWithInvalidOversizesCaretOffset() {
PsiFile psiFile = myFixture.configureByText("foo.html.twig", "<a href=\"#\">FOO<caret>BAR</a>");
assertTrue(
TwigUtil.getInjectedTwigElement(psiFile, 3).getContainingFile().getFileType() == TwigFileType.INSTANCE
);
assertNull(TwigUtil.getInjectedTwigElement(psiFile, 300000));
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getTwigFileMethodUsageOnIndex
*/
public void testGetTwigFileMethodUsageOnIndex() {
myFixture.copyFileToProject("GetTwigFileMethodUsageOnIndex.php");
Set<Function> methods = TwigUtil.getTwigFileMethodUsageOnIndex(getProject(), Collections.singletonList("car.html.twig"));
assertNotNull(ContainerUtil.find(methods, method -> method.getFQN().equals("\\Template\\Bar\\MyTemplate.fooAction")));
assertNotNull(ContainerUtil.find(methods, method -> method.getFQN().equals("\\foo")));
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getFoldingTemplateName
*/
public void testGetFoldingTemplateName() {
assertEquals("Foo:edit", TwigUtil.getFoldingTemplateName("FooBundle:edit.html.twig"));
assertEquals("Foo:edit", TwigUtil.getFoldingTemplateName("FooBundle:edit.html.php"));
assertEquals("Bundle:", TwigUtil.getFoldingTemplateName("Bundle:.html.twig"));
assertEquals("edit", TwigUtil.getFoldingTemplateName("edit.html.twig"));
assertNull(TwigUtil.getFoldingTemplateName("FooBundle:edit.foo.twig"));
assertNull(TwigUtil.getFoldingTemplateName(""));
assertNull(TwigUtil.getFoldingTemplateName(null));
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#visitTemplateIncludes
*/
public void testVisitTemplateIncludes() {
Collection<String> includes = new ArrayList<>();
PsiFile fileFromText = PsiFileFactory.getInstance(getProject()).createFileFromText(TwigLanguage.INSTANCE,
"{% form_theme form ':Foobar:fields.html.twig' %}" +
"{% form_theme form.foobar \":Foobar:fields_foobar.html.twig\" %}" +
"{% form_theme form.foobar with [\":Foobar:fields_foobar_1.html.twig\"] %}"
);
TwigUtil.visitTemplateIncludes((TwigFile) fileFromText, templateInclude ->
includes.add(templateInclude.getTemplateName())
);
assertContainsElements(includes, ":Foobar:fields.html.twig", ":Foobar:fields_foobar.html.twig", ":Foobar:fields_foobar_1.html.twig");
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getImportedMacros
*/
public void testGetImportedMacros() {
PsiFile psiFile = PsiFileFactory.getInstance(getProject()).createFileFromText(TwigLanguage.INSTANCE,
"{% macro foobar %}{% endmacro %}\n" +
"{% from _self import foobar as input, foobar %}\n" +
"{% from 'foobar.html.twig' import foobar_twig %}\n" +
"{% from \"foobar2.html.twig\" import foobar_twig_2 %}"
);
Collection<TwigMacro> importedMacros = TwigUtil.getImportedMacros(psiFile);
assertTrue(importedMacros.stream().anyMatch(twigMacro ->
"input".equals(twigMacro.getName()) && "foobar".equals(twigMacro.getOriginalName()))
);
assertTrue(importedMacros.stream().anyMatch(twigMacro ->
"foobar".equals(twigMacro.getName()) && "_self".equals(twigMacro.getTemplate()))
);
assertTrue(importedMacros.stream().anyMatch(twigMacro ->
"foobar_twig".equals(twigMacro.getName()) && "foobar.html.twig".equals(twigMacro.getTemplate()))
);
assertTrue(importedMacros.stream().anyMatch(twigMacro ->
"foobar_twig_2".equals(twigMacro.getName()) && "foobar2.html.twig".equals(twigMacro.getTemplate()))
);
}
public void testGetImportedMacrosTargets() {
PsiFile psiFile = PsiFileFactory.getInstance(getProject()).createFileFromText(TwigLanguage.INSTANCE,
"{% macro foobar %}{% endmacro %}\n" +
"{% from _self import foobar as input, foobar %}\n"
);
assertTrue(TwigUtil.getImportedMacros(psiFile, "foobar").stream().anyMatch(psiElement ->
psiElement.getNode().getElementType() == TwigElementTypes.MACRO_TAG
));
assertTrue(TwigUtil.getImportedMacros(psiFile, "input").stream().anyMatch(psiElement ->
psiElement.getNode().getElementType() == TwigElementTypes.MACRO_TAG
));
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getImportedMacrosNamespaces
*/
public void testGetImportedMacrosNamespaces() {
PsiFile psiFile = PsiFileFactory.getInstance(getProject()).createFileFromText(TwigLanguage.INSTANCE,
"{% import _self as macros %}\n" +
"{% macro foobar %}{% endmacro %}\n"
);
assertTrue(
TwigUtil.getImportedMacrosNamespaces(psiFile).stream().anyMatch(twigMacro -> "macros.foobar".equals(twigMacro.getName()))
);
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getSetDeclaration
*/
public void testGetSetDeclaration() {
PsiFile psiFile = PsiFileFactory.getInstance(getProject()).createFileFromText(TwigLanguage.INSTANCE,
"{% set foobar = 'foo' %}\n" +
"{% set footag %}{% endset %}\n"
);
Collection<TwigSet> setDeclaration = TwigUtil.getSetDeclaration(psiFile);
assertTrue(
setDeclaration.stream().anyMatch(twigMacro -> "foobar".equals(twigMacro.getName()))
);
assertTrue(
setDeclaration.stream().anyMatch(twigMacro -> "footag".equals(twigMacro.getName()))
);
}
/**
* @see fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil#getImportedMacrosNamespaces
*/
public void testGetImportedMacrosNamespacesTargets() {
PsiFile psiFile = PsiFileFactory.getInstance(getProject()).createFileFromText(TwigLanguage.INSTANCE,
"{% macro my_foobar %}{% endmacro %}\n" +
"{% import _self as foobar %}\n"
);
assertTrue(TwigUtil.getImportedMacrosNamespaces(psiFile, "foobar.my_foobar").stream().anyMatch(
psiElement -> psiElement.getNode().getElementType() == TwigElementTypes.MACRO_TAG
));
}
private PsiElement createPsiElementAndFindString(@NotNull String content, @NotNull IElementType type) {
PsiElement psiElement = TwigElementFactory.createPsiElement(getProject(), content, type);
if(psiElement == null) {
fail();
}
final PsiElement[] string = {null};
psiElement.acceptChildren(new PsiRecursiveElementVisitor() {
@Override
public void visitElement(PsiElement element) {
if (string[0] == null && element.getNode().getElementType() == TwigTokenTypes.STRING_TEXT) {
string[0] = element;
}
super.visitElement(element);
}
});
return string[0];
}
}