imageloader_test.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. // Copyright 2006 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.net.ImageLoaderTest');
  15. goog.setTestOnly('goog.net.ImageLoaderTest');
  16. goog.require('goog.Promise');
  17. goog.require('goog.Timer');
  18. goog.require('goog.array');
  19. goog.require('goog.dispose');
  20. goog.require('goog.events');
  21. goog.require('goog.events.Event');
  22. goog.require('goog.events.EventType');
  23. goog.require('goog.net.EventType');
  24. goog.require('goog.net.ImageLoader');
  25. goog.require('goog.object');
  26. goog.require('goog.string');
  27. goog.require('goog.testing.TestCase');
  28. goog.require('goog.testing.jsunit');
  29. goog.require('goog.testing.recordFunction');
  30. var TEST_EVENT_TYPES = [
  31. goog.events.EventType.LOAD, goog.net.EventType.COMPLETE,
  32. goog.net.EventType.ERROR
  33. ];
  34. /**
  35. * Mapping from test image file name to:
  36. * [expected width, expected height, expected event to be fired].
  37. */
  38. var TEST_IMAGES = {
  39. 'imageloader_testimg1.gif': [20, 20, goog.events.EventType.LOAD],
  40. 'imageloader_testimg2.gif': [20, 20, goog.events.EventType.LOAD],
  41. 'imageloader_testimg3.gif': [32, 32, goog.events.EventType.LOAD],
  42. 'this-is-not-image-1.gif': [0, 0, goog.net.EventType.ERROR],
  43. 'this-is-not-image-2.gif': [0, 0, goog.net.EventType.ERROR]
  44. };
  45. var startTime;
  46. var loader;
  47. function setUpPage() {
  48. // Increase the timeout to 5 seconds to allow more time for images to load.
  49. goog.testing.TestCase.getActiveTestCase().promiseTimeout = 5 * 1000;
  50. }
  51. function setUp() {
  52. startTime = goog.now();
  53. loader = new goog.net.ImageLoader();
  54. // Adds test images to the loader.
  55. var i = 0;
  56. for (var key in TEST_IMAGES) {
  57. var imageId = 'img_' + i++;
  58. loader.addImage(imageId, key);
  59. }
  60. }
  61. function tearDown() {
  62. goog.dispose(loader);
  63. }
  64. /**
  65. * Tests loading image and disposing before loading completes.
  66. */
  67. function testDisposeInTheMiddleOfLoadingWorks() {
  68. var resolver = goog.Promise.withResolver();
  69. goog.events.listen(loader, TEST_EVENT_TYPES, function(e) {
  70. assertFalse(
  71. 'Handler is still invoked after loader is disposed.',
  72. loader.isDisposed());
  73. switch (e.type) {
  74. case goog.net.EventType.COMPLETE:
  75. resolver.reject('This test should never get COMPLETE event.');
  76. return;
  77. case goog.events.EventType.LOAD:
  78. case goog.net.EventType.ERROR:
  79. loader.dispose();
  80. break;
  81. }
  82. // Make sure that handler is never called again after disposal before
  83. // marking test as successful.
  84. goog.Timer.callOnce(function() { resolver.resolve(); }, 500);
  85. });
  86. loader.start();
  87. return resolver.promise;
  88. }
  89. /**
  90. * Tests loading of images until completion.
  91. */
  92. function testLoadingUntilCompletion() {
  93. var resolver = goog.Promise.withResolver();
  94. var results = {};
  95. goog.events.listen(loader, TEST_EVENT_TYPES, function(e) {
  96. switch (e.type) {
  97. case goog.events.EventType.LOAD:
  98. var image = e.target;
  99. results[image.src.substring(image.src.lastIndexOf('/') + 1)] =
  100. [image.naturalWidth, image.naturalHeight, e.type];
  101. return;
  102. case goog.net.EventType.ERROR:
  103. var image = e.target;
  104. results[image.src.substring(image.src.lastIndexOf('/') + 1)] =
  105. [image.naturalWidth, image.naturalHeight, e.type];
  106. return;
  107. case goog.net.EventType.COMPLETE:
  108. try {
  109. assertImagesAreCorrect(results);
  110. } catch (e) {
  111. resolver.reject(e);
  112. return;
  113. }
  114. resolver.resolve();
  115. return;
  116. }
  117. });
  118. loader.start();
  119. return resolver.promise;
  120. }
  121. function assertImagesAreCorrect(results) {
  122. assertEquals(
  123. goog.object.getCount(TEST_IMAGES), goog.object.getCount(results));
  124. goog.object.forEach(TEST_IMAGES, function(value, key) {
  125. // Check if fires the COMPLETE event.
  126. assertTrue('Image is not loaded completely.', key in results);
  127. var image = results[key];
  128. // Check image size.
  129. assertEquals('Image width is not correct', value[0], image[0]);
  130. assertEquals('Image length is not correct', value[1], image[1]);
  131. // Check if fired the correct event.
  132. assertEquals('Event *' + value[2] + '* must be fired', value[2], image[2]);
  133. });
  134. }
  135. /**
  136. * Overrides the loader's loadImage_ method so that it dispatches an image
  137. * loaded event immediately, causing any event listeners to receive them
  138. * synchronously. This allows tests to assume synchronous execution.
  139. */
  140. function makeLoaderSynchronous(loader) {
  141. var originalLoadImage = loader.loadImage_;
  142. loader.loadImage_ = function(request, id) {
  143. originalLoadImage.call(this, request, id);
  144. var event = new goog.events.Event(goog.events.EventType.LOAD);
  145. event.currentTarget = this.imageIdToImageMap_[id];
  146. loader.onNetworkEvent_(event);
  147. };
  148. // Make listen() a no-op.
  149. loader.handler_.listen = goog.nullFunction;
  150. }
  151. /**
  152. * Verifies that if an additional image is added after start() was called, but
  153. * before COMPLETE was dispatched, no COMPLETE event is sent. Verifies COMPLETE
  154. * is finally sent when .start() is called again and all images have now
  155. * completed loading.
  156. */
  157. function testImagesAddedAfterStart() {
  158. // Use synchronous image loading.
  159. makeLoaderSynchronous(loader);
  160. // Add another image once the first images finishes loading.
  161. goog.events.listenOnce(loader, goog.events.EventType.LOAD, function() {
  162. loader.addImage('extra_image', 'extra_image.gif');
  163. });
  164. // Keep track of the total # of image loads.
  165. var loadRecordFn = goog.testing.recordFunction();
  166. goog.events.listen(loader, goog.events.EventType.LOAD, loadRecordFn);
  167. // Keep track of how many times COMPLETE was dispatched.
  168. var completeRecordFn = goog.testing.recordFunction();
  169. goog.events.listen(loader, goog.net.EventType.COMPLETE, completeRecordFn);
  170. // Start testing.
  171. loader.start();
  172. assertEquals(
  173. 'COMPLETE event should not have been dispatched yet: An image was ' +
  174. 'added after the initial batch was started.',
  175. 0, completeRecordFn.getCallCount());
  176. assertEquals(
  177. 'Just the test images should have loaded',
  178. goog.object.getCount(TEST_IMAGES), loadRecordFn.getCallCount());
  179. loader.start();
  180. assertEquals(
  181. 'COMPLETE should have been dispatched once.', 1,
  182. completeRecordFn.getCallCount());
  183. assertEquals(
  184. 'All images should have been loaded',
  185. goog.object.getCount(TEST_IMAGES) + 1, loadRecordFn.getCallCount());
  186. }
  187. /**
  188. * Verifies that more images can be added after an upload starts, and start()
  189. * can be called for them, resulting in just one COMPLETE event once all the
  190. * images have completed.
  191. */
  192. function testImagesAddedAndStartedAfterStart() {
  193. // Use synchronous image loading.
  194. makeLoaderSynchronous(loader);
  195. // Keep track of the total # of image loads.
  196. var loadRecordFn = goog.testing.recordFunction();
  197. goog.events.listen(loader, goog.events.EventType.LOAD, loadRecordFn);
  198. // Add more images once the first images finishes loading, and call start()
  199. // to get them going.
  200. goog.events.listenOnce(loader, goog.events.EventType.LOAD, function(e) {
  201. loader.addImage('extra_image', 'extra_image.gif');
  202. loader.addImage('extra_image2', 'extra_image2.gif');
  203. loader.start();
  204. });
  205. // Keep track of how many times COMPLETE was dispatched.
  206. var completeRecordFn = goog.testing.recordFunction();
  207. goog.events.listen(loader, goog.net.EventType.COMPLETE, completeRecordFn);
  208. // Start testing. Make sure all 7 images loaded.
  209. loader.start();
  210. assertEquals(
  211. 'COMPLETE should have been dispatched once.', 1,
  212. completeRecordFn.getCallCount());
  213. assertEquals(
  214. 'All images should have been loaded',
  215. goog.object.getCount(TEST_IMAGES) + 2, loadRecordFn.getCallCount());
  216. }
  217. /**
  218. * Verifies that if images are removed after loading has started, COMPLETE
  219. * is dispatched once the remaining images have finished.
  220. */
  221. function testImagesRemovedAfterStart() {
  222. // Use synchronous image loading.
  223. makeLoaderSynchronous(loader);
  224. // Remove 2 images once the first image finishes loading.
  225. goog.events.listenOnce(loader, goog.events.EventType.LOAD, function(e) {
  226. loader.removeImage(
  227. goog.array.peek(goog.object.getKeys(this.imageIdToRequestMap_)));
  228. loader.removeImage(
  229. goog.array.peek(goog.object.getKeys(this.imageIdToRequestMap_)));
  230. });
  231. // Keep track of the total # of image loads.
  232. var loadRecordFn = goog.testing.recordFunction();
  233. goog.events.listen(loader, goog.events.EventType.LOAD, loadRecordFn);
  234. // Keep track of how many times COMPLETE was dispatched.
  235. var completeRecordFn = goog.testing.recordFunction();
  236. goog.events.listen(loader, goog.net.EventType.COMPLETE, completeRecordFn);
  237. // Start testing. Make sure only the 3 images remaining loaded.
  238. loader.start();
  239. assertEquals(
  240. 'COMPLETE should have been dispatched once.', 1,
  241. completeRecordFn.getCallCount());
  242. assertEquals(
  243. 'All images should have been loaded',
  244. goog.object.getCount(TEST_IMAGES) - 2, loadRecordFn.getCallCount());
  245. }
  246. /**
  247. * Verifies that the correct image attribute is set when using CORS requests.
  248. */
  249. function testSetsCorsAttribute() {
  250. // Use synchronous image loading.
  251. makeLoaderSynchronous(loader);
  252. // Verify the crossOrigin attribute of the requested images.
  253. goog.events.listen(loader, goog.events.EventType.LOAD, function(e) {
  254. var image = e.target;
  255. if (image.id == 'cors_request') {
  256. assertEquals(
  257. 'CORS requested image should have a crossOrigin attribute set',
  258. 'anonymous', image.crossOrigin);
  259. } else {
  260. assertTrue(
  261. 'Non-CORS requested images should not have a crossOrigin attribute',
  262. goog.string.isEmptyOrWhitespace(
  263. goog.string.makeSafe(image.crossOrigin)));
  264. }
  265. });
  266. // Make a new request for one of the images, this time using CORS.
  267. var srcs = goog.object.getKeys(TEST_IMAGES);
  268. loader.addImage(
  269. 'cors_request', srcs[0], goog.net.ImageLoader.CorsRequestType.ANONYMOUS);
  270. loader.start();
  271. }