UNPKG

accessibility-developer-tools

Version:

This is a library of accessibility-related testing and utility code.

1,475 lines (1,229 loc) 64.2 kB
// Copyright 2008 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. goog.provide('goog.module.ModuleManagerTest'); goog.setTestOnly('goog.module.ModuleManagerTest'); goog.require('goog.array'); goog.require('goog.functions'); goog.require('goog.module.BaseModule'); goog.require('goog.module.ModuleManager'); goog.require('goog.testing'); goog.require('goog.testing.MockClock'); goog.require('goog.testing.jsunit'); goog.require('goog.testing.recordFunction'); var clock; var requestCount = 0; function tearDown() { clock.dispose(); } function setUp() { clock = new goog.testing.MockClock(true); requestCount = 0; } function getModuleManager(infoMap) { var mm = new goog.module.ModuleManager(); mm.setAllModuleInfo(infoMap); mm.isModuleLoaded = function(id) { return this.getModuleInfo(id).isLoaded(); }; return mm; } function createSuccessfulBatchLoader(moduleMgr) { return { loadModules: function( ids, moduleInfoMap, opt_successFn, opt_errFn, opt_timeoutFn) { requestCount++; setTimeout(goog.bind(this.onLoad, this, ids.concat(), 0), 5); }, onLoad: function(ids, idxLoaded) { moduleMgr.beforeLoadModuleCode(ids[idxLoaded]); moduleMgr.setLoaded(ids[idxLoaded]); moduleMgr.afterLoadModuleCode(ids[idxLoaded]); var idx = idxLoaded + 1; if (idx < ids.length) { setTimeout(goog.bind(this.onLoad, this, ids, idx), 2); } } }; } function createSuccessfulNonBatchLoader(moduleMgr) { return { loadModules: function( ids, moduleInfoMap, opt_successFn, opt_errFn, opt_timeoutFn) { requestCount++; setTimeout(function() { moduleMgr.beforeLoadModuleCode(ids[0]); moduleMgr.setLoaded(ids[0]); moduleMgr.afterLoadModuleCode(ids[0]); if (opt_successFn) { opt_successFn(); } }, 5); } }; } function createUnsuccessfulLoader(moduleMgr, status) { return { loadModules: function( ids, moduleInfoMap, opt_successFn, opt_errFn, opt_timeoutFn) { moduleMgr.beforeLoadModuleCode(ids[0]); setTimeout(function() { opt_errFn(status); }, 5); } }; } function createUnsuccessfulBatchLoader(moduleMgr, status) { return { loadModules: function( ids, moduleInfoMap, opt_successFn, opt_errFn, opt_timeoutFn) { setTimeout(function() { opt_errFn(status); }, 5); } }; } function createTimeoutLoader(moduleMgr, status) { return { loadModules: function( ids, moduleInfoMap, opt_successFn, opt_errFn, opt_timeoutFn) { setTimeout(function() { opt_timeoutFn(status); }, 5); } }; } /** * Tests loading a module under different conditions i.e. unloaded * module, already loaded module, module loaded through user initiated * actions, synchronous callback for a module that has been already * loaded. Test both batch and non-batch loaders. */ function testExecOnLoad() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); execOnLoad_(mm); mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setLoader(createSuccessfulBatchLoader(mm)); mm.setBatchModeEnabled(true); execOnLoad_(mm); } /** * Tests execOnLoad with the specified module manager. * @param {goog.module.ModuleManager} mm The module manager. */ function execOnLoad_(mm) { // When module is unloaded, execOnLoad is async. var execCalled1 = false; mm.execOnLoad('a', function() { execCalled1 = true; }); assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a')); assertTrue('module "a" should be loading', mm.isModuleLoading('a')); assertFalse('execCalled1 should not be set yet', execCalled1); assertTrue('ModuleManager should be active', mm.isActive()); assertFalse('ModuleManager should not be user active', mm.isUserActive()); clock.tick(5); assertTrue('module "a" should be loaded', mm.isModuleLoaded('a')); assertFalse('module "a" should not be loading', mm.isModuleLoading('a')); assertTrue('execCalled1 should be set', execCalled1); assertFalse('ModuleManager should not be active', mm.isActive()); assertFalse('ModuleManager should not be user active', mm.isUserActive()); // When module is already loaded, execOnLoad is still async unless // specified otherwise. var execCalled2 = false; mm.execOnLoad('a', function() { execCalled2 = true; }); assertTrue('module "a" should be loaded', mm.isModuleLoaded('a')); assertFalse('module "a" should not be loading', mm.isModuleLoading('a')); assertFalse('execCalled2 should not be set yet', execCalled2); clock.tick(5); assertTrue('execCalled2 should be set', execCalled2); // When module is unloaded, execOnLoad is async (user active). var execCalled5 = false; mm.execOnLoad('c', function() { execCalled5 = true; }, null, null, true); assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c')); assertTrue('module "c" should be loading', mm.isModuleLoading('c')); assertFalse('execCalled1 should not be set yet', execCalled5); assertTrue('ModuleManager should be active', mm.isActive()); assertTrue('ModuleManager should be user active', mm.isUserActive()); clock.tick(5); assertTrue('module "c" should be loaded', mm.isModuleLoaded('c')); assertFalse('module "c" should not be loading', mm.isModuleLoading('c')); assertTrue('execCalled1 should be set', execCalled5); assertFalse('ModuleManager should not be active', mm.isActive()); assertFalse('ModuleManager should not be user active', mm.isUserActive()); // When module is already loaded, execOnLoad is still synchronous when // so specified var execCalled6 = false; mm.execOnLoad('c', function() { execCalled6 = true; }, undefined, undefined, undefined, true); assertTrue('module "c" should be loaded', mm.isModuleLoaded('c')); assertFalse('module "c" should not be loading', mm.isModuleLoading('c')); assertTrue('execCalled6 should be set', execCalled6); clock.tick(5); assertTrue('execCalled6 should still be set', execCalled6); } /** * Test aborting the callback called on module load. */ function testExecOnLoadAbort() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); // When module is unloaded and abort is called, module still gets // loaded, but callback is cancelled. var execCalled1 = false; var callback1 = mm.execOnLoad('b', function() { execCalled1 = true; }); callback1.abort(); clock.tick(5); assertTrue('module "b" should be loaded', mm.isModuleLoaded('b')); assertFalse('execCalled3 should not be set', execCalled1); // When module is already loaded, execOnLoad is still async, so calling // abort should still cancel the callback. var execCalled2 = false; var callback2 = mm.execOnLoad('a', function() { execCalled2 = true; }); callback2.abort(); clock.tick(5); assertFalse('execCalled2 should not be set', execCalled2); } /** * Test preloading modules and ensure that the before load, after load * and set load called are called only once per module. */ function testExecOnLoadWhilePreloadingAndViceVersa() { var mm = getModuleManager({'c': [], 'd': []}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); execOnLoadWhilePreloadingAndViceVersa_(mm); mm = getModuleManager({'c': [], 'd': []}); mm.setLoader(createSuccessfulBatchLoader(mm)); mm.setBatchModeEnabled(true); execOnLoadWhilePreloadingAndViceVersa_(mm); } /** * Perform tests with the specified module manager. * @param {goog.module.ModuleManager} mm The module manager. */ function execOnLoadWhilePreloadingAndViceVersa_(mm) { var mm = getModuleManager({'c': [], 'd': []}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); var origSetLoaded = mm.setLoaded; var calls = [0, 0, 0]; mm.beforeLoadModuleCode = function(id) { calls[0]++; }; mm.setLoaded = function(id) { calls[1]++; origSetLoaded.call(mm, id); }; mm.afterLoadModuleCode = function(id) { calls[2]++; }; mm.preloadModule('c', 2); assertFalse('module "c" should not be loading yet', mm.isModuleLoading('c')); clock.tick(2); assertTrue('module "c" should now be loading', mm.isModuleLoading('c')); mm.execOnLoad('c', function() {}); assertTrue('module "c" should still be loading', mm.isModuleLoading('c')); clock.tick(5); assertFalse('module "c" should be done loading', mm.isModuleLoading('c')); assertEquals('beforeLoad should only be called once for "c"', 1, calls[0]); assertEquals('setLoaded should only be called once for "c"', 1, calls[1]); assertEquals('afterLoad should only be called once for "c"', 1, calls[2]); mm.execOnLoad('d', function() {}); assertTrue('module "d" should now be loading', mm.isModuleLoading('d')); mm.preloadModule('d', 2); clock.tick(5); assertFalse('module "d" should be done loading', mm.isModuleLoading('d')); assertTrue('module "d" should now be loaded', mm.isModuleLoaded('d')); assertEquals('beforeLoad should only be called once for "d"', 2, calls[0]); assertEquals('setLoaded should only be called once for "d"', 2, calls[1]); assertEquals('afterLoad should only be called once for "d"', 2, calls[2]); } /** * Tests that multiple callbacks on the same module don't cause * confusion about the active state after the module is finally loaded. */ function testUserInitiatedExecOnLoadEventuallyLeavesManagerIdle() { var mm = getModuleManager({'c': [], 'd': []}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); var calledBack1 = false; var calledBack2 = false; mm.execOnLoad( 'c', function() { calledBack1 = true; }, undefined, undefined, true); mm.execOnLoad( 'c', function() { calledBack2 = true; }, undefined, undefined, true); mm.load('c'); assertTrue( 'Manager should be active while waiting for load', mm.isUserActive()); clock.tick(5); assertTrue('First callback should be called', calledBack1); assertTrue('Second callback should be called', calledBack2); assertFalse( 'Manager should be inactive after loading is complete', mm.isUserActive()); } /** * Tests loading a module by requesting a Deferred object. */ function testLoad() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); var calledBack = false; var error = null; var d = mm.load('a'); d.addCallback(function(ctx) { calledBack = true; }); d.addErrback(function(err) { error = err; }); assertFalse(calledBack); assertNull(error); assertFalse(mm.isUserActive()); clock.tick(5); assertTrue(calledBack); assertNull(error); } /** * Tests loading 2 modules asserting that the loads happen in parallel * in one unit of time. */ function testLoad_concurrent() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setConcurrentLoadingEnabled(true); mm.setLoader(createSuccessfulNonBatchLoader(mm)); var calledBack = false; var error = null; mm.load('a'); mm.load('b'); assertEquals(2, requestCount); // Only time for one serialized download. clock.tick(5); assertTrue(mm.getModuleInfo('a').isLoaded()); assertTrue(mm.getModuleInfo('b').isLoaded()); } function testLoad_concurrentSecondIsDepOfFist() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setBatchModeEnabled(true); mm.setConcurrentLoadingEnabled(true); mm.setLoader(createSuccessfulBatchLoader(mm)); var calledBack = false; var error = null; mm.loadMultiple(['a', 'b']); mm.load('b'); assertEquals('No 2nd request expected', 1, requestCount); // Only time for one serialized download. clock.tick(5); clock.tick(2); // Makes second module come in from batch requst. assertTrue(mm.getModuleInfo('a').isLoaded()); assertTrue(mm.getModuleInfo('b').isLoaded()); } function testLoad_nonConcurrent() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); var calledBack = false; var error = null; mm.load('a'); mm.load('b'); assertEquals(1, requestCount); // Only time for one serialized download. clock.tick(5); assertTrue(mm.getModuleInfo('a').isLoaded()); assertFalse(mm.getModuleInfo('b').isLoaded()); } function testLoadUnknown() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); var e = assertThrows(function() { mm.load('DoesNotExist'); }); assertEquals('Unknown module: DoesNotExist', e.message); } /** * Tests loading multiple modules by requesting a Deferred object. */ function testLoadMultiple() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setBatchModeEnabled(true); mm.setLoader(createSuccessfulBatchLoader(mm)); var calledBack = false; var error = null; var calledBack2 = false; var error2 = null; var dMap = mm.loadMultiple(['a', 'b']); dMap['a'].addCallback(function(ctx) { calledBack = true; }); dMap['a'].addErrback(function(err) { error = err; }); dMap['b'].addCallback(function(ctx) { calledBack2 = true; }); dMap['b'].addErrback(function(err) { error2 = err; }); assertFalse(calledBack); assertFalse(calledBack2); clock.tick(5); assertTrue(calledBack); assertFalse(calledBack2); assertTrue('module "a" should be loaded', mm.isModuleLoaded('a')); assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b')); assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c')); clock.tick(2); assertTrue(calledBack); assertTrue(calledBack2); assertTrue('module "a" should be loaded', mm.isModuleLoaded('a')); assertTrue('module "b" should be loaded', mm.isModuleLoaded('b')); assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c')); assertNull(error); assertNull(error2); } /** * Tests loading multiple modules with deps by requesting a Deferred object. */ function testLoadMultipleWithDeps() { var mm = getModuleManager({'a': [], 'b': ['c'], 'c': []}); mm.setBatchModeEnabled(true); mm.setLoader(createSuccessfulBatchLoader(mm)); var calledBack = false; var error = null; var calledBack2 = false; var error2 = null; var dMap = mm.loadMultiple(['a', 'b']); dMap['a'].addCallback(function(ctx) { calledBack = true; }); dMap['a'].addErrback(function(err) { error = err; }); dMap['b'].addCallback(function(ctx) { calledBack2 = true; }); dMap['b'].addErrback(function(err) { error2 = err; }); assertFalse(calledBack); assertFalse(calledBack2); clock.tick(5); assertTrue(calledBack); assertFalse(calledBack2); assertTrue('module "a" should be loaded', mm.isModuleLoaded('a')); assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b')); assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c')); clock.tick(2); assertFalse(calledBack2); assertTrue('module "a" should be loaded', mm.isModuleLoaded('a')); assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b')); assertTrue('module "c" should be loaded', mm.isModuleLoaded('c')); clock.tick(2); assertTrue(calledBack2); assertTrue('module "a" should be loaded', mm.isModuleLoaded('a')); assertTrue('module "b" should be loaded', mm.isModuleLoaded('b')); assertTrue('module "c" should be loaded', mm.isModuleLoaded('c')); assertNull(error); assertNull(error2); } /** * Tests loading multiple modules by requesting a Deferred object when * a server error occurs. */ function testLoadMultipleWithErrors() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setBatchModeEnabled(true); mm.setLoader(createUnsuccessfulLoader(mm, 500)); var calledBack = false; var error = null; var calledBack2 = false; var error2 = null; var calledBack3 = false; var error3 = null; var dMap = mm.loadMultiple(['a', 'b', 'c']); dMap['a'].addCallback(function(ctx) { calledBack = true; }); dMap['a'].addErrback(function(err) { error = err; }); dMap['b'].addCallback(function(ctx) { calledBack2 = true; }); dMap['b'].addErrback(function(err) { error2 = err; }); dMap['c'].addCallback(function(ctx) { calledBack3 = true; }); dMap['c'].addErrback(function(err) { error3 = err; }); assertFalse(calledBack); assertFalse(calledBack2); assertFalse(calledBack3); clock.tick(4); // A module request is now underway using the unsuccessful loader. // We substitute a successful loader for future module load requests. mm.setLoader(createSuccessfulBatchLoader(mm)); clock.tick(1); assertFalse(calledBack); assertFalse(calledBack2); assertFalse(calledBack3); assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a')); assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b')); assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c')); // Retry should happen after a backoff clock.tick(5 + mm.getBackOff_()); assertTrue(calledBack); assertFalse(calledBack2); assertFalse(calledBack3); assertTrue('module "a" should be loaded', mm.isModuleLoaded('a')); assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b')); assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c')); clock.tick(2); assertTrue(calledBack2); assertFalse(calledBack3); assertTrue('module "b" should be loaded', mm.isModuleLoaded('b')); assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c')); clock.tick(2); assertTrue(calledBack3); assertTrue('module "c" should be loaded', mm.isModuleLoaded('c')); assertNull(error); assertNull(error2); assertNull(error3); } /** * Tests loading multiple modules by requesting a Deferred object when * consecutive server error occur and the loader falls back to serial * loads. */ function testLoadMultipleWithErrorsFallbackOnSerial() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setBatchModeEnabled(true); mm.setLoader(createUnsuccessfulLoader(mm, 500)); var calledBack = false; var error = null; var calledBack2 = false; var error2 = null; var calledBack3 = false; var error3 = null; var dMap = mm.loadMultiple(['a', 'b', 'c']); dMap['a'].addCallback(function(ctx) { calledBack = true; }); dMap['a'].addErrback(function(err) { error = err; }); dMap['b'].addCallback(function(ctx) { calledBack2 = true; }); dMap['b'].addErrback(function(err) { error2 = err; }); dMap['c'].addCallback(function(ctx) { calledBack3 = true; }); dMap['c'].addErrback(function(err) { error3 = err; }); assertFalse(calledBack); assertFalse(calledBack2); assertFalse(calledBack3); clock.tick(5); assertFalse(calledBack); assertFalse(calledBack2); assertFalse(calledBack3); assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a')); assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b')); assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c')); // Retry should happen and fail after a backoff clock.tick(5 + mm.getBackOff_()); assertFalse(calledBack); assertFalse(calledBack2); assertFalse(calledBack3); assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a')); assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b')); assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c')); // A second retry should happen after a backoff clock.tick(4 + mm.getBackOff_()); // The second retry is now underway using the unsuccessful loader. // We substitute a successful loader for future module load requests. mm.setLoader(createSuccessfulBatchLoader(mm)); clock.tick(1); // A second retry should fail now assertFalse(calledBack); assertFalse(calledBack2); assertFalse(calledBack3); assertFalse('module "a" should not be loaded', mm.isModuleLoaded('a')); assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b')); assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c')); // Each module should be loaded individually now, each taking 5 ticks clock.tick(5); assertTrue(calledBack); assertFalse(calledBack2); assertFalse(calledBack3); assertTrue('module "a" should be loaded', mm.isModuleLoaded('a')); assertFalse('module "b" should not be loaded', mm.isModuleLoaded('b')); assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c')); clock.tick(5); assertTrue(calledBack2); assertFalse(calledBack3); assertTrue('module "b" should be loaded', mm.isModuleLoaded('b')); assertFalse('module "c" should not be loaded', mm.isModuleLoaded('c')); clock.tick(5); assertTrue(calledBack3); assertTrue('module "c" should be loaded', mm.isModuleLoaded('c')); assertNull(error); assertNull(error2); assertNull(error3); } /** * Tests loading a module by user action by requesting a Deferred object. */ function testLoadForUser() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); var calledBack = false; var error = null; var d = mm.load('a', true); d.addCallback(function(ctx) { calledBack = true; }); d.addErrback(function(err) { error = err; }); assertFalse(calledBack); assertNull(error); assertTrue(mm.isUserActive()); clock.tick(5); assertTrue(calledBack); assertNull(error); } /** * Tests that preloading a module calls back the deferred object. */ function testPreloadDeferredWhenNotLoaded() { var mm = getModuleManager({'a': []}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); var calledBack = false; var d = mm.preloadModule('a'); d.addCallback(function(ctx) { calledBack = true; }); // First load should take five ticks. assertFalse('module "a" should not be loaded yet', calledBack); clock.tick(5); assertTrue('module "a" should be loaded', calledBack); } /** * Tests preloading an already loaded module. */ function testPreloadDeferredWhenLoaded() { var mm = getModuleManager({'a': []}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); var calledBack = false; mm.preloadModule('a'); clock.tick(5); var d = mm.preloadModule('a'); d.addCallback(function(ctx) { calledBack = true; }); // Module is already loaded, should be called back after the setTimeout // in preloadModule. assertFalse('deferred for module "a" should not be called yet', calledBack); clock.tick(1); assertTrue('module "a" should be loaded', calledBack); } /** * Tests preloading a module that is currently loading. */ function testPreloadDeferredWhenLoading() { var mm = getModuleManager({'a': []}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); mm.preloadModule('a'); clock.tick(1); // 'b' is in the middle of loading, should get called back when it's done. var calledBack = false; var d = mm.preloadModule('a'); d.addCallback(function(ctx) { calledBack = true; }); assertFalse('module "a" should not be loaded yet', calledBack); clock.tick(4); assertTrue('module "a" should be loaded', calledBack); } /** * Tests that load doesn't trigger another load if a module is already * preloading. */ function testLoadWhenPreloading() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); var origSetLoaded = mm.setLoaded; var calls = [0, 0, 0]; mm.beforeLoadModuleCode = function(id) { calls[0]++; }; mm.setLoaded = function(id) { calls[1]++; origSetLoaded.call(mm, id); }; mm.afterLoadModuleCode = function(id) { calls[2]++; }; var calledBack = false; var error = null; mm.preloadModule('c', 2); assertFalse('module "c" should not be loading yet', mm.isModuleLoading('c')); clock.tick(2); assertTrue('module "c" should now be loading', mm.isModuleLoading('c')); var d = mm.load('c'); d.addCallback(function(ctx) { calledBack = true; }); d.addErrback(function(err) { error = err; }); assertTrue('module "c" should still be loading', mm.isModuleLoading('c')); clock.tick(5); assertFalse('module "c" should be done loading', mm.isModuleLoading('c')); assertEquals('beforeLoad should only be called once for "c"', 1, calls[0]); assertEquals('setLoaded should only be called once for "c"', 1, calls[1]); assertEquals('afterLoad should only be called once for "c"', 1, calls[2]); assertTrue(calledBack); assertNull(error); } /** * Tests that load doesn't trigger another load if a module is already * preloading. */ function testLoadMultipleWhenPreloading() { var mm = getModuleManager({'a': [], 'b': ['d'], 'c': [], 'd': []}); mm.setLoader(createSuccessfulBatchLoader(mm)); mm.setBatchModeEnabled(true); var origSetLoaded = mm.setLoaded; var calls = {'a': [0, 0, 0], 'b': [0, 0, 0], 'c': [0, 0, 0], 'd': [0, 0, 0]}; mm.beforeLoadModuleCode = function(id) { calls[id][0]++; }; mm.setLoaded = function(id) { calls[id][1]++; origSetLoaded.call(mm, id); }; mm.afterLoadModuleCode = function(id) { calls[id][2]++; }; var calledBack = false; var error = null; var calledBack2 = false; var error2 = null; var calledBack3 = false; var error3 = null; mm.preloadModule('c', 2); mm.preloadModule('d', 3); assertFalse('module "c" should not be loading yet', mm.isModuleLoading('c')); assertFalse('module "d" should not be loading yet', mm.isModuleLoading('d')); clock.tick(2); assertTrue('module "c" should now be loading', mm.isModuleLoading('c')); clock.tick(1); assertTrue('module "d" should now be loading', mm.isModuleLoading('d')); var dMap = mm.loadMultiple(['a', 'b', 'c']); dMap['a'].addCallback(function(ctx) { calledBack = true; }); dMap['a'].addErrback(function(err) { error = err; }); dMap['b'].addCallback(function(ctx) { calledBack2 = true; }); dMap['b'].addErrback(function(err) { error2 = err; }); dMap['c'].addCallback(function(ctx) { calledBack3 = true; }); dMap['c'].addErrback(function(err) { error3 = err; }); assertTrue('module "a" should be loading', mm.isModuleLoading('a')); assertTrue('module "b" should be loading', mm.isModuleLoading('b')); assertTrue('module "c" should still be loading', mm.isModuleLoading('c')); clock.tick(4); assertTrue(calledBack3); assertFalse('module "c" should be done loading', mm.isModuleLoading('c')); assertTrue('module "d" should still be loading', mm.isModuleLoading('d')); clock.tick(5); assertFalse('module "d" should be done loading', mm.isModuleLoading('d')); assertFalse(calledBack); assertFalse(calledBack2); assertTrue('module "a" should still be loading', mm.isModuleLoading('a')); assertTrue('module "b" should still be loading', mm.isModuleLoading('b')); clock.tick(7); assertTrue(calledBack); assertTrue(calledBack2); assertFalse('module "a" should be done loading', mm.isModuleLoading('a')); assertFalse('module "b" should be done loading', mm.isModuleLoading('b')); assertEquals( 'beforeLoad should only be called once for "a"', 1, calls['a'][0]); assertEquals( 'setLoaded should only be called once for "a"', 1, calls['a'][1]); assertEquals( 'afterLoad should only be called once for "a"', 1, calls['a'][2]); assertEquals( 'beforeLoad should only be called once for "b"', 1, calls['b'][0]); assertEquals( 'setLoaded should only be called once for "b"', 1, calls['b'][1]); assertEquals( 'afterLoad should only be called once for "b"', 1, calls['b'][2]); assertEquals( 'beforeLoad should only be called once for "c"', 1, calls['c'][0]); assertEquals( 'setLoaded should only be called once for "c"', 1, calls['c'][1]); assertEquals( 'afterLoad should only be called once for "c"', 1, calls['c'][2]); assertEquals( 'beforeLoad should only be called once for "d"', 1, calls['d'][0]); assertEquals( 'setLoaded should only be called once for "d"', 1, calls['d'][1]); assertEquals( 'afterLoad should only be called once for "d"', 1, calls['d'][2]); assertNull(error); assertNull(error2); assertNull(error3); } /** * Tests that the deferred is still called when loadMultiple loads modules * that are already preloading. */ function testLoadMultipleWhenPreloadingSameModules() { var mm = getModuleManager({'a': [], 'b': ['d'], 'c': [], 'd': []}); mm.setLoader(createSuccessfulBatchLoader(mm)); mm.setBatchModeEnabled(true); var origSetLoaded = mm.setLoaded; var calls = {'c': [0, 0, 0], 'd': [0, 0, 0]}; mm.beforeLoadModuleCode = function(id) { calls[id][0]++; }; mm.setLoaded = function(id) { calls[id][1]++; origSetLoaded.call(mm, id); }; mm.afterLoadModuleCode = function(id) { calls[id][2]++; }; var calledBack = false; var error = null; var calledBack2 = false; var error2 = null; mm.preloadModule('c', 2); mm.preloadModule('d', 3); assertFalse('module "c" should not be loading yet', mm.isModuleLoading('c')); assertFalse('module "d" should not be loading yet', mm.isModuleLoading('d')); clock.tick(2); assertTrue('module "c" should now be loading', mm.isModuleLoading('c')); clock.tick(1); assertTrue('module "d" should now be loading', mm.isModuleLoading('d')); var dMap = mm.loadMultiple(['c', 'd']); dMap['c'].addCallback(function(ctx) { calledBack = true; }); dMap['c'].addErrback(function(err) { error = err; }); dMap['d'].addCallback(function(ctx) { calledBack2 = true; }); dMap['d'].addErrback(function(err) { error2 = err; }); assertTrue('module "c" should still be loading', mm.isModuleLoading('c')); clock.tick(4); assertFalse('module "c" should be done loading', mm.isModuleLoading('c')); assertTrue('module "d" should still be loading', mm.isModuleLoading('d')); clock.tick(5); assertFalse('module "d" should be done loading', mm.isModuleLoading('d')); assertTrue(calledBack); assertTrue(calledBack2); assertEquals( 'beforeLoad should only be called once for "c"', 1, calls['c'][0]); assertEquals( 'setLoaded should only be called once for "c"', 1, calls['c'][1]); assertEquals( 'afterLoad should only be called once for "c"', 1, calls['c'][2]); assertEquals( 'beforeLoad should only be called once for "d"', 1, calls['d'][0]); assertEquals( 'setLoaded should only be called once for "d"', 1, calls['d'][1]); assertEquals( 'afterLoad should only be called once for "d"', 1, calls['d'][2]); assertNull(error); assertNull(error2); } /** * Tests loading a module via load when the module is already * loaded. The deferred's callback should be called immediately. */ function testLoadWhenLoaded() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); var calledBack = false; var error = null; mm.preloadModule('b', 2); clock.tick(10); assertFalse('module "b" should be done loading', mm.isModuleLoading('b')); var d = mm.load('b'); d.addCallback(function(ctx) { calledBack = true; }); d.addErrback(function(err) { error = err; }); assertTrue(calledBack); assertNull(error); } /** * Tests that the deferred's errbacks are called if the module fails to load. */ function testLoadWithFailingModule() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setLoader(createUnsuccessfulLoader(mm, 401)); mm.registerCallback( goog.module.ModuleManager.CallbackType.ERROR, function(callbackType, id, cause) { assertEquals( 'Failure cause was not as expected', goog.module.ModuleManager.FailureType.UNAUTHORIZED, cause); firedLoadFailed = true; }); var calledBack = false; var error = null; var d = mm.load('a'); d.addCallback(function(ctx) { calledBack = true; }); d.addErrback(function(err) { error = err; }); assertFalse(calledBack); assertNull(error); clock.tick(500); assertFalse(calledBack); // NOTE: Deferred always calls errbacks with an Error object. For now the // module manager just passes the FailureType which gets set as the Error // object's message. assertEquals( 'Failure cause was not as expected', goog.module.ModuleManager.FailureType.UNAUTHORIZED, Number(error.message)); } /** * Tests that the deferred's errbacks are called if a module fails to load. */ function testLoadMultipleWithFailingModule() { var mm = getModuleManager({'a': [], 'b': [], 'c': []}); mm.setLoader(createUnsuccessfulLoader(mm, 401)); mm.setBatchModeEnabled(true); mm.registerCallback( goog.module.ModuleManager.CallbackType.ERROR, function(callbackType, id, cause) { assertEquals( 'Failure cause was not as expected', goog.module.ModuleManager.FailureType.UNAUTHORIZED, cause); }); var calledBack11 = false; var error11 = null; var calledBack12 = false; var error12 = null; var calledBack21 = false; var error21 = null; var calledBack22 = false; var error22 = null; var dMap = mm.loadMultiple(['a', 'b']); dMap['a'].addCallback(function(ctx) { calledBack11 = true; }); dMap['a'].addErrback(function(err) { error11 = err; }); dMap['b'].addCallback(function(ctx) { calledBack12 = true; }); dMap['b'].addErrback(function(err) { error12 = err; }); var dMap2 = mm.loadMultiple(['b', 'c']); dMap2['b'].addCallback(function(ctx) { calledBack21 = true; }); dMap2['b'].addErrback(function(err) { error21 = err; }); dMap2['c'].addCallback(function(ctx) { calledBack22 = true; }); dMap2['c'].addErrback(function(err) { error22 = err; }); assertFalse(calledBack11); assertFalse(calledBack12); assertFalse(calledBack21); assertFalse(calledBack22); assertNull(error11); assertNull(error12); assertNull(error21); assertNull(error22); clock.tick(5); assertFalse(calledBack11); assertFalse(calledBack12); assertFalse(calledBack21); assertFalse(calledBack22); // NOTE: Deferred always calls errbacks with an Error object. For now the // module manager just passes the FailureType which gets set as the Error // object's message. assertEquals( 'Failure cause was not as expected', goog.module.ModuleManager.FailureType.UNAUTHORIZED, Number(error11.message)); assertEquals( 'Failure cause was not as expected', goog.module.ModuleManager.FailureType.UNAUTHORIZED, Number(error12.message)); // The first deferred of the second load should be called since it asks for // one of the failed modules. assertEquals( 'Failure cause was not as expected', goog.module.ModuleManager.FailureType.UNAUTHORIZED, Number(error21.message)); // The last deferred should be dropped so it is neither called back nor an // error. assertFalse(calledBack22); assertNull(error22); } /** * Tests that the right dependencies are cancelled on a loadMultiple failure. */ function testLoadMultipleWithFailingModuleDependencies() { var mm = getModuleManager({'a': [], 'b': [], 'c': ['b'], 'd': ['c'], 'e': []}); mm.setLoader(createUnsuccessfulLoader(mm, 401)); mm.setBatchModeEnabled(true); var cancelledIds = []; mm.registerCallback( goog.module.ModuleManager.CallbackType.ERROR, function(callbackType, id, cause) { assertEquals( 'Failure cause was not as expected', goog.module.ModuleManager.FailureType.UNAUTHORIZED, cause); cancelledIds.push(id); }); var calledBack11 = false; var error11 = null; var calledBack12 = false; var error12 = null; var calledBack21 = false; var error21 = null; var calledBack22 = false; var error22 = null; var calledBack23 = false; var error23 = null; var dMap = mm.loadMultiple(['a', 'b']); dMap['a'].addCallback(function(ctx) { calledBack11 = true; }); dMap['a'].addErrback(function(err) { error11 = err; }); dMap['b'].addCallback(function(ctx) { calledBack12 = true; }); dMap['b'].addErrback(function(err) { error12 = err; }); var dMap2 = mm.loadMultiple(['c', 'd', 'e']); dMap2['c'].addCallback(function(ctx) { calledBack21 = true; }); dMap2['c'].addErrback(function(err) { error21 = err; }); dMap2['d'].addCallback(function(ctx) { calledBack22 = true; }); dMap2['d'].addErrback(function(err) { error22 = err; }); dMap2['e'].addCallback(function(ctx) { calledBack23 = true; }); dMap2['e'].addErrback(function(err) { error23 = err; }); assertFalse(calledBack11); assertFalse(calledBack12); assertFalse(calledBack21); assertFalse(calledBack22); assertFalse(calledBack23); assertNull(error11); assertNull(error12); assertNull(error21); assertNull(error22); assertNull(error23); clock.tick(5); assertFalse(calledBack11); assertFalse(calledBack12); assertFalse(calledBack21); assertFalse(calledBack22); assertFalse(calledBack23); // NOTE: Deferred always calls errbacks with an Error object. For now the // module manager just passes the FailureType which gets set as the Error // object's message. assertEquals( 'Failure cause was not as expected', goog.module.ModuleManager.FailureType.UNAUTHORIZED, Number(error11.message)); assertEquals( 'Failure cause was not as expected', goog.module.ModuleManager.FailureType.UNAUTHORIZED, Number(error12.message)); // Check that among the failed modules, 'c' and 'd' are also cancelled // due to dependencies. assertTrue(goog.array.equals(['a', 'b', 'c', 'd'], cancelledIds.sort())); } /** * Tests that when loading multiple modules, the input array is not modified * when it has duplicates. */ function testLoadMultipleWithDuplicates() { var mm = getModuleManager({'a': [], 'b': []}); mm.setBatchModeEnabled(true); mm.setLoader(createSuccessfulBatchLoader(mm)); var listWithDuplicates = ['a', 'a', 'b']; mm.loadMultiple(listWithDuplicates); assertArrayEquals( 'loadMultiple should not modify its input', ['a', 'a', 'b'], listWithDuplicates); } /** * Test loading dependencies transitively. */ function testLoadingDepsInNonBatchMode1() { var mm = getModuleManager({'i': [], 'j': [], 'k': ['j'], 'l': ['i', 'j', 'k']}); mm.setLoader(createSuccessfulNonBatchLoader(mm)); mm.preloadModule('j'); clock.tick(5); assertTrue('module "j" should be loaded', mm.isModuleLoaded('j')); assertFalse('module "i" should not be loaded (1)', mm.isModuleLoaded('i')); assertFalse('module "k" should not be loaded (1)', mm.isModuleLoaded('k')); assertFalse('module "l" should not be loaded (1)', mm.isModuleLoaded('l')); // When loading a module in non-batch mode, its dependencies should be // requested independently, and in dependency order. mm.preloadModule('l'); clock.tick(5); assertTrue('module "i" should be loaded', mm.isModuleLoaded('i')); assertFalse('module "k" should not be loaded (2)', mm.isModuleLoaded('k')); assertFalse('module "l" should not be loaded (2)', mm.isModuleLoaded('l')); clock.tick(5); assertTrue('module "k" should be loaded', mm.isModuleLoaded('k')); assertFalse('module "l" should not be loaded (3)', mm.isModuleLoaded('l')); clock.tick(5); assertTrue('module "l" should be loaded', mm.isModuleLoaded('l')); } /** * Test loading dependencies transitively and in dependency order. */ function testLoadingDepsInNonBatchMode2() { var mm = getModuleManager({ 'h': [], 'i': ['h'], 'j': ['i'], 'k': ['j'], 'l': ['i', 'j', 'k'], 'm': ['l'] }); mm.setLoader(createSuccessfulNonBatchLoader(mm)); // When loading a module in non-batch mode, its dependencies should be // requested independently, and in dependency order. The order in this // case should be h,i,j,k,l,m. mm.preloadModule('m'); clock.tick(5); assertTrue('module "h" should be loaded', mm.isModuleLoaded('h')); assertFalse('module "i" should not be loaded (1)', mm.isModuleLoaded('i')); assertFalse('module "j" should not be loaded (1)', mm.isModuleLoaded('j')); assertFalse('module "k" should not be loaded (1)', mm.isModuleLoaded('k')); assertFalse('module "l" should not be loaded (1)', mm.isModuleLoaded('l')); assertFalse('module "m" should not be loaded (1)', mm.isModuleLoaded('m')); clock.tick(5); assertTrue('module "i" should be loaded', mm.isModuleLoaded('i')); assertFalse('module "j" should not be loaded (2)', mm.isModuleLoaded('j')); assertFalse('module "k" should not be loaded (2)', mm.isModuleLoaded('k')); assertFalse('module "l" should not be loaded (2)', mm.isModuleLoaded('l')); assertFalse('module "m" should not be loaded (2)', mm.isModuleLoaded('m')); clock.tick(5); assertTrue('module "j" should be loaded', mm.isModuleLoaded('j')); assertFalse('module "k" should not be loaded (3)', mm.isModuleLoaded('k')); assertFalse('module "l" should not be loaded (3)', mm.isModuleLoaded('l')); assertFalse('module "m" should not be loaded (3)', mm.isModuleLoaded('m')); clock.tick(5); assertTrue('module "k" should be loaded', mm.isModuleLoaded('k')); assertFalse('module "l" should not be loaded (4)', mm.isModuleLoaded('l')); assertFalse('module "m" should not be loaded (4)', mm.isModuleLoaded('m')); clock.tick(5); assertTrue('module "l" should be loaded', mm.isModuleLoaded('l')); assertFalse('module "m" should not be loaded (5)', mm.isModuleLoaded('m')); clock.tick(5); assertTrue('module "m" should be loaded', mm.isModuleLoaded('m')); } function testLoadingDepsInBatchMode() { var mm = getModuleManager({'e': [], 'f': [], 'g': ['f'], 'h': ['e', 'f', 'g']}); mm.setLoader(createSuccessfulBatchLoader(mm)); mm.setBatchModeEnabled(true); mm.preloadModule('f'); clock.tick(5); assertTrue('module "f" should be loaded', mm.isModuleLoaded('f')); assertFalse('module "e" should not be loaded (1)', mm.isModuleLoaded('e')); assertFalse('module "g" should not be loaded (1)', mm.isModuleLoaded('g')); assertFalse('module "h" should not be loaded (1)', mm.isModuleLoaded('h')); // When loading a module in batch mode, its not-yet-loaded dependencies // should be requested at the same time, and in dependency order. mm.preloadModule('h'); clock.tick(5); assertTrue('module "e" should be loaded', mm.isModuleLoaded('e')); assertFalse('module "g" should not be loaded (2)', mm.isModuleLoaded('g')); assertFalse('module "h" should not be loaded (2)', mm.isModuleLoaded('h')); clock.tick(2); assertTrue('module "g" should be loaded', mm.isModuleLoaded('g')); assertFalse('module "h" should not be loaded (3)', mm.isModuleLoaded('h')); clock.tick(2); assertTrue('module "h" should be loaded', mm.isModuleLoaded('h')); } /** * Test unauthorized errors while loading modules. */ function testUnauthorizedLoading() { var mm = getModuleManager({'m': [], 'n': [], 'o': ['n']}); mm.setLoader(createUnsuccessfulLoader(mm, 401)); // Callback checks for an unauthorized error var firedLoadFailed = false; mm.registerCallback( goog.module.ModuleManager.CallbackType.ERROR, function(callbackType, id, cause) { assertEquals( 'Failure cause was not as expected', goog.module.ModuleManager.FailureType.UNAUTHORIZED, cause); firedLoadFailed = true; }); mm.execOnLoad('o', function() {}); assertTrue('module "o" should be loading', mm.isModuleLoading('o')); assertTrue('module "n" should be loading', mm.isModuleLoading('n')); clock.tick(5); assertTrue( 'should have called unauthorized module callback', firedLoadFailed); assertFalse('module "o" should not be loaded', mm.isModuleLoaded('o')); assertFalse('module "o" should not be loading', mm.isModuleLoading('o')); assertFalse('module "n" should not be loaded', mm.isModuleLoaded('n')); assertFalse('module "n" should not be loading', mm.isModuleLoading('n')); } /** * Test error loading modules which are retried. */ function testErrorLoadingModule() { var mm = getModuleManager({'p': ['q'], 'q': [], 'r': ['q', 'p']}); mm.setLoader(createUnsuccessfulLoader(mm, 500)); mm.preloadModule('r'); clock.tick(4); // A module request is now underway using the unsuccessful loader. // We substitute a successful loader for future module load requests. mm.setLoader(createSuccessfulNonBatchLoader(mm)); clock.tick(1); assertFalse('module "q" should not be loaded (1)', mm.isModuleLoaded('q')); assertFalse('module "p" should not be loaded (1)', mm.isModuleLoaded('p')); assertFalse('module "r" should not be loaded (1)', mm.isModuleLoaded('r')); // Failed loads are automatically retried after a backOff. clock.tick(5 + mm.getBackOff_()); assertTrue('module "q" should be loaded', mm.isModuleLoaded('q')); assertFalse('module "p" should not be loaded (2)', mm.isModuleLoaded('p')); assertFalse('module "r" should not be loaded (2)', mm.isModuleLoaded('r')); // A successful load decrements the backOff. clock.tick(5); assertTrue('module "p" should be loaded', mm.isModuleLoaded('p')); assertFalse('module "r" should not be loaded (3)', mm.isModuleLoaded('r')); clock.tick(5); assertTrue('module "r" should be loaded', mm.isModuleLoaded('r')); } /** * Tests error loading modules which are retried. */ function testErrorLoadingModule_batchMode() { var mm = getModuleManager({'p': ['q'], 'q': [], 'r': ['q', 'p']}); mm.setLoader(createUnsuccessfulBatchLoader(mm, 500)); mm.setBatchModeEnabled(true); mm.preloadModule('r'); clock.tick(4); // A module request is now underway using the unsuccessful loader. // We substitute a successful loader for future module load requests. mm.setLoader(createSuccessfulBatchLoader(mm)); clock.tick(1); assertFalse('module "q" should not be loaded (1)', mm.isModuleLoaded('q')); assertFalse('module "p" should not be loaded (1)', mm.isModuleLoaded('p')); assertFalse('module "r" should not be loaded (1)', mm.isModuleLoaded('r')); // Failed loads are automatically retried after a backOff. clock.tick(5 + mm.getBackOff_()); assertTrue('module "q" should be loaded', mm.isModuleLoaded('q')); clock.tick(2); assertTrue('module "p" should not be loaded (2)', mm.isModuleLoaded('p')); clock.tick(2); assertTrue('module "r" should not be loaded (2)', mm.isModuleLoaded('r')); } /** * Test consecutive errors in loading modules. */ function testConsecutiveErrors() { var mm = getModuleManager({'s': []}); mm.setLoader(createUnsuccessfulLoader(mm, 500)); // Register an error callback for consecutive failures. var firedLoadFailed = false; mm.registerCallback( goog.module.ModuleManager.CallbackType.ERROR, function(callbackType, id, cause) { assertEquals( 'Failure cause was not as expected', goog.module.ModuleManager.FailureType.CONSECUTIVE_FAILURES, cause); firedLoadFailed = true; }); mm.preloadModule('s'); assertFalse('module "s" should not be loaded (0)', mm.isModuleLoaded('s')); // Fail twice. for (var i = 0; i < 2; i++) { clock.tick(5 + mm.getBackOff_()); assertFalse('module "s" should not be loaded (1)', mm.isModuleLoaded('s')); assertFalse('should not fire failed callback (1)', firedLoadFailed); } // Fail a third time and check that the callback is fired. clock.tick(5 + mm.getBackOff_()); assertFalse('module "s" should not be loaded (2)', mm.isModuleLoaded('s')); assertTrue('should have fired failed callback', firedLoadFailed); // Check that it doesn't attempt to load the module anymore after it has // failed. var triedLoad = false; mm.setLoader({ loadModules: function(ids, moduleInfoMap, opt_successFn, opt_errFn) { triedLoad = true; } }); // Also reset the failed callback flag and make sure it isn't called // again. firedLoadFailed = false; clock.tick(10 + mm.getBackOff_()); assertFalse('module "s" should not be loaded (3)', mm.isModuleLoaded('s')); assertFalse('No more loads should have been tried', triedLoad); assertFalse( 'The load failed callback should be fired only once', firedLoadFailed); } /** * Test loading errors due to old code. */ function testOldCodeGoneError() { var mm = getModuleManager({'s': []}); mm.setLoader(createUnsuccessfulLoader(mm, 410)); // Callback checks for an old code failure var firedLoadFailed = false; mm.registerCallback( goog.module.ModuleManager.CallbackType.ERROR, function(callbackType, id, cause) { assertEquals( 'Failure cause was not as expected', goog.module.ModuleManager.FailureType.OLD_CODE_GONE, cause); firedLoadFailed = true; }); mm.preloadModule('s', 0); assertFalse('module "s" should not be loaded (0)', mm.isModuleLoaded('s')); clock.tick(5); assertFalse('module "s" should not be loaded (1)', mm.isModuleLoaded('s')); assertTrue('should have called old code gone callback', firedLoadFailed); } /** * Test timeout. */ function testTimeout() { var mm = getModuleManager({'s': []}); mm.setLoader(createTimeoutLoader(mm)); // Callback checks for timeout var firedTimeout = false; mm.registerCallback( goog.module.ModuleManager.CallbackType.ERROR, function(callbackType, id, cause) { assertEquals( 'Failure cause was not as expected', goog.module.ModuleManager.FailureType.TIMEOUT, cause); firedTimeout = true; }); mm.preloadModule('s', 0); assertFalse('module "s" should not be loaded (0)', mm.isModuleLoaded('s')); clock.tick(5); assertFalse('module "s" should not be loaded (1)', mm.isModuleLoaded('s')); assertTrue