331 lines
11 KiB
JavaScript
331 lines
11 KiB
JavaScript
// Copyright 2006 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.net.ImageLoaderTest');
|
|
goog.setTestOnly('goog.net.ImageLoaderTest');
|
|
|
|
goog.require('goog.Timer');
|
|
goog.require('goog.array');
|
|
goog.require('goog.dispose');
|
|
goog.require('goog.events');
|
|
goog.require('goog.events.Event');
|
|
goog.require('goog.events.EventType');
|
|
goog.require('goog.net.EventType');
|
|
goog.require('goog.net.ImageLoader');
|
|
goog.require('goog.object');
|
|
goog.require('goog.string');
|
|
goog.require('goog.testing.AsyncTestCase');
|
|
goog.require('goog.testing.jsunit');
|
|
goog.require('goog.testing.recordFunction');
|
|
|
|
var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall(document.title);
|
|
|
|
// Set the AsyncTestCase timeout to larger value to allow more time
|
|
// for images to load.
|
|
asyncTestCase.stepTimeout = 5000;
|
|
|
|
|
|
var TEST_EVENT_TYPES = [
|
|
goog.events.EventType.LOAD,
|
|
goog.net.EventType.COMPLETE,
|
|
goog.net.EventType.ERROR
|
|
];
|
|
|
|
|
|
/**
|
|
* Mapping from test image file name to:
|
|
* [expected width, expected height, expected event to be fired].
|
|
*/
|
|
var TEST_IMAGES = {
|
|
'imageloader_testimg1.gif': [20, 20, goog.events.EventType.LOAD],
|
|
'imageloader_testimg2.gif': [20, 20, goog.events.EventType.LOAD],
|
|
'imageloader_testimg3.gif': [32, 32, goog.events.EventType.LOAD],
|
|
|
|
'this-is-not-image-1.gif': [0, 0, goog.net.EventType.ERROR],
|
|
'this-is-not-image-2.gif': [0, 0, goog.net.EventType.ERROR]
|
|
};
|
|
|
|
|
|
var startTime;
|
|
var loader;
|
|
|
|
|
|
function setUp() {
|
|
startTime = goog.now();
|
|
|
|
loader = new goog.net.ImageLoader();
|
|
|
|
// Adds test images to the loader.
|
|
var i = 0;
|
|
for (var key in TEST_IMAGES) {
|
|
var imageId = 'img_' + i++;
|
|
loader.addImage(imageId, key);
|
|
}
|
|
}
|
|
|
|
|
|
function tearDown() {
|
|
goog.dispose(loader);
|
|
}
|
|
|
|
|
|
/**
|
|
* Tests loading image and disposing before loading completes.
|
|
*/
|
|
function testDisposeInTheMiddleOfLoadingWorks() {
|
|
goog.events.listen(loader, TEST_EVENT_TYPES,
|
|
goog.partial(handleDisposalImageLoaderEvent, loader));
|
|
// waitForAsync before starting loader just in case
|
|
// handleDisposalImageLoaderEvent is called from within loader.start
|
|
// (before we yield control). This may happen in IE7/IE8.
|
|
asyncTestCase.waitForAsync('Waiting for loader handler to fire.');
|
|
loader.start();
|
|
}
|
|
|
|
|
|
function handleDisposalImageLoaderEvent(loader, e) {
|
|
assertFalse('Handler is still invoked after loader is disposed.',
|
|
loader.isDisposed());
|
|
|
|
switch (e.type) {
|
|
case goog.net.EventType.COMPLETE:
|
|
fail('This test should never get COMPLETE event.');
|
|
return;
|
|
|
|
case goog.events.EventType.LOAD:
|
|
case goog.net.EventType.ERROR:
|
|
loader.dispose();
|
|
break;
|
|
}
|
|
|
|
// Make sure that handler is never called again after disposal before
|
|
// marking test as successful.
|
|
asyncTestCase.waitForAsync('Wait to ensure that COMPLETE is never fired');
|
|
goog.Timer.callOnce(function() {
|
|
asyncTestCase.continueTesting();
|
|
}, 500);
|
|
}
|
|
|
|
|
|
/**
|
|
* Tests loading of images until completion.
|
|
*/
|
|
function testLoadingUntilCompletion() {
|
|
var results = {};
|
|
goog.events.listen(loader, TEST_EVENT_TYPES,
|
|
function(e) {
|
|
switch (e.type) {
|
|
case goog.events.EventType.LOAD:
|
|
var image = e.target;
|
|
results[image.src.substring(image.src.lastIndexOf('/') + 1)] =
|
|
[image.naturalWidth, image.naturalHeight, e.type];
|
|
return;
|
|
|
|
case goog.net.EventType.ERROR:
|
|
var image = e.target;
|
|
results[image.src.substring(image.src.lastIndexOf('/') + 1)] =
|
|
[image.naturalWidth, image.naturalHeight, e.type];
|
|
return;
|
|
|
|
case goog.net.EventType.COMPLETE:
|
|
// Test completes successfully.
|
|
asyncTestCase.continueTesting();
|
|
|
|
assertImagesAreCorrect(results);
|
|
return;
|
|
}
|
|
});
|
|
|
|
// waitForAsync before starting loader just in case handleImageLoaderEvent
|
|
// is called from within loader.start (before we yield control).
|
|
// This may happen in IE7/IE8.
|
|
asyncTestCase.waitForAsync('Waiting for loader handler to fire.');
|
|
loader.start();
|
|
}
|
|
|
|
|
|
function assertImagesAreCorrect(results) {
|
|
assertEquals(
|
|
goog.object.getCount(TEST_IMAGES), goog.object.getCount(results));
|
|
goog.object.forEach(TEST_IMAGES, function(value, key) {
|
|
// Check if fires the COMPLETE event.
|
|
assertTrue('Image is not loaded completely.', key in results);
|
|
|
|
var image = results[key];
|
|
|
|
// Check image size.
|
|
assertEquals('Image width is not correct', value[0], image[0]);
|
|
assertEquals('Image length is not correct', value[1], image[1]);
|
|
|
|
// Check if fired the correct event.
|
|
assertEquals('Event *' + value[2] + '* must be fired', value[2], image[2]);
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* Overrides the loader's loadImage_ method so that it dispatches an image
|
|
* loaded event immediately, causing any event listners to receive them
|
|
* synchronously. This allows tests to assume synchronous execution.
|
|
*/
|
|
function makeLoaderSynchronous(loader) {
|
|
var originalLoadImage = loader.loadImage_;
|
|
loader.loadImage_ = function(request, id) {
|
|
originalLoadImage.call(this, request, id);
|
|
|
|
var event = new goog.events.Event(goog.events.EventType.LOAD);
|
|
event.currentTarget = this.imageIdToImageMap_[id];
|
|
loader.onNetworkEvent_(event);
|
|
};
|
|
|
|
// Make listen() a no-op.
|
|
loader.handler_.listen = goog.nullFunction;
|
|
}
|
|
|
|
|
|
/**
|
|
* Verifies that if an additional image is added after start() was called, but
|
|
* before COMPLETE was dispatched, no COMPLETE event is sent. Verifies COMPLETE
|
|
* is finally sent when .start() is called again and all images have now
|
|
* completed loading.
|
|
*/
|
|
function testImagesAddedAfterStart() {
|
|
// Use synchronous image loading.
|
|
makeLoaderSynchronous(loader);
|
|
|
|
// Add another image once the first images finishes loading.
|
|
goog.events.listenOnce(loader, goog.events.EventType.LOAD, function() {
|
|
loader.addImage('extra_image', 'extra_image.gif');
|
|
});
|
|
|
|
// Keep track of the total # of image loads.
|
|
var loadRecordFn = goog.testing.recordFunction();
|
|
goog.events.listen(loader, goog.events.EventType.LOAD, loadRecordFn);
|
|
|
|
// Keep track of how many times COMPLETE was dispatched.
|
|
var completeRecordFn = goog.testing.recordFunction();
|
|
goog.events.listen(loader, goog.net.EventType.COMPLETE, completeRecordFn);
|
|
|
|
// Start testing.
|
|
loader.start();
|
|
assertEquals(
|
|
'COMPLETE event should not have been dispatched yet: An image was ' +
|
|
'added after the initial batch was started.',
|
|
0, completeRecordFn.getCallCount());
|
|
assertEquals('Just the test images should have loaded',
|
|
goog.object.getCount(TEST_IMAGES), loadRecordFn.getCallCount());
|
|
|
|
loader.start();
|
|
assertEquals('COMPLETE should have been dispatched once.',
|
|
1, completeRecordFn.getCallCount());
|
|
assertEquals('All images should have been loaded',
|
|
goog.object.getCount(TEST_IMAGES) + 1, loadRecordFn.getCallCount());
|
|
}
|
|
|
|
|
|
/**
|
|
* Verifies that more images can be added after an upload starts, and start()
|
|
* can be called for them, resulting in just one COMPLETE event once all the
|
|
* images have completed.
|
|
*/
|
|
function testImagesAddedAndStartedAfterStart() {
|
|
// Use synchronous image loading.
|
|
makeLoaderSynchronous(loader);
|
|
|
|
// Keep track of the total # of image loads.
|
|
var loadRecordFn = goog.testing.recordFunction();
|
|
goog.events.listen(loader, goog.events.EventType.LOAD, loadRecordFn);
|
|
|
|
// Add more images once the first images finishes loading, and call start()
|
|
// to get them going.
|
|
goog.events.listenOnce(loader, goog.events.EventType.LOAD, function(e) {
|
|
loader.addImage('extra_image', 'extra_image.gif');
|
|
loader.addImage('extra_image2', 'extra_image2.gif');
|
|
loader.start();
|
|
});
|
|
|
|
// Keep track of how many times COMPLETE was dispatched.
|
|
var completeRecordFn = goog.testing.recordFunction();
|
|
goog.events.listen(loader, goog.net.EventType.COMPLETE, completeRecordFn);
|
|
|
|
// Start testing. Make sure all 7 images loaded.
|
|
loader.start();
|
|
assertEquals('COMPLETE should have been dispatched once.',
|
|
1, completeRecordFn.getCallCount());
|
|
assertEquals('All images should have been loaded',
|
|
goog.object.getCount(TEST_IMAGES) + 2, loadRecordFn.getCallCount());
|
|
}
|
|
|
|
|
|
/**
|
|
* Verifies that if images are removed after loading has started, COMPLETE
|
|
* is dispatched once the remaining images have finished.
|
|
*/
|
|
function testImagesRemovedAfterStart() {
|
|
// Use synchronous image loading.
|
|
makeLoaderSynchronous(loader);
|
|
|
|
// Remove 2 images once the first image finishes loading.
|
|
goog.events.listenOnce(loader, goog.events.EventType.LOAD, function(e) {
|
|
loader.removeImage(
|
|
goog.array.peek(goog.object.getKeys(this.imageIdToRequestMap_)));
|
|
loader.removeImage(
|
|
goog.array.peek(goog.object.getKeys(this.imageIdToRequestMap_)));
|
|
});
|
|
|
|
// Keep track of the total # of image loads.
|
|
var loadRecordFn = goog.testing.recordFunction();
|
|
goog.events.listen(loader, goog.events.EventType.LOAD, loadRecordFn);
|
|
|
|
// Keep track of how many times COMPLETE was dispatched.
|
|
var completeRecordFn = goog.testing.recordFunction();
|
|
goog.events.listen(loader, goog.net.EventType.COMPLETE, completeRecordFn);
|
|
|
|
// Start testing. Make sure only the 3 images remaining loaded.
|
|
loader.start();
|
|
assertEquals('COMPLETE should have been dispatched once.',
|
|
1, completeRecordFn.getCallCount());
|
|
assertEquals('All images should have been loaded',
|
|
goog.object.getCount(TEST_IMAGES) - 2, loadRecordFn.getCallCount());
|
|
}
|
|
|
|
|
|
/**
|
|
* Verifies that the correct image attribute is set when using CORS requests.
|
|
*/
|
|
function testSetsCorsAttribute() {
|
|
// Use synchronous image loading.
|
|
makeLoaderSynchronous(loader);
|
|
|
|
// Verify the crossOrigin attribute of the requested images.
|
|
goog.events.listen(loader, goog.events.EventType.LOAD, function(e) {
|
|
var image = e.target;
|
|
if (image.id == 'cors_request') {
|
|
assertEquals(
|
|
'CORS requested image should have a crossOrigin attribute set',
|
|
'anonymous', image.crossOrigin);
|
|
} else {
|
|
assertTrue(
|
|
'Non-CORS requested images should not have a crossOrigin attribute',
|
|
goog.string.isEmptyOrWhitespace(goog.string.makeSafe(image.crossOrigin)));
|
|
}
|
|
});
|
|
|
|
// Make a new request for one of the images, this time using CORS.
|
|
var srcs = goog.object.getKeys(TEST_IMAGES);
|
|
loader.addImage(
|
|
'cors_request', srcs[0], goog.net.ImageLoader.CorsRequestType.ANONYMOUS);
|
|
loader.start();
|
|
}
|