/* * Copyright 2016 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.ClosureRewriteModule.MISSING_MODULE_OR_PROVIDE; import static com.google.javascript.jscomp.ProcessClosurePrimitives.MISSING_PROVIDE_ERROR; import com.google.javascript.rhino.Node; /** * Tests for the "missing provides" checks in {@link ProcessClosurePrimitives} and {@link * ClosureRewriteModule}. * * @author stalcup@google.com (John Stalcup) */ public final class MissingProvideTest extends Es6CompilerTestCase { public MissingProvideTest() { enableRewriteClosureCode(); enableClosurePass(); } @Override protected CompilerPass getProcessor(Compiler compiler) { // No-op. We're just checking for warnings during Closure passes. return new CompilerPass() { @Override public void process(Node externs, Node root) {} }; } // Test matrix: // ----------------------------------------------------- // Leaf Import type Root File status Result // ----------------------------------------------------- // legacy goog.require module decl leg pass // legacy goog.require module normal pass // legacy goog.require module missing fail // legacy goog.module.get module normal pass // legacy goog.module.get module missing fail // module var goog.require module normal pass // module var goog.require module missing fail // module goog.module.get module normal pass // module goog.module.get module missing fail // module var goog.require legacy normal pass // module var goog.require legacy missing fail // module goog.module.get legacy normal pass // module goog.module.get legacy missing fail // legacy goog.require legacy normal pass // legacy goog.require legacy missing fail // legacy goog.module.get legacy normal pass // legacy goog.module.get legacy missing fail public void test_Legacy_Require_Module_DeclLeg_Pass() { String googModule = LINE_JOINER.join( "goog.module('normal.goog.module.A');", "goog.module.declareLegacyNamespace();", "/** @constructor */ function A() {}", "exports = A;"); String legacyScript = LINE_JOINER.join( "goog.provide('legacy.script.B');", "goog.require('normal.goog.module.A');", "new normal.goog.module.A;"); test(new String[] {googModule, legacyScript}, null, null, null, null); } public void test_Legacy_Require_Module_Normal_Pass() { String googModule = LINE_JOINER.join( "goog.module('normal.goog.module.A');", "/** @constructor */ function A() {}", "exports = A;"); String legacyScript = LINE_JOINER.join( "goog.provide('legacy.script.B');", "goog.require('normal.goog.module.A');"); test(new String[] {googModule, legacyScript}, null, null, null, null); } public void test_Legacy_Require_Module_Missing_Fail() { String legacyScript = LINE_JOINER.join( "goog.provide('legacy.script.B');", "goog.scope(function() {", " var A = goog.module.get('missing.goog.module.A');", "});"); String warning = "Required namespace \"missing.goog.module.A\" never defined."; test(legacyScript, null, MISSING_MODULE_OR_PROVIDE, null, warning); } public void test_Legacy_ModuleGet_Module_Normal_Pass() { String googModule = LINE_JOINER.join( "goog.module('normal.goog.module.A');", "/** @constructor */ function A() {}", "exports = A;"); String legacyScript = LINE_JOINER.join( "goog.provide('legacy.script.B');", "goog.scope(function() {", " var A = goog.module.get('normal.goog.module.A');", "});"); test(new String[] {googModule, legacyScript}, null, null, null, null); } public void test_Legacy_ModuleGet_Module_Missing_Fail() { String legacyScript = LINE_JOINER.join( "goog.provide('legacy.script.B');", "goog.scope(function() {", " var A = goog.module.get('missing.goog.module.A');", "});"); String warning = "Required namespace \"missing.goog.module.A\" never defined."; test(legacyScript, null, MISSING_MODULE_OR_PROVIDE, null, warning); } public void test_Module_Require_Module_Normal_Pass() { String googModule1 = LINE_JOINER.join( "goog.module('normal.goog.module.A');", "/** @constructor */ function A() {}", "exports = A;"); String googModule2 = LINE_JOINER.join( "goog.module('normal.goog.module.B');", "var A = goog.require('normal.goog.module.A');", "/** @constructor */ function B() {}", "B.prototype = new A;", "exports = B;"); test(new String[] {googModule1, googModule2}, null, null, null, null); } public void test_Module_Require_Module_Missing_Fail() { // When something is goog.require()'d in a module and the referenced thing is missing it's not // really possible to know if the missing thing is a module or script. String googModule = LINE_JOINER.join( "goog.module('normal.goog.module.B');", "var A = goog.require('missing.goog.module.A');", "/** @constructor */ function B() {}", "B.prototype = new A;", "exports = B;"); String warning = "Required namespace \"missing.goog.module.A\" never defined."; test(googModule, null, MISSING_MODULE_OR_PROVIDE, null, warning); } public void test_Module_ModuleGet_Module_Normal_Pass() { String googModule1 = LINE_JOINER.join( "goog.module('normal.goog.module.A');", "/** @constructor */ function A() {}", "exports = A;"); String googModule2 = LINE_JOINER.join( "goog.module('normal.goog.module.B');", "var A = goog.forwardDeclare('normal.goog.module.A');", "/** @constructor */ function B() {}", "B.prototype.createA = function() {", " A = goog.module.get('normal.goog.module.A');", " new A;", "}", "exports = B;"); test(new String[] {googModule1, googModule2}, null, null, null, null); } public void test_Module_Require_Legacy_Normal_Pass() { String legacyScript = LINE_JOINER.join( "goog.provide('legacy.script.A');", "/** @constructor */ legacy.script.A = function () {}"); String googModule = LINE_JOINER.join( "goog.module('normal.goog.module.B');", "var A = goog.require('legacy.script.A');", "/** @constructor */ function B() {}", "B.prototype = new A;", "exports = B;"); test(new String[] {legacyScript, googModule}, null, null, null, null); } public void test_Module_Require_Legacy_Missing_Fail() { // When something is goog.require()'d in a module and the referenced thing is missing it's not // really possible to know if the missing thing is a module or script. String googModule = LINE_JOINER.join( "goog.module('normal.goog.module.B');", "var A = goog.require('legacy.script.A');", "/** @constructor */ function B() {}", "B.prototype = new A;", "exports = B;"); String warning = "Required namespace \"legacy.script.A\" never defined."; test(googModule, null, MISSING_MODULE_OR_PROVIDE, null, warning); } public void test_Module_ModuleGet_Legacy_Normal_Pass() { // goog.module.get inside of a goog.module() can reference legacy files, to be consistent with // goog.require() behavior in the same context. String legacyScript = LINE_JOINER.join( "goog.provide('legacy.script.A');", "/** @constructor */ legacy.script.A = function () {}"); String googModule = LINE_JOINER.join( "goog.module('normal.goog.module.B');", "var A = goog.forwardDeclare('legacy.script.A');", "/** @constructor */ function B() {}", "B.prototype.createA = function() {", " A = goog.module.get('legacy.script.A');", " new A;", "}", "exports = B;"); test(new String[] {legacyScript, googModule}, null, null, null, null); } public void test_Module_ModuleGet_Missing_Fail() { // goog.module.get inside of a goog.module() cannot reference files that do not exist. String googModule = LINE_JOINER.join( "goog.module('normal.goog.module.B');", "function f() {", " return goog.module.get('missing.legacy.script.A');", "}", "exports = f;"); String warning = "Required namespace \"missing.legacy.script.A\" never defined."; test(googModule, null, MISSING_MODULE_OR_PROVIDE, null, warning); } public void test_Module_ForwardDeclare_Missing_Fail() { // Short goog.forwardDeclare inside a goog.module() cannot reference files that do not exist. String googModule = LINE_JOINER.join( "goog.module('normal.goog.module.B');", "var A = goog.forwardDeclare('missing.legacy.script.A');", "/** @constructor */ function B() {}", "exports = B;"); String warning = "Required namespace \"missing.legacy.script.A\" never defined."; test(googModule, null, MISSING_MODULE_OR_PROVIDE, null, warning); } public void test_Legacy_ForwardDeclare_Missing_Pass() { // Legacy goog.forwardDeclare inside a goog.module() works the same as outside a module. String googModule = LINE_JOINER.join( "goog.module('normal.goog.module.B');", "goog.forwardDeclare('missing.legacy.script.A');", "/** @constructor */ function B() {}", "exports = B;"); test(new String[] { googModule }, null, null, null, null); } public void test_Legacy_Require_Legacy_Normal_Pass() { String legacyScript = LINE_JOINER.join( "goog.provide('legacy.script.A');", "/** @constructor */ legacy.script.A = function () {}"); String legacyScript2 = LINE_JOINER.join( "goog.provide('legacy.script.B');", "goog.require('legacy.script.A');", "new legacy.script.A;"); test(new String[] {legacyScript, legacyScript2}, null, null, null, null); } public void test_Legacy_Require_Legacy_Missing_Fail() { String legacyScript = LINE_JOINER.join( "goog.provide('legacy.script.B');", "goog.require('legacy.script.A');", "new legacy.script.A;"); String warning = "required \"legacy.script.A\" namespace never provided"; test(legacyScript, null, MISSING_PROVIDE_ERROR, null, warning); } public void test_Legacy_ModuleGet_Legacy_Normal_Pass() { String legacyScript = LINE_JOINER.join( "goog.provide('legacy.script.A');", "/** @constructor */ legacy.script.A = function () {}"); String legacyScript2 = LINE_JOINER.join( "goog.provide('legacy.script.B');", "/** @constructor */ legacy.script.B = function () {}", "B.prototype.createA = function() {", " var A = goog.module.get('legacy.script.A');", " new A;", "}"); test(new String[] {legacyScript, legacyScript2}, null, null, null, null); } public void test_Legacy_ModuleGet_Legacy_Missing_Fail() { String legacyScript = LINE_JOINER.join( "goog.provide('legacy.script.B');", "/** @constructor */ legacy.script.B = function () {}", "B.prototype.createA = function() {", " var A = goog.module.get('legacy.script.A');", " new A;", "}"); String warning = "Required namespace \"legacy.script.A\" never defined."; test(legacyScript, null, MISSING_MODULE_OR_PROVIDE, null, warning); } }