123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- // Copyright 2009 The Closure Library Authors. 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.
- /**
- * @fileoverview Tests for goog.module.ModuleLoader.
- * @author nicksantos@google.com (Nick Santos)
- */
- goog.provide('goog.module.ModuleLoaderTest');
- goog.require('goog.Promise');
- goog.require('goog.array');
- goog.require('goog.dom');
- goog.require('goog.dom.TagName');
- goog.require('goog.events');
- goog.require('goog.functions');
- goog.require('goog.module.ModuleLoader');
- goog.require('goog.module.ModuleManager');
- goog.require('goog.net.BulkLoader');
- goog.require('goog.net.XmlHttp');
- goog.require('goog.object');
- goog.require('goog.testing.PropertyReplacer');
- goog.require('goog.testing.TestCase');
- goog.require('goog.testing.events.EventObserver');
- goog.require('goog.testing.jsunit');
- goog.require('goog.userAgent');
- goog.setTestOnly('goog.module.ModuleLoaderTest');
- var modA1Loaded = false;
- var modA2Loaded = false;
- var modB1Loaded = false;
- var moduleLoader = null;
- var moduleManager = null;
- var stubs = new goog.testing.PropertyReplacer();
- var EventType = goog.module.ModuleLoader.EventType;
- var observer;
- function setUpPage() {
- goog.testing.TestCase.getActiveTestCase().promiseTimeout = 10000; // 10s
- }
- function setUp() {
- modA1Loaded = false;
- modA2Loaded = false;
- modB1Loaded = false;
- goog.provide = goog.nullFunction;
- moduleManager = goog.module.ModuleManager.getInstance();
- stubs.replace(moduleManager, 'getBackOff_', goog.functions.constant(0));
- moduleLoader = new goog.module.ModuleLoader();
- observer = new goog.testing.events.EventObserver();
- goog.events.listen(moduleLoader, goog.object.getValues(EventType), observer);
- moduleManager.setLoader(moduleLoader);
- moduleManager.setAllModuleInfo({'modA': [], 'modB': ['modA']});
- moduleManager.setModuleUris({
- 'modA': ['testdata/modA_1.js', 'testdata/modA_2.js'],
- 'modB': ['testdata/modB_1.js']
- });
- assertNotLoaded('modA');
- assertNotLoaded('modB');
- assertFalse(modA1Loaded);
- }
- function tearDown() {
- stubs.reset();
- goog.dispose(moduleLoader);
- // Ensure that the module manager was created.
- assertNotNull(goog.module.ModuleManager.getInstance());
- moduleManager = goog.module.ModuleManager.instance_ = null;
- // tear down the module loaded flag.
- modA1Loaded = false;
- // Remove all the fake scripts.
- var scripts =
- goog.array.clone(goog.dom.getElementsByTagName(goog.dom.TagName.SCRIPT));
- for (var i = 0; i < scripts.length; i++) {
- if (scripts[i].src.indexOf('testdata') != -1) {
- goog.dom.removeNode(scripts[i]);
- }
- }
- }
- function testLoadModuleA() {
- return new goog
- .Promise(function(resolve, reject) {
- moduleManager.execOnLoad('modA', function() {
- assertLoaded('modA');
- assertNotLoaded('modB');
- assertTrue(modA1Loaded);
- // The code is not evaluated immediately, but only after a browser
- // yield.
- assertEquals(
- 'EVALUATE_CODE', 0,
- observer.getEvents(EventType.EVALUATE_CODE).length);
- assertEquals(
- 'REQUEST_SUCCESS', 1,
- observer.getEvents(EventType.REQUEST_SUCCESS).length);
- assertArrayEquals(
- ['modA'],
- observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
- assertEquals(
- 'REQUEST_ERROR', 0,
- observer.getEvents(EventType.REQUEST_ERROR).length);
- resolve();
- });
- })
- .then(function() {
- assertEquals(
- 'EVALUATE_CODE after tick', 1,
- observer.getEvents(EventType.EVALUATE_CODE).length);
- assertEquals(
- 'REQUEST_SUCCESS after tick', 1,
- observer.getEvents(EventType.REQUEST_SUCCESS).length);
- assertEquals(
- 'REQUEST_ERROR after tick', 0,
- observer.getEvents(EventType.REQUEST_ERROR).length);
- });
- }
- function testLoadModuleB() {
- return new goog
- .Promise(function(resolve, reject) {
- moduleManager.execOnLoad('modB', resolve);
- })
- .then(function() {
- assertLoaded('modA');
- assertLoaded('modB');
- assertTrue(modA1Loaded);
- });
- }
- function testLoadDebugModuleA() {
- moduleLoader.setDebugMode(true);
- return new goog
- .Promise(function(resolve, reject) {
- moduleManager.execOnLoad('modA', resolve);
- })
- .then(function() {
- assertLoaded('modA');
- assertNotLoaded('modB');
- assertTrue(modA1Loaded);
- });
- }
- function testLoadDebugModuleB() {
- moduleLoader.setDebugMode(true);
- return new goog
- .Promise(function(resolve, reject) {
- moduleManager.execOnLoad('modB', resolve);
- })
- .then(function() {
- assertLoaded('modA');
- assertLoaded('modB');
- assertTrue(modA1Loaded);
- });
- }
- function testLoadDebugModuleAThenB() {
- // Swap the script tags of module A, to introduce a race condition.
- // See the comments on this in ModuleLoader's debug loader.
- moduleManager.setModuleUris({
- 'modA': ['testdata/modA_2.js', 'testdata/modA_1.js'],
- 'modB': ['testdata/modB_1.js']
- });
- moduleLoader.setDebugMode(true);
- return new goog
- .Promise(function(resolve, reject) {
- moduleManager.execOnLoad('modB', resolve);
- })
- .then(function() {
- assertLoaded('modA');
- assertLoaded('modB');
- var scripts = goog.array.clone(
- goog.dom.getElementsByTagName(goog.dom.TagName.SCRIPT));
- var seenLastScriptOfModuleA = false;
- for (var i = 0; i < scripts.length; i++) {
- var uri = scripts[i].src;
- if (uri.indexOf('modA_1.js') >= 0) {
- seenLastScriptOfModuleA = true;
- } else if (uri.indexOf('modB') >= 0) {
- assertTrue(seenLastScriptOfModuleA);
- }
- }
- });
- }
- function testSourceInjection() {
- moduleLoader.setSourceUrlInjection(true);
- return assertSourceInjection();
- }
- function testSourceInjectionViaDebugMode() {
- moduleLoader.setDebugMode(true);
- return assertSourceInjection();
- }
- function assertSourceInjection() {
- return new goog
- .Promise(function(resolve, reject) {
- moduleManager.execOnLoad('modB', resolve);
- })
- .then(function() {
- assertTrue(!!throwErrorInModuleB);
- var ex = assertThrows(function() { throwErrorInModuleB(); });
- if (!ex.stack) {
- return;
- }
- var stackTrace = ex.stack.toString();
- var expectedString = 'testdata/modB_1.js';
- if (goog.module.ModuleLoader.supportsSourceUrlStackTraces()) {
- // Source URL should be added in eval or in jsloader.
- assertContains(expectedString, stackTrace);
- } else if (moduleLoader.getDebugMode()) {
- // Browsers used jsloader, thus URLs are present.
- assertContains(expectedString, stackTrace);
- } else {
- // Browser used eval, does not support source URL.
- assertNotContains(expectedString, stackTrace);
- }
- });
- }
- function testModuleLoaderRecursesTooDeep(opt_numModules) {
- // There was a bug in the module loader where it would retry recursively
- // whenever there was a synchronous failure in the module load. When you
- // asked for modB, it would try to load its dependency modA. When modA
- // failed, it would move onto modB, and then start over, repeating until it
- // ran out of stack.
- var numModules = opt_numModules || 1;
- var uris = {};
- var deps = {};
- var mods = [];
- for (var num = 0; num < numModules; num++) {
- var modName = 'mod' + num;
- mods.unshift(modName);
- uris[modName] = [];
- deps[modName] = num ? ['mod' + (num - 1)] : [];
- for (var i = 0; i < 5; i++) {
- uris[modName].push(
- 'http://www.google.com/crossdomain' + num + 'x' + i + '.js');
- }
- }
- moduleManager.setAllModuleInfo(deps);
- moduleManager.setModuleUris(uris);
- // Make all XHRs throw an error, so that we test the error-handling
- // functionality.
- var oldXmlHttp = goog.net.XmlHttp;
- stubs.set(goog.net, 'XmlHttp', function() {
- return {open: goog.functions.error('mock error'), abort: goog.nullFunction};
- });
- goog.object.extend(goog.net.XmlHttp, oldXmlHttp);
- var errorCount = 0;
- var errorIds = [];
- var errorHandler = function(ignored, modId) {
- errorCount++;
- errorIds.push(modId);
- };
- moduleManager.registerCallback(
- goog.module.ModuleManager.CallbackType.ERROR, errorHandler);
- moduleManager.execOnLoad(
- mods[0], function() { fail('modB should not load successfully'); });
- assertEquals(mods.length, errorCount);
- goog.array.sort(mods);
- goog.array.sort(errorIds);
- assertArrayEquals(mods, errorIds);
- assertArrayEquals([], moduleManager.requestedModuleIdsQueue_);
- assertArrayEquals([], moduleManager.userInitiatedLoadingModuleIds_);
- }
- function testModuleLoaderRecursesTooDeep2modules() {
- testModuleLoaderRecursesTooDeep(2);
- }
- function testModuleLoaderRecursesTooDeep3modules() {
- testModuleLoaderRecursesTooDeep(3);
- }
- function testModuleLoaderRecursesTooDeep4modules() {
- testModuleLoaderRecursesTooDeep(3);
- }
- function testErrback() {
- // Don't run this test on IE, because the way the test runner catches
- // errors on IE plays badly with the simulated errors in the test.
- if (goog.userAgent.IE) return;
- // Modules will throw an exception if this boolean is set to true.
- modA1Loaded = true;
- return new goog.Promise(function(resolve, reject) {
- var errorHandler = function() {
- assertNotLoaded('modA');
- resolve();
- };
- moduleManager.registerCallback(
- goog.module.ModuleManager.CallbackType.ERROR, errorHandler);
- moduleManager.execOnLoad(
- 'modA', function() { fail('modA should not load successfully'); });
- });
- }
- function testEventError() {
- // Don't run this test on older IE, because the way the test runner catches
- // errors on IE plays badly with the simulated errors in the test.
- if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(11)) {
- return;
- }
- // Modules will throw an exception if this boolean is set to true.
- modA1Loaded = true;
- return new goog
- .Promise(function(resolve, reject) {
- var errorHandler = function() {
- assertNotLoaded('modA');
- resolve();
- };
- moduleManager.registerCallback(
- goog.module.ModuleManager.CallbackType.ERROR, errorHandler);
- moduleManager.execOnLoad(
- 'modA', function() { fail('modA should not load successfully'); });
- })
- .then(function() {
- assertEquals(
- 'EVALUATE_CODE', 3,
- observer.getEvents(EventType.EVALUATE_CODE).length);
- assertUndefined(observer.getEvents(EventType.EVALUATE_CODE)[0].error);
- assertEquals(
- 'REQUEST_SUCCESS', 3,
- observer.getEvents(EventType.REQUEST_SUCCESS).length);
- assertUndefined(observer.getEvents(EventType.REQUEST_SUCCESS)[0].error);
- var requestErrors = observer.getEvents(EventType.REQUEST_ERROR);
- assertEquals('REQUEST_ERROR', 3, requestErrors.length);
- assertNotNull(requestErrors[0].error);
- var expectedString = 'loaded twice';
- var messageAndStack =
- requestErrors[0].error.message + requestErrors[0].error.stack;
- assertContains(expectedString, messageAndStack);
- });
- }
- function testPrefetchThenLoadModuleA() {
- moduleManager.prefetchModule('modA');
- stubs.set(goog.net.BulkLoader.prototype, 'load', function() {
- fail('modA should not be reloaded');
- });
- return new goog
- .Promise(function(resolve, reject) {
- moduleManager.execOnLoad('modA', resolve);
- })
- .then(function() {
- assertLoaded('modA');
- assertEquals(
- 'REQUEST_SUCCESS', 1,
- observer.getEvents(EventType.REQUEST_SUCCESS).length);
- assertArrayEquals(
- ['modA'],
- observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
- assertEquals(
- 'REQUEST_ERROR', 0,
- observer.getEvents(EventType.REQUEST_ERROR).length);
- });
- }
- function testPrefetchThenLoadModuleB() {
- moduleManager.prefetchModule('modB');
- stubs.set(goog.net.BulkLoader.prototype, 'load', function() {
- fail('modA and modB should not be reloaded');
- });
- return new goog
- .Promise(function(resolve, reject) {
- moduleManager.execOnLoad('modB', resolve);
- })
- .then(function() {
- assertLoaded('modA');
- assertLoaded('modB');
- assertEquals(
- 'REQUEST_SUCCESS', 2,
- observer.getEvents(EventType.REQUEST_SUCCESS).length);
- assertArrayEquals(
- ['modA'],
- observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
- assertArrayEquals(
- ['modB'],
- observer.getEvents(EventType.REQUEST_SUCCESS)[1].moduleIds);
- assertEquals(
- 'REQUEST_ERROR', 0,
- observer.getEvents(EventType.REQUEST_ERROR).length);
- });
- }
- function testPrefetchModuleAThenLoadModuleB() {
- moduleManager.prefetchModule('modA');
- return new goog
- .Promise(function(resolve, reject) {
- moduleManager.execOnLoad('modB', resolve);
- })
- .then(function() {
- assertLoaded('modA');
- assertLoaded('modB');
- assertEquals(
- 'REQUEST_SUCCESS', 2,
- observer.getEvents(EventType.REQUEST_SUCCESS).length);
- assertArrayEquals(
- ['modA'],
- observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
- assertArrayEquals(
- ['modB'],
- observer.getEvents(EventType.REQUEST_SUCCESS)[1].moduleIds);
- assertEquals(
- 'REQUEST_ERROR', 0,
- observer.getEvents(EventType.REQUEST_ERROR).length);
- });
- }
- function testLoadModuleBThenPrefetchModuleA() {
- return new goog
- .Promise(function(resolve, reject) {
- moduleManager.execOnLoad('modB', resolve);
- })
- .then(function() {
- assertLoaded('modA');
- assertLoaded('modB');
- assertEquals(
- 'REQUEST_SUCCESS', 2,
- observer.getEvents(EventType.REQUEST_SUCCESS).length);
- assertArrayEquals(
- ['modA'],
- observer.getEvents(EventType.REQUEST_SUCCESS)[0].moduleIds);
- assertArrayEquals(
- ['modB'],
- observer.getEvents(EventType.REQUEST_SUCCESS)[1].moduleIds);
- assertEquals(
- 'REQUEST_ERROR', 0,
- observer.getEvents(EventType.REQUEST_ERROR).length);
- assertThrows('Module load already requested: modB', function() {
- moduleManager.prefetchModule('modA');
- });
- });
- }
- function testPrefetchModuleWithBatchModeEnabled() {
- moduleManager.setBatchModeEnabled(true);
- assertThrows(
- 'Modules prefetching is not supported in batch mode',
- function() { moduleManager.prefetchModule('modA'); });
- }
- function testLoadErrorCallbackExecutedWhenPrefetchFails() {
- // Make all XHRs throw an error, so that we test the error-handling
- // functionality.
- var oldXmlHttp = goog.net.XmlHttp;
- stubs.set(goog.net, 'XmlHttp', function() {
- return {open: goog.functions.error('mock error'), abort: goog.nullFunction};
- });
- goog.object.extend(goog.net.XmlHttp, oldXmlHttp);
- var errorCount = 0;
- var errorHandler = function() { errorCount++; };
- moduleManager.registerCallback(
- goog.module.ModuleManager.CallbackType.ERROR, errorHandler);
- moduleLoader.prefetchModule('modA', moduleManager.moduleInfoMap_['modA']);
- moduleLoader.loadModules(['modA'], moduleManager.moduleInfoMap_, function() {
- fail('modA should not load successfully');
- }, errorHandler);
- assertEquals(1, errorCount);
- }
- function assertLoaded(id) {
- assertTrue(moduleManager.getModuleInfo(id).isLoaded());
- }
- function assertNotLoaded(id) {
- assertFalse(moduleManager.getModuleInfo(id).isLoaded());
- }
|