// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.collide.client.code.autocomplete.codemirror;
import static com.google.collide.client.code.autocomplete.TestUtils.CTRL_SHIFT_SPACE;
import static com.google.collide.client.code.autocomplete.TestUtils.CTRL_SPACE;
import static com.google.collide.client.codeunderstanding.CodeGraphTestUtils.createCodeGraph;
import static com.google.collide.client.codeunderstanding.CodeGraphTestUtils.createFreshness;
import static org.waveprotocol.wave.client.common.util.SignalEvent.KeySignalType;
import com.google.collide.client.code.autocomplete.AutocompleteProposals;
import com.google.collide.client.code.autocomplete.AutocompleteResult;
import com.google.collide.client.code.autocomplete.DefaultAutocompleteResult;
import com.google.collide.client.code.autocomplete.LanguageSpecificAutocompleter.ExplicitAction;
import com.google.collide.client.code.autocomplete.LanguageSpecificAutocompleter.ExplicitActionType;
import com.google.collide.client.code.autocomplete.MockAutocompleterEnvironment;
import com.google.collide.client.code.autocomplete.SignalEventEssence;
import com.google.collide.client.code.autocomplete.TestUtils;
import com.google.collide.client.code.autocomplete.codegraph.ParsingTask;
import com.google.collide.client.code.autocomplete.integration.DocumentParserListenerAdapter;
import com.google.collide.client.code.autocomplete.integration.TaggableLineUtil;
import com.google.collide.client.testutil.CodeMirrorTestCase;
import com.google.collide.client.util.PathUtil;
import com.google.collide.codemirror2.Token;
import com.google.collide.dto.CodeBlock;
import com.google.collide.dto.client.DtoClientImpls;
import com.google.collide.dto.client.DtoClientImpls.CodeBlockImpl;
import com.google.collide.dto.client.DtoClientImpls.CodeGraphImpl;
import com.google.collide.dto.client.DtoClientImpls.CodeGraphResponseImpl;
import com.google.collide.dto.client.DtoClientImpls.MockCodeBlockImpl;
import com.google.collide.json.client.JsoArray;
import com.google.collide.json.shared.JsonArray;
import com.google.collide.shared.TaggableLine;
import com.google.collide.shared.document.Line;
/**
* Test for various auto-completion cases, when CodeMirror parser is used.
*/
public class HtmlCodemirrorTest extends CodeMirrorTestCase {
private static void setupHelper(MockAutocompleterEnvironment helper, String text) {
helper.setup(new PathUtil("foo.html"), text, 0, text.length(), true);
helper.parser.begin();
}
@Override
public String getModuleName() {
return "com.google.collide.client.TestCode";
}
public void testExplicit() {
MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
setupHelper(helper, "<html><body><");
AutocompleteResult commonResult = helper.autocompleter.htmlAutocompleter
.getExplicitAction(helper.editor.getSelection(), new SignalEventEssence('/'), false)
.getExplicitAutocompletion();
assertTrue("result type", commonResult instanceof DefaultAutocompleteResult);
DefaultAutocompleteResult result = (DefaultAutocompleteResult) commonResult;
assertEquals("/body>", result.getAutocompletionText());
}
public void testCssFindAutocompletions() {
MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
setupHelper(helper, "<html><head><style>p {color:bl");
Line line = helper.editor.getDocument().getLastLine();
JsonArray<Token> tokens = helper.parser.parseLineSync(line);
assertEquals("html", tokens.get(0).getMode());
assertEquals("css", tokens.get(tokens.size() - 1).getMode());
AutocompleteProposals proposals =
helper.autocompleter.htmlAutocompleter.findAutocompletions(
helper.editor.getSelection(), CTRL_SPACE);
assertEquals(2, proposals.size());
assertEquals("blue", proposals.get(1).getName());
}
public void testCssFindAutocompletionsAfterEmptyLine() {
MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
String line0 = "<html><head><style>p {color:";
String line1 = "";
String line2 = "bl";
String text = line0 + "\n" + line1 + "\n" + line2;
helper.setup(new PathUtil("foo.html"), text, 2, line2.length(), true);
helper.parser.begin();
Line line = helper.editor.getDocument().getFirstLine();
JsonArray<Token> tokens = helper.parser.parseLineSync(line);
assertEquals("html", tokens.get(0).getMode());
assertEquals("css", tokens.get(tokens.size() - 2).getMode());
assertEquals("", tokens.get(tokens.size() - 1).getMode());
line = helper.editor.getDocument().getLineFinder().findLine(1).line();
tokens = helper.parser.parseLineSync(line);
assertEquals(1, tokens.size());
assertEquals("", tokens.get(0).getMode());
line = helper.editor.getDocument().getLastLine();
tokens = helper.parser.parseLineSync(line);
assertEquals(1, tokens.size());
assertEquals("css", tokens.get(0).getMode());
AutocompleteProposals proposals =
helper.autocompleter.htmlAutocompleter.findAutocompletions(
helper.editor.getSelection(), CTRL_SPACE);
assertEquals(2, proposals.size());
assertEquals("blue", proposals.get(1).getName());
}
public void testCssGetExplicitAutocompletion() {
MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
setupHelper(helper, "<html><head><style>p ");
Line line = helper.editor.getDocument().getLastLine();
JsonArray<Token> tokens = helper.parser.parseLineSync(line);
assertEquals("html", tokens.get(0).getMode());
assertEquals("css", tokens.get(tokens.size() - 1).getMode());
helper.autocompleter.htmlAutocompleter.updateModeAnchors(line, tokens);
SignalEventEssence trigger = new SignalEventEssence('{');
AutocompleteResult result = helper.autocompleter.htmlAutocompleter
.getExplicitAction(helper.editor.getSelection(), trigger, false)
.getExplicitAutocompletion();
assertTrue("result type", result instanceof DefaultAutocompleteResult);
DefaultAutocompleteResult defaultResult = (DefaultAutocompleteResult) result;
assertEquals("{\n \n}", defaultResult.getAutocompletionText());
}
public void testAfterCssMultiplexing() {
MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
setupHelper(helper, "<html><head><style>p {color:blue;}</style><a");
AutocompleteProposals proposals =
helper.autocompleter.htmlAutocompleter.findAutocompletions(
helper.editor.getSelection(), CTRL_SPACE);
assertEquals(7, proposals.size());
assertEquals("abbr", proposals.get(1).getName());
}
public void testJavascriptFindAutocompletions() {
MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
setupHelper(helper,
"<html><body><script type=\"text/javascript\">function a() { var abba, apple, arrow; a");
Line line = helper.editor.getDocument().getLastLine();
JsonArray<Token> tokens = helper.parser.parseLineSync(line);
assertEquals("html", tokens.get(0).getMode());
assertEquals("javascript", tokens.get(tokens.size() - 1).getMode());
TaggableLine previousLine = TaggableLineUtil.getPreviousLine(line);
helper.autocompleter.htmlAutocompleter.updateModeAnchors(line, tokens);
new ParsingTask(helper.autocompleter.localPrefixIndexStorage).onParseLine(
previousLine, line, tokens);
AutocompleteProposals proposals = helper.autocompleter.htmlAutocompleter.findAutocompletions(
helper.editor.getSelection(), CTRL_SHIFT_SPACE);
assertEquals(3, proposals.size());
assertEquals("apple", proposals.get(1).getName());
proposals = helper.autocompleter.htmlAutocompleter.findAutocompletions(
helper.editor.getSelection(), CTRL_SPACE);
assertEquals(4, proposals.size());
assertEquals("arguments", proposals.get(2).getName());
}
public void testJavascriptFindAutocompletionsOnEmptyLine() {
MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
helper.setup(new PathUtil("foo.html"),
"<html><head><script>\n\n</script></head></html>", 1, 0, true);
helper.parser.getListenerRegistrar().add(new DocumentParserListenerAdapter(
helper.autocompleter, helper.editor));
helper.parser.begin();
helper.parseScheduler.requests.pop().run(10);
AutocompleteProposals proposals = helper.autocompleter.htmlAutocompleter
.findAutocompletions(helper.editor.getSelection(), CTRL_SPACE);
assertTrue(TestUtils.findProposalByName(proposals.getItems(), "break") != null);
}
public void testJavascriptApplyAutocompletions() {
MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
String prologue = "<html>\n<script type=\"text/javascript\">\n";
String epilogue = "</script>\n</html>\n";
String text = prologue + "d\n" + epilogue;
helper.setup(new PathUtil("foo.html"), text, 2, 1, true);
helper.parser.begin();
helper.parseScheduler.requests.get(0).run(10);
AutocompleteProposals proposals = helper.autocompleter.htmlAutocompleter.findAutocompletions(
helper.editor.getSelection(), CTRL_SPACE);
assertEquals(3, proposals.size());
assertEquals("delete", proposals.get(1).getName());
helper.autocompleter.reallyFinishAutocompletion(proposals.select(1));
assertEquals(prologue + "delete \n" + epilogue, helper.editor.getDocument().asText());
}
public void testJavascriptFindAutocompletionsSeesFunctionInCodegraph() {
MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
// Something like function aFoo() {}
CodeBlockImpl aFoo = MockCodeBlockImpl
.make()
.setBlockType(CodeBlock.Type.VALUE_FUNCTION)
.setName("aFoo")
.setChildren(JsoArray.<CodeBlock>create())
.setStartLineNumber(0)
.setStartColumn(0)
.setEndLineNumber(0)
.setEndColumn(19);
CodeBlockImpl fileCodeBlock = MockCodeBlockImpl
.make()
.setBlockType(CodeBlock.Type.VALUE_FILE)
.setName("/foo.js")
.setChildren(JsoArray.<CodeBlock>from(aFoo))
.setStartLineNumber(0)
.setStartColumn(0)
.setEndLineNumber(0)
.setEndColumn(19);
CodeGraphImpl codeGraph = createCodeGraph(fileCodeBlock);
CodeGraphResponseImpl response = DtoClientImpls.MockCodeGraphResponseImpl.make();
response.setFreshness(createFreshness("0", "1", "0"));
response.setFullGraphJson(codeGraph.serialize());
// This will immediately fire api call
helper.cubeClient.setPath("/foo.js");
assertEquals("one api call after setDocument", 1,
helper.cubeClient.api.collectedCallbacks.size());
helper.cubeClient.api.collectedCallbacks.get(0).onMessageReceived(response);
setupHelper(helper,
"<html><body><script type=\"text/javascript\">function a() { var abba, apple, arrow; a");
Line line = helper.editor.getDocument().getLastLine();
JsonArray<Token> tokens = helper.parser.parseLineSync(line);
assertEquals("html", tokens.get(0).getMode());
assertEquals("javascript", tokens.get(tokens.size() - 1).getMode());
TaggableLine previousLine = TaggableLineUtil.getPreviousLine(line);
helper.autocompleter.htmlAutocompleter.updateModeAnchors(line, tokens);
new ParsingTask(helper.autocompleter.localPrefixIndexStorage).onParseLine(
previousLine, line, tokens);
AutocompleteProposals proposals = helper.autocompleter.htmlAutocompleter.findAutocompletions(
helper.editor.getSelection(), CTRL_SHIFT_SPACE);
assertEquals(3, proposals.size());
assertEquals("apple", proposals.get(1).getName());
proposals = helper.autocompleter.htmlAutocompleter.findAutocompletions(
helper.editor.getSelection(), CTRL_SPACE);
assertEquals(5, proposals.size());
assertEquals("aFoo", proposals.get(1).getName());
}
public void testJavascriptGetExplicitAutocompletion() {
MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
setupHelper(helper, "<html><body><script type=\"text/javascript\">foo");
Line line = helper.editor.getDocument().getLastLine();
JsonArray<Token> tokens = helper.parser.parseLineSync(line);
assertEquals("html", tokens.get(0).getMode());
assertEquals("javascript", tokens.get(tokens.size() - 1).getMode());
TaggableLine previousLine = TaggableLineUtil.getPreviousLine(line);
helper.autocompleter.htmlAutocompleter.updateModeAnchors(line, tokens);
new ParsingTask(helper.autocompleter.localPrefixIndexStorage).onParseLine(
previousLine, line, tokens);
SignalEventEssence trigger = new SignalEventEssence('[');
AutocompleteResult result = helper.autocompleter.htmlAutocompleter
.getExplicitAction(helper.editor.getSelection(), trigger, false)
.getExplicitAutocompletion();
assertTrue("result type", result instanceof DefaultAutocompleteResult);
DefaultAutocompleteResult defaultResult = (DefaultAutocompleteResult) result;
assertEquals("[]", defaultResult.getAutocompletionText());
trigger = new SignalEventEssence('(');
result = helper.autocompleter.htmlAutocompleter
.getExplicitAction(helper.editor.getSelection(), trigger, false)
.getExplicitAutocompletion();
assertTrue("result type", result instanceof DefaultAutocompleteResult);
defaultResult = (DefaultAutocompleteResult) result;
assertEquals("()", defaultResult.getAutocompletionText());
}
public void testPopupDoNotAnnoyUsers() {
final MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
helper.setup(new PathUtil("foo.html"),
"<html>\n <body>\n <script>\n\n </script>\n </body>\n</html>", 3, 0, true);
ExplicitActionType action = helper.autocompleter.htmlAutocompleter.getExplicitAction(
helper.editor.getSelection(), new SignalEventEssence(' '), false).getType();
assertTrue("no popup before mode is determined", action == ExplicitActionType.DEFAULT);
helper.parser.getListenerRegistrar().add(new DocumentParserListenerAdapter(
helper.autocompleter, helper.editor));
helper.parser.begin();
helper.parseScheduler.requests.pop().run(10);
action = helper.autocompleter.htmlAutocompleter.getExplicitAction(
helper.editor.getSelection(), new SignalEventEssence(' '), false).getType();
assertTrue("no popup in JS mode", action == ExplicitActionType.DEFAULT);
}
public void testNoPopupAfterClosingTag() {
final MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
helper.setup(new PathUtil("foo.html"), "<html>\n <script\n<body style=''>\n", 2, 8, true);
helper.parser.getListenerRegistrar().add(new DocumentParserListenerAdapter(
helper.autocompleter, helper.editor));
helper.parser.begin();
helper.parseScheduler.requests.pop().run(10);
SignalEventEssence signalGt = new SignalEventEssence(
'>', false, false, true, false, KeySignalType.INPUT);
ExplicitActionType action = helper.autocompleter.htmlAutocompleter.getExplicitAction(
helper.editor.getSelection(), signalGt, false).getType();
assertTrue("no popup after closing tag", action == ExplicitActionType.DEFAULT);
}
/**
* Tests a sharp edge: when html parser changes it's mode.
*
* <p>Parser changes it's mode when it meets ">" that finishes opening
* "script" tag.
*
* <p>As a consequence, xml-analyzer may interpret the following
* content "inside-out".
*/
public void testScriptTagDoNotConfuseXmlProcessing() {
MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
helper.setup(new PathUtil("foo.html"),
"<html>\n <script></script>\n <body>\n", 2, 7, true);
helper.parser.getListenerRegistrar().add(new DocumentParserListenerAdapter(
helper.autocompleter, helper.editor));
helper.parser.begin();
helper.parseScheduler.requests.pop().run(10);
AutocompleteProposals proposals = helper.autocompleter.htmlAutocompleter
.findAutocompletions(helper.editor.getSelection(), CTRL_SPACE);
assertEquals(0, proposals.size());
}
/**
* Tests a sharp edge: when html parser changes it's mode.
*
* <p>Parser changes it's mode when it meets ">" that finishes opening
* "script" tag.
*
* <p>This situation could confuse autocompleter and cause unwanted popup
* when user press space just after ">".
*/
public void testNoJsPopupOnAfterSpace() {
MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
helper.setup(new PathUtil("foo.html"),
"<html>\n <script>\n", 1, 9, true);
helper.parser.getListenerRegistrar().add(new DocumentParserListenerAdapter(
helper.autocompleter, helper.editor));
helper.parser.begin();
helper.parseScheduler.requests.pop().run(10);
ExplicitAction action = helper.autocompleter.htmlAutocompleter
.getExplicitAction(helper.editor.getSelection(), new SignalEventEssence(' '), false);
assertFalse(action.getType() == ExplicitActionType.DEFERRED_COMPLETE);
}
/**
* Tests a sharp edge: when html parser is asked for proposals on line that
* is not parsed yet.
*
* <p>Previously we got NPE in this case.
*/
public void testNoProposalsOnGrayLine() {
MockAutocompleterEnvironment helper = new MockAutocompleterEnvironment();
helper.setup(new PathUtil("foo.html"),
"<html>\n <body>\n <di", 2, 5, true);
helper.parser.getListenerRegistrar().add(new DocumentParserListenerAdapter(
helper.autocompleter, helper.editor));
helper.parser.begin();
helper.parseScheduler.requests.pop().run(1);
AutocompleteProposals proposals = helper.autocompleter.htmlAutocompleter
.findAutocompletions(helper.editor.getSelection(), CTRL_SPACE);
assertEquals(0, proposals.size());
}
}