/* * Copyright 2008 The Closure Compiler Authors. * * 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.javascript.jscomp.deps; import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.javascript.jscomp.ErrorManager; import com.google.javascript.jscomp.PrintStreamErrorManager; import java.util.Collections; import junit.framework.TestCase; /** * Tests for {@link JsFileParser}. * * @author agrieve@google.com (Andrew Grieve) */ public final class JsFileParserTest extends TestCase { JsFileParser parser; private ErrorManager errorManager; private static final String SRC_PATH = "a"; private static final String CLOSURE_PATH = "b"; @Override public void setUp() { errorManager = new PrintStreamErrorManager(System.err); parser = new JsFileParser(errorManager); parser.setShortcutMode(true); } /** * Tests: * -Parsing of comments, * -Parsing of different styles of quotes, * -Correct recording of what was parsed. */ public void testParseFile() { String contents = "/*" + "goog.provide('no1');*//*\n" + "goog.provide('no2');\n" + "*/goog.provide('yes1');\n" + "/* blah */goog.provide(\"yes2\")/* blah*/\n" + "goog.require('yes3'); // goog.provide('no3');\n" + "// goog.provide('no4');\n" + "goog.require(\"bar.data.SuperstarAddStarThreadActionRequestDelegate\"); " + "//no new line at EOF"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("yes1", "yes2"), ImmutableList.of("yes3", "bar.data.SuperstarAddStarThreadActionRequestDelegate"), false); DependencyInfo result = parser.parseFile(SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } /** * Tests: * -Correct recording of what was parsed. */ public void testParseFile2() { String contents = "" + "goog.module('yes1');\n" + "var yes2 = goog.require('yes2');\n" + "var C = goog.require(\"a.b.C\");\n" + "let {D, E} = goog.require('a.b.d');"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("yes1"), ImmutableList.of("yes2", "a.b.C", "a.b.d"), ImmutableMap.of("module", "goog")); DependencyInfo result = parser.parseFile(SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } /** * Tests: * -Correct recording of what was parsed. */ public void testParseFile3() { String contents = "" + "goog.module('yes1');\n" + "var yes2=goog.require('yes2');\n" + "var C=goog.require(\"a.b.C\");\n" + "const {\n D,\n E\n}=goog.require(\"a.b.d\");"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("yes1"), ImmutableList.of("yes2", "a.b.C", "a.b.d"), ImmutableMap.of("module", "goog")); DependencyInfo result = parser.parseFile(SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } /** * Tests: * -Correct recording of what was parsed. */ public void testParseWrappedGoogModule() { String contents = "" + "goog.loadModule(function(){\"use strict\";goog.module('yes1');\n" + "var yes2=goog.require('yes2');\n" + "var C=goog.require(\"a.b.C\");\n" + "const {\n D,\n E\n}=goog.require(\"a.b.d\");});"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("yes1"), ImmutableList.of("yes2", "a.b.C", "a.b.d"), ImmutableMap.<String, String>of()); // wrapped modules aren't marked as modules DependencyInfo result = parser.parseFile(SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } // TODO(sdh): Add a test for import with .js suffix once #1897 is fixed. /** * Tests: * -ES6 modules parsed correctly, particularly the various formats. */ public void testParseEs6Module() { String contents = "" + "import def, {yes2} from './yes2';\n" + "import C from './a/b/C';\n" + "import * as d from './a/b/d';\n" + "import \"./dquote\";\n" + "export * from './exported';\n"; DependencyInfo expected = new SimpleDependencyInfo("a.js", "b.js", ImmutableList.of("module$b"), ImmutableList.of( "module$yes2", "module$a$b$C", "module$a$b$d", "module$dquote", "module$exported"), ImmutableMap.of("module", "es6")); DependencyInfo result = parser.parseFile("b.js", "a.js", contents); assertDeps(expected, result); } /** * Tests: * -Relative paths resolved correctly. */ public void testParseEs6Module2() { String contents = "" + "import './x';\n" + "import '../y';\n" + "import '../a/z';\n" + "import '../c/w';\n"; DependencyInfo expected = new SimpleDependencyInfo( "../../a/b.js", "/foo/bar/a/b.js", ImmutableList.of("module$foo$bar$a$b"), ImmutableList.of( "module$foo$bar$a$x", "module$foo$bar$y", "module$foo$bar$a$z", "module$foo$bar$c$w"), ImmutableMap.of("module", "es6")); DependencyInfo result = parser.parseFile("/foo/bar/a/b.js", "../../a/b.js", contents); assertDeps(expected, result); } /** * Tests: * -Handles goog.require and import 'goog:...'. */ public void testParseEs6Module3() { String contents = "" + "import 'goog:foo.bar.baz';\n" + "goog.require('baz.qux');\n"; DependencyInfo expected = new SimpleDependencyInfo("b.js", "a.js", ImmutableList.of("module$a"), ImmutableList.of("foo.bar.baz", "baz.qux"), ImmutableMap.of("module", "es6")); DependencyInfo result = parser.parseFile("a.js", "b.js", contents); assertDeps(expected, result); } /** * Tests: * -setModuleLoader taken into account */ public void testParseEs6Module4() { ModuleLoader loader = new ModuleLoader( null, ImmutableList.of("/foo"), ImmutableList.<DependencyInfo>of(), ModuleLoader.ResolutionMode.LEGACY); String contents = "" + "import './a';\n" + "import './qux/b';\n" + "import '../closure/c';\n" + "import '../closure/d/e';\n" + "import '../../corge/f';\n"; DependencyInfo expected = new SimpleDependencyInfo("../bar/baz.js", "/foo/js/bar/baz.js", ImmutableList.of("module$js$bar$baz"), ImmutableList.of( "module$js$bar$a", "module$js$bar$qux$b", "module$js$closure$c", "module$js$closure$d$e", "module$corge$f"), ImmutableMap.of("module", "es6")); DependencyInfo result = parser .setModuleLoader(loader) .parseFile("/foo/js/bar/baz.js", "../bar/baz.js", contents); assertDeps(expected, result); } /** * Tests: * -Shortcut mode doesn't stop at setTestOnly() or declareLegacyNamespace(). */ public void testNoShortcutForCommonModuleModifiers() { String contents = "" + "goog.module('yes1');\n" + "goog.module.declareLegacyNamespace();\n" + "goog.setTestOnly();\n" + "var yes2=goog.require('yes2');\n" + "var C=goog.require(\"a.b.C\");\n" + "const {\n D,\n E\n}=goog.require(\"a.b.d\");"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("yes1"), ImmutableList.of("yes2", "a.b.C", "a.b.d"), true); DependencyInfo result = parser.parseFile(SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testMultiplePerLine() { String contents = "goog.provide('yes1');goog.provide('yes2');/*" + "goog.provide('no1');*/goog.provide('yes3');//goog.provide('no2');"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("yes1", "yes2", "yes3"), Collections.<String>emptyList(), false); DependencyInfo result = parser.parseFile(SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testShortcutMode1() { // For efficiency reasons, we stop reading after the ctor. String contents = " // hi ! \n /* this is a comment */ " + "goog.provide('yes1');\n /* and another comment */ \n" + "goog.provide('yes2'); // include this\n" + "foo = function() {};\n" + "goog.provide('no1');"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("yes1", "yes2"), Collections.<String>emptyList(), false); DependencyInfo result = parser.parseFile(SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testShortcutMode2() { String contents = "/** goog.provide('no1'); \n" + " * goog.provide('no2');\n */\n" + "goog.provide('yes1');\n"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("yes1"), Collections.<String>emptyList(), false); DependencyInfo result = parser.parseFile(SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testShortcutMode3() { String contents = "/**\n" + " * goog.provide('no1');\n */\n" + "goog.provide('yes1');\n"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("yes1"), Collections.<String>emptyList(), false); DependencyInfo result = parser.parseFile(SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testIncludeGoog1() { String contents = "/**\n" + " * the first constant in base.js\n" + " */\n" + "var COMPILED = false;\n"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("goog"), Collections.<String>emptyList(), false); DependencyInfo result = parser.setIncludeGoogBase(true).parseFile( SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testIncludeGoog2() { String contents = "goog.require('bar');"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.<String>of(), ImmutableList.of("goog", "bar"), false); DependencyInfo result = parser.setIncludeGoogBase(true).parseFile( SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testIncludeGoog3() { // This is pretending to provide goog, but it really doesn't. String contents = "goog.provide('x');\n" + "/**\n" + " * the first constant in base.js\n" + " */\n" + "var COMPILED = false;\n"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.of("x"), ImmutableList.of("goog"), false); DependencyInfo result = parser.setIncludeGoogBase(true).parseFile( SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } public void testIncludeGoog4() { String contents = "goog.addDependency('foo', [], []);\n"; DependencyInfo expected = new SimpleDependencyInfo(CLOSURE_PATH, SRC_PATH, ImmutableList.<String>of(), ImmutableList.of("goog"), false); DependencyInfo result = parser.setIncludeGoogBase(true).parseFile( SRC_PATH, CLOSURE_PATH, contents); assertDeps(expected, result); } /** Asserts the deps match without errors */ private void assertDeps(DependencyInfo expected, DependencyInfo actual) { assertThat(actual).isEqualTo(expected); assertThat(errorManager.getErrorCount()).isEqualTo(0); assertThat(errorManager.getWarningCount()).isEqualTo(0); } }