/* * Copyright 2015 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; import static com.google.javascript.jscomp.CheckRequiresForConstructors.EXTRA_REQUIRE_WARNING; import com.google.common.collect.ImmutableList; import java.util.List; /** * Tests for the "extra requires" check in {@link CheckRequiresForConstructors}. */ public final class ExtraRequireTest extends Es6CompilerTestCase { public ExtraRequireTest() { super(); } @Override protected CompilerOptions getOptions(CompilerOptions options) { options.setWarningLevel(DiagnosticGroups.EXTRA_REQUIRE, CheckLevel.ERROR); return super.getOptions(options); } @Override protected CompilerPass getProcessor(Compiler compiler) { return new CheckRequiresForConstructors(compiler, CheckRequiresForConstructors.Mode.FULL_COMPILE); } public void testNoWarning() { testSame("goog.require('foo.Bar'); var x = new foo.Bar();"); testSameEs6("goog.require('foo.Bar'); let x = new foo.Bar();"); testSameEs6("goog.require('foo.Bar'); const x = new foo.Bar();"); testSame("goog.require('foo.Bar'); /** @type {foo.Bar} */ var x;"); testSame("goog.require('foo.Bar'); /** @type {Array<foo.Bar>} */ var x;"); testSame("goog.require('foo.Bar'); var x = new foo.Bar.Baz();"); testSame("goog.require('foo.bar'); var x = foo.bar();"); testSame("goog.require('foo.bar'); var x = /** @type {foo.bar} */ (null);"); testSame("goog.require('foo.bar'); function f(/** foo.bar */ x) {}"); testSame("goog.require('foo.bar'); alert(foo.bar.baz);"); testSame("/** @suppress {extraRequire} */ goog.require('foo.bar');"); testSame("goog.require('foo.bar'); goog.scope(function() { var bar = foo.bar; alert(bar); });"); testSame("goog.require('foo'); foo();"); testSame("goog.require('foo'); new foo();"); testSame("/** @suppress {extraRequire} */ var bar = goog.require('foo.bar');"); } public void testNoWarning_externsJsDoc() { String js = "goog.require('ns.Foo'); /** @type {ns.Foo} */ var f;"; List<SourceFile> externs = ImmutableList.of(SourceFile.fromCode("externs", "/** @const */ var ns;")); test(externs, js, js, null, null, null); } public void testNoWarning_externsNew() { String js = "goog.require('ns.Foo'); new ns.Foo();"; List<SourceFile> externs = ImmutableList.of(SourceFile.fromCode("externs", "/** @const */ var ns;")); test(externs, js, js, null, null, null); } public void testNoWarning_objlitShorthand() { testSameEs6( LINE_JOINER.join( "goog.module('example.module');", "", "const X = goog.require('example.X');", "alert({X});")); testSameEs6( LINE_JOINER.join( "goog.require('X');", "alert({X});")); } public void testNoWarning_InnerClassInExtends() { String js = LINE_JOINER.join( "var goog = {};", "goog.require('goog.foo.Bar');", "", "/** @constructor @extends {goog.foo.Bar.Inner} */", "function SubClass() {}"); testSame(js); } public void testWarning() { testError("goog.require('foo.bar');", EXTRA_REQUIRE_WARNING); testErrorEs6(LINE_JOINER.join( "goog.require('Bar');", "function func( {a} ){}", "func( {a: 1} );"), EXTRA_REQUIRE_WARNING); testErrorEs6(LINE_JOINER.join( "goog.require('Bar');", "function func( a = 1 ){}", "func(42);"), EXTRA_REQUIRE_WARNING); } public void testNoWarningMultipleFiles() { String[] js = new String[] { "goog.require('Foo'); var foo = new Foo();", "goog.require('Bar'); var bar = new Bar();" }; testSame(js); } public void testPassModule() { testSameEs6( LINE_JOINER.join( "import {Foo} from 'bar';", "new Foo();")); testSameEs6( LINE_JOINER.join( "import Bar from 'bar';", "new Bar();")); testSameEs6( LINE_JOINER.join( "import {CoolFeature as Foo} from 'bar';", "new Foo();")); testSameEs6( LINE_JOINER.join( "import Bar, {CoolFeature as Foo, OtherThing as Baz} from 'bar';", "new Foo(); new Bar(); new Baz();")); } public void testFailModule() { testErrorEs6( "import {Foo} from 'bar';", EXTRA_REQUIRE_WARNING); testErrorEs6( LINE_JOINER.join( "import {Foo} from 'bar';", "goog.require('example.ExtraRequire');", "new Foo;"), EXTRA_REQUIRE_WARNING); } public void testPassForwardDeclareInModule() { testSame( LINE_JOINER.join( "goog.module('example');", "", "var Event = goog.forwardDeclare('goog.events.Event');", "", "/**", " * @param {!Event} event", " */", "function listener(event) {", " alert(event);", "}", "", "exports = listener;")); } public void testUnusedForwardDeclareInModule() { // Reports extra require warning, but only in single-file mode. testSame( LINE_JOINER.join( "goog.module('example');", "", "var Event = goog.forwardDeclare('goog.events.Event');", "var Unused = goog.forwardDeclare('goog.events.Unused');", "", "/**", " * @param {!Event} event", " */", "function listener(event) {", " alert(event);", "}", "", "exports = listener;")); } public void testPassForwardDeclare() { testSame( LINE_JOINER.join( "goog.forwardDeclare('goog.events.Event');", "", "/**", " * @param {!goog.events.Event} event", " */", "function listener(event) {", " alert(event);", "}")); } public void testFailForwardDeclare() { // Reports extra require warning, but only in single-file mode. testSame( LINE_JOINER.join( "goog.forwardDeclare('goog.events.Event');", "goog.forwardDeclare('goog.events.Unused');", "", "/**", " * @param {!goog.events.Event} event", " */", "function listener(event) {", " alert(event);", "}")); } public void testGoogModuleGet() { testSame( LINE_JOINER.join( "goog.provide('x.y');", "goog.require('foo.bar');", "", "goog.scope(function() {", "var bar = goog.module.get('foo.bar');", "x.y = function() {};", "});")); } public void testGoogModuleWithDestructuringRequire() { testErrorEs6( LINE_JOINER.join( "goog.module('example');", "", "var dom = goog.require('goog.dom');", "var {assert} = goog.require('goog.asserts');", "", "/**", " * @param {Array<string>} ids", " * @return {Array<HTMLElement>}", " */", "function getElems(ids) {", " return ids.map(id => dom.getElement(id));", "}", "", "exports = getElems;"), EXTRA_REQUIRE_WARNING); testSameEs6( LINE_JOINER.join( "goog.module('example');", "", "var {assert : googAssert} = goog.require('goog.asserts');", "", "exports = function() {", " googAssert(true);", "};")); testErrorEs6( LINE_JOINER.join( "goog.module('example');", "", "var {assert, fail} = goog.require('goog.asserts');", "", "exports = function() {", " assert(true);", "};"), EXTRA_REQUIRE_WARNING); testErrorEs6( LINE_JOINER.join( "goog.module('example');", "", "var {assert : googAssert} = goog.require('goog.asserts');", "", "exports = function() {", " goog.asserts(true);", "};"), EXTRA_REQUIRE_WARNING); testErrorEs6( LINE_JOINER.join( "goog.module('example');", "", "var {assert : googAssert} = goog.require('goog.asserts');", "", "exports = function() {", " assert(true);", "};"), EXTRA_REQUIRE_WARNING); } public void testGoogModuleWithEmptyDestructuringRequire() { testErrorEs6( LINE_JOINER.join( "goog.module('example');", "", "var {} = goog.require('goog.asserts');"), EXTRA_REQUIRE_WARNING); } }