1 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package org.angular2.lang.html.parser;
4 import com.intellij.html.HtmlParsingTest;
5 import com.intellij.javascript.JSHtmlEmbeddedContentSupport;
6 import com.intellij.lang.LanguageASTFactory;
7 import com.intellij.lang.css.CSSLanguage;
8 import com.intellij.lang.css.CSSParserDefinition;
9 import com.intellij.lang.javascript.JavascriptParserDefinition;
10 import com.intellij.lang.javascript.dialects.ECMA6ParserDefinition;
11 import com.intellij.lang.javascript.dialects.JSLanguageLevel;
12 import com.intellij.lexer.EmbeddedTokenTypesProvider;
13 import com.intellij.lexer.HtmlEmbeddedContentSupport;
14 import com.intellij.openapi.progress.EmptyProgressIndicator;
15 import com.intellij.openapi.util.TextRange;
16 import com.intellij.pom.tree.events.TreeChangeEvent;
17 import com.intellij.psi.PsiFile;
18 import com.intellij.psi.css.CssElementDescriptorProvider;
19 import com.intellij.psi.css.CssEmbeddedTokenTypesProvider;
20 import com.intellij.psi.css.CssHtmlEmbeddedContentSupport;
21 import com.intellij.psi.css.impl.CssTreeElementFactory;
22 import com.intellij.psi.css.impl.util.scheme.CssElementDescriptorFactory2;
23 import com.intellij.psi.css.impl.util.scheme.CssElementDescriptorProviderImpl;
24 import com.intellij.psi.impl.BlockSupportImpl;
25 import com.intellij.psi.impl.DebugUtil;
26 import com.intellij.psi.impl.DiffLog;
27 import org.angular2.lang.expr.parser.Angular2ParserDefinition;
28 import org.angularjs.AngularTestUtil;
29 import org.jetbrains.annotations.NotNull;
31 import java.io.IOException;
32 import java.util.Collections;
34 public class Angular2HtmlParsingTest extends HtmlParsingTest {
36 public Angular2HtmlParsingTest() {
38 new Angular2HtmlParserDefinition(),
39 new Angular2ParserDefinition(),
40 new JavascriptParserDefinition(),
41 new CSSParserDefinition());
45 protected void setUp() throws Exception {
47 registerExtensions(EmbeddedTokenTypesProvider.EXTENSION_POINT_NAME, EmbeddedTokenTypesProvider.class,
48 Collections.singletonList(new CssEmbeddedTokenTypesProvider()));
49 registerExtension(HtmlEmbeddedContentSupport.EP_NAME, new CssHtmlEmbeddedContentSupport());
50 registerExtension(HtmlEmbeddedContentSupport.EP_NAME, new JSHtmlEmbeddedContentSupport());
52 addExplicitExtension(LanguageASTFactory.INSTANCE, CSSLanguage.INSTANCE, new CssTreeElementFactory());
53 registerExtensionPoint(CssElementDescriptorProvider.EP_NAME, CssElementDescriptorProvider.class);
54 registerExtension(CssElementDescriptorProvider.EP_NAME, new CssElementDescriptorProviderImpl());
55 getApplication().registerService(CssElementDescriptorFactory2.class,
56 new CssElementDescriptorFactory2("css-parsing-tests.xml"));
58 // Update parser definition if version is changed
59 assert JSLanguageLevel.DEFAULT == JSLanguageLevel.ES6;
60 registerParserDefinition(new ECMA6ParserDefinition());
64 protected void registerEmbeddedContentProviders() {
65 super.registerEmbeddedContentProviders();
69 protected void checkResult(@NotNull String targetDataName, @NotNull PsiFile file) throws IOException {
70 super.checkResult(targetDataName, file);
71 ensureReparsingConsistent(file);
74 private static void ensureReparsingConsistent(@NotNull PsiFile file) {
75 DebugUtil.performPsiModification("ensureReparsingConsistent", () -> {
76 final String fileText = file.getText();
77 final DiffLog diffLog = new BlockSupportImpl().reparseRange(
78 file, file.getNode(), TextRange.allOf(fileText), fileText, new EmptyProgressIndicator(), fileText);
79 TreeChangeEvent event = diffLog.performActualPsiChange(file);
80 assertEmpty(event.getChangedElements());
85 protected String getTestDataPath() {
86 return AngularTestUtil.getBaseTestDataPath(Angular2HtmlParsingTest.class);
89 public void testNgParseElementsInsideNgTemplate() throws Exception {
90 doTestHtml("<ng-template><span></span></ng-template>");
93 public void testNgSupportVoidElements() throws Exception {
94 doTestHtml("<link rel=\"author license\" href=\"/about\">");
97 public void _testNgNotErrorOnVoidHtml5Elements() throws Exception {
98 doTestHtml("<map><area></map><div><br></div><colgroup><col></colgroup>" +
99 "<div><embed></div><div><hr></div><div><img></div><div><input></div>" +
100 "<object><param>/<object><audio><source></audio><audio><track></audio>" +
104 public void _testNgReportClosingTagForVoidElement() throws Exception {
105 doTestHtml("<input></input>");
108 public void _testNgReportSelfClosingHtmlElement() throws Exception {
112 public void testNgCloseVoidElementsOnTextNodes() throws Exception {
113 doTestHtml("<p>before<br>after</p>");
116 public void testNgSupportOptionalEndTags() throws Exception {
117 doTestHtml("<div><p>1<p>2</div>");
120 public void testNgSupportNestedElements() throws Exception {
121 doTestHtml("<ul><li><ul><li></li></ul></li></ul>");
124 public void testNgSupportSelfClosingVoidElements() throws Exception {
125 doTestHtml("<input />");
128 public void testNgParseExpansionForms1() throws Exception {
129 doTestHtml("<div>before{messages.length, plural, =0 {You have <b>no</b> messages} =1 {One {{message}}}}after</div>");
132 public void testNgParseExpansionForms2() throws Exception {
133 doTestHtml("<div><span>{a, plural, =0 {b}}</span></div>");
136 public void testNgParseExpansionForms3() throws Exception {
137 doTestHtml("{messages.length, plural, =0 { {p.gender, select, male {m}} }}");
140 public void testNgErrorOnUnterminatedExpansionForm() throws Exception {
141 doTestHtml("{messages.length, plural, =0 {one}");
144 public void testNgICUWithNumbers() throws Exception {
145 doTestHtml("{sex, select, male {m} female {f} 0 {other}}");
148 public void testNgErrorOnUnterminatedExpansionCase() throws Exception {
149 doTestHtml("{messages.length, plural, =0 {one");
152 public void testNgErrorOnInvalidHTMLInExpansionCase() throws Exception {
153 doTestHtml("{messages.length, plural, =0 {<div>}}");
156 public void testNgWhitespacesInExpansionCase() throws Exception {
157 doTestHtml("{ messages . length, plural , =0 { <div> } }");
160 public void testExpansionFormComplex() throws Exception {
161 doTestHtml("<div>Text{ form, open, =23 {{{{foo: 12} }} is {inner, open, =34{{{\"test\"}} cool } =12{<tag test='12'></tag>}}}}}} {}");
164 public void testNgReportUnexpectedClosingTag() throws Exception {
165 doTestHtml("<div></p></div>");
168 public void testNgReportSubsequentOpenTagWithoutCloseTag() throws Exception {
169 doTestHtml("<div</div>");
172 public void testNgParseBoundProperties() throws Exception {
173 doTestHtml("<div [someProp]='v'></div>" +
174 "<div [some-prop]='v'></div>" +
175 "<div [dot.name]='v'></div>" +
176 "<div [attr.someAttr]='v'></div>" +
177 "<div [class.some-class]='v'></div>" +
178 "<div [style.someStyle]='v'></div>" +
179 "<div data-[style.someStyle]='v'></div>" +
180 "<div bind-prop='v'></div>" +
181 "<div prop='{{v}}'></div>" +
182 "<div bind-animate-someAnimation='val'></div>" +
183 "<div [@someAnimation]='v'></div>" +
184 "<div @someAnimation='v'></div>");
187 public void testNgParseEvents() throws Exception {
188 doTestHtml("<div (window:event)='v'></div>" +
189 "<div (event)='v'></div>" +
190 "<div data-(event)='v'></div>" +
191 "<div (some-event)='v'></div>" +
192 "<div (someEvent)='v'></div>" +
193 "<div on-event='v'></div>");
196 public void testNgParseAnimationEvents() throws Exception {
197 doTestHtml("<a (@click)='doStuff()'></a>" +
198 "<b on-animate-click='doStuff()'></b>" +
199 "<a (@click.done)='doStuff()'></a>" +
200 "<b on-animate-click.start='doStuff()'></b>");
203 public void testNgParseReferences() throws Exception {
204 doTestHtml("<div #a></div>" +
205 "<div ref-a></div>" +
206 "<div a #a='dirA'></div>" +
209 "<div ref- ></div>");
212 public void testNgParseVariables() throws Exception {
213 doTestHtml("<div let-a></div>" +
214 "<ng-template let-a='b'></ng-template>");
217 public void testNgParseInlineTemplates() throws Exception {
218 doTestHtml("<div *ngIf></div>" +
219 "<div *ngIf='condition'></div>" +
220 "<div *ngIf='#a=b'>Report error on vars with #</div>" +
221 "<div *ngIf='let a=b'></div>" +
222 "<div data-*ngIf='let a=b'></div>" +
223 "<div *ngIf='expr as local'></div>");
226 public void testNgReportErrorsInExpressions() throws Exception {
227 doTestHtml("<div [prop]='a b'></div>");
230 public void testNgBindingAttributeComplex() throws Exception {
231 doTestHtml("<div (lang)=\"{'current':i == (wordIndex | async)}\"></div>");
234 public void testNgCss() throws Exception {
235 doTestHtml("<div *ngFor=\"let something of items\" class=\"inDaClass foo\" style=\"color: #fff\"></div>");
238 public void testNgBindingElvis() throws Exception {
239 doTestHtml("<div lang=\"{{interpolation?.here}}\"></div>");
242 public void testNgEntity() throws Exception {
243 doTestHtml("<div>{{foo ? ' —' + bar : \"\"}}</div>");
246 public void testNgStringWithEntity() throws Exception {
247 doTestHtml("{{ "fo{o\" }}");
250 public void testNgStringWithEntity2() throws Exception {
251 doTestHtml("<div [input]=''foo"‐''");
254 public void testNgStringWithEntity3() throws Exception {
255 doTestHtml("<div [input]=''foo"‐'\"second\"'");
258 public void testNgWeb20713() throws Exception {
259 doTestHtml("<h5>Last Updated: {{(viewModel.lastUpdated$ | async) | date:'mediumTime'}}</h5>");
262 public void testNgWeb24804() throws Exception {
263 doTestHtml("<div *myStructuralDirective style=\"z-index: 10;\"></div>");
266 public void testNgTextInterpolation() throws Exception {
267 doTestHtml("<div>my {{interpolated}} text</div>\n" +
268 "<div>my{{interpolated}}text</div>\n" +
269 "<div>my{{double}}{{interpolated}}text</div>\n" +
270 "<div>my{{double}}double{{interpolated}}text</div>");
273 public void testNgTextInterpolationWithLineBreaks() throws Exception {
274 doTestHtml("{{todo\n" +
275 " | started : status\n" +
276 " | search : term\n" +
280 public void testNgIgnoredInterpolation() throws Exception {
281 doTestHtml("this {{ is {{ <ignored/> interpolation }}");
284 public void testNgIgnoredInterpolationInTag() throws Exception {
285 doTestHtml("<div>this{{is{{<ignored/> interpolation}}</div>another{{ignored{{<interpolation/>");
288 public void testNgInterpolationEmpty() throws Exception {
289 doTestHtml("empty {{}} interpolation");
293 public void testNgScriptWithEventAndAngularAttr() throws Exception {
294 doTestHtml("<script src=\"//example.com\" onerror=\"console.log(1)\" (error)='console.log(1)'" +
295 "onload=\"console.log(1)\" (load)='console.log(1)'>\n" +
296 " console.log(2)\n" +
301 public void testNgStyleTag() throws Exception {
302 doTestHtml("<style>\n" +
309 public void testNgStyleAngularAttr() throws Exception {
310 doTestHtml("<style (load)='disabled=true'>\n" +
317 public void testNgStyleWithEventAndAngularAttr() throws Exception {
318 doTestHtml("<style (load)='disabled=true' onload=\"this.disabled=true\" (load)='disabled=true'>\n" +
325 public void testNgContentSelect() throws Exception {
326 doTestHtml("<div><ng-content select='foo,bar'></ng-content></div>");
329 public void testNgNonBindable() throws Exception {
330 doTestHtml("<div><span ngNonBindable>f{{bar}}a</span>f{{foo}}a</div>");
333 public void testNgNonBindable2() throws Exception {
334 doTestHtml("<div><span ngNonBindable ngNonBindable>s{{bar}}e</span>s{{foo}}e</div>");
337 public void testNgNonBindable3() throws Exception {
338 doTestHtml("<div><span ngNonBindable ngNonBindable>{{bar}}<b ngNonBindable>{{boo}}</b></span>{{foo}}</div>");
341 public void testNgNonBindable4() throws Exception {
342 doTestHtml("<p ngNonBindable>{{foo}}<p>{{bar}}");
345 public void testNgNonQuotedAttrs() throws Exception {
346 doTestHtml("<div (click)=doIt()></div>\n" +
347 "<div [id]=foo></div>\n" +
348 "<div #foo=bar></div>\n" +
349 "<ng-content select=[header-content]></ng-content>\n");
352 public void testEmptyLetAndRef() throws Exception {
353 doTestHtml("<ng-template let-/><div let-/><div #/><div ref-/>");