Adding float-no-zero branch hosted build
This commit is contained in:
1243
float-no-zero/closure-library/closure/goog/testing/asserts.js
Normal file
1243
float-no-zero/closure-library/closure/goog/testing/asserts.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,174 @@
|
||||
// Copyright 2010 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 A wrapper for MockControl that provides mocks and assertions
|
||||
* for testing asynchronous code. All assertions will only be verified when
|
||||
* $verifyAll is called on the wrapped MockControl.
|
||||
*
|
||||
* This class is meant primarily for testing code that exposes asynchronous APIs
|
||||
* without being truly asynchronous (using asynchronous primitives like browser
|
||||
* events or timeouts). This is often the case when true asynchronous
|
||||
* depedencies have been mocked out. This means that it doesn't rely on
|
||||
* AsyncTestCase or DeferredTestCase, although it can be used with those as
|
||||
* well.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* <pre>
|
||||
* var mockControl = new goog.testing.MockControl();
|
||||
* var asyncMockControl = new goog.testing.async.MockControl(mockControl);
|
||||
*
|
||||
* myAsyncObject.onSuccess(asyncMockControl.asyncAssertEquals(
|
||||
* 'callback should run and pass the correct value',
|
||||
* 'http://someurl.com');
|
||||
* asyncMockControl.assertDeferredEquals(
|
||||
* 'deferred object should be resolved with the correct value',
|
||||
* 'http://someurl.com',
|
||||
* myAsyncObject.getDeferredUrl());
|
||||
* asyncMockControl.run();
|
||||
* mockControl.$verifyAll();
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.testing.async.MockControl');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.async.Deferred');
|
||||
goog.require('goog.debug');
|
||||
goog.require('goog.testing.asserts');
|
||||
goog.require('goog.testing.mockmatchers.IgnoreArgument');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Provides asynchronous mocks and assertions controlled by a parent
|
||||
* MockControl.
|
||||
*
|
||||
* @param {goog.testing.MockControl} mockControl The parent MockControl.
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.async.MockControl = function(mockControl) {
|
||||
/**
|
||||
* The parent MockControl.
|
||||
* @type {goog.testing.MockControl}
|
||||
* @private
|
||||
*/
|
||||
this.mockControl_ = mockControl;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a function that will assert that it will be called, and run the given
|
||||
* callback when it is.
|
||||
*
|
||||
* @param {string} name The name of the callback mock.
|
||||
* @param {function(...[*]) : *} callback The wrapped callback. This will be
|
||||
* called when the returned function is called.
|
||||
* @param {Object=} opt_selfObj The object which this should point to when the
|
||||
* callback is run.
|
||||
* @return {!Function} The mock callback.
|
||||
* @suppress {missingProperties} Mocks do not fit in the type system well.
|
||||
*/
|
||||
goog.testing.async.MockControl.prototype.createCallbackMock = function(
|
||||
name, callback, opt_selfObj) {
|
||||
goog.asserts.assert(
|
||||
goog.isString(name),
|
||||
'name parameter ' + goog.debug.deepExpose(name) + ' should be a string');
|
||||
|
||||
var ignored = new goog.testing.mockmatchers.IgnoreArgument();
|
||||
|
||||
// Use everyone's favorite "double-cast" trick to subvert the type system.
|
||||
var obj = /** @type {Object} */ (this.mockControl_.createFunctionMock(name));
|
||||
var fn = /** @type {Function} */ (obj);
|
||||
|
||||
fn(ignored).$does(function(args) {
|
||||
if (opt_selfObj) {
|
||||
callback = goog.bind(callback, opt_selfObj);
|
||||
}
|
||||
return callback.apply(this, args);
|
||||
});
|
||||
fn.$replay();
|
||||
return function() { return fn(arguments); };
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a function that will assert that its arguments are equal to the
|
||||
* arguments given to asyncAssertEquals. In addition, the function also asserts
|
||||
* that it will be called.
|
||||
*
|
||||
* @param {string} message A message to print if the arguments are wrong.
|
||||
* @param {...*} var_args The arguments to assert.
|
||||
* @return {function(...[*]) : void} The mock callback.
|
||||
*/
|
||||
goog.testing.async.MockControl.prototype.asyncAssertEquals = function(
|
||||
message, var_args) {
|
||||
var expectedArgs = Array.prototype.slice.call(arguments, 1);
|
||||
return this.createCallbackMock('asyncAssertEquals', function() {
|
||||
assertObjectEquals(
|
||||
message, expectedArgs, Array.prototype.slice.call(arguments));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that a deferred object will have an error and call its errback
|
||||
* function.
|
||||
* @param {goog.async.Deferred} deferred The deferred object.
|
||||
* @param {function() : void} fn A function wrapping the code in which the error
|
||||
* will occur.
|
||||
*/
|
||||
goog.testing.async.MockControl.prototype.assertDeferredError = function(
|
||||
deferred, fn) {
|
||||
deferred.addErrback(this.createCallbackMock(
|
||||
'assertDeferredError', function() {}));
|
||||
goog.testing.asserts.callWithoutLogging(fn);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that a deferred object will call its callback with the given value.
|
||||
*
|
||||
* @param {string} message A message to print if the arguments are wrong.
|
||||
* @param {goog.async.Deferred|*} expected The expected value. If this is a
|
||||
* deferred object, then the expected value is the deferred value.
|
||||
* @param {goog.async.Deferred|*} actual The actual value. If this is a deferred
|
||||
* object, then the actual value is the deferred value. Either this or
|
||||
* 'expected' must be deferred.
|
||||
*/
|
||||
goog.testing.async.MockControl.prototype.assertDeferredEquals = function(
|
||||
message, expected, actual) {
|
||||
if (expected instanceof goog.async.Deferred &&
|
||||
actual instanceof goog.async.Deferred) {
|
||||
// Assert that the first deferred is resolved.
|
||||
expected.addCallback(this.createCallbackMock(
|
||||
'assertDeferredEquals', function(exp) {
|
||||
// Assert that the second deferred is resolved, and that the value is
|
||||
// as expected.
|
||||
actual.addCallback(this.asyncAssertEquals(message, exp));
|
||||
}, this));
|
||||
} else if (expected instanceof goog.async.Deferred) {
|
||||
expected.addCallback(this.createCallbackMock(
|
||||
'assertDeferredEquals', function(exp) {
|
||||
assertObjectEquals(message, exp, actual);
|
||||
}));
|
||||
} else if (actual instanceof goog.async.Deferred) {
|
||||
actual.addCallback(this.asyncAssertEquals(message, expected));
|
||||
} else {
|
||||
throw Error('Either expected or actual must be deferred');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,824 @@
|
||||
// Copyright 2010 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.
|
||||
// All Rights Reserved.
|
||||
|
||||
/**
|
||||
* @fileoverview A class representing a set of test functions that use
|
||||
* asynchronous functions that cannot be meaningfully mocked.
|
||||
*
|
||||
* To create a Google-compatable JsUnit test using this test case, put the
|
||||
* following snippet in your test:
|
||||
*
|
||||
* var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
|
||||
*
|
||||
* To make the test runner wait for your asynchronous behaviour, use:
|
||||
*
|
||||
* asyncTestCase.waitForAsync('Waiting for xhr to respond');
|
||||
*
|
||||
* The next test will not start until the following call is made, or a
|
||||
* timeout occurs:
|
||||
*
|
||||
* asyncTestCase.continueTesting();
|
||||
*
|
||||
* There does NOT need to be a 1:1 mapping of waitForAsync calls and
|
||||
* continueTesting calls. The next test will be run after a single call to
|
||||
* continueTesting is made, as long as there is no subsequent call to
|
||||
* waitForAsync in the same thread.
|
||||
*
|
||||
* Example:
|
||||
* // Returning here would cause the next test to be run.
|
||||
* asyncTestCase.waitForAsync('description 1');
|
||||
* // Returning here would *not* cause the next test to be run.
|
||||
* // Only effect of additional waitForAsync() calls is an updated
|
||||
* // description in the case of a timeout.
|
||||
* asyncTestCase.waitForAsync('updated description');
|
||||
* asyncTestCase.continueTesting();
|
||||
* // Returning here would cause the next test to be run.
|
||||
* asyncTestCase.waitForAsync('just kidding, still running.');
|
||||
* // Returning here would *not* cause the next test to be run.
|
||||
*
|
||||
* This class supports asynchronous behaviour in all test functions except for
|
||||
* tearDownPage. If such support is needed, it can be added.
|
||||
*
|
||||
* Example Usage:
|
||||
*
|
||||
* var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
|
||||
* // Optionally, set a longer-than-normal step timeout.
|
||||
* asyncTestCase.stepTimeout = 30 * 1000;
|
||||
*
|
||||
* function testSetTimeout() {
|
||||
* var step = 0;
|
||||
* function stepCallback() {
|
||||
* step++;
|
||||
* switch (step) {
|
||||
* case 1:
|
||||
* var startTime = goog.now();
|
||||
* asyncTestCase.waitForAsync('step 1');
|
||||
* window.setTimeout(stepCallback, 100);
|
||||
* break;
|
||||
* case 2:
|
||||
* assertTrue('Timeout fired too soon',
|
||||
* goog.now() - startTime >= 100);
|
||||
* asyncTestCase.waitForAsync('step 2');
|
||||
* window.setTimeout(stepCallback, 100);
|
||||
* break;
|
||||
* case 3:
|
||||
* assertTrue('Timeout fired too soon',
|
||||
* goog.now() - startTime >= 200);
|
||||
* asyncTestCase.continueTesting();
|
||||
* break;
|
||||
* default:
|
||||
* fail('Unexpected call to stepCallback');
|
||||
* }
|
||||
* }
|
||||
* stepCallback();
|
||||
* }
|
||||
*
|
||||
* Known Issues:
|
||||
* IE7 Exceptions:
|
||||
* As the failingtest.html will show, it appears as though ie7 does not
|
||||
* propagate an exception past a function called using the func.call()
|
||||
* syntax. This causes case 3 of the failing tests (exceptions) to show up
|
||||
* as timeouts in IE.
|
||||
* window.onerror:
|
||||
* This seems to catch errors only in ff2/ff3. It does not work in Safari or
|
||||
* IE7. The consequence of this is that exceptions that would have been
|
||||
* caught by window.onerror show up as timeouts.
|
||||
*
|
||||
* @author agrieve@google.com (Andrew Grieve)
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.AsyncTestCase');
|
||||
goog.provide('goog.testing.AsyncTestCase.ControlBreakingException');
|
||||
|
||||
goog.require('goog.testing.TestCase');
|
||||
goog.require('goog.testing.TestCase.Test');
|
||||
goog.require('goog.testing.asserts');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A test case that is capable of running tests the contain asynchronous logic.
|
||||
* @param {string=} opt_name A descriptive name for the test case.
|
||||
* @extends {goog.testing.TestCase}
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.AsyncTestCase = function(opt_name) {
|
||||
goog.testing.TestCase.call(this, opt_name);
|
||||
};
|
||||
goog.inherits(goog.testing.AsyncTestCase, goog.testing.TestCase);
|
||||
|
||||
|
||||
/**
|
||||
* Represents result of top stack function call.
|
||||
* @typedef {{controlBreakingExceptionThrown: boolean, message: string}}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.TopStackFuncResult_;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An exception class used solely for control flow.
|
||||
* @param {string=} opt_message Error message.
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.AsyncTestCase.ControlBreakingException = function(opt_message) {
|
||||
/**
|
||||
* The exception message.
|
||||
* @type {string}
|
||||
*/
|
||||
this.message = opt_message || '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Return value for .toString().
|
||||
* @type {string}
|
||||
*/
|
||||
goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING =
|
||||
'[AsyncTestCase.ControlBreakingException]';
|
||||
|
||||
|
||||
/**
|
||||
* Marks this object as a ControlBreakingException
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.testing.AsyncTestCase.ControlBreakingException.prototype.
|
||||
isControlBreakingException = true;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.AsyncTestCase.ControlBreakingException.prototype.toString =
|
||||
function() {
|
||||
// This shows up in the console when the exception is not caught.
|
||||
return goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* How long to wait for a single step of a test to complete in milliseconds.
|
||||
* A step starts when a call to waitForAsync() is made.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.stepTimeout = 1000;
|
||||
|
||||
|
||||
/**
|
||||
* How long to wait after a failed test before moving onto the next one.
|
||||
* The purpose of this is to allow any pending async callbacks from the failing
|
||||
* test to finish up and not cause the next test to fail.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.timeToSleepAfterFailure = 500;
|
||||
|
||||
|
||||
/**
|
||||
* Turn on extra logging to help debug failing async. tests.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.enableDebugLogs_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* A reference to the original asserts.js assert_() function.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.origAssert_;
|
||||
|
||||
|
||||
/**
|
||||
* A reference to the original asserts.js fail() function.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.origFail_;
|
||||
|
||||
|
||||
/**
|
||||
* A reference to the original window.onerror function.
|
||||
* @type {Function|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.origOnError_;
|
||||
|
||||
|
||||
/**
|
||||
* The stage of the test we are currently on.
|
||||
* @type {Function|undefined}}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.curStepFunc_;
|
||||
|
||||
|
||||
/**
|
||||
* The name of the stage of the test we are currently on.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.curStepName_ = '';
|
||||
|
||||
|
||||
/**
|
||||
* The stage of the test we should run next.
|
||||
* @type {Function|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.nextStepFunc;
|
||||
|
||||
|
||||
/**
|
||||
* The name of the stage of the test we should run next.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.nextStepName_ = '';
|
||||
|
||||
|
||||
/**
|
||||
* The handle to the current setTimeout timer.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.timeoutHandle_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Marks if the cleanUp() function has been called for the currently running
|
||||
* test.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.cleanedUp_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* The currently active test.
|
||||
* @type {goog.testing.TestCase.Test|undefined}
|
||||
* @protected
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.activeTest;
|
||||
|
||||
|
||||
/**
|
||||
* A flag to prevent recursive exception handling.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.inException_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Flag used to determine if we can move to the next step in the testing loop.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.isReady_ = true;
|
||||
|
||||
|
||||
/**
|
||||
* Flag that tells us if there is a function in the call stack that will make
|
||||
* a call to pump_().
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.returnWillPump_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* The number of times we have thrown a ControlBreakingException so that we
|
||||
* know not to complain in our window.onerror handler. In Webkit, window.onerror
|
||||
* is not supported, and so this counter will keep going up but we won't care
|
||||
* about it.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.numControlExceptionsExpected_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* The current step name.
|
||||
* @return {!string} Step name.
|
||||
* @protected
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.getCurrentStepName = function() {
|
||||
return this.curStepName_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Preferred way of creating an AsyncTestCase. Creates one and initializes it
|
||||
* with the G_testRunner.
|
||||
* @param {string=} opt_name A descriptive name for the test case.
|
||||
* @return {!goog.testing.AsyncTestCase} The created AsyncTestCase.
|
||||
*/
|
||||
goog.testing.AsyncTestCase.createAndInstall = function(opt_name) {
|
||||
var asyncTestCase = new goog.testing.AsyncTestCase(opt_name);
|
||||
goog.testing.TestCase.initializeTestRunner(asyncTestCase);
|
||||
return asyncTestCase;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Informs the testcase not to continue to the next step in the test cycle
|
||||
* until continueTesting is called.
|
||||
* @param {string=} opt_name A description of what we are waiting for.
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.waitForAsync = function(opt_name) {
|
||||
this.isReady_ = false;
|
||||
this.curStepName_ = opt_name || this.curStepName_;
|
||||
|
||||
// Reset the timer that tracks if the async test takes too long.
|
||||
this.stopTimeoutTimer_();
|
||||
this.startTimeoutTimer_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Continue with the next step in the test cycle.
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.continueTesting = function() {
|
||||
if (!this.isReady_) {
|
||||
// We are a potential entry point, so we pump.
|
||||
this.isReady_ = true;
|
||||
this.stopTimeoutTimer_();
|
||||
// Run this in a setTimeout so that the caller has a chance to call
|
||||
// waitForAsync() again before we continue.
|
||||
this.timeout(goog.bind(this.pump_, this, null), 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles an exception thrown by a test.
|
||||
* @param {*=} opt_e The exception object associated with the failure
|
||||
* or a string.
|
||||
* @throws Always throws a ControlBreakingException.
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.doAsyncError = function(opt_e) {
|
||||
// If we've caught an exception that we threw, then just pass it along. This
|
||||
// can happen if doAsyncError() was called from a call to assert and then
|
||||
// again by pump_().
|
||||
if (opt_e && opt_e.isControlBreakingException) {
|
||||
throw opt_e;
|
||||
}
|
||||
|
||||
// Prevent another timeout error from triggering for this test step.
|
||||
this.stopTimeoutTimer_();
|
||||
|
||||
// doError() uses test.name. Here, we create a dummy test and give it a more
|
||||
// helpful name based on the step we're currently on.
|
||||
var fakeTestObj = new goog.testing.TestCase.Test(this.curStepName_,
|
||||
goog.nullFunction);
|
||||
if (this.activeTest) {
|
||||
fakeTestObj.name = this.activeTest.name + ' [' + fakeTestObj.name + ']';
|
||||
}
|
||||
|
||||
if (this.activeTest) {
|
||||
// Note: if the test has an error, and then tearDown has an error, they will
|
||||
// both be reported.
|
||||
this.doError(fakeTestObj, opt_e);
|
||||
} else {
|
||||
this.exceptionBeforeTest = opt_e;
|
||||
}
|
||||
|
||||
// This is a potential entry point, so we pump. We also add in a bit of a
|
||||
// delay to try and prevent any async behavior from the failed test from
|
||||
// causing the next test to fail.
|
||||
this.timeout(goog.bind(this.pump_, this, this.doAsyncErrorTearDown_),
|
||||
this.timeToSleepAfterFailure);
|
||||
|
||||
// We just caught an exception, so we do not want the code above us on the
|
||||
// stack to continue executing. If pump_ is in our call-stack, then it will
|
||||
// batch together multiple errors, so we only increment the count if pump_ is
|
||||
// not in the stack and let pump_ increment the count when it batches them.
|
||||
if (!this.returnWillPump_) {
|
||||
this.numControlExceptionsExpected_ += 1;
|
||||
this.dbgLog_('doAsynError: numControlExceptionsExpected_ = ' +
|
||||
this.numControlExceptionsExpected_ + ' and throwing exception.');
|
||||
}
|
||||
|
||||
// Copy the error message to ControlBreakingException.
|
||||
var message = '';
|
||||
if (typeof opt_e == 'string') {
|
||||
message = opt_e;
|
||||
} else if (opt_e && opt_e.message) {
|
||||
message = opt_e.message;
|
||||
}
|
||||
throw new goog.testing.AsyncTestCase.ControlBreakingException(message);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets up the test page and then waits until the test case has been marked
|
||||
* as ready before executing the tests.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.runTests = function() {
|
||||
this.hookAssert_();
|
||||
this.hookOnError_();
|
||||
|
||||
this.setNextStep_(this.doSetUpPage_, 'setUpPage');
|
||||
// We are an entry point, so we pump.
|
||||
this.pump_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Starts the tests.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.cycleTests = function() {
|
||||
// We are an entry point, so we pump.
|
||||
this.saveMessage('Start');
|
||||
this.setNextStep_(this.doIteration_, 'doIteration');
|
||||
this.pump_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Finalizes the test case, called when the tests have finished executing.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.finalize = function() {
|
||||
this.unhookAll_();
|
||||
this.setNextStep_(null, 'finalized');
|
||||
goog.testing.AsyncTestCase.superClass_.finalize.call(this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Enables verbose logging of what is happening inside of the AsyncTestCase.
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.enableDebugLogging = function() {
|
||||
this.enableDebugLogs_ = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs the given debug message to the console (when enabled).
|
||||
* @param {string} message The message to log.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.dbgLog_ = function(message) {
|
||||
if (this.enableDebugLogs_) {
|
||||
this.log('AsyncTestCase - ' + message);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Wraps doAsyncError() for when we are sure that the test runner has no user
|
||||
* code above it in the stack.
|
||||
* @param {string|Error=} opt_e The exception object associated with the
|
||||
* failure or a string.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.doTopOfStackAsyncError_ =
|
||||
function(opt_e) {
|
||||
/** @preserveTry */
|
||||
try {
|
||||
this.doAsyncError(opt_e);
|
||||
} catch (e) {
|
||||
// We know that we are on the top of the stack, so there is no need to
|
||||
// throw this exception in this case.
|
||||
if (e.isControlBreakingException) {
|
||||
this.numControlExceptionsExpected_ -= 1;
|
||||
this.dbgLog_('doTopOfStackAsyncError_: numControlExceptionsExpected_ = ' +
|
||||
this.numControlExceptionsExpected_ + ' and catching exception.');
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls the tearDown function, catching any errors, and then moves on to
|
||||
* the next step in the testing cycle.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.doAsyncErrorTearDown_ = function() {
|
||||
if (this.inException_) {
|
||||
// We get here if tearDown is throwing the error.
|
||||
// Upon calling continueTesting, the inline function 'doAsyncError' (set
|
||||
// below) is run.
|
||||
this.continueTesting();
|
||||
} else {
|
||||
this.inException_ = true;
|
||||
this.isReady_ = true;
|
||||
|
||||
// The continue point is different depending on if the error happened in
|
||||
// setUpPage() or in setUp()/test*()/tearDown().
|
||||
var stepFuncAfterError = this.nextStepFunc_;
|
||||
var stepNameAfterError = 'TestCase.execute (after error)';
|
||||
if (this.activeTest) {
|
||||
stepFuncAfterError = this.doIteration_;
|
||||
stepNameAfterError = 'doIteration (after error)';
|
||||
}
|
||||
|
||||
// We must set the next step before calling tearDown.
|
||||
this.setNextStep_(function() {
|
||||
this.inException_ = false;
|
||||
// This is null when an error happens in setUpPage.
|
||||
this.setNextStep_(stepFuncAfterError, stepNameAfterError);
|
||||
}, 'doAsyncError');
|
||||
|
||||
// Call the test's tearDown().
|
||||
if (!this.cleanedUp_) {
|
||||
this.cleanedUp_ = true;
|
||||
this.tearDown();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Replaces the asserts.js assert_() and fail() functions with a wrappers to
|
||||
* catch the exceptions.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.hookAssert_ = function() {
|
||||
if (!this.origAssert_) {
|
||||
this.origAssert_ = _assert;
|
||||
this.origFail_ = fail;
|
||||
var self = this;
|
||||
_assert = function() {
|
||||
/** @preserveTry */
|
||||
try {
|
||||
self.origAssert_.apply(this, arguments);
|
||||
} catch (e) {
|
||||
self.dbgLog_('Wrapping failed assert()');
|
||||
self.doAsyncError(e);
|
||||
}
|
||||
};
|
||||
fail = function() {
|
||||
/** @preserveTry */
|
||||
try {
|
||||
self.origFail_.apply(this, arguments);
|
||||
} catch (e) {
|
||||
self.dbgLog_('Wrapping fail()');
|
||||
self.doAsyncError(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets a window.onerror handler for catching exceptions that happen in async
|
||||
* callbacks. Note that as of Safari 3.1, Safari does not support this.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.hookOnError_ = function() {
|
||||
if (!this.origOnError_) {
|
||||
this.origOnError_ = window.onerror;
|
||||
var self = this;
|
||||
window.onerror = function(error, url, line) {
|
||||
// Ignore exceptions that we threw on purpose.
|
||||
var cbe =
|
||||
goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING;
|
||||
if (String(error).indexOf(cbe) != -1 &&
|
||||
self.numControlExceptionsExpected_) {
|
||||
self.numControlExceptionsExpected_ -= 1;
|
||||
self.dbgLog_('window.onerror: numControlExceptionsExpected_ = ' +
|
||||
self.numControlExceptionsExpected_ + ' and ignoring exception. ' +
|
||||
error);
|
||||
// Tell the browser not to compain about the error.
|
||||
return true;
|
||||
} else {
|
||||
self.dbgLog_('window.onerror caught exception.');
|
||||
var message = error + '\nURL: ' + url + '\nLine: ' + line;
|
||||
self.doTopOfStackAsyncError_(message);
|
||||
// Tell the browser to complain about the error.
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Unhooks window.onerror and _assert.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.unhookAll_ = function() {
|
||||
if (this.origOnError_) {
|
||||
window.onerror = this.origOnError_;
|
||||
this.origOnError_ = null;
|
||||
_assert = this.origAssert_;
|
||||
this.origAssert_ = null;
|
||||
fail = this.origFail_;
|
||||
this.origFail_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Enables the timeout timer. This timer fires unless continueTesting is
|
||||
* called.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.startTimeoutTimer_ = function() {
|
||||
if (!this.timeoutHandle_ && this.stepTimeout > 0) {
|
||||
this.timeoutHandle_ = this.timeout(goog.bind(function() {
|
||||
this.dbgLog_('Timeout timer fired with id ' + this.timeoutHandle_);
|
||||
this.timeoutHandle_ = 0;
|
||||
|
||||
this.doTopOfStackAsyncError_('Timed out while waiting for ' +
|
||||
'continueTesting() to be called.');
|
||||
}, this, null), this.stepTimeout);
|
||||
this.dbgLog_('Started timeout timer with id ' + this.timeoutHandle_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Disables the timeout timer.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.stopTimeoutTimer_ = function() {
|
||||
if (this.timeoutHandle_) {
|
||||
this.dbgLog_('Clearing timeout timer with id ' + this.timeoutHandle_);
|
||||
this.clearTimeout(this.timeoutHandle_);
|
||||
this.timeoutHandle_ = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the next function to call in our sequence of async callbacks.
|
||||
* @param {Function} func The function that executes the next step.
|
||||
* @param {string} name A description of the next step.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.setNextStep_ = function(func, name) {
|
||||
this.nextStepFunc_ = func && goog.bind(func, this);
|
||||
this.nextStepName_ = name;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls the given function, redirecting any exceptions to doAsyncError.
|
||||
* @param {Function} func The function to call.
|
||||
* @return {!goog.testing.AsyncTestCase.TopStackFuncResult_} Returns a
|
||||
* TopStackFuncResult_.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.callTopOfStackFunc_ = function(func) {
|
||||
/** @preserveTry */
|
||||
try {
|
||||
func.call(this);
|
||||
return {controlBreakingExceptionThrown: false, message: ''};
|
||||
} catch (e) {
|
||||
this.dbgLog_('Caught exception in callTopOfStackFunc_');
|
||||
/** @preserveTry */
|
||||
try {
|
||||
this.doAsyncError(e);
|
||||
return {controlBreakingExceptionThrown: false, message: ''};
|
||||
} catch (e2) {
|
||||
if (!e2.isControlBreakingException) {
|
||||
throw e2;
|
||||
}
|
||||
return {controlBreakingExceptionThrown: true, message: e2.message};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls the next callback when the isReady_ flag is true.
|
||||
* @param {Function=} opt_doFirst A function to call before pumping.
|
||||
* @private
|
||||
* @throws Throws a ControlBreakingException if there were any failing steps.
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.pump_ = function(opt_doFirst) {
|
||||
// If this function is already above us in the call-stack, then we should
|
||||
// return rather than pumping in order to minimize call-stack depth.
|
||||
if (!this.returnWillPump_) {
|
||||
this.setBatchTime(this.now());
|
||||
this.returnWillPump_ = true;
|
||||
var topFuncResult = {};
|
||||
|
||||
if (opt_doFirst) {
|
||||
topFuncResult = this.callTopOfStackFunc_(opt_doFirst);
|
||||
}
|
||||
// Note: we don't check for this.running here because it is not set to true
|
||||
// while executing setUpPage and tearDownPage.
|
||||
// Also, if isReady_ is false, then one of two things will happen:
|
||||
// 1. Our timeout callback will be called.
|
||||
// 2. The tests will call continueTesting(), which will call pump_() again.
|
||||
while (this.isReady_ && this.nextStepFunc_ &&
|
||||
!topFuncResult.controlBreakingExceptionThrown) {
|
||||
this.curStepFunc_ = this.nextStepFunc_;
|
||||
this.curStepName_ = this.nextStepName_;
|
||||
this.nextStepFunc_ = null;
|
||||
this.nextStepName_ = '';
|
||||
|
||||
this.dbgLog_('Performing step: ' + this.curStepName_);
|
||||
topFuncResult =
|
||||
this.callTopOfStackFunc_(/** @type {Function} */(this.curStepFunc_));
|
||||
|
||||
// If the max run time is exceeded call this function again async so as
|
||||
// not to block the browser.
|
||||
var delta = this.now() - this.getBatchTime();
|
||||
if (delta > goog.testing.TestCase.MAX_RUN_TIME &&
|
||||
!topFuncResult.controlBreakingExceptionThrown) {
|
||||
this.saveMessage('Breaking async');
|
||||
var self = this;
|
||||
this.timeout(function() { self.pump_(); }, 100);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.returnWillPump_ = false;
|
||||
} else if (opt_doFirst) {
|
||||
opt_doFirst.call(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets up the test page and then waits untill the test case has been marked
|
||||
* as ready before executing the tests.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.doSetUpPage_ = function() {
|
||||
this.setNextStep_(this.execute, 'TestCase.execute');
|
||||
this.setUpPage();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Step 1: Move to the next test.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.doIteration_ = function() {
|
||||
this.activeTest = this.next();
|
||||
if (this.activeTest && this.running) {
|
||||
this.result_.runCount++;
|
||||
// If this test should be marked as having failed, doIteration will go
|
||||
// straight to the next test.
|
||||
if (this.maybeFailTestEarly(this.activeTest)) {
|
||||
this.setNextStep_(this.doIteration_, 'doIteration');
|
||||
} else {
|
||||
this.setNextStep_(this.doSetUp_, 'setUp');
|
||||
}
|
||||
} else {
|
||||
// All tests done.
|
||||
this.finalize();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Step 2: Call setUp().
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.doSetUp_ = function() {
|
||||
this.log('Running test: ' + this.activeTest.name);
|
||||
this.cleanedUp_ = false;
|
||||
this.setNextStep_(this.doExecute_, this.activeTest.name);
|
||||
this.setUp();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Step 3: Call test.execute().
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.doExecute_ = function() {
|
||||
this.setNextStep_(this.doTearDown_, 'tearDown');
|
||||
this.activeTest.execute();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Step 4: Call tearDown().
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.doTearDown_ = function() {
|
||||
this.cleanedUp_ = true;
|
||||
this.setNextStep_(this.doNext_, 'doNext');
|
||||
this.tearDown();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Step 5: Call doSuccess()
|
||||
* @private
|
||||
*/
|
||||
goog.testing.AsyncTestCase.prototype.doNext_ = function() {
|
||||
this.setNextStep_(this.doIteration_, 'doIteration');
|
||||
this.doSuccess(/** @type {goog.testing.TestCase.Test} */(this.activeTest));
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2010 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.testing.benchmark');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.testing.PerformanceTable');
|
||||
goog.require('goog.testing.PerformanceTimer');
|
||||
goog.require('goog.testing.TestCase');
|
||||
|
||||
|
||||
/**
|
||||
* Run the benchmarks.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.benchmark.run_ = function() {
|
||||
// Parse the 'times' query parameter if it's set.
|
||||
var times = 200;
|
||||
var search = window.location.search;
|
||||
var timesMatch = search.match(/(?:\?|&)times=([^?&]+)/i);
|
||||
if (timesMatch) {
|
||||
times = Number(timesMatch[1]);
|
||||
}
|
||||
|
||||
var prefix = 'benchmark';
|
||||
|
||||
// First, get the functions.
|
||||
var testSource = goog.testing.TestCase.getGlobals(prefix);
|
||||
|
||||
var benchmarks = {};
|
||||
var names = [];
|
||||
|
||||
for (var name in testSource) {
|
||||
try {
|
||||
var ref = testSource[name];
|
||||
} catch (ex) {
|
||||
// NOTE(brenneman): When running tests from a file:// URL on Firefox 3.5
|
||||
// for Windows, any reference to window.sessionStorage raises
|
||||
// an "Operation is not supported" exception. Ignore any exceptions raised
|
||||
// by simply accessing global properties.
|
||||
}
|
||||
|
||||
if ((new RegExp('^' + prefix)).test(name) && goog.isFunction(ref)) {
|
||||
benchmarks[name] = ref;
|
||||
names.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
document.body.appendChild(
|
||||
goog.dom.createTextNode(
|
||||
'Running ' + names.length + ' benchmarks ' + times + ' times each.'));
|
||||
document.body.appendChild(goog.dom.createElement(goog.dom.TagName.BR));
|
||||
|
||||
names.sort();
|
||||
|
||||
// Build a table and timer.
|
||||
var performanceTimer = new goog.testing.PerformanceTimer(times);
|
||||
performanceTimer.setDiscardOutliers(true);
|
||||
|
||||
var performanceTable = new goog.testing.PerformanceTable(document.body,
|
||||
performanceTimer, 2);
|
||||
|
||||
// Next, run the benchmarks.
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
performanceTable.run(benchmarks[names[i]], names[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Onload handler that runs the benchmarks.
|
||||
* @param {Event} e The event object.
|
||||
*/
|
||||
window.onload = function(e) {
|
||||
goog.testing.benchmark.run_();
|
||||
};
|
||||
@@ -0,0 +1,689 @@
|
||||
// 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 Defines test classes for tests that can wait for conditions.
|
||||
*
|
||||
* Normal unit tests must complete their test logic within a single function
|
||||
* execution. This is ideal for most tests, but makes it difficult to test
|
||||
* routines that require real time to complete. The tests and TestCase in this
|
||||
* file allow for tests that can wait until a condition is true before
|
||||
* continuing execution.
|
||||
*
|
||||
* Each test has the typical three phases of execution: setUp, the test itself,
|
||||
* and tearDown. During each phase, the test function may add wait conditions,
|
||||
* which result in new test steps being added for that phase. All steps in a
|
||||
* given phase must complete before moving on to the next phase. An error in
|
||||
* any phase will stop that test and report the error to the test runner.
|
||||
*
|
||||
* This class should not be used where adequate mocks exist. Time-based routines
|
||||
* should use the MockClock, which runs much faster and provides equivalent
|
||||
* results. Continuation tests should be used for testing code that depends on
|
||||
* browser behaviors that are difficult to mock. For example, testing code that
|
||||
* relies on Iframe load events, event or layout code that requires a setTimeout
|
||||
* to become valid, and other browser-dependent native object interactions for
|
||||
* which mocks are insufficient.
|
||||
*
|
||||
* Sample usage:
|
||||
*
|
||||
* <pre>
|
||||
* var testCase = new goog.testing.ContinuationTestCase();
|
||||
* testCase.autoDiscoverTests();
|
||||
*
|
||||
* if (typeof G_testRunner != 'undefined') {
|
||||
* G_testRunner.initialize(testCase);
|
||||
* }
|
||||
*
|
||||
* function testWaiting() {
|
||||
* var someVar = true;
|
||||
* waitForTimeout(function() {
|
||||
* assertTrue(someVar)
|
||||
* }, 500);
|
||||
* }
|
||||
*
|
||||
* function testWaitForEvent() {
|
||||
* var et = goog.events.EventTarget();
|
||||
* waitForEvent(et, 'test', function() {
|
||||
* // Test step runs after the event fires.
|
||||
* })
|
||||
* et.dispatchEvent(et, 'test');
|
||||
* }
|
||||
*
|
||||
* function testWaitForCondition() {
|
||||
* var counter = 0;
|
||||
*
|
||||
* waitForCondition(function() {
|
||||
* // This function is evaluated periodically until it returns true, or it
|
||||
* // times out.
|
||||
* return ++counter >= 3;
|
||||
* }, function() {
|
||||
* // This test step is run once the condition becomes true.
|
||||
* assertEquals(3, counter);
|
||||
* });
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author brenneman@google.com (Shawn Brenneman)
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.testing.ContinuationTestCase');
|
||||
goog.provide('goog.testing.ContinuationTestCase.Step');
|
||||
goog.provide('goog.testing.ContinuationTestCase.Test');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.testing.TestCase');
|
||||
goog.require('goog.testing.TestCase.Test');
|
||||
goog.require('goog.testing.asserts');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a test case that supports tests with continuations. Test functions
|
||||
* may issue "wait" commands that suspend the test temporarily and continue once
|
||||
* the wait condition is met.
|
||||
*
|
||||
* @param {string=} opt_name Optional name for the test case.
|
||||
* @constructor
|
||||
* @extends {goog.testing.TestCase}
|
||||
*/
|
||||
goog.testing.ContinuationTestCase = function(opt_name) {
|
||||
goog.testing.TestCase.call(this, opt_name);
|
||||
|
||||
/**
|
||||
* An event handler for waiting on Closure or browser events during tests.
|
||||
* @type {goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.handler_ = new goog.events.EventHandler(this);
|
||||
};
|
||||
goog.inherits(goog.testing.ContinuationTestCase, goog.testing.TestCase);
|
||||
|
||||
|
||||
/**
|
||||
* The default maximum time to wait for a single test step in milliseconds.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.MAX_TIMEOUT = 1000;
|
||||
|
||||
|
||||
/**
|
||||
* Lock used to prevent multiple test steps from running recursively.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.locked_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* The current test being run.
|
||||
* @type {goog.testing.ContinuationTestCase.Test}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.prototype.currentTest_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Enables or disables the wait functions in the global scope.
|
||||
* @param {boolean} enable Whether the wait functions should be exported.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.prototype.enableWaitFunctions_ =
|
||||
function(enable) {
|
||||
if (enable) {
|
||||
goog.exportSymbol('waitForCondition',
|
||||
goog.bind(this.waitForCondition, this));
|
||||
goog.exportSymbol('waitForEvent', goog.bind(this.waitForEvent, this));
|
||||
goog.exportSymbol('waitForTimeout', goog.bind(this.waitForTimeout, this));
|
||||
} else {
|
||||
// Internet Explorer doesn't allow deletion of properties on the window.
|
||||
goog.global['waitForCondition'] = undefined;
|
||||
goog.global['waitForEvent'] = undefined;
|
||||
goog.global['waitForTimeout'] = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.ContinuationTestCase.prototype.runTests = function() {
|
||||
this.enableWaitFunctions_(true);
|
||||
goog.testing.ContinuationTestCase.superClass_.runTests.call(this);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.ContinuationTestCase.prototype.finalize = function() {
|
||||
this.enableWaitFunctions_(false);
|
||||
goog.testing.ContinuationTestCase.superClass_.finalize.call(this);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.ContinuationTestCase.prototype.cycleTests = function() {
|
||||
// Get the next test in the queue.
|
||||
if (!this.currentTest_) {
|
||||
this.currentTest_ = this.createNextTest_();
|
||||
}
|
||||
|
||||
// Run the next step of the current test, or exit if all tests are complete.
|
||||
if (this.currentTest_) {
|
||||
this.runNextStep_();
|
||||
} else {
|
||||
this.finalize();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the next test in the queue.
|
||||
* @return {goog.testing.ContinuationTestCase.Test} The next test to execute, or
|
||||
* null if no pending tests remain.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.prototype.createNextTest_ = function() {
|
||||
var test = this.next();
|
||||
if (!test) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
var name = test.name;
|
||||
goog.testing.TestCase.currentTestName = name;
|
||||
this.result_.runCount++;
|
||||
this.log('Running test: ' + name);
|
||||
|
||||
return new goog.testing.ContinuationTestCase.Test(
|
||||
new goog.testing.TestCase.Test(name, this.setUp, this),
|
||||
test,
|
||||
new goog.testing.TestCase.Test(name, this.tearDown, this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Cleans up a finished test and cycles to the next test.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.prototype.finishTest_ = function() {
|
||||
var err = this.currentTest_.getError();
|
||||
if (err) {
|
||||
this.doError(this.currentTest_, err);
|
||||
} else {
|
||||
this.doSuccess(this.currentTest_);
|
||||
}
|
||||
|
||||
goog.testing.TestCase.currentTestName = null;
|
||||
this.currentTest_ = null;
|
||||
this.locked_ = false;
|
||||
this.handler_.removeAll();
|
||||
|
||||
this.timeout(goog.bind(this.cycleTests, this), 0);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Executes the next step in the current phase, advancing through each phase as
|
||||
* all steps are completed.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.prototype.runNextStep_ = function() {
|
||||
if (this.locked_) {
|
||||
// Attempting to run a step before the previous step has finished. Try again
|
||||
// after that step has released the lock.
|
||||
return;
|
||||
}
|
||||
|
||||
var phase = this.currentTest_.getCurrentPhase();
|
||||
|
||||
if (!phase || !phase.length) {
|
||||
// No more steps for this test.
|
||||
this.finishTest_();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the next step that is not in a wait state.
|
||||
var stepIndex = goog.array.findIndex(phase, function(step) {
|
||||
return !step.waiting;
|
||||
});
|
||||
|
||||
if (stepIndex < 0) {
|
||||
// All active steps are currently waiting. Return until one wakes up.
|
||||
return;
|
||||
}
|
||||
|
||||
this.locked_ = true;
|
||||
var step = phase[stepIndex];
|
||||
|
||||
try {
|
||||
step.execute();
|
||||
// Remove the successfully completed step. If an error is thrown, all steps
|
||||
// will be removed for this phase.
|
||||
goog.array.removeAt(phase, stepIndex);
|
||||
|
||||
} catch (e) {
|
||||
this.currentTest_.setError(e);
|
||||
|
||||
// An assertion has failed, or an exception was raised. Clear the current
|
||||
// phase, whether it is setUp, test, or tearDown.
|
||||
this.currentTest_.cancelCurrentPhase();
|
||||
|
||||
// Cancel the setUp and test phase no matter where the error occurred. The
|
||||
// tearDown phase will still run if it has pending steps.
|
||||
this.currentTest_.cancelTestPhase();
|
||||
}
|
||||
|
||||
this.locked_ = false;
|
||||
this.runNextStep_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new test step that will run after a user-specified
|
||||
* timeout. No guarantee is made on the execution order of the
|
||||
* continuation, except for those provided by each browser's
|
||||
* window.setTimeout. In particular, if two continuations are
|
||||
* registered at the same time with very small delta for their
|
||||
* durations, this class can not guarantee that the continuation with
|
||||
* the smaller duration will be executed first.
|
||||
* @param {Function} continuation The test function to invoke after the timeout.
|
||||
* @param {number=} opt_duration The length of the timeout in milliseconds.
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.prototype.waitForTimeout =
|
||||
function(continuation, opt_duration) {
|
||||
var step = this.addStep_(continuation);
|
||||
step.setTimeout(goog.bind(this.handleComplete_, this, step),
|
||||
opt_duration || 0);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new test step that will run after an event has fired. If the event
|
||||
* does not fire within a reasonable timeout, the test will fail.
|
||||
* @param {goog.events.EventTarget|EventTarget} eventTarget The target that will
|
||||
* fire the event.
|
||||
* @param {string} eventType The type of event to listen for.
|
||||
* @param {Function} continuation The test function to invoke after the event
|
||||
* fires.
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.prototype.waitForEvent = function(
|
||||
eventTarget,
|
||||
eventType,
|
||||
continuation) {
|
||||
|
||||
var step = this.addStep_(continuation);
|
||||
|
||||
var duration = goog.testing.ContinuationTestCase.MAX_TIMEOUT;
|
||||
step.setTimeout(goog.bind(this.handleTimeout_, this, step, duration),
|
||||
duration);
|
||||
|
||||
this.handler_.listenOnce(eventTarget,
|
||||
eventType,
|
||||
goog.bind(this.handleComplete_, this, step));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new test step which will run once a condition becomes true. The
|
||||
* condition will be polled at a user-specified interval until it becomes true,
|
||||
* or until a maximum timeout is reached.
|
||||
* @param {Function} condition The condition to poll.
|
||||
* @param {Function} continuation The test code to evaluate once the condition
|
||||
* becomes true.
|
||||
* @param {number=} opt_interval The polling interval in milliseconds.
|
||||
* @param {number=} opt_maxTimeout The maximum amount of time to wait for the
|
||||
* condition in milliseconds (defaults to 1000).
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.prototype.waitForCondition = function(
|
||||
condition,
|
||||
continuation,
|
||||
opt_interval,
|
||||
opt_maxTimeout) {
|
||||
|
||||
var interval = opt_interval || 100;
|
||||
var timeout = opt_maxTimeout || goog.testing.ContinuationTestCase.MAX_TIMEOUT;
|
||||
|
||||
var step = this.addStep_(continuation);
|
||||
this.testCondition_(step, condition, goog.now(), interval, timeout);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new asynchronous test step which will be added to the current test
|
||||
* phase.
|
||||
* @param {Function} func The test function that will be executed for this step.
|
||||
* @return {goog.testing.ContinuationTestCase.Step} A new test step.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.prototype.addStep_ = function(func) {
|
||||
if (!this.currentTest_) {
|
||||
throw Error('Cannot add test steps outside of a running test.');
|
||||
}
|
||||
|
||||
var step = new goog.testing.ContinuationTestCase.Step(
|
||||
this.currentTest_.name,
|
||||
func,
|
||||
this.currentTest_.scope);
|
||||
this.currentTest_.addStep(step);
|
||||
return step;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles completion of a step's wait condition. Advances the test, allowing
|
||||
* the step's test method to run.
|
||||
* @param {goog.testing.ContinuationTestCase.Step} step The step that has
|
||||
* finished waiting.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.prototype.handleComplete_ = function(step) {
|
||||
step.clearTimeout();
|
||||
step.waiting = false;
|
||||
this.runNextStep_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the timeout event for a step that has exceeded the maximum time. This
|
||||
* causes the current test to fail.
|
||||
* @param {goog.testing.ContinuationTestCase.Step} step The timed-out step.
|
||||
* @param {number} duration The length of the timeout in milliseconds.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.prototype.handleTimeout_ =
|
||||
function(step, duration) {
|
||||
step.ref = function() {
|
||||
fail('Continuation timed out after ' + duration + 'ms.');
|
||||
};
|
||||
|
||||
// Since the test is failing, cancel any other pending event listeners.
|
||||
this.handler_.removeAll();
|
||||
this.handleComplete_(step);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Tests a wait condition and executes the associated test step once the
|
||||
* condition is true.
|
||||
*
|
||||
* If the condition does not become true before the maximum duration, the
|
||||
* interval will stop and the test step will fail in the kill timer.
|
||||
*
|
||||
* @param {goog.testing.ContinuationTestCase.Step} step The waiting test step.
|
||||
* @param {Function} condition The test condition.
|
||||
* @param {number} startTime Time when the test step began waiting.
|
||||
* @param {number} interval The duration in milliseconds to wait between tests.
|
||||
* @param {number} timeout The maximum amount of time to wait for the condition
|
||||
* to become true. Measured from the startTime in milliseconds.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.prototype.testCondition_ = function(
|
||||
step,
|
||||
condition,
|
||||
startTime,
|
||||
interval,
|
||||
timeout) {
|
||||
|
||||
var duration = goog.now() - startTime;
|
||||
|
||||
if (condition()) {
|
||||
this.handleComplete_(step);
|
||||
} else if (duration < timeout) {
|
||||
step.setTimeout(goog.bind(this.testCondition_,
|
||||
this,
|
||||
step,
|
||||
condition,
|
||||
startTime,
|
||||
interval,
|
||||
timeout),
|
||||
interval);
|
||||
} else {
|
||||
this.handleTimeout_(step, duration);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a continuation test case, which consists of multiple test steps that
|
||||
* occur in several phases.
|
||||
*
|
||||
* The steps are distributed between setUp, test, and tearDown phases. During
|
||||
* the execution of each step, 0 or more steps may be added to the current
|
||||
* phase. Once all steps in a phase have completed, the next phase will be
|
||||
* executed.
|
||||
*
|
||||
* If any errors occur (such as an assertion failure), the setUp and Test phases
|
||||
* will be cancelled immediately. The tearDown phase will always start, but may
|
||||
* be cancelled as well if it raises an error.
|
||||
*
|
||||
* @param {goog.testing.TestCase.Test} setUp A setUp test method to run before
|
||||
* the main test phase.
|
||||
* @param {goog.testing.TestCase.Test} test A test method to run.
|
||||
* @param {goog.testing.TestCase.Test} tearDown A tearDown test method to run
|
||||
* after the test method completes or fails.
|
||||
* @constructor
|
||||
* @extends {goog.testing.TestCase.Test}
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Test = function(setUp, test, tearDown) {
|
||||
// This test container has a name, but no evaluation function or scope.
|
||||
goog.testing.TestCase.Test.call(this, test.name, null, null);
|
||||
|
||||
/**
|
||||
* The list of test steps to run during setUp.
|
||||
* @type {Array.<goog.testing.TestCase.Test>}
|
||||
* @private
|
||||
*/
|
||||
this.setUp_ = [setUp];
|
||||
|
||||
/**
|
||||
* The list of test steps to run for the actual test.
|
||||
* @type {Array.<goog.testing.TestCase.Test>}
|
||||
* @private
|
||||
*/
|
||||
this.test_ = [test];
|
||||
|
||||
/**
|
||||
* The list of test steps to run during the tearDown phase.
|
||||
* @type {Array.<goog.testing.TestCase.Test>}
|
||||
* @private
|
||||
*/
|
||||
this.tearDown_ = [tearDown];
|
||||
};
|
||||
goog.inherits(goog.testing.ContinuationTestCase.Test,
|
||||
goog.testing.TestCase.Test);
|
||||
|
||||
|
||||
/**
|
||||
* The first error encountered during the test run, if any.
|
||||
* @type {Error}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Test.prototype.error_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* @return {Error} The first error to be raised during the test run or null if
|
||||
* no errors occurred.
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Test.prototype.getError = function() {
|
||||
return this.error_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets an error for the test so it can be reported. Only the first error set
|
||||
* during a test will be reported. Additional errors that occur in later test
|
||||
* phases will be discarded.
|
||||
* @param {Error} e An error.
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Test.prototype.setError = function(e) {
|
||||
this.error_ = this.error_ || e;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Array.<goog.testing.TestCase.Test>} The current phase of steps
|
||||
* being processed. Returns null if all steps have been completed.
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Test.prototype.getCurrentPhase = function() {
|
||||
if (this.setUp_.length) {
|
||||
return this.setUp_;
|
||||
}
|
||||
|
||||
if (this.test_.length) {
|
||||
return this.test_;
|
||||
}
|
||||
|
||||
if (this.tearDown_.length) {
|
||||
return this.tearDown_;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a new test step to the end of the current phase. The new step will wait
|
||||
* for a condition to be met before running, or will fail after a timeout.
|
||||
* @param {goog.testing.ContinuationTestCase.Step} step The test step to add.
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Test.prototype.addStep = function(step) {
|
||||
var phase = this.getCurrentPhase();
|
||||
if (phase) {
|
||||
phase.push(step);
|
||||
} else {
|
||||
throw Error('Attempted to add a step to a completed test.');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Cancels all remaining steps in the current phase. Called after an error in
|
||||
* any phase occurs.
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Test.prototype.cancelCurrentPhase =
|
||||
function() {
|
||||
this.cancelPhase_(this.getCurrentPhase());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Skips the rest of the setUp and test phases, but leaves the tearDown phase to
|
||||
* clean up.
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Test.prototype.cancelTestPhase = function() {
|
||||
this.cancelPhase_(this.setUp_);
|
||||
this.cancelPhase_(this.test_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears a test phase and cancels any pending steps found.
|
||||
* @param {Array.<goog.testing.TestCase.Test>} phase A list of test steps.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Test.prototype.cancelPhase_ =
|
||||
function(phase) {
|
||||
while (phase && phase.length) {
|
||||
var step = phase.pop();
|
||||
if (step instanceof goog.testing.ContinuationTestCase.Step) {
|
||||
step.clearTimeout();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a single step in a larger continuation test. Each step is similar
|
||||
* to a typical TestCase test, except it may wait for an event or timeout to
|
||||
* occur before running the test function.
|
||||
*
|
||||
* @param {string} name The test name.
|
||||
* @param {Function} ref The test function to run.
|
||||
* @param {Object=} opt_scope The object context to run the test in.
|
||||
* @constructor
|
||||
* @extends {goog.testing.TestCase.Test}
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Step = function(name, ref, opt_scope) {
|
||||
goog.testing.TestCase.Test.call(this, name, ref, opt_scope);
|
||||
};
|
||||
goog.inherits(goog.testing.ContinuationTestCase.Step,
|
||||
goog.testing.TestCase.Test);
|
||||
|
||||
|
||||
/**
|
||||
* Whether the step is currently waiting for a condition to continue. All new
|
||||
* steps begin in wait state.
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Step.prototype.waiting = true;
|
||||
|
||||
|
||||
/**
|
||||
* A saved reference to window.clearTimeout so that MockClock or other overrides
|
||||
* don't affect continuation timeouts.
|
||||
* @type {Function}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Step.protectedClearTimeout_ =
|
||||
window.clearTimeout;
|
||||
|
||||
|
||||
/**
|
||||
* A saved reference to window.setTimeout so that MockClock or other overrides
|
||||
* don't affect continuation timeouts.
|
||||
* @type {Function}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Step.protectedSetTimeout_ = window.setTimeout;
|
||||
|
||||
|
||||
/**
|
||||
* Key to this step's timeout. If the step is waiting for an event, the timeout
|
||||
* will be used as a kill timer. If the step is waiting
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Step.prototype.timeout_;
|
||||
|
||||
|
||||
/**
|
||||
* Starts a timeout for this step. Each step may have only one timeout active at
|
||||
* a time.
|
||||
* @param {Function} func The function to call after the timeout.
|
||||
* @param {number} duration The number of milliseconds to wait before invoking
|
||||
* the function.
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Step.prototype.setTimeout =
|
||||
function(func, duration) {
|
||||
|
||||
this.clearTimeout();
|
||||
|
||||
var setTimeout = goog.testing.ContinuationTestCase.Step.protectedSetTimeout_;
|
||||
this.timeout_ = setTimeout(func, duration);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the current timeout if it is active.
|
||||
*/
|
||||
goog.testing.ContinuationTestCase.Step.prototype.clearTimeout = function() {
|
||||
if (this.timeout_) {
|
||||
var clear = goog.testing.ContinuationTestCase.Step.protectedClearTimeout_;
|
||||
|
||||
clear(this.timeout_);
|
||||
delete this.timeout_;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,155 @@
|
||||
// Copyright 2010 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 Defines DeferredTestCase class. By calling waitForDeferred(),
|
||||
* tests in DeferredTestCase can wait for a Deferred object to complete its
|
||||
* callbacks before continuing to the next test.
|
||||
*
|
||||
* Example Usage:
|
||||
*
|
||||
* var deferredTestCase = goog.testing.DeferredTestCase.createAndInstall();
|
||||
* // Optionally, set a longer-than-usual step timeout.
|
||||
* deferredTestCase.stepTimeout = 15 * 1000; // 15 seconds
|
||||
*
|
||||
* function testDeferredCallbacks() {
|
||||
* var callbackTime = goog.now();
|
||||
* var callbacks = new goog.async.Deferred();
|
||||
* deferredTestCase.addWaitForAsync('Waiting for 1st callback', callbacks);
|
||||
* callbacks.addCallback(
|
||||
* function() {
|
||||
* assertTrue(
|
||||
* 'We\'re going back in time!', goog.now() >= callbackTime);
|
||||
* callbackTime = goog.now();
|
||||
* });
|
||||
* deferredTestCase.addWaitForAsync('Waiting for 2nd callback', callbacks);
|
||||
* callbacks.addCallback(
|
||||
* function() {
|
||||
* assertTrue(
|
||||
* 'We\'re going back in time!', goog.now() >= callbackTime);
|
||||
* callbackTime = goog.now();
|
||||
* });
|
||||
* deferredTestCase.addWaitForAsync('Waiting for last callback', callbacks);
|
||||
* callbacks.addCallback(
|
||||
* function() {
|
||||
* assertTrue(
|
||||
* 'We\'re going back in time!', goog.now() >= callbackTime);
|
||||
* callbackTime = goog.now();
|
||||
* });
|
||||
*
|
||||
* deferredTestCase.waitForDeferred(callbacks);
|
||||
* }
|
||||
*
|
||||
* Note that DeferredTestCase still preserves the functionality of
|
||||
* AsyncTestCase.
|
||||
*
|
||||
* @see.goog.async.Deferred
|
||||
* @see goog.testing.AsyncTestCase
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.DeferredTestCase');
|
||||
|
||||
goog.require('goog.async.Deferred');
|
||||
goog.require('goog.testing.AsyncTestCase');
|
||||
goog.require('goog.testing.TestCase');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A test case that can asynchronously wait on a Deferred object.
|
||||
* @param {string=} opt_name A descriptive name for the test case.
|
||||
* @constructor
|
||||
* @extends {goog.testing.AsyncTestCase}
|
||||
*/
|
||||
goog.testing.DeferredTestCase = function(opt_name) {
|
||||
goog.testing.AsyncTestCase.call(this, opt_name);
|
||||
};
|
||||
goog.inherits(goog.testing.DeferredTestCase, goog.testing.AsyncTestCase);
|
||||
|
||||
|
||||
/**
|
||||
* Preferred way of creating a DeferredTestCase. Creates one and initializes it
|
||||
* with the G_testRunner.
|
||||
* @param {string=} opt_name A descriptive name for the test case.
|
||||
* @return {goog.testing.DeferredTestCase} The created DeferredTestCase.
|
||||
*/
|
||||
goog.testing.DeferredTestCase.createAndInstall = function(opt_name) {
|
||||
var deferredTestCase = new goog.testing.DeferredTestCase(opt_name);
|
||||
goog.testing.TestCase.initializeTestRunner(deferredTestCase);
|
||||
return deferredTestCase;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for when the test produces an error.
|
||||
* @param {Error|string} err The error object.
|
||||
* @protected
|
||||
* @throws Always throws a ControlBreakingException.
|
||||
*/
|
||||
goog.testing.DeferredTestCase.prototype.onError = function(err) {
|
||||
this.doAsyncError(err);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for when the test succeeds.
|
||||
* @protected
|
||||
*/
|
||||
goog.testing.DeferredTestCase.prototype.onSuccess = function() {
|
||||
this.continueTesting();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a callback to update the wait message of this async test case. Using
|
||||
* this method generously also helps to document the test flow.
|
||||
* @param {string} msg The update wait status message.
|
||||
* @param {goog.async.Deferred} d The deferred object to add the waitForAsync
|
||||
* callback to.
|
||||
* @see goog.testing.AsyncTestCase#waitForAsync
|
||||
*/
|
||||
goog.testing.DeferredTestCase.prototype.addWaitForAsync = function(msg, d) {
|
||||
d.addCallback(goog.bind(this.waitForAsync, this, msg));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Wires up given Deferred object to the test case, then starts the
|
||||
* goog.async.Deferred object's callback.
|
||||
* @param {!string|goog.async.Deferred} a The wait status message or the
|
||||
* deferred object to wait for.
|
||||
* @param {goog.async.Deferred=} opt_b The deferred object to wait for.
|
||||
*/
|
||||
goog.testing.DeferredTestCase.prototype.waitForDeferred = function(a, opt_b) {
|
||||
var waitMsg;
|
||||
var deferred;
|
||||
switch (arguments.length) {
|
||||
case 1:
|
||||
deferred = a;
|
||||
waitMsg = null;
|
||||
break;
|
||||
case 2:
|
||||
deferred = opt_b;
|
||||
waitMsg = a;
|
||||
break;
|
||||
default: // Shouldn't be here in compiled mode
|
||||
throw Error('Invalid number of arguments');
|
||||
}
|
||||
deferred.addCallbacks(this.onSuccess, this.onError, this);
|
||||
if (!waitMsg) {
|
||||
waitMsg = 'Waiting for deferred in ' + this.getCurrentStepName();
|
||||
}
|
||||
this.waitForAsync( /** @type {!string} */ (waitMsg));
|
||||
deferred.callback(true);
|
||||
};
|
||||
608
float-no-zero/closure-library/closure/goog/testing/dom.js
Normal file
608
float-no-zero/closure-library/closure/goog/testing/dom.js
Normal file
@@ -0,0 +1,608 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview Testing utilities for DOM related tests.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.dom');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.NodeIterator');
|
||||
goog.require('goog.dom.NodeType');
|
||||
goog.require('goog.dom.TagIterator');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.iter');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.testing.asserts');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
/**
|
||||
* A unique object to use as an end tag marker.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.dom.END_TAG_MARKER_ = {};
|
||||
|
||||
|
||||
/**
|
||||
* Tests if the given iterator over nodes matches the given Array of node
|
||||
* descriptors. Throws an error if any match fails.
|
||||
* @param {goog.iter.Iterator} it An iterator over nodes.
|
||||
* @param {Array.<Node|number|string>} array Array of node descriptors to match
|
||||
* against. Node descriptors can be any of the following:
|
||||
* Node: Test if the two nodes are equal.
|
||||
* number: Test node.nodeType == number.
|
||||
* string starting with '#': Match the node's id with the text
|
||||
* after "#".
|
||||
* other string: Match the text node's contents.
|
||||
*/
|
||||
goog.testing.dom.assertNodesMatch = function(it, array) {
|
||||
var i = 0;
|
||||
goog.iter.forEach(it, function(node) {
|
||||
if (array.length <= i) {
|
||||
fail('Got more nodes than expected: ' + goog.testing.dom.describeNode_(
|
||||
node));
|
||||
}
|
||||
var expected = array[i];
|
||||
|
||||
if (goog.dom.isNodeLike(expected)) {
|
||||
assertEquals('Nodes should match at position ' + i, expected, node);
|
||||
} else if (goog.isNumber(expected)) {
|
||||
assertEquals('Node types should match at position ' + i, expected,
|
||||
node.nodeType);
|
||||
} else if (expected.charAt(0) == '#') {
|
||||
assertEquals('Expected element at position ' + i,
|
||||
goog.dom.NodeType.ELEMENT, node.nodeType);
|
||||
var expectedId = expected.substr(1);
|
||||
assertEquals('IDs should match at position ' + i,
|
||||
expectedId, node.id);
|
||||
|
||||
} else {
|
||||
assertEquals('Expected text node at position ' + i,
|
||||
goog.dom.NodeType.TEXT, node.nodeType);
|
||||
assertEquals('Node contents should match at position ' + i,
|
||||
expected, node.nodeValue);
|
||||
}
|
||||
|
||||
i++;
|
||||
});
|
||||
|
||||
assertEquals('Used entire match array', array.length, i);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Exposes a node as a string.
|
||||
* @param {Node} node A node.
|
||||
* @return {string} A string representation of the node.
|
||||
*/
|
||||
goog.testing.dom.exposeNode = function(node) {
|
||||
return (node.tagName || node.nodeValue) + (node.id ? '#' + node.id : '') +
|
||||
':"' + (node.innerHTML || '') + '"';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Exposes the nodes of a range wrapper as a string.
|
||||
* @param {goog.dom.AbstractRange} range A range.
|
||||
* @return {string} A string representation of the range.
|
||||
*/
|
||||
goog.testing.dom.exposeRange = function(range) {
|
||||
// This is deliberately not implemented as
|
||||
// goog.dom.AbstractRange.prototype.toString, because it is non-authoritative.
|
||||
// Two equivalent ranges may have very different exposeRange values, and
|
||||
// two different ranges may have equal exposeRange values.
|
||||
// (The mapping of ranges to DOM nodes/offsets is a many-to-many mapping).
|
||||
if (!range) {
|
||||
return 'null';
|
||||
}
|
||||
return goog.testing.dom.exposeNode(range.getStartNode()) + ':' +
|
||||
range.getStartOffset() + ' to ' +
|
||||
goog.testing.dom.exposeNode(range.getEndNode()) + ':' +
|
||||
range.getEndOffset();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Determines if the current user agent matches the specified string. Returns
|
||||
* false if the string does specify at least one user agent but does not match
|
||||
* the running agent.
|
||||
* @param {string} userAgents Space delimited string of user agents.
|
||||
* @return {boolean} Whether the user agent was matched. Also true if no user
|
||||
* agent was listed in the expectation string.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.dom.checkUserAgents_ = function(userAgents) {
|
||||
if (goog.string.startsWith(userAgents, '!')) {
|
||||
if (goog.string.contains(userAgents, ' ')) {
|
||||
throw new Error('Only a single negative user agent may be specified');
|
||||
}
|
||||
return !goog.userAgent[userAgents.substr(1)];
|
||||
}
|
||||
|
||||
var agents = userAgents.split(' ');
|
||||
var hasUserAgent = false;
|
||||
for (var i = 0, len = agents.length; i < len; i++) {
|
||||
var cls = agents[i];
|
||||
if (cls in goog.userAgent) {
|
||||
hasUserAgent = true;
|
||||
if (goog.userAgent[cls]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we got here, there was a user agent listed but we didn't match it.
|
||||
return !hasUserAgent;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Map function that converts end tags to a specific object.
|
||||
* @param {Node} node The node to map.
|
||||
* @param {undefined} ignore Always undefined.
|
||||
* @param {goog.dom.TagIterator} iterator The iterator.
|
||||
* @return {Node|Object} The resulting iteration item.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.dom.endTagMap_ = function(node, ignore, iterator) {
|
||||
return iterator.isEndTag() ? goog.testing.dom.END_TAG_MARKER_ : node;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check if the given node is important. A node is important if it is a
|
||||
* non-empty text node, a non-annotated element, or an element annotated to
|
||||
* match on this user agent.
|
||||
* @param {Node} node The node to test.
|
||||
* @return {boolean} Whether this node should be included for iteration.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.dom.nodeFilter_ = function(node) {
|
||||
if (node.nodeType == goog.dom.NodeType.TEXT) {
|
||||
// If a node is part of a string of text nodes and it has spaces in it,
|
||||
// we allow it since it's going to affect the merging of nodes done below.
|
||||
if (goog.string.isBreakingWhitespace(node.nodeValue) &&
|
||||
(!node.previousSibling ||
|
||||
node.previousSibling.nodeType != goog.dom.NodeType.TEXT) &&
|
||||
(!node.nextSibling ||
|
||||
node.nextSibling.nodeType != goog.dom.NodeType.TEXT)) {
|
||||
return false;
|
||||
}
|
||||
// Allow optional text to be specified as [[BROWSER1 BROWSER2]]Text
|
||||
var match = node.nodeValue.match(/^\[\[(.+)\]\]/);
|
||||
if (match) {
|
||||
return goog.testing.dom.checkUserAgents_(match[1]);
|
||||
}
|
||||
} else if (node.className) {
|
||||
return goog.testing.dom.checkUserAgents_(node.className);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Determines the text to match from the given node, removing browser
|
||||
* specification strings.
|
||||
* @param {Node} node The node expected to match.
|
||||
* @return {string} The text, stripped of browser specification strings.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.dom.getExpectedText_ = function(node) {
|
||||
// Strip off the browser specifications.
|
||||
return node.nodeValue.match(/^(\[\[.+\]\])?(.*)/)[2];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Describes the given node.
|
||||
* @param {Node} node The node to describe.
|
||||
* @return {string} A description of the node.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.dom.describeNode_ = function(node) {
|
||||
if (node.nodeType == goog.dom.NodeType.TEXT) {
|
||||
return '[Text: ' + node.nodeValue + ']';
|
||||
} else {
|
||||
return '<' + node.tagName + (node.id ? ' #' + node.id : '') + ' .../>';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Assert that the html in {@code actual} is substantially similar to
|
||||
* htmlPattern. This method tests for the same set of styles, for the same
|
||||
* order of nodes, and the presence of attributes. Breaking whitespace nodes
|
||||
* are ignored. Elements can be
|
||||
* annotated with classnames corresponding to keys in goog.userAgent and will be
|
||||
* expected to show up in that user agent and expected not to show up in
|
||||
* others.
|
||||
* @param {string} htmlPattern The pattern to match.
|
||||
* @param {!Element} actual The element to check: its contents are matched
|
||||
* against the HTML pattern.
|
||||
* @param {boolean=} opt_strictAttributes If false, attributes that appear in
|
||||
* htmlPattern must be in actual, but actual can have attributes not
|
||||
* present in htmlPattern. If true, htmlPattern and actual must have the
|
||||
* same set of attributes. Default is false.
|
||||
*/
|
||||
goog.testing.dom.assertHtmlContentsMatch = function(htmlPattern, actual,
|
||||
opt_strictAttributes) {
|
||||
var div = goog.dom.createDom(goog.dom.TagName.DIV);
|
||||
div.innerHTML = htmlPattern;
|
||||
|
||||
var errorSuffix = '\nExpected\n' + htmlPattern + '\nActual\n' +
|
||||
actual.innerHTML;
|
||||
|
||||
var actualIt = goog.iter.filter(
|
||||
goog.iter.map(new goog.dom.TagIterator(actual),
|
||||
goog.testing.dom.endTagMap_),
|
||||
goog.testing.dom.nodeFilter_);
|
||||
|
||||
var expectedIt = goog.iter.filter(new goog.dom.NodeIterator(div),
|
||||
goog.testing.dom.nodeFilter_);
|
||||
|
||||
var actualNode;
|
||||
var preIterated = false;
|
||||
var advanceActualNode = function() {
|
||||
// If the iterator has already been advanced, don't advance it again.
|
||||
if (!preIterated) {
|
||||
actualNode = /** @type {Node} */ (goog.iter.nextOrValue(actualIt, null));
|
||||
}
|
||||
preIterated = false;
|
||||
|
||||
// Advance the iterator so long as it is return end tags.
|
||||
while (actualNode == goog.testing.dom.END_TAG_MARKER_) {
|
||||
actualNode = /** @type {Node} */ (goog.iter.nextOrValue(actualIt, null));
|
||||
}
|
||||
};
|
||||
|
||||
// HACK(brenneman): IE has unique ideas about whitespace handling when setting
|
||||
// innerHTML. This results in elision of leading whitespace in the expected
|
||||
// nodes where doing so doesn't affect visible rendering. As a workaround, we
|
||||
// remove the leading whitespace in the actual nodes where necessary.
|
||||
//
|
||||
// The collapsible variable tracks whether we should collapse the whitespace
|
||||
// in the next Text node we encounter.
|
||||
var IE_TEXT_COLLAPSE =
|
||||
goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9');
|
||||
|
||||
var collapsible = true;
|
||||
|
||||
var number = 0;
|
||||
goog.iter.forEach(expectedIt, function(expectedNode) {
|
||||
expectedNode = /** @type {Node} */ (expectedNode);
|
||||
|
||||
advanceActualNode();
|
||||
assertNotNull('Finished actual HTML before finishing expected HTML at ' +
|
||||
'node number ' + number + ': ' +
|
||||
goog.testing.dom.describeNode_(expectedNode) + errorSuffix,
|
||||
actualNode);
|
||||
|
||||
// Do no processing for expectedNode == div.
|
||||
if (expectedNode == div) {
|
||||
return;
|
||||
}
|
||||
|
||||
assertEquals('Should have the same node type, got ' +
|
||||
goog.testing.dom.describeNode_(actualNode) + ' but expected ' +
|
||||
goog.testing.dom.describeNode_(expectedNode) + '.' + errorSuffix,
|
||||
expectedNode.nodeType, actualNode.nodeType);
|
||||
|
||||
if (expectedNode.nodeType == goog.dom.NodeType.ELEMENT) {
|
||||
assertEquals('Tag names should match' + errorSuffix,
|
||||
expectedNode.tagName, actualNode.tagName);
|
||||
assertObjectEquals('Should have same styles' + errorSuffix,
|
||||
goog.style.parseStyleAttribute(expectedNode.style.cssText),
|
||||
goog.style.parseStyleAttribute(actualNode.style.cssText));
|
||||
goog.testing.dom.assertAttributesEqual_(errorSuffix, expectedNode,
|
||||
actualNode, !!opt_strictAttributes);
|
||||
|
||||
if (IE_TEXT_COLLAPSE &&
|
||||
goog.style.getCascadedStyle(
|
||||
/** @type {Element} */ (actualNode), 'display') != 'inline') {
|
||||
// Text may be collapsed after any non-inline element.
|
||||
collapsible = true;
|
||||
}
|
||||
} else {
|
||||
// Concatenate text nodes until we reach a non text node.
|
||||
var actualText = actualNode.nodeValue;
|
||||
preIterated = true;
|
||||
while ((actualNode = /** @type {Node} */
|
||||
(goog.iter.nextOrValue(actualIt, null))) &&
|
||||
actualNode.nodeType == goog.dom.NodeType.TEXT) {
|
||||
actualText += actualNode.nodeValue;
|
||||
}
|
||||
|
||||
if (IE_TEXT_COLLAPSE) {
|
||||
// Collapse the leading whitespace, unless the string consists entirely
|
||||
// of whitespace.
|
||||
if (collapsible && !goog.string.isEmpty(actualText)) {
|
||||
actualText = goog.string.trimLeft(actualText);
|
||||
}
|
||||
// Prepare to collapse whitespace in the next Text node if this one does
|
||||
// not end in a whitespace character.
|
||||
collapsible = /\s$/.test(actualText);
|
||||
}
|
||||
|
||||
var expectedText = goog.testing.dom.getExpectedText_(expectedNode);
|
||||
if ((actualText && !goog.string.isBreakingWhitespace(actualText)) ||
|
||||
(expectedText && !goog.string.isBreakingWhitespace(expectedText))) {
|
||||
var normalizedActual = actualText.replace(/\s+/g, ' ');
|
||||
var normalizedExpected = expectedText.replace(/\s+/g, ' ');
|
||||
|
||||
assertEquals('Text should match' + errorSuffix, normalizedExpected,
|
||||
normalizedActual);
|
||||
}
|
||||
}
|
||||
|
||||
number++;
|
||||
});
|
||||
|
||||
advanceActualNode();
|
||||
assertNull('Finished expected HTML before finishing actual HTML' +
|
||||
errorSuffix, goog.iter.nextOrValue(actualIt, null));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Assert that the html in {@code actual} is substantially similar to
|
||||
* htmlPattern. This method tests for the same set of styles, and for the same
|
||||
* order of nodes. Breaking whitespace nodes are ignored. Elements can be
|
||||
* annotated with classnames corresponding to keys in goog.userAgent and will be
|
||||
* expected to show up in that user agent and expected not to show up in
|
||||
* others.
|
||||
* @param {string} htmlPattern The pattern to match.
|
||||
* @param {string} actual The html to check.
|
||||
*/
|
||||
goog.testing.dom.assertHtmlMatches = function(htmlPattern, actual) {
|
||||
var div = goog.dom.createDom(goog.dom.TagName.DIV);
|
||||
div.innerHTML = actual;
|
||||
|
||||
goog.testing.dom.assertHtmlContentsMatch(htmlPattern, div);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Finds the first text node descendant of root with the given content. Note
|
||||
* that this operates on a text node level, so if text nodes get split this
|
||||
* may not match the user visible text. Using normalize() may help here.
|
||||
* @param {string|RegExp} textOrRegexp The text to find, or a regular
|
||||
* expression to find a match of.
|
||||
* @param {Element} root The element to search in.
|
||||
* @return {Node} The first text node that matches, or null if none is found.
|
||||
*/
|
||||
goog.testing.dom.findTextNode = function(textOrRegexp, root) {
|
||||
var it = new goog.dom.NodeIterator(root);
|
||||
var ret = goog.iter.nextOrValue(goog.iter.filter(it, function(node) {
|
||||
if (node.nodeType == goog.dom.NodeType.TEXT) {
|
||||
if (goog.isString(textOrRegexp)) {
|
||||
return node.nodeValue == textOrRegexp;
|
||||
} else {
|
||||
return !!node.nodeValue.match(textOrRegexp);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}), null);
|
||||
return /** @type {Node} */ (ret);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Assert the end points of a range.
|
||||
*
|
||||
* Notice that "Are two ranges visually identical?" and "Do two ranges have
|
||||
* the same endpoint?" are independent questions. Two visually identical ranges
|
||||
* may have different endpoints. And two ranges with the same endpoints may
|
||||
* be visually different.
|
||||
*
|
||||
* @param {Node} start The expected start node.
|
||||
* @param {number} startOffset The expected start offset.
|
||||
* @param {Node} end The expected end node.
|
||||
* @param {number} endOffset The expected end offset.
|
||||
* @param {goog.dom.AbstractRange} range The actual range.
|
||||
*/
|
||||
goog.testing.dom.assertRangeEquals = function(start, startOffset, end,
|
||||
endOffset, range) {
|
||||
assertEquals('Unexpected start node', start, range.getStartNode());
|
||||
assertEquals('Unexpected end node', end, range.getEndNode());
|
||||
assertEquals('Unexpected start offset', startOffset, range.getStartOffset());
|
||||
assertEquals('Unexpected end offset', endOffset, range.getEndOffset());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value of a DOM attribute in deterministic way.
|
||||
* @param {!Node} node A node.
|
||||
* @param {string} name Attribute name.
|
||||
* @return {*} Attribute value.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.dom.getAttributeValue_ = function(node, name) {
|
||||
// These hacks avoid nondetermistic results in the following cases:
|
||||
// IE7: document.createElement('input').height returns a random number.
|
||||
// FF3: getAttribute('disabled') returns different value for <div disabled="">
|
||||
// and <div disabled="disabled">
|
||||
// WebKit: Two radio buttons with the same name can't be checked at the same
|
||||
// time, even if only one of them is in the document.
|
||||
if (goog.userAgent.WEBKIT && node.tagName == 'INPUT' &&
|
||||
node['type'] == 'radio' && name == 'checked') {
|
||||
return false;
|
||||
}
|
||||
return goog.isDef(node[name]) &&
|
||||
typeof node.getAttribute(name) != typeof node[name] ?
|
||||
node[name] : node.getAttribute(name);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Assert that the attributes of two Nodes are the same (ignoring any
|
||||
* instances of the style attribute).
|
||||
* @param {string} errorSuffix String to add to end of error messages.
|
||||
* @param {Node} expectedNode The node whose attributes we are expecting.
|
||||
* @param {Node} actualNode The node with the actual attributes.
|
||||
* @param {boolean} strictAttributes If false, attributes that appear in
|
||||
* expectedNode must also be in actualNode, but actualNode can have
|
||||
* attributes not present in expectedNode. If true, expectedNode and
|
||||
* actualNode must have the same set of attributes.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.dom.assertAttributesEqual_ = function(errorSuffix,
|
||||
expectedNode, actualNode, strictAttributes) {
|
||||
if (strictAttributes) {
|
||||
goog.testing.dom.compareClassAttribute_(expectedNode, actualNode);
|
||||
}
|
||||
|
||||
var expectedAttributes = expectedNode.attributes;
|
||||
var actualAttributes = actualNode.attributes;
|
||||
|
||||
for (var i = 0, len = expectedAttributes.length; i < len; i++) {
|
||||
var expectedName = expectedAttributes[i].name;
|
||||
var expectedValue = goog.testing.dom.getAttributeValue_(expectedNode,
|
||||
expectedName);
|
||||
|
||||
var actualAttribute = actualAttributes[expectedName];
|
||||
var actualValue = goog.testing.dom.getAttributeValue_(actualNode,
|
||||
expectedName);
|
||||
|
||||
// IE enumerates attribute names in the expected node that are not present,
|
||||
// causing an undefined actualAttribute.
|
||||
if (!expectedValue && !actualValue) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (expectedName == 'id' && goog.userAgent.IE) {
|
||||
goog.testing.dom.compareIdAttributeForIe_(
|
||||
/** @type {string} */ (expectedValue), actualAttribute,
|
||||
strictAttributes, errorSuffix);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (goog.testing.dom.ignoreAttribute_(expectedName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assertNotUndefined('Expected to find attribute with name ' +
|
||||
expectedName + ', in element ' +
|
||||
goog.testing.dom.describeNode_(actualNode) + errorSuffix,
|
||||
actualAttribute);
|
||||
assertEquals('Expected attribute ' + expectedName +
|
||||
' has a different value ' + errorSuffix,
|
||||
expectedValue,
|
||||
goog.testing.dom.getAttributeValue_(actualNode, actualAttribute.name));
|
||||
}
|
||||
|
||||
if (strictAttributes) {
|
||||
for (i = 0; i < actualAttributes.length; i++) {
|
||||
var actualName = actualAttributes[i].name;
|
||||
var actualAttribute = actualAttributes.getNamedItem(actualName);
|
||||
|
||||
if (!actualAttribute || goog.testing.dom.ignoreAttribute_(actualName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assertNotUndefined('Unexpected attribute with name ' +
|
||||
actualName + ' in element ' +
|
||||
goog.testing.dom.describeNode_(actualNode) + errorSuffix,
|
||||
expectedAttributes[actualName]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Assert the class attribute of actualNode is the same as the one in
|
||||
* expectedNode, ignoring classes that are useragents.
|
||||
* @param {Node} expectedNode The DOM node whose class we expect.
|
||||
* @param {Node} actualNode The DOM node with the actual class.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.dom.compareClassAttribute_ = function(expectedNode,
|
||||
actualNode) {
|
||||
var classes = goog.dom.classes.get(expectedNode);
|
||||
|
||||
var expectedClasses = [];
|
||||
for (var i = 0, len = classes.length; i < len; i++) {
|
||||
if (!(classes[i] in goog.userAgent)) {
|
||||
expectedClasses.push(classes[i]);
|
||||
}
|
||||
}
|
||||
expectedClasses.sort();
|
||||
|
||||
var actualClasses = goog.dom.classes.get(actualNode);
|
||||
actualClasses.sort();
|
||||
|
||||
assertArrayEquals(
|
||||
'Expected class was: ' + expectedClasses.join(' ') +
|
||||
', but actual class was: ' + actualNode.className,
|
||||
expectedClasses, actualClasses);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Set of attributes IE adds to elements randomly.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.dom.BAD_IE_ATTRIBUTES_ = goog.object.createSet(
|
||||
'methods', 'CHECKED', 'dataFld', 'dataFormatAs', 'dataSrc');
|
||||
|
||||
|
||||
/**
|
||||
* Whether to ignore the attribute.
|
||||
* @param {string} name Name of the attribute.
|
||||
* @return {boolean} True if the attribute should be ignored.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.dom.ignoreAttribute_ = function(name) {
|
||||
if (name == 'style' || name == 'class') {
|
||||
return true;
|
||||
}
|
||||
return goog.userAgent.IE && goog.testing.dom.BAD_IE_ATTRIBUTES_[name];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Compare id attributes for IE. In IE, if an element lacks an id attribute
|
||||
* in the original HTML, the element object will still have such an attribute,
|
||||
* but its value will be the empty string.
|
||||
* @param {string} expectedValue The expected value of the id attribute.
|
||||
* @param {Attr} actualAttribute The actual id attribute.
|
||||
* @param {boolean} strictAttributes Whether strict attribute checking should be
|
||||
* done.
|
||||
* @param {string} errorSuffix String to append to error messages.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.dom.compareIdAttributeForIe_ = function(expectedValue,
|
||||
actualAttribute, strictAttributes, errorSuffix) {
|
||||
if (expectedValue === '') {
|
||||
if (strictAttributes) {
|
||||
assertTrue('Unexpected attribute with name id in element ' +
|
||||
errorSuffix, actualAttribute.value == '');
|
||||
}
|
||||
} else {
|
||||
assertNotUndefined('Expected to find attribute with name id, in element ' +
|
||||
errorSuffix, actualAttribute);
|
||||
assertNotEquals('Expected to find attribute with name id, in element ' +
|
||||
errorSuffix, '', actualAttribute.value);
|
||||
assertEquals('Expected attribute has a different value ' + errorSuffix,
|
||||
expectedValue, actualAttribute.value);
|
||||
}
|
||||
};
|
||||
293
float-no-zero/closure-library/closure/goog/testing/editor/dom.js
Normal file
293
float-no-zero/closure-library/closure/goog/testing/editor/dom.js
Normal file
@@ -0,0 +1,293 @@
|
||||
// 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 Testing utilities for editor specific DOM related tests.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.editor.dom');
|
||||
|
||||
goog.require('goog.dom.NodeType');
|
||||
goog.require('goog.dom.TagIterator');
|
||||
goog.require('goog.dom.TagWalkType');
|
||||
goog.require('goog.iter');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.testing.asserts');
|
||||
|
||||
|
||||
/**
|
||||
* Returns the previous (in document order) node from the given node that is a
|
||||
* non-empty text node, or null if none is found or opt_stopAt is not an
|
||||
* ancestor of node. Note that if the given node has children, the search will
|
||||
* start from the end tag of the node, meaning all its descendants will be
|
||||
* included in the search, unless opt_skipDescendants is true.
|
||||
* @param {Node} node Node to start searching from.
|
||||
* @param {Node=} opt_stopAt Node to stop searching at (search will be
|
||||
* restricted to this node's subtree), defaults to the body of the document
|
||||
* containing node.
|
||||
* @param {boolean=} opt_skipDescendants Whether to skip searching the given
|
||||
* node's descentants.
|
||||
* @return {Text} The previous (in document order) node from the given node
|
||||
* that is a non-empty text node, or null if none is found.
|
||||
*/
|
||||
goog.testing.editor.dom.getPreviousNonEmptyTextNode = function(
|
||||
node, opt_stopAt, opt_skipDescendants) {
|
||||
return goog.testing.editor.dom.getPreviousNextNonEmptyTextNodeHelper_(
|
||||
node, opt_stopAt, opt_skipDescendants, true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next (in document order) node from the given node that is a
|
||||
* non-empty text node, or null if none is found or opt_stopAt is not an
|
||||
* ancestor of node. Note that if the given node has children, the search will
|
||||
* start from the start tag of the node, meaning all its descendants will be
|
||||
* included in the search, unless opt_skipDescendants is true.
|
||||
* @param {Node} node Node to start searching from.
|
||||
* @param {Node=} opt_stopAt Node to stop searching at (search will be
|
||||
* restricted to this node's subtree), defaults to the body of the document
|
||||
* containing node.
|
||||
* @param {boolean=} opt_skipDescendants Whether to skip searching the given
|
||||
* node's descentants.
|
||||
* @return {Text} The next (in document order) node from the given node that
|
||||
* is a non-empty text node, or null if none is found or opt_stopAt is not
|
||||
* an ancestor of node.
|
||||
*/
|
||||
goog.testing.editor.dom.getNextNonEmptyTextNode = function(
|
||||
node, opt_stopAt, opt_skipDescendants) {
|
||||
return goog.testing.editor.dom.getPreviousNextNonEmptyTextNodeHelper_(
|
||||
node, opt_stopAt, opt_skipDescendants, false);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helper that returns the previous or next (in document order) node from the
|
||||
* given node that is a non-empty text node, or null if none is found or
|
||||
* opt_stopAt is not an ancestor of node. Note that if the given node has
|
||||
* children, the search will start from the end or start tag of the node
|
||||
* (depending on whether it's searching for the previous or next node), meaning
|
||||
* all its descendants will be included in the search, unless
|
||||
* opt_skipDescendants is true.
|
||||
* @param {Node} node Node to start searching from.
|
||||
* @param {Node=} opt_stopAt Node to stop searching at (search will be
|
||||
* restricted to this node's subtree), defaults to the body of the document
|
||||
* containing node.
|
||||
* @param {boolean=} opt_skipDescendants Whether to skip searching the given
|
||||
* node's descentants.
|
||||
* @param {boolean=} opt_isPrevious Whether to search for the previous non-empty
|
||||
* text node instead of the next one.
|
||||
* @return {Text} The next (in document order) node from the given node that
|
||||
* is a non-empty text node, or null if none is found or opt_stopAt is not
|
||||
* an ancestor of node.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.editor.dom.getPreviousNextNonEmptyTextNodeHelper_ = function(
|
||||
node, opt_stopAt, opt_skipDescendants, opt_isPrevious) {
|
||||
opt_stopAt = opt_stopAt || node.ownerDocument.body;
|
||||
// Initializing the iterator to iterate over the children of opt_stopAt
|
||||
// makes it stop only when it finishes iterating through all of that
|
||||
// node's children, even though we will start at a different node and exit
|
||||
// that starting node's subtree in the process.
|
||||
var iter = new goog.dom.TagIterator(opt_stopAt, opt_isPrevious);
|
||||
|
||||
// TODO(user): Move this logic to a new method in TagIterator such as
|
||||
// skipToNode().
|
||||
// Then we set the iterator to start at the given start node, not opt_stopAt.
|
||||
var walkType; // Let TagIterator set the initial walk type by default.
|
||||
var depth = goog.testing.editor.dom.getRelativeDepth_(node, opt_stopAt);
|
||||
if (depth == -1) {
|
||||
return null; // Fail because opt_stopAt is not an ancestor of node.
|
||||
}
|
||||
if (node.nodeType == goog.dom.NodeType.ELEMENT) {
|
||||
if (opt_skipDescendants) {
|
||||
// Specifically set the initial walk type so that we skip the descendant
|
||||
// subtree by starting at the start if going backwards or at the end if
|
||||
// going forwards.
|
||||
walkType = opt_isPrevious ? goog.dom.TagWalkType.START_TAG :
|
||||
goog.dom.TagWalkType.END_TAG;
|
||||
} else {
|
||||
// We're starting "inside" an element node so the depth needs to be one
|
||||
// deeper than the node's actual depth. That's how TagIterator works!
|
||||
depth++;
|
||||
}
|
||||
}
|
||||
iter.setPosition(node, walkType, depth);
|
||||
|
||||
// Advance the iterator so it skips the start node.
|
||||
try {
|
||||
iter.next();
|
||||
} catch (e) {
|
||||
return null; // It could have been a leaf node.
|
||||
}
|
||||
// Now just get the first non-empty text node the iterator finds.
|
||||
var filter = goog.iter.filter(iter,
|
||||
goog.testing.editor.dom.isNonEmptyTextNode_);
|
||||
try {
|
||||
return /** @type {Text} */ (filter.next());
|
||||
} catch (e) { // No next item is available so return null.
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given node is a non-empty text node.
|
||||
* @param {Node} node Node to be checked.
|
||||
* @return {boolean} Whether the given node is a non-empty text node.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.editor.dom.isNonEmptyTextNode_ = function(node) {
|
||||
return !!node && node.nodeType == goog.dom.NodeType.TEXT && node.length > 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the depth of the given node relative to the given parent node, or -1
|
||||
* if the given node is not a descendant of the given parent node. E.g. if
|
||||
* node == parentNode returns 0, if node.parentNode == parentNode returns 1,
|
||||
* etc.
|
||||
* @param {Node} node Node whose depth to get.
|
||||
* @param {Node} parentNode Node relative to which the depth should be
|
||||
* calculated.
|
||||
* @return {number} The depth of the given node relative to the given parent
|
||||
* node, or -1 if the given node is not a descendant of the given parent
|
||||
* node.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.editor.dom.getRelativeDepth_ = function(node, parentNode) {
|
||||
var depth = 0;
|
||||
while (node) {
|
||||
if (node == parentNode) {
|
||||
return depth;
|
||||
}
|
||||
node = node.parentNode;
|
||||
depth++;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Assert that the range is surrounded by the given strings. This is useful
|
||||
* because different browsers can place the range endpoints inside different
|
||||
* nodes even when visually the range looks the same. Also, there may be empty
|
||||
* text nodes in the way (again depending on the browser) making it difficult to
|
||||
* use assertRangeEquals.
|
||||
* @param {string} before String that should occur immediately before the start
|
||||
* point of the range. If this is the empty string, assert will only succeed
|
||||
* if there is no text before the start point of the range.
|
||||
* @param {string} after String that should occur immediately after the end
|
||||
* point of the range. If this is the empty string, assert will only succeed
|
||||
* if there is no text after the end point of the range.
|
||||
* @param {goog.dom.AbstractRange} range The range to be tested.
|
||||
* @param {Node=} opt_stopAt Node to stop searching at (search will be
|
||||
* restricted to this node's subtree).
|
||||
*/
|
||||
goog.testing.editor.dom.assertRangeBetweenText = function(before,
|
||||
after,
|
||||
range,
|
||||
opt_stopAt) {
|
||||
var previousText =
|
||||
goog.testing.editor.dom.getTextFollowingRange_(range, true, opt_stopAt);
|
||||
if (before == '') {
|
||||
assertNull('Expected nothing before range but found <' + previousText + '>',
|
||||
previousText);
|
||||
} else {
|
||||
assertNotNull('Expected <' + before + '> before range but found nothing',
|
||||
previousText);
|
||||
assertTrue('Expected <' + before + '> before range but found <' +
|
||||
previousText + '>',
|
||||
goog.string.endsWith(
|
||||
/** @type {string} */ (previousText), before));
|
||||
}
|
||||
var nextText =
|
||||
goog.testing.editor.dom.getTextFollowingRange_(range, false, opt_stopAt);
|
||||
if (after == '') {
|
||||
assertNull('Expected nothing after range but found <' + nextText + '>',
|
||||
nextText);
|
||||
} else {
|
||||
assertNotNull('Expected <' + after + '> after range but found nothing',
|
||||
nextText);
|
||||
assertTrue('Expected <' + after + '> after range but found <' +
|
||||
nextText + '>',
|
||||
goog.string.startsWith(
|
||||
/** @type {string} */ (nextText), after));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the text that follows the given range, where the term "follows" means
|
||||
* "comes immediately before the start of the range" if isBefore is true, and
|
||||
* "comes immediately after the end of the range" if isBefore is false, or null
|
||||
* if no non-empty text node is found.
|
||||
* @param {goog.dom.AbstractRange} range The range to search from.
|
||||
* @param {boolean} isBefore Whether to search before the range instead of
|
||||
* after it.
|
||||
* @param {Node=} opt_stopAt Node to stop searching at (search will be
|
||||
* restricted to this node's subtree).
|
||||
* @return {?string} The text that follows the given range, or null if no
|
||||
* non-empty text node is found.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.editor.dom.getTextFollowingRange_ = function(range,
|
||||
isBefore,
|
||||
opt_stopAt) {
|
||||
var followingTextNode;
|
||||
var endpointNode = isBefore ? range.getStartNode() : range.getEndNode();
|
||||
var endpointOffset = isBefore ? range.getStartOffset() : range.getEndOffset();
|
||||
var getFollowingTextNode =
|
||||
isBefore ? goog.testing.editor.dom.getPreviousNonEmptyTextNode :
|
||||
goog.testing.editor.dom.getNextNonEmptyTextNode;
|
||||
|
||||
if (endpointNode.nodeType == goog.dom.NodeType.TEXT) {
|
||||
// Range endpoint is in a text node.
|
||||
var endText = endpointNode.nodeValue;
|
||||
if (isBefore ? endpointOffset > 0 : endpointOffset < endText.length) {
|
||||
// There is text in this node following the endpoint so return the portion
|
||||
// that follows the endpoint.
|
||||
return isBefore ? endText.substr(0, endpointOffset) :
|
||||
endText.substr(endpointOffset);
|
||||
} else {
|
||||
// There is no text following the endpoint so look for the follwing text
|
||||
// node.
|
||||
followingTextNode = getFollowingTextNode(endpointNode, opt_stopAt);
|
||||
return followingTextNode && followingTextNode.nodeValue;
|
||||
}
|
||||
} else {
|
||||
// Range endpoint is in an element node.
|
||||
var numChildren = endpointNode.childNodes.length;
|
||||
if (isBefore ? endpointOffset > 0 : endpointOffset < numChildren) {
|
||||
// There is at least one child following the endpoint.
|
||||
var followingChild =
|
||||
endpointNode.childNodes[isBefore ? endpointOffset - 1 :
|
||||
endpointOffset];
|
||||
if (goog.testing.editor.dom.isNonEmptyTextNode_(followingChild)) {
|
||||
// The following child has text so return that.
|
||||
return followingChild.nodeValue;
|
||||
} else {
|
||||
// The following child has no text so look for the following text node.
|
||||
followingTextNode = getFollowingTextNode(followingChild, opt_stopAt);
|
||||
return followingTextNode && followingTextNode.nodeValue;
|
||||
}
|
||||
} else {
|
||||
// There is no child following the endpoint, so search from the endpoint
|
||||
// node, but don't search its children because they are not following the
|
||||
// endpoint!
|
||||
followingTextNode = getFollowingTextNode(endpointNode, opt_stopAt, true);
|
||||
return followingTextNode && followingTextNode.nodeValue;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview Mock of goog.editor.field.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.editor.FieldMock');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.Range');
|
||||
goog.require('goog.editor.Field');
|
||||
goog.require('goog.testing.LooseMock');
|
||||
goog.require('goog.testing.mockmatchers');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Mock of goog.editor.Field.
|
||||
* @param {Window=} opt_window Window the field would edit. Defaults to
|
||||
* {@code window}.
|
||||
* @param {Window=} opt_appWindow "AppWindow" of the field, which can be
|
||||
* different from {@code opt_window} when mocking a field that uses an
|
||||
* iframe. Defaults to {@code opt_window}.
|
||||
* @param {goog.dom.AbstractRange=} opt_range An object (mock or real) to be
|
||||
* returned by getRange(). If ommitted, a new goog.dom.Range is created
|
||||
* from the window every time getRange() is called.
|
||||
* @constructor
|
||||
* @extends {goog.testing.LooseMock}
|
||||
* @suppress {missingProperties} Mocks do not fit in the type system well.
|
||||
*/
|
||||
goog.testing.editor.FieldMock =
|
||||
function(opt_window, opt_appWindow, opt_range) {
|
||||
goog.testing.LooseMock.call(this, goog.editor.Field);
|
||||
opt_window = opt_window || window;
|
||||
opt_appWindow = opt_appWindow || opt_window;
|
||||
|
||||
this.getAppWindow();
|
||||
this.$anyTimes();
|
||||
this.$returns(opt_appWindow);
|
||||
|
||||
this.getRange();
|
||||
this.$anyTimes();
|
||||
this.$does(function() {
|
||||
return opt_range || goog.dom.Range.createFromWindow(opt_window);
|
||||
});
|
||||
|
||||
this.getEditableDomHelper();
|
||||
this.$anyTimes();
|
||||
this.$returns(goog.dom.getDomHelper(opt_window.document));
|
||||
|
||||
this.usesIframe();
|
||||
this.$anyTimes();
|
||||
|
||||
this.getBaseZindex();
|
||||
this.$anyTimes();
|
||||
this.$returns(0);
|
||||
|
||||
this.restoreSavedRange(goog.testing.mockmatchers.ignoreArgument);
|
||||
this.$anyTimes();
|
||||
this.$does(function(range) {
|
||||
if (range) {
|
||||
range.restore();
|
||||
}
|
||||
this.focus();
|
||||
});
|
||||
|
||||
// These methods cannot be set on the prototype, because the prototype
|
||||
// gets stepped on by the mock framework.
|
||||
var inModalMode = false;
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether we're in modal interaction mode.
|
||||
*/
|
||||
this.inModalMode = function() {
|
||||
return inModalMode;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {boolean} mode Sets whether we're in modal interaction mode.
|
||||
*/
|
||||
this.setModalMode = function(mode) {
|
||||
inModalMode = mode;
|
||||
};
|
||||
};
|
||||
goog.inherits(goog.testing.editor.FieldMock, goog.testing.LooseMock);
|
||||
@@ -0,0 +1,176 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview Class that allows for simple text editing tests.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.editor.TestHelper');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.Range');
|
||||
goog.require('goog.editor.BrowserFeature');
|
||||
goog.require('goog.editor.node');
|
||||
goog.require('goog.editor.plugins.AbstractBubblePlugin');
|
||||
goog.require('goog.testing.dom');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a new test controller.
|
||||
* @param {Element} root The root editable element.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
*/
|
||||
goog.testing.editor.TestHelper = function(root) {
|
||||
if (!root) {
|
||||
throw Error('Null root');
|
||||
}
|
||||
goog.Disposable.call(this);
|
||||
|
||||
/**
|
||||
* Convenience variable for root DOM element.
|
||||
* @type {!Element}
|
||||
* @private
|
||||
*/
|
||||
this.root_ = root;
|
||||
|
||||
/**
|
||||
* The starting HTML of the editable element.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.savedHtml_ = '';
|
||||
};
|
||||
goog.inherits(goog.testing.editor.TestHelper, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* Selects a new root element.
|
||||
* @param {Element} root The root editable element.
|
||||
*/
|
||||
goog.testing.editor.TestHelper.prototype.setRoot = function(root) {
|
||||
if (!root) {
|
||||
throw Error('Null root');
|
||||
}
|
||||
this.root_ = root;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Make the root element editable. Alse saves its HTML to be restored
|
||||
* in tearDown.
|
||||
*/
|
||||
goog.testing.editor.TestHelper.prototype.setUpEditableElement = function() {
|
||||
this.savedHtml_ = this.root_.innerHTML;
|
||||
if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
|
||||
this.root_.contentEditable = true;
|
||||
} else {
|
||||
this.root_.ownerDocument.designMode = 'on';
|
||||
}
|
||||
this.root_.setAttribute('g_editable', 'true');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Reset the element previously initialized, restoring its HTML and making it
|
||||
* non editable.
|
||||
*/
|
||||
goog.testing.editor.TestHelper.prototype.tearDownEditableElement = function() {
|
||||
if (goog.editor.BrowserFeature.HAS_CONTENT_EDITABLE) {
|
||||
this.root_.contentEditable = false;
|
||||
} else {
|
||||
this.root_.ownerDocument.designMode = 'off';
|
||||
}
|
||||
goog.dom.removeChildren(this.root_);
|
||||
this.root_.innerHTML = this.savedHtml_;
|
||||
this.root_.removeAttribute('g_editable');
|
||||
|
||||
if (goog.editor.plugins && goog.editor.plugins.AbstractBubblePlugin) {
|
||||
// Remove old bubbles.
|
||||
for (var key in goog.editor.plugins.AbstractBubblePlugin.bubbleMap_) {
|
||||
goog.editor.plugins.AbstractBubblePlugin.bubbleMap_[key].dispose();
|
||||
}
|
||||
// Ensure we get a new bubble for each test.
|
||||
goog.editor.plugins.AbstractBubblePlugin.bubbleMap_ = {};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Assert that the html in 'root' is substantially similar to htmlPattern.
|
||||
* This method tests for the same set of styles, and for the same order of
|
||||
* nodes. Breaking whitespace nodes are ignored. Elements can be annotated
|
||||
* with classnames corresponding to keys in goog.userAgent and will be
|
||||
* expected to show up in that user agent and expected not to show up in
|
||||
* others.
|
||||
* @param {string} htmlPattern The pattern to match.
|
||||
*/
|
||||
goog.testing.editor.TestHelper.prototype.assertHtmlMatches = function(
|
||||
htmlPattern) {
|
||||
goog.testing.dom.assertHtmlContentsMatch(htmlPattern, this.root_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Finds the first text node descendant of root with the given content.
|
||||
* @param {string|RegExp} textOrRegexp The text to find, or a regular
|
||||
* expression to find a match of.
|
||||
* @return {Node} The first text node that matches, or null if none is found.
|
||||
*/
|
||||
goog.testing.editor.TestHelper.prototype.findTextNode = function(textOrRegexp) {
|
||||
return goog.testing.dom.findTextNode(textOrRegexp, this.root_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Select from the given from offset in the given from node to the given
|
||||
* to offset in the optionally given to node. If nodes are passed in, uses them,
|
||||
* otherwise uses findTextNode to find the nodes to select. Selects a caret
|
||||
* if opt_to and opt_toOffset are not given.
|
||||
* @param {Node|string} from Node or text of the node to start the selection at.
|
||||
* @param {number} fromOffset Offset within the above node to start the
|
||||
* selection at.
|
||||
* @param {Node|string=} opt_to Node or text of the node to end the selection
|
||||
* at.
|
||||
* @param {number=} opt_toOffset Offset within the above node to end the
|
||||
* selection at.
|
||||
*/
|
||||
goog.testing.editor.TestHelper.prototype.select = function(from, fromOffset,
|
||||
opt_to, opt_toOffset) {
|
||||
var end;
|
||||
var start = end = goog.isString(from) ? this.findTextNode(from) : from;
|
||||
var endOffset;
|
||||
var startOffset = endOffset = fromOffset;
|
||||
|
||||
if (opt_to && goog.isNumber(opt_toOffset)) {
|
||||
end = goog.isString(opt_to) ? this.findTextNode(opt_to) : opt_to;
|
||||
endOffset = opt_toOffset;
|
||||
}
|
||||
|
||||
goog.dom.Range.createFromNodes(start, startOffset, end, endOffset).select();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.editor.TestHelper.prototype.disposeInternal = function() {
|
||||
if (goog.editor.node.isEditableContainer(this.root_)) {
|
||||
this.tearDownEditableElement();
|
||||
}
|
||||
delete this.root_;
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright 2010 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 Event observer.
|
||||
*
|
||||
* Provides an event observer that holds onto events that it handles. This
|
||||
* can be used in unit testing to verify an event target's events --
|
||||
* that the order count, types, etc. are correct.
|
||||
*
|
||||
* Example usage:
|
||||
* <pre>
|
||||
* var observer = new goog.testing.events.EventObserver();
|
||||
* var widget = new foo.Widget();
|
||||
* goog.events.listen(widget, ['select', 'submit'], observer);
|
||||
* // Simulate user action of 3 select events and 2 submit events.
|
||||
* assertEquals(3, observer.getEvents('select').length);
|
||||
* assertEquals(2, observer.getEvents('submit').length);
|
||||
* </pre>
|
||||
*
|
||||
* @author nnaze@google.com (Nathan Naze)
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.events.EventObserver');
|
||||
|
||||
goog.require('goog.array');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Event observer. Implements a handleEvent interface so it may be used as
|
||||
* a listener in listening functions and methods.
|
||||
* @see goog.events.listen
|
||||
* @see goog.events.EventHandler
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.events.EventObserver = function() {
|
||||
|
||||
/**
|
||||
* A list of events handled by the observer in order of handling, oldest to
|
||||
* newest.
|
||||
* @type {!Array.<!goog.events.Event>}
|
||||
* @private
|
||||
*/
|
||||
this.events_ = [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles an event and remembers it. Event listening functions and methods
|
||||
* will call this method when this observer is used as a listener.
|
||||
* @see goog.events.listen
|
||||
* @see goog.events.EventHandler
|
||||
* @param {!goog.events.Event} e Event to handle.
|
||||
*/
|
||||
goog.testing.events.EventObserver.prototype.handleEvent = function(e) {
|
||||
this.events_.push(e);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string=} opt_type If given, only return events of this type.
|
||||
* @return {!Array.<!goog.events.Event>} The events handled, oldest to newest.
|
||||
*/
|
||||
goog.testing.events.EventObserver.prototype.getEvents = function(opt_type) {
|
||||
var events = goog.array.clone(this.events_);
|
||||
|
||||
if (opt_type) {
|
||||
events = goog.array.filter(events, function(event) {
|
||||
return event.type == opt_type;
|
||||
});
|
||||
}
|
||||
|
||||
return events;
|
||||
};
|
||||
@@ -0,0 +1,713 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview Event Simulation.
|
||||
*
|
||||
* Utility functions for simulating events at the Closure level. All functions
|
||||
* in this package generate events by calling goog.events.fireListeners,
|
||||
* rather than interfacing with the browser directly. This is intended for
|
||||
* testing purposes, and should not be used in production code.
|
||||
*
|
||||
* The decision to use Closure events and dispatchers instead of the browser's
|
||||
* native events and dispatchers was conscious and deliberate. Native event
|
||||
* dispatchers have their own set of quirks and edge cases. Pure JS dispatchers
|
||||
* are more robust and transparent.
|
||||
*
|
||||
* If you think you need a testing mechanism that uses native Event objects,
|
||||
* please, please email closure-tech first to explain your use case before you
|
||||
* sink time into this.
|
||||
*
|
||||
* @author nicksantos@google.com (Nick Santos)
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.events');
|
||||
goog.provide('goog.testing.events.Event');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom.NodeType');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.BrowserEvent');
|
||||
goog.require('goog.events.BrowserFeature');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* goog.events.BrowserEvent expects an Event so we provide one for JSCompiler.
|
||||
*
|
||||
* This clones a lot of the functionality of goog.events.Event. This used to
|
||||
* use a mixin, but the mixin results in confusing the two types when compiled.
|
||||
*
|
||||
* @param {string} type Event Type.
|
||||
* @param {Object=} opt_target Reference to the object that is the target of
|
||||
* this event.
|
||||
* @constructor
|
||||
* @extends {Event}
|
||||
*/
|
||||
goog.testing.events.Event = function(type, opt_target) {
|
||||
this.type = type;
|
||||
|
||||
this.target = /** @type {EventTarget} */ (opt_target || null);
|
||||
|
||||
this.currentTarget = this.target;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether to cancel the event in internal capture/bubble processing for IE.
|
||||
* @type {boolean}
|
||||
* @suppress {underscore} Technically public, but referencing this outside
|
||||
* this package is strongly discouraged.
|
||||
*/
|
||||
goog.testing.events.Event.prototype.propagationStopped_ = false;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.events.Event.prototype.defaultPrevented = false;
|
||||
|
||||
|
||||
/**
|
||||
* Return value for in internal capture/bubble processing for IE.
|
||||
* @type {boolean}
|
||||
* @suppress {underscore} Technically public, but referencing this outside
|
||||
* this package is strongly discouraged.
|
||||
*/
|
||||
goog.testing.events.Event.prototype.returnValue_ = true;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.events.Event.prototype.stopPropagation = function() {
|
||||
this.propagationStopped_ = true;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.events.Event.prototype.preventDefault = function() {
|
||||
this.defaultPrevented = true;
|
||||
this.returnValue_ = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts an event target exists. This will fail if target is not defined.
|
||||
*
|
||||
* TODO(nnaze): Gradually add this to the methods in this file, and eventually
|
||||
* update the method signatures to not take nullables. See http://b/8961907
|
||||
*
|
||||
* @param {EventTarget} target A target to assert.
|
||||
* @return {!EventTarget} The target, guaranteed to exist.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.events.assertEventTarget_ = function(target) {
|
||||
return goog.asserts.assert(target, 'Target should not be defined.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A static helper function that sets the mouse position to the event.
|
||||
* @param {Event} event A simulated native event.
|
||||
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @private
|
||||
*/
|
||||
goog.testing.events.setEventClientXY_ = function(event, opt_coords) {
|
||||
if (!opt_coords && event.target &&
|
||||
event.target.nodeType == goog.dom.NodeType.ELEMENT) {
|
||||
try {
|
||||
opt_coords =
|
||||
goog.style.getClientPosition(/** @type {Element} **/ (event.target));
|
||||
} catch (ex) {
|
||||
// IE sometimes throws if it can't get the position.
|
||||
}
|
||||
}
|
||||
event.clientX = opt_coords ? opt_coords.x : 0;
|
||||
event.clientY = opt_coords ? opt_coords.y : 0;
|
||||
|
||||
// Pretend the browser window is at (0, 0).
|
||||
event.screenX = event.clientX;
|
||||
event.screenY = event.clientY;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a mousedown, mouseup, and then click on the given event target,
|
||||
* with the left mouse button.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
|
||||
* defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
|
||||
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @param {Object=} opt_eventProperties Event properties to be mixed into the
|
||||
* BrowserEvent.
|
||||
* @return {boolean} The returnValue of the sequence: false if preventDefault()
|
||||
* was called on any of the events, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireClickSequence =
|
||||
function(target, opt_button, opt_coords, opt_eventProperties) {
|
||||
// Fire mousedown, mouseup, and click. Then return the bitwise AND of the 3.
|
||||
return !!(goog.testing.events.fireMouseDownEvent(
|
||||
target, opt_button, opt_coords, opt_eventProperties) &
|
||||
goog.testing.events.fireMouseUpEvent(
|
||||
target, opt_button, opt_coords, opt_eventProperties) &
|
||||
goog.testing.events.fireClickEvent(
|
||||
target, opt_button, opt_coords, opt_eventProperties));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates the sequence of events fired by the browser when the user double-
|
||||
* clicks the given target.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @param {Object=} opt_eventProperties Event properties to be mixed into the
|
||||
* BrowserEvent.
|
||||
* @return {boolean} The returnValue of the sequence: false if preventDefault()
|
||||
* was called on any of the events, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireDoubleClickSequence = function(
|
||||
target, opt_coords, opt_eventProperties) {
|
||||
// Fire mousedown, mouseup, click, mousedown, mouseup, click, dblclick.
|
||||
// Then return the bitwise AND of the 7.
|
||||
var btn = goog.events.BrowserEvent.MouseButton.LEFT;
|
||||
return !!(goog.testing.events.fireMouseDownEvent(
|
||||
target, btn, opt_coords, opt_eventProperties) &
|
||||
goog.testing.events.fireMouseUpEvent(
|
||||
target, btn, opt_coords, opt_eventProperties) &
|
||||
goog.testing.events.fireClickEvent(
|
||||
target, btn, opt_coords, opt_eventProperties) &
|
||||
// IE fires a selectstart instead of the second mousedown in a
|
||||
// dblclick, but we don't care about selectstart.
|
||||
(goog.userAgent.IE ||
|
||||
goog.testing.events.fireMouseDownEvent(
|
||||
target, btn, opt_coords, opt_eventProperties)) &
|
||||
goog.testing.events.fireMouseUpEvent(
|
||||
target, btn, opt_coords, opt_eventProperties) &
|
||||
// IE doesn't fire the second click in a dblclick.
|
||||
(goog.userAgent.IE ||
|
||||
goog.testing.events.fireClickEvent(
|
||||
target, btn, opt_coords, opt_eventProperties)) &
|
||||
goog.testing.events.fireDoubleClickEvent(
|
||||
target, opt_coords, opt_eventProperties));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a complete keystroke (keydown, keypress, and keyup). Note that
|
||||
* if preventDefault is called on the keydown, the keypress will not fire.
|
||||
*
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {number} keyCode The keycode of the key pressed.
|
||||
* @param {Object=} opt_eventProperties Event properties to be mixed into the
|
||||
* BrowserEvent.
|
||||
* @return {boolean} The returnValue of the sequence: false if preventDefault()
|
||||
* was called on any of the events, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireKeySequence = function(
|
||||
target, keyCode, opt_eventProperties) {
|
||||
return goog.testing.events.fireNonAsciiKeySequence(target, keyCode, keyCode,
|
||||
opt_eventProperties);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a complete keystroke (keydown, keypress, and keyup) when typing
|
||||
* a non-ASCII character. Same as fireKeySequence, the keypress will not fire
|
||||
* if preventDefault is called on the keydown.
|
||||
*
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {number} keyCode The keycode of the keydown and keyup events.
|
||||
* @param {number} keyPressKeyCode The keycode of the keypress event.
|
||||
* @param {Object=} opt_eventProperties Event properties to be mixed into the
|
||||
* BrowserEvent.
|
||||
* @return {boolean} The returnValue of the sequence: false if preventDefault()
|
||||
* was called on any of the events, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireNonAsciiKeySequence = function(
|
||||
target, keyCode, keyPressKeyCode, opt_eventProperties) {
|
||||
var keydown =
|
||||
new goog.testing.events.Event(goog.events.EventType.KEYDOWN, target);
|
||||
var keyup =
|
||||
new goog.testing.events.Event(goog.events.EventType.KEYUP, target);
|
||||
var keypress =
|
||||
new goog.testing.events.Event(goog.events.EventType.KEYPRESS, target);
|
||||
keydown.keyCode = keyup.keyCode = keyCode;
|
||||
keypress.keyCode = keyPressKeyCode;
|
||||
|
||||
if (opt_eventProperties) {
|
||||
goog.object.extend(keydown, opt_eventProperties);
|
||||
goog.object.extend(keyup, opt_eventProperties);
|
||||
goog.object.extend(keypress, opt_eventProperties);
|
||||
}
|
||||
|
||||
// Fire keydown, keypress, and keyup. Note that if the keydown is
|
||||
// prevent-defaulted, then the keypress will not fire on IE.
|
||||
var result = true;
|
||||
if (!goog.testing.events.isBrokenGeckoMacActionKey_(keydown)) {
|
||||
result = goog.testing.events.fireBrowserEvent(keydown);
|
||||
}
|
||||
if (goog.events.KeyCodes.firesKeyPressEvent(
|
||||
keyCode, undefined, keydown.shiftKey, keydown.ctrlKey,
|
||||
keydown.altKey) &&
|
||||
!(goog.userAgent.IE && !result)) {
|
||||
result &= goog.testing.events.fireBrowserEvent(keypress);
|
||||
}
|
||||
return !!(result & goog.testing.events.fireBrowserEvent(keyup));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {goog.testing.events.Event} e The event.
|
||||
* @return {boolean} Whether this is the Gecko/Mac's Meta-C/V/X, which
|
||||
* is broken and requires special handling.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.events.isBrokenGeckoMacActionKey_ = function(e) {
|
||||
return goog.userAgent.MAC && goog.userAgent.GECKO &&
|
||||
(e.keyCode == goog.events.KeyCodes.C ||
|
||||
e.keyCode == goog.events.KeyCodes.X ||
|
||||
e.keyCode == goog.events.KeyCodes.V) && e.metaKey;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a mouseover event on the given target.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {EventTarget} relatedTarget The related target for the event (e.g.,
|
||||
* the node that the mouse is being moved out of).
|
||||
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @return {boolean} The returnValue of the event: false if preventDefault() was
|
||||
* called on it, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireMouseOverEvent = function(target, relatedTarget,
|
||||
opt_coords) {
|
||||
var mouseover =
|
||||
new goog.testing.events.Event(goog.events.EventType.MOUSEOVER, target);
|
||||
mouseover.relatedTarget = relatedTarget;
|
||||
goog.testing.events.setEventClientXY_(mouseover, opt_coords);
|
||||
return goog.testing.events.fireBrowserEvent(mouseover);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a mousemove event on the given target.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @return {boolean} The returnValue of the event: false if preventDefault() was
|
||||
* called on it, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireMouseMoveEvent = function(target, opt_coords) {
|
||||
var mousemove =
|
||||
new goog.testing.events.Event(goog.events.EventType.MOUSEMOVE, target);
|
||||
|
||||
goog.testing.events.setEventClientXY_(mousemove, opt_coords);
|
||||
return goog.testing.events.fireBrowserEvent(mousemove);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a mouseout event on the given target.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {EventTarget} relatedTarget The related target for the event (e.g.,
|
||||
* the node that the mouse is being moved into).
|
||||
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @return {boolean} The returnValue of the event: false if preventDefault() was
|
||||
* called on it, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireMouseOutEvent = function(target, relatedTarget,
|
||||
opt_coords) {
|
||||
var mouseout =
|
||||
new goog.testing.events.Event(goog.events.EventType.MOUSEOUT, target);
|
||||
mouseout.relatedTarget = relatedTarget;
|
||||
goog.testing.events.setEventClientXY_(mouseout, opt_coords);
|
||||
return goog.testing.events.fireBrowserEvent(mouseout);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a mousedown event on the given target.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
|
||||
* defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
|
||||
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @param {Object=} opt_eventProperties Event properties to be mixed into the
|
||||
* BrowserEvent.
|
||||
* @return {boolean} The returnValue of the event: false if preventDefault() was
|
||||
* called on it, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireMouseDownEvent =
|
||||
function(target, opt_button, opt_coords, opt_eventProperties) {
|
||||
|
||||
var button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
|
||||
button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
|
||||
goog.events.BrowserEvent.IEButtonMap[button] : button;
|
||||
return goog.testing.events.fireMouseButtonEvent_(
|
||||
goog.events.EventType.MOUSEDOWN, target, button, opt_coords,
|
||||
opt_eventProperties);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a mouseup event on the given target.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
|
||||
* defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
|
||||
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @param {Object=} opt_eventProperties Event properties to be mixed into the
|
||||
* BrowserEvent.
|
||||
* @return {boolean} The returnValue of the event: false if preventDefault() was
|
||||
* called on it, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireMouseUpEvent =
|
||||
function(target, opt_button, opt_coords, opt_eventProperties) {
|
||||
var button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
|
||||
button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
|
||||
goog.events.BrowserEvent.IEButtonMap[button] : button;
|
||||
return goog.testing.events.fireMouseButtonEvent_(
|
||||
goog.events.EventType.MOUSEUP, target, button, opt_coords,
|
||||
opt_eventProperties);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a click event on the given target. IE only supports click with
|
||||
* the left mouse button.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
|
||||
* defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
|
||||
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @param {Object=} opt_eventProperties Event properties to be mixed into the
|
||||
* BrowserEvent.
|
||||
* @return {boolean} The returnValue of the event: false if preventDefault() was
|
||||
* called on it, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireClickEvent =
|
||||
function(target, opt_button, opt_coords, opt_eventProperties) {
|
||||
return goog.testing.events.fireMouseButtonEvent_(goog.events.EventType.CLICK,
|
||||
target, opt_button, opt_coords, opt_eventProperties);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a double-click event on the given target. Always double-clicks
|
||||
* with the left mouse button since no browser supports double-clicking with
|
||||
* any other buttons.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @param {Object=} opt_eventProperties Event properties to be mixed into the
|
||||
* BrowserEvent.
|
||||
* @return {boolean} The returnValue of the event: false if preventDefault() was
|
||||
* called on it, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireDoubleClickEvent =
|
||||
function(target, opt_coords, opt_eventProperties) {
|
||||
return goog.testing.events.fireMouseButtonEvent_(
|
||||
goog.events.EventType.DBLCLICK, target,
|
||||
goog.events.BrowserEvent.MouseButton.LEFT, opt_coords,
|
||||
opt_eventProperties);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to fire a mouse event.
|
||||
* with the left mouse button since no browser supports double-clicking with
|
||||
* any other buttons.
|
||||
* @param {string} type The event type.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {number=} opt_button Mouse button; defaults to
|
||||
* {@code goog.events.BrowserEvent.MouseButton.LEFT}.
|
||||
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @param {Object=} opt_eventProperties Event properties to be mixed into the
|
||||
* BrowserEvent.
|
||||
* @return {boolean} The returnValue of the event: false if preventDefault() was
|
||||
* called on it, true otherwise.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.events.fireMouseButtonEvent_ =
|
||||
function(type, target, opt_button, opt_coords, opt_eventProperties) {
|
||||
var e =
|
||||
new goog.testing.events.Event(type, target);
|
||||
e.button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
|
||||
goog.testing.events.setEventClientXY_(e, opt_coords);
|
||||
if (opt_eventProperties) {
|
||||
goog.object.extend(e, opt_eventProperties);
|
||||
}
|
||||
return goog.testing.events.fireBrowserEvent(e);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a contextmenu event on the given target.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @return {boolean} The returnValue of the event: false if preventDefault() was
|
||||
* called on it, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireContextMenuEvent = function(target, opt_coords) {
|
||||
var button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ?
|
||||
goog.events.BrowserEvent.MouseButton.LEFT :
|
||||
goog.events.BrowserEvent.MouseButton.RIGHT;
|
||||
var contextmenu =
|
||||
new goog.testing.events.Event(goog.events.EventType.CONTEXTMENU, target);
|
||||
contextmenu.button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
|
||||
goog.events.BrowserEvent.IEButtonMap[button] : button;
|
||||
contextmenu.ctrlKey = goog.userAgent.MAC;
|
||||
goog.testing.events.setEventClientXY_(contextmenu, opt_coords);
|
||||
return goog.testing.events.fireBrowserEvent(contextmenu);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a mousedown, contextmenu, and the mouseup on the given event
|
||||
* target, with the right mouse button.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @return {boolean} The returnValue of the sequence: false if preventDefault()
|
||||
* was called on any of the events, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireContextMenuSequence = function(target, opt_coords) {
|
||||
var props = goog.userAgent.MAC ? {ctrlKey: true} : {};
|
||||
var button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ?
|
||||
goog.events.BrowserEvent.MouseButton.LEFT :
|
||||
goog.events.BrowserEvent.MouseButton.RIGHT;
|
||||
|
||||
var result = goog.testing.events.fireMouseDownEvent(target,
|
||||
button, opt_coords, props);
|
||||
if (goog.userAgent.WINDOWS) {
|
||||
// All browsers are consistent on Windows.
|
||||
result &= goog.testing.events.fireMouseUpEvent(target,
|
||||
button, opt_coords) &
|
||||
goog.testing.events.fireContextMenuEvent(target, opt_coords);
|
||||
} else {
|
||||
result &= goog.testing.events.fireContextMenuEvent(target, opt_coords);
|
||||
|
||||
// GECKO on Mac and Linux always fires the mouseup after the contextmenu.
|
||||
|
||||
// WEBKIT is really weird.
|
||||
//
|
||||
// On Linux, it sometimes fires mouseup, but most of the time doesn't.
|
||||
// It's really hard to reproduce consistently. I think there's some
|
||||
// internal race condition. If contextmenu is preventDefaulted, then
|
||||
// mouseup always fires.
|
||||
//
|
||||
// On Mac, it always fires mouseup and then fires a click.
|
||||
result &= goog.testing.events.fireMouseUpEvent(target,
|
||||
button, opt_coords, props);
|
||||
|
||||
if (goog.userAgent.WEBKIT && goog.userAgent.MAC) {
|
||||
result &= goog.testing.events.fireClickEvent(
|
||||
target, button, opt_coords, props);
|
||||
}
|
||||
}
|
||||
return !!result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a popstate event on the given target.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {Object} state History state object.
|
||||
* @return {boolean} The returnValue of the event: false if preventDefault() was
|
||||
* called on it, true otherwise.
|
||||
*/
|
||||
goog.testing.events.firePopStateEvent = function(target, state) {
|
||||
var e = new goog.testing.events.Event(goog.events.EventType.POPSTATE, target);
|
||||
e.state = state;
|
||||
return goog.testing.events.fireBrowserEvent(e);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulate a focus event on the given target.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @return {boolean} The value returned by firing the focus browser event,
|
||||
* which returns false iff 'preventDefault' was invoked.
|
||||
*/
|
||||
goog.testing.events.fireFocusEvent = function(target) {
|
||||
var e = new goog.testing.events.Event(
|
||||
goog.events.EventType.FOCUS, target);
|
||||
return goog.testing.events.fireBrowserEvent(e);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates an event's capturing and bubbling phases.
|
||||
* @param {Event} event A simulated native event. It will be wrapped in a
|
||||
* normalized BrowserEvent and dispatched to Closure listeners on all
|
||||
* ancestors of its target (inclusive).
|
||||
* @return {boolean} The returnValue of the event: false if preventDefault() was
|
||||
* called on it, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireBrowserEvent = function(event) {
|
||||
event.returnValue_ = true;
|
||||
|
||||
// generate a list of ancestors
|
||||
var ancestors = [];
|
||||
for (var current = event.target; current; current = current.parentNode) {
|
||||
ancestors.push(current);
|
||||
}
|
||||
|
||||
// dispatch capturing listeners
|
||||
for (var j = ancestors.length - 1;
|
||||
j >= 0 && !event.propagationStopped_;
|
||||
j--) {
|
||||
goog.events.fireListeners(ancestors[j], event.type, true,
|
||||
new goog.events.BrowserEvent(event, ancestors[j]));
|
||||
}
|
||||
|
||||
// dispatch bubbling listeners
|
||||
for (var j = 0;
|
||||
j < ancestors.length && !event.propagationStopped_;
|
||||
j++) {
|
||||
goog.events.fireListeners(ancestors[j], event.type, false,
|
||||
new goog.events.BrowserEvent(event, ancestors[j]));
|
||||
}
|
||||
|
||||
return event.returnValue_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a touchstart event on the given target.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @param {Object=} opt_eventProperties Event properties to be mixed into the
|
||||
* BrowserEvent.
|
||||
* @return {boolean} The returnValue of the event: false if preventDefault() was
|
||||
* called on it, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireTouchStartEvent = function(
|
||||
target, opt_coords, opt_eventProperties) {
|
||||
// TODO: Support multi-touch events with array of coordinates.
|
||||
var touchstart =
|
||||
new goog.testing.events.Event(goog.events.EventType.TOUCHSTART, target);
|
||||
goog.testing.events.setEventClientXY_(touchstart, opt_coords);
|
||||
if (opt_eventProperties) {
|
||||
goog.object.extend(touchstart, opt_eventProperties);
|
||||
}
|
||||
return goog.testing.events.fireBrowserEvent(touchstart);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a touchmove event on the given target.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @param {Object=} opt_eventProperties Event properties to be mixed into the
|
||||
* BrowserEvent.
|
||||
* @return {boolean} The returnValue of the event: false if preventDefault() was
|
||||
* called on it, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireTouchMoveEvent = function(
|
||||
target, opt_coords, opt_eventProperties) {
|
||||
// TODO: Support multi-touch events with array of coordinates.
|
||||
var touchmove =
|
||||
new goog.testing.events.Event(goog.events.EventType.TOUCHMOVE, target);
|
||||
goog.testing.events.setEventClientXY_(touchmove, opt_coords);
|
||||
if (opt_eventProperties) {
|
||||
goog.object.extend(touchmove, opt_eventProperties);
|
||||
}
|
||||
return goog.testing.events.fireBrowserEvent(touchmove);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a touchend event on the given target.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @param {Object=} opt_eventProperties Event properties to be mixed into the
|
||||
* BrowserEvent.
|
||||
* @return {boolean} The returnValue of the event: false if preventDefault() was
|
||||
* called on it, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireTouchEndEvent = function(
|
||||
target, opt_coords, opt_eventProperties) {
|
||||
// TODO: Support multi-touch events with array of coordinates.
|
||||
var touchend =
|
||||
new goog.testing.events.Event(goog.events.EventType.TOUCHEND, target);
|
||||
goog.testing.events.setEventClientXY_(touchend, opt_coords);
|
||||
if (opt_eventProperties) {
|
||||
goog.object.extend(touchend, opt_eventProperties);
|
||||
}
|
||||
return goog.testing.events.fireBrowserEvent(touchend);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates a simple touch sequence on the given target.
|
||||
* @param {EventTarget} target The target for the event.
|
||||
* @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event
|
||||
* target's position (if available), otherwise (0, 0).
|
||||
* @param {Object=} opt_eventProperties Event properties to be mixed into the
|
||||
* BrowserEvent.
|
||||
* @return {boolean} The returnValue of the sequence: false if preventDefault()
|
||||
* was called on any of the events, true otherwise.
|
||||
*/
|
||||
goog.testing.events.fireTouchSequence = function(
|
||||
target, opt_coords, opt_eventProperties) {
|
||||
// TODO: Support multi-touch events with array of coordinates.
|
||||
// Fire touchstart, touchmove, touchend then return the bitwise AND of the 3.
|
||||
return !!(goog.testing.events.fireTouchStartEvent(
|
||||
target, opt_coords, opt_eventProperties) &
|
||||
goog.testing.events.fireTouchEndEvent(
|
||||
target, opt_coords, opt_eventProperties));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Mixins a listenable into the given object. This turns the object
|
||||
* into a goog.events.Listenable. This is useful, for example, when
|
||||
* you need to mock a implementation of listenable and still want it
|
||||
* to work with goog.events.
|
||||
* @param {!Object} obj The object to mixin into.
|
||||
*/
|
||||
goog.testing.events.mixinListenable = function(obj) {
|
||||
var listenable = new goog.events.EventTarget();
|
||||
|
||||
listenable.setTargetForTesting(obj);
|
||||
|
||||
var listenablePrototype = goog.events.EventTarget.prototype;
|
||||
var disposablePrototype = goog.Disposable.prototype;
|
||||
for (var key in listenablePrototype) {
|
||||
if (listenablePrototype.hasOwnProperty(key) ||
|
||||
disposablePrototype.hasOwnProperty(key)) {
|
||||
var member = listenablePrototype[key];
|
||||
if (goog.isFunction(member)) {
|
||||
obj[key] = goog.bind(member, listenable);
|
||||
} else {
|
||||
obj[key] = member;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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 Mock matchers for event related arguments.
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.events.EventMatcher');
|
||||
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.testing.mockmatchers.ArgumentMatcher');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that verifies that an argument is a {@code goog.events.Event} of a
|
||||
* particular type.
|
||||
* @param {string} type The single type the event argument must be of.
|
||||
* @constructor
|
||||
* @extends {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.events.EventMatcher = function(type) {
|
||||
goog.testing.mockmatchers.ArgumentMatcher.call(this,
|
||||
function(obj) {
|
||||
return obj instanceof goog.events.Event &&
|
||||
obj.type == type;
|
||||
}, 'isEventOfType(' + type + ')');
|
||||
};
|
||||
goog.inherits(goog.testing.events.EventMatcher,
|
||||
goog.testing.mockmatchers.ArgumentMatcher);
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright 2012 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 NetworkStatusMonitor test double.
|
||||
* @author dbk@google.com (David Barrett-Kahn)
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.events.OnlineHandler');
|
||||
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.net.NetworkStatusMonitor');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* NetworkStatusMonitor test double.
|
||||
* @param {boolean} initialState The initial online state of the mock.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
* @implements {goog.net.NetworkStatusMonitor}
|
||||
*/
|
||||
goog.testing.events.OnlineHandler = function(initialState) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* Whether the mock is online.
|
||||
* @private {boolean}
|
||||
*/
|
||||
this.online_ = initialState;
|
||||
};
|
||||
goog.inherits(goog.testing.events.OnlineHandler, goog.events.EventTarget);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.events.OnlineHandler.prototype.isOnline = function() {
|
||||
return this.online_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the online state.
|
||||
* @param {boolean} newOnlineState The new online state.
|
||||
*/
|
||||
goog.testing.events.OnlineHandler.prototype.setOnline =
|
||||
function(newOnlineState) {
|
||||
if (newOnlineState != this.online_) {
|
||||
this.online_ = newOnlineState;
|
||||
this.dispatchEvent(newOnlineState ?
|
||||
goog.net.NetworkStatusMonitor.EventType.ONLINE :
|
||||
goog.net.NetworkStatusMonitor.EventType.OFFLINE);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,236 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview Helper class to allow for expected unit test failures.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.ExpectedFailures');
|
||||
|
||||
goog.require('goog.debug.DivConsole');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.testing.JsUnitException');
|
||||
goog.require('goog.testing.TestCase');
|
||||
goog.require('goog.testing.asserts');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Helper class for allowing some unit tests to fail, particularly designed to
|
||||
* mark tests that should be fixed on a given browser.
|
||||
*
|
||||
* <pre>
|
||||
* var expectedFailures = new goog.testing.ExpectedFailures();
|
||||
*
|
||||
* function tearDown() {
|
||||
* expectedFailures.handleTearDown();
|
||||
* }
|
||||
*
|
||||
* function testSomethingThatBreaksInWebKit() {
|
||||
* expectedFailures.expectFailureFor(goog.userAgent.WEBKIT);
|
||||
*
|
||||
* try {
|
||||
* ...
|
||||
* assert(somethingThatFailsInWebKit);
|
||||
* ...
|
||||
* } catch (e) {
|
||||
* expectedFailures.handleException(e);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.ExpectedFailures = function() {
|
||||
goog.testing.ExpectedFailures.setUpConsole_();
|
||||
this.reset_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The lazily created debugging console.
|
||||
* @type {goog.debug.DivConsole?}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ExpectedFailures.console_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Logger for the expected failures.
|
||||
* @type {goog.log.Logger}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ExpectedFailures.prototype.logger_ =
|
||||
goog.log.getLogger('goog.testing.ExpectedFailures');
|
||||
|
||||
|
||||
/**
|
||||
* Whether or not we are expecting failure.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ExpectedFailures.prototype.expectingFailure_;
|
||||
|
||||
|
||||
/**
|
||||
* The string to emit upon an expected failure.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ExpectedFailures.prototype.failureMessage_;
|
||||
|
||||
|
||||
/**
|
||||
* An array of suppressed failures.
|
||||
* @type {Array}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ExpectedFailures.prototype.suppressedFailures_;
|
||||
|
||||
|
||||
/**
|
||||
* Sets up the debug console, if it isn't already set up.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ExpectedFailures.setUpConsole_ = function() {
|
||||
if (!goog.testing.ExpectedFailures.console_) {
|
||||
var xButton = goog.dom.createDom(goog.dom.TagName.DIV, {
|
||||
'style': 'position: absolute; border-left:1px solid #333;' +
|
||||
'border-bottom:1px solid #333; right: 0; top: 0; width: 1em;' +
|
||||
'height: 1em; cursor: pointer; background-color: #cde;' +
|
||||
'text-align: center; color: black'
|
||||
}, 'X');
|
||||
var div = goog.dom.createDom(goog.dom.TagName.DIV, {
|
||||
'style': 'position: absolute; border: 1px solid #333; right: 10px;' +
|
||||
'top : 10px; width: 400px; display: none'
|
||||
}, xButton);
|
||||
document.body.appendChild(div);
|
||||
goog.events.listen(xButton, goog.events.EventType.CLICK, function() {
|
||||
goog.style.setElementShown(div, false);
|
||||
});
|
||||
|
||||
goog.testing.ExpectedFailures.console_ = new goog.debug.DivConsole(div);
|
||||
goog.log.addHandler(goog.testing.ExpectedFailures.prototype.logger_,
|
||||
goog.bind(goog.style.setElementShown, null, div, true));
|
||||
goog.log.addHandler(goog.testing.ExpectedFailures.prototype.logger_,
|
||||
goog.bind(goog.testing.ExpectedFailures.console_.addLogRecord,
|
||||
goog.testing.ExpectedFailures.console_));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Register to expect failure for the given condition. Multiple calls to this
|
||||
* function act as a boolean OR. The first applicable message will be used.
|
||||
* @param {boolean} condition Whether to expect failure.
|
||||
* @param {string=} opt_message Descriptive message of this expected failure.
|
||||
*/
|
||||
goog.testing.ExpectedFailures.prototype.expectFailureFor = function(
|
||||
condition, opt_message) {
|
||||
this.expectingFailure_ = this.expectingFailure_ || condition;
|
||||
if (condition) {
|
||||
this.failureMessage_ = this.failureMessage_ || opt_message || '';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Determines if the given exception was expected.
|
||||
* @param {Object} ex The exception to check.
|
||||
* @return {boolean} Whether the exception was expected.
|
||||
*/
|
||||
goog.testing.ExpectedFailures.prototype.isExceptionExpected = function(ex) {
|
||||
return this.expectingFailure_ && ex instanceof goog.testing.JsUnitException;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle an exception, suppressing it if it is a unit test failure that we
|
||||
* expected.
|
||||
* @param {Error} ex The exception to handle.
|
||||
*/
|
||||
goog.testing.ExpectedFailures.prototype.handleException = function(ex) {
|
||||
if (this.isExceptionExpected(ex)) {
|
||||
goog.log.info(this.logger_, 'Suppressing test failure in ' +
|
||||
goog.testing.TestCase.currentTestName + ':' +
|
||||
(this.failureMessage_ ? '\n(' + this.failureMessage_ + ')' : ''),
|
||||
ex);
|
||||
this.suppressedFailures_.push(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
// Rethrow the exception if we weren't expecting it or if it is a normal
|
||||
// exception.
|
||||
throw ex;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Run the given function, catching any expected failures.
|
||||
* @param {Function} func The function to run.
|
||||
* @param {boolean=} opt_lenient Whether to ignore if the expected failures
|
||||
* didn't occur. In this case a warning will be logged in handleTearDown.
|
||||
*/
|
||||
goog.testing.ExpectedFailures.prototype.run = function(func, opt_lenient) {
|
||||
try {
|
||||
func();
|
||||
} catch (ex) {
|
||||
this.handleException(ex);
|
||||
}
|
||||
|
||||
if (!opt_lenient && this.expectingFailure_ &&
|
||||
!this.suppressedFailures_.length) {
|
||||
fail(this.getExpectationMessage_());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} A warning describing an expected failure that didn't occur.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ExpectedFailures.prototype.getExpectationMessage_ = function() {
|
||||
return 'Expected a test failure in \'' +
|
||||
goog.testing.TestCase.currentTestName + '\' but the test passed.';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle the tearDown phase of a test, alerting the user if an expected test
|
||||
* was not suppressed.
|
||||
*/
|
||||
goog.testing.ExpectedFailures.prototype.handleTearDown = function() {
|
||||
if (this.expectingFailure_ && !this.suppressedFailures_.length) {
|
||||
goog.log.warning(this.logger_, this.getExpectationMessage_());
|
||||
}
|
||||
this.reset_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Reset internal state.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ExpectedFailures.prototype.reset_ = function() {
|
||||
this.expectingFailure_ = false;
|
||||
this.failureMessage_ = '';
|
||||
this.suppressedFailures_ = [];
|
||||
};
|
||||
116
float-no-zero/closure-library/closure/goog/testing/fs/blob.js
Normal file
116
float-no-zero/closure-library/closure/goog/testing/fs/blob.js
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2011 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 Mock blob object.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.fs.Blob');
|
||||
|
||||
goog.require('goog.crypt.base64');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A mock Blob object. The data is stored as a string.
|
||||
*
|
||||
* @param {string=} opt_data The string data encapsulated by the blob.
|
||||
* @param {string=} opt_type The mime type of the blob.
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.fs.Blob = function(opt_data, opt_type) {
|
||||
/**
|
||||
* @see http://www.w3.org/TR/FileAPI/#dfn-type
|
||||
* @type {string}
|
||||
*/
|
||||
this.type = opt_type || '';
|
||||
|
||||
this.setDataInternal(opt_data || '');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The string data encapsulated by the blob.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.Blob.prototype.data_;
|
||||
|
||||
|
||||
/**
|
||||
* @see http://www.w3.org/TR/FileAPI/#dfn-size
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.fs.Blob.prototype.size;
|
||||
|
||||
|
||||
/**
|
||||
* @see http://www.w3.org/TR/FileAPI/#dfn-slice
|
||||
* @param {number} start The start byte offset.
|
||||
* @param {number} length The number of bytes to slice.
|
||||
* @param {string=} opt_contentType The type of the resulting Blob.
|
||||
* @return {!goog.testing.fs.Blob} The result of the slice operation.
|
||||
*/
|
||||
goog.testing.fs.Blob.prototype.slice = function(
|
||||
start, length, opt_contentType) {
|
||||
start = Math.max(0, start);
|
||||
return new goog.testing.fs.Blob(
|
||||
this.data_.substring(start, start + Math.max(length, 0)),
|
||||
opt_contentType);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The string data encapsulated by the blob.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.fs.Blob.prototype.toString = function() {
|
||||
return this.data_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {ArrayBuffer} The string data encapsulated by the blob as an
|
||||
* ArrayBuffer.
|
||||
*/
|
||||
goog.testing.fs.Blob.prototype.toArrayBuffer = function() {
|
||||
var buf = new ArrayBuffer(this.data_.length * 2);
|
||||
var arr = new Uint16Array(buf);
|
||||
for (var i = 0; i < this.data_.length; i++) {
|
||||
arr[i] = this.data_.charCodeAt(i);
|
||||
}
|
||||
return buf;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The string data encapsulated by the blob as a data: URI.
|
||||
*/
|
||||
goog.testing.fs.Blob.prototype.toDataUrl = function() {
|
||||
return 'data:' + this.type + ';base64,' +
|
||||
goog.crypt.base64.encodeString(this.data_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the internal contents of the blob. This should only be called by other
|
||||
* functions inside the {@code goog.testing.fs} namespace.
|
||||
*
|
||||
* @param {string} data The data for this Blob.
|
||||
*/
|
||||
goog.testing.fs.Blob.prototype.setDataInternal = function(data) {
|
||||
this.data_ = data;
|
||||
this.size = data.length;
|
||||
};
|
||||
621
float-no-zero/closure-library/closure/goog/testing/fs/entry.js
Normal file
621
float-no-zero/closure-library/closure/goog/testing/fs/entry.js
Normal file
@@ -0,0 +1,621 @@
|
||||
// Copyright 2011 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 Mock filesystem objects. These are all in the same file to
|
||||
* avoid circular dependency issues.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.fs.DirectoryEntry');
|
||||
goog.provide('goog.testing.fs.Entry');
|
||||
goog.provide('goog.testing.fs.FileEntry');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.async.Deferred');
|
||||
goog.require('goog.fs.DirectoryEntry');
|
||||
goog.require('goog.fs.DirectoryEntryImpl');
|
||||
goog.require('goog.fs.Entry');
|
||||
goog.require('goog.fs.Error');
|
||||
goog.require('goog.fs.FileEntry');
|
||||
goog.require('goog.functions');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.testing.fs.File');
|
||||
goog.require('goog.testing.fs.FileWriter');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A mock filesystem entry object.
|
||||
*
|
||||
* @param {!goog.testing.fs.FileSystem} fs The filesystem containing this entry.
|
||||
* @param {!goog.testing.fs.DirectoryEntry} parent The directory entry directly
|
||||
* containing this entry.
|
||||
* @param {string} name The name of this entry.
|
||||
* @constructor
|
||||
* @implements {goog.fs.Entry}
|
||||
*/
|
||||
goog.testing.fs.Entry = function(fs, parent, name) {
|
||||
/**
|
||||
* This entry's filesystem.
|
||||
* @type {!goog.testing.fs.FileSystem}
|
||||
* @private
|
||||
*/
|
||||
this.fs_ = fs;
|
||||
|
||||
/**
|
||||
* The name of this entry.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.name_ = name;
|
||||
|
||||
/**
|
||||
* The parent of this entry.
|
||||
* @type {!goog.testing.fs.DirectoryEntry}
|
||||
*/
|
||||
this.parent = parent;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether or not this entry has been deleted.
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.testing.fs.Entry.prototype.deleted = false;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.Entry.prototype.isFile = goog.abstractMethod;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.Entry.prototype.isDirectory = goog.abstractMethod;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.Entry.prototype.getName = function() {
|
||||
return this.name_;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.Entry.prototype.getFullPath = function() {
|
||||
if (this.getName() == '' || this.parent.getName() == '') {
|
||||
// The root directory has an empty name
|
||||
return '/' + this.name_;
|
||||
} else {
|
||||
return this.parent.getFullPath() + '/' + this.name_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!goog.testing.fs.FileSystem}
|
||||
* @override
|
||||
*/
|
||||
goog.testing.fs.Entry.prototype.getFileSystem = function() {
|
||||
return this.fs_;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.Entry.prototype.getLastModified = goog.abstractMethod;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.Entry.prototype.getMetadata = goog.abstractMethod;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.Entry.prototype.moveTo = function(parent, opt_newName) {
|
||||
var msg = 'moving ' + this.getFullPath() + ' into ' + parent.getFullPath() +
|
||||
(opt_newName ? ', renaming to ' + opt_newName : '');
|
||||
var newFile;
|
||||
return this.checkNotDeleted(msg).
|
||||
addCallback(function() { return this.copyTo(parent, opt_newName); }).
|
||||
addCallback(function(file) {
|
||||
newFile = file;
|
||||
return this.remove();
|
||||
}).addCallback(function() { return newFile; });
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.Entry.prototype.copyTo = function(parent, opt_newName) {
|
||||
goog.asserts.assert(parent instanceof goog.testing.fs.DirectoryEntry);
|
||||
var msg = 'copying ' + this.getFullPath() + ' into ' + parent.getFullPath() +
|
||||
(opt_newName ? ', renaming to ' + opt_newName : '');
|
||||
return this.checkNotDeleted(msg).addCallback(function() {
|
||||
var name = opt_newName || this.getName();
|
||||
var entry = this.clone();
|
||||
parent.children[name] = entry;
|
||||
parent.lastModifiedTimestamp_ = goog.now();
|
||||
entry.name_ = name;
|
||||
entry.parent = parent;
|
||||
return entry;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!goog.testing.fs.Entry} A shallow copy of this entry object.
|
||||
*/
|
||||
goog.testing.fs.Entry.prototype.clone = goog.abstractMethod;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.Entry.prototype.toUrl = function(opt_mimetype) {
|
||||
return 'fakefilesystem:' + this.getFullPath();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.Entry.prototype.toUri = goog.testing.fs.Entry.prototype.toUrl;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.Entry.prototype.wrapEntry = goog.abstractMethod;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.Entry.prototype.remove = function() {
|
||||
var msg = 'removing ' + this.getFullPath();
|
||||
return this.checkNotDeleted(msg).addCallback(function() {
|
||||
delete this.parent.children[this.getName()];
|
||||
this.parent.lastModifiedTimestamp_ = goog.now();
|
||||
this.deleted = true;
|
||||
return;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.Entry.prototype.getParent = function() {
|
||||
var msg = 'getting parent of ' + this.getFullPath();
|
||||
return this.checkNotDeleted(msg).
|
||||
addCallback(function() { return this.parent; });
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Return a deferred that will call its errback if this entry has been deleted.
|
||||
* In addition, the deferred will only run after a timeout of 0, and all its
|
||||
* callbacks will run with the entry as "this".
|
||||
*
|
||||
* @param {string} action The name of the action being performed. For error
|
||||
* reporting.
|
||||
* @return {!goog.async.Deferred} The deferred that will be called after a
|
||||
* timeout of 0.
|
||||
* @protected
|
||||
*/
|
||||
goog.testing.fs.Entry.prototype.checkNotDeleted = function(action) {
|
||||
var d = new goog.async.Deferred(undefined, this);
|
||||
goog.Timer.callOnce(function() {
|
||||
if (this.deleted) {
|
||||
d.errback(new goog.fs.Error(goog.fs.Error.ErrorCode.NOT_FOUND, action));
|
||||
} else {
|
||||
d.callback();
|
||||
}
|
||||
}, 0, this);
|
||||
return d;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A mock directory entry object.
|
||||
*
|
||||
* @param {!goog.testing.fs.FileSystem} fs The filesystem containing this entry.
|
||||
* @param {goog.testing.fs.DirectoryEntry} parent The directory entry directly
|
||||
* containing this entry. If this is null, that means this is the root
|
||||
* directory and so is its own parent.
|
||||
* @param {string} name The name of this entry.
|
||||
* @param {!Object.<!goog.testing.fs.Entry>} children The map of child names to
|
||||
* entry objects.
|
||||
* @constructor
|
||||
* @extends {goog.testing.fs.Entry}
|
||||
* @implements {goog.fs.DirectoryEntry}
|
||||
*/
|
||||
goog.testing.fs.DirectoryEntry = function(fs, parent, name, children) {
|
||||
goog.base(this, fs, parent || this, name);
|
||||
|
||||
/**
|
||||
* The map of child names to entry objects.
|
||||
* @type {!Object.<!goog.testing.fs.Entry>}
|
||||
*/
|
||||
this.children = children;
|
||||
|
||||
/**
|
||||
* The modification time of the directory. Measured using goog.now, which may
|
||||
* be overridden with mock time providers.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.lastModifiedTimestamp_ = goog.now();
|
||||
};
|
||||
goog.inherits(goog.testing.fs.DirectoryEntry, goog.testing.fs.Entry);
|
||||
|
||||
|
||||
/**
|
||||
* Constructs and returns the metadata object for this entry.
|
||||
* @return {{modificationTime: Date}} The metadata object.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.DirectoryEntry.prototype.getMetadata_ = function() {
|
||||
return {
|
||||
'modificationTime': new Date(this.lastModifiedTimestamp_)
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.DirectoryEntry.prototype.isFile = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.DirectoryEntry.prototype.isDirectory = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.DirectoryEntry.prototype.getLastModified = function() {
|
||||
var msg = 'reading last modified date for ' + this.getFullPath();
|
||||
return this.checkNotDeleted(msg).
|
||||
addCallback(function() {return new Date(this.lastModifiedTimestamp_)});
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.DirectoryEntry.prototype.getMetadata = function() {
|
||||
var msg = 'reading metadata for ' + this.getFullPath();
|
||||
return this.checkNotDeleted(msg).
|
||||
addCallback(function() {return this.getMetadata_()});
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.DirectoryEntry.prototype.clone = function() {
|
||||
return new goog.testing.fs.DirectoryEntry(
|
||||
this.getFileSystem(), this.parent, this.getName(), this.children);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.DirectoryEntry.prototype.remove = function() {
|
||||
if (!goog.object.isEmpty(this.children)) {
|
||||
var d = new goog.async.Deferred();
|
||||
goog.Timer.callOnce(function() {
|
||||
d.errback(new goog.fs.Error(
|
||||
goog.fs.Error.ErrorCode.INVALID_MODIFICATION,
|
||||
'removing ' + this.getFullPath()));
|
||||
}, 0, this);
|
||||
return d;
|
||||
} else {
|
||||
return goog.base(this, 'remove');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.DirectoryEntry.prototype.getFile = function(
|
||||
path, opt_behavior) {
|
||||
var msg = 'loading file ' + path + ' from ' + this.getFullPath();
|
||||
opt_behavior = opt_behavior || goog.fs.DirectoryEntry.Behavior.DEFAULT;
|
||||
return this.checkNotDeleted(msg).addCallback(function() {
|
||||
try {
|
||||
return goog.async.Deferred.succeed(this.getFileSync(path, opt_behavior));
|
||||
} catch (e) {
|
||||
return goog.async.Deferred.fail(e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.DirectoryEntry.prototype.getDirectory = function(
|
||||
path, opt_behavior) {
|
||||
var msg = 'loading directory ' + path + ' from ' + this.getFullPath();
|
||||
opt_behavior = opt_behavior || goog.fs.DirectoryEntry.Behavior.DEFAULT;
|
||||
return this.checkNotDeleted(msg).addCallback(function() {
|
||||
try {
|
||||
return goog.async.Deferred.succeed(
|
||||
this.getDirectorySync(path, opt_behavior));
|
||||
} catch (e) {
|
||||
return goog.async.Deferred.fail(e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get a file entry synchronously, without waiting for a Deferred to resolve.
|
||||
*
|
||||
* @param {string} path The path to the file, relative to this directory.
|
||||
* @param {goog.fs.DirectoryEntry.Behavior=} opt_behavior The behavior for
|
||||
* loading the file.
|
||||
* @return {!goog.testing.fs.FileEntry} The loaded file.
|
||||
*/
|
||||
goog.testing.fs.DirectoryEntry.prototype.getFileSync = function(
|
||||
path, opt_behavior) {
|
||||
opt_behavior = opt_behavior || goog.fs.DirectoryEntry.Behavior.DEFAULT;
|
||||
return (/** @type {!goog.testing.fs.FileEntry} */ (this.getEntry_(
|
||||
path, opt_behavior, true /* isFile */,
|
||||
goog.bind(function(parent, name) {
|
||||
return new goog.testing.fs.FileEntry(
|
||||
this.getFileSystem(), parent, name, '');
|
||||
}, this))));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a file synchronously. This is a shorthand for getFileSync, useful for
|
||||
* setting up tests.
|
||||
*
|
||||
* @param {string} path The path to the file, relative to this directory.
|
||||
* @return {!goog.testing.fs.FileEntry} The created file.
|
||||
*/
|
||||
goog.testing.fs.DirectoryEntry.prototype.createFileSync = function(path) {
|
||||
return this.getFileSync(path, goog.fs.DirectoryEntry.Behavior.CREATE);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get a directory synchronously, without waiting for a Deferred to resolve.
|
||||
*
|
||||
* @param {string} path The path to the directory, relative to this one.
|
||||
* @param {goog.fs.DirectoryEntry.Behavior=} opt_behavior The behavior for
|
||||
* loading the directory.
|
||||
* @return {!goog.testing.fs.DirectoryEntry} The loaded directory.
|
||||
*/
|
||||
goog.testing.fs.DirectoryEntry.prototype.getDirectorySync = function(
|
||||
path, opt_behavior) {
|
||||
opt_behavior = opt_behavior || goog.fs.DirectoryEntry.Behavior.DEFAULT;
|
||||
return (/** @type {!goog.testing.fs.DirectoryEntry} */ (this.getEntry_(
|
||||
path, opt_behavior, false /* isFile */,
|
||||
goog.bind(function(parent, name) {
|
||||
return new goog.testing.fs.DirectoryEntry(
|
||||
this.getFileSystem(), parent, name, {});
|
||||
}, this))));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a directory synchronously. This is a shorthand for getFileSync,
|
||||
* useful for setting up tests.
|
||||
*
|
||||
* @param {string} path The path to the directory, relative to this directory.
|
||||
* @return {!goog.testing.fs.DirectoryEntry} The created directory.
|
||||
*/
|
||||
goog.testing.fs.DirectoryEntry.prototype.createDirectorySync = function(path) {
|
||||
return this.getDirectorySync(path, goog.fs.DirectoryEntry.Behavior.CREATE);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get a file or directory entry from a path. This handles parsing the path for
|
||||
* subdirectories and throwing appropriate errors should something go wrong.
|
||||
*
|
||||
* @param {string} path The path to the entry, relative to this directory.
|
||||
* @param {goog.fs.DirectoryEntry.Behavior} behavior The behavior for loading
|
||||
* the entry.
|
||||
* @param {boolean} isFile Whether a file or directory is being loaded.
|
||||
* @param {function(!goog.testing.fs.DirectoryEntry, string) :
|
||||
* !goog.testing.fs.Entry} createFn
|
||||
* The function for creating the entry if it doesn't yet exist. This is
|
||||
* passed the parent entry and the name of the new entry.
|
||||
* @return {!goog.testing.fs.Entry} The loaded entry.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.DirectoryEntry.prototype.getEntry_ = function(
|
||||
path, behavior, isFile, createFn) {
|
||||
// Filter out leading, trailing, and duplicate slashes.
|
||||
var components = goog.array.filter(path.split('/'), goog.functions.identity);
|
||||
|
||||
var basename = /** @type {string} */ (goog.array.peek(components)) || '';
|
||||
var dir = goog.string.startsWith(path, '/') ?
|
||||
this.getFileSystem().getRoot() : this;
|
||||
|
||||
goog.array.forEach(components.slice(0, -1), function(p) {
|
||||
var subdir = dir.children[p];
|
||||
if (!subdir) {
|
||||
throw new goog.fs.Error(
|
||||
goog.fs.Error.ErrorCode.NOT_FOUND,
|
||||
'loading ' + path + ' from ' + this.getFullPath() + ' (directory ' +
|
||||
dir.getFullPath() + '/' + p + ')');
|
||||
}
|
||||
dir = subdir;
|
||||
}, this);
|
||||
|
||||
// If there is no basename, the path must resolve to the root directory.
|
||||
var entry = basename ? dir.children[basename] : dir;
|
||||
|
||||
if (!entry) {
|
||||
if (behavior == goog.fs.DirectoryEntry.Behavior.DEFAULT) {
|
||||
throw new goog.fs.Error(
|
||||
goog.fs.Error.ErrorCode.NOT_FOUND,
|
||||
'loading ' + path + ' from ' + this.getFullPath());
|
||||
} else {
|
||||
goog.asserts.assert(
|
||||
behavior == goog.fs.DirectoryEntry.Behavior.CREATE ||
|
||||
behavior == goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE);
|
||||
entry = createFn(dir, basename);
|
||||
dir.children[basename] = entry;
|
||||
this.lastModifiedTimestamp_ = goog.now();
|
||||
return entry;
|
||||
}
|
||||
} else if (behavior == goog.fs.DirectoryEntry.Behavior.CREATE_EXCLUSIVE) {
|
||||
throw new goog.fs.Error(
|
||||
goog.fs.Error.ErrorCode.PATH_EXISTS,
|
||||
'loading ' + path + ' from ' + this.getFullPath());
|
||||
} else if (entry.isFile() != isFile) {
|
||||
throw new goog.fs.Error(
|
||||
goog.fs.Error.ErrorCode.TYPE_MISMATCH,
|
||||
'loading ' + path + ' from ' + this.getFullPath());
|
||||
} else {
|
||||
if (behavior == goog.fs.DirectoryEntry.Behavior.CREATE) {
|
||||
this.lastModifiedTimestamp_ = goog.now();
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether this directory has a child with the given name.
|
||||
*
|
||||
* @param {string} name The name of the entry to check for.
|
||||
* @return {boolean} Whether or not this has a child with the given name.
|
||||
*/
|
||||
goog.testing.fs.DirectoryEntry.prototype.hasChild = function(name) {
|
||||
return name in this.children;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.DirectoryEntry.prototype.removeRecursively = function() {
|
||||
var msg = 'removing ' + this.getFullPath() + ' recursively';
|
||||
return this.checkNotDeleted(msg).addCallback(function() {
|
||||
var d = goog.async.Deferred.succeed(null);
|
||||
goog.object.forEach(this.children, function(child) {
|
||||
d.awaitDeferred(
|
||||
child.isDirectory() ? child.removeRecursively() : child.remove());
|
||||
});
|
||||
d.addCallback(function() { return this.remove(); }, this);
|
||||
return d;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.DirectoryEntry.prototype.listDirectory = function() {
|
||||
var msg = 'listing ' + this.getFullPath();
|
||||
return this.checkNotDeleted(msg).addCallback(function() {
|
||||
return goog.object.getValues(this.children);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.DirectoryEntry.prototype.createPath =
|
||||
// This isn't really type-safe.
|
||||
/** @type {!Function} */ (goog.fs.DirectoryEntryImpl.prototype.createPath);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A mock file entry object.
|
||||
*
|
||||
* @param {!goog.testing.fs.FileSystem} fs The filesystem containing this entry.
|
||||
* @param {!goog.testing.fs.DirectoryEntry} parent The directory entry directly
|
||||
* containing this entry.
|
||||
* @param {string} name The name of this entry.
|
||||
* @param {string} data The data initially contained in the file.
|
||||
* @constructor
|
||||
* @extends {goog.testing.fs.Entry}
|
||||
* @implements {goog.fs.FileEntry}
|
||||
*/
|
||||
goog.testing.fs.FileEntry = function(fs, parent, name, data) {
|
||||
goog.base(this, fs, parent, name);
|
||||
|
||||
/**
|
||||
* The internal file blob referenced by this file entry.
|
||||
* @type {!goog.testing.fs.File}
|
||||
* @private
|
||||
*/
|
||||
this.file_ = new goog.testing.fs.File(name, new Date(goog.now()), data);
|
||||
|
||||
/**
|
||||
* The metadata for file.
|
||||
* @type {{modificationTime: Date}}
|
||||
* @private
|
||||
*/
|
||||
this.metadata_ = {
|
||||
'modificationTime': this.file_.lastModifiedDate
|
||||
};
|
||||
};
|
||||
goog.inherits(goog.testing.fs.FileEntry, goog.testing.fs.Entry);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.FileEntry.prototype.isFile = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.FileEntry.prototype.isDirectory = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.FileEntry.prototype.clone = function() {
|
||||
return new goog.testing.fs.FileEntry(
|
||||
this.getFileSystem(), this.parent,
|
||||
this.getName(), this.fileSync().toString());
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.FileEntry.prototype.getLastModified = function() {
|
||||
return this.file().addCallback(function(file) {
|
||||
return file.lastModifiedDate;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.FileEntry.prototype.getMetadata = function() {
|
||||
var msg = 'getting metadata for ' + this.getFullPath();
|
||||
return this.checkNotDeleted(msg).addCallback(function() {
|
||||
return this.metadata_;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.FileEntry.prototype.createWriter = function() {
|
||||
var d = new goog.async.Deferred();
|
||||
goog.Timer.callOnce(
|
||||
goog.bind(d.callback, d, new goog.testing.fs.FileWriter(this)));
|
||||
return d;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.FileEntry.prototype.file = function() {
|
||||
var msg = 'getting file for ' + this.getFullPath();
|
||||
return this.checkNotDeleted(msg).addCallback(function() {
|
||||
return this.fileSync();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the internal file representation synchronously, without waiting for a
|
||||
* Deferred to resolve.
|
||||
*
|
||||
* @return {!goog.testing.fs.File} The internal file blob referenced by this
|
||||
* FileEntry.
|
||||
*/
|
||||
goog.testing.fs.FileEntry.prototype.fileSync = function() {
|
||||
return this.file_;
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2011 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 Mock file object.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.fs.File');
|
||||
|
||||
goog.require('goog.testing.fs.Blob');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A mock file object.
|
||||
*
|
||||
* @param {string} name The name of the file.
|
||||
* @param {Date=} opt_lastModified The last modified date for this file. May be
|
||||
* null if file modification dates are not supported.
|
||||
* @param {string=} opt_data The string data encapsulated by the blob.
|
||||
* @param {string=} opt_type The mime type of the blob.
|
||||
* @constructor
|
||||
* @extends {goog.testing.fs.Blob}
|
||||
*/
|
||||
goog.testing.fs.File = function(name, opt_lastModified, opt_data, opt_type) {
|
||||
goog.base(this, opt_data, opt_type);
|
||||
|
||||
/**
|
||||
* @see http://www.w3.org/TR/FileAPI/#dfn-name
|
||||
* @type {string}
|
||||
*/
|
||||
this.name = name;
|
||||
|
||||
/**
|
||||
* @see http://www.w3.org/TR/FileAPI/#dfn-lastModifiedDate
|
||||
* @type {Date}
|
||||
*/
|
||||
this.lastModifiedDate = opt_lastModified || null;
|
||||
};
|
||||
goog.inherits(goog.testing.fs.File, goog.testing.fs.Blob);
|
||||
@@ -0,0 +1,272 @@
|
||||
// Copyright 2011 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 Mock FileReader object.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.fs.FileReader');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.fs.Error');
|
||||
goog.require('goog.fs.FileReader.EventType');
|
||||
goog.require('goog.fs.FileReader.ReadyState');
|
||||
goog.require('goog.testing.fs.File');
|
||||
goog.require('goog.testing.fs.ProgressEvent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A mock FileReader object. This emits the same events as
|
||||
* {@link goog.fs.FileReader}.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.testing.fs.FileReader = function() {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The current state of the reader.
|
||||
* @type {goog.fs.FileReader.ReadyState}
|
||||
* @private
|
||||
*/
|
||||
this.readyState_ = goog.fs.FileReader.ReadyState.INIT;
|
||||
};
|
||||
goog.inherits(goog.testing.fs.FileReader, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* The most recent error experienced by this reader.
|
||||
* @type {goog.fs.Error}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.FileReader.prototype.error_;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the current operation has been aborted.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.FileReader.prototype.aborted_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* The blob this reader is reading from.
|
||||
* @type {goog.testing.fs.Blob}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.FileReader.prototype.blob_;
|
||||
|
||||
|
||||
/**
|
||||
* The possible return types.
|
||||
* @enum {number}
|
||||
*/
|
||||
goog.testing.fs.FileReader.ReturnType = {
|
||||
/**
|
||||
* Used when reading as text.
|
||||
*/
|
||||
TEXT: 1,
|
||||
|
||||
/**
|
||||
* Used when reading as binary string.
|
||||
*/
|
||||
BINARY_STRING: 2,
|
||||
|
||||
/**
|
||||
* Used when reading as array buffer.
|
||||
*/
|
||||
ARRAY_BUFFER: 3,
|
||||
|
||||
/**
|
||||
* Used when reading as data URL.
|
||||
*/
|
||||
DATA_URL: 4
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The return type we're reading.
|
||||
* @type {goog.testing.fs.FileReader.ReturnType}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.FileReader.returnType_;
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileReader#getReadyState}
|
||||
* @return {goog.fs.FileReader.ReadyState} The current ready state.
|
||||
*/
|
||||
goog.testing.fs.FileReader.prototype.getReadyState = function() {
|
||||
return this.readyState_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileReader#getError}
|
||||
* @return {goog.fs.Error} The current error.
|
||||
*/
|
||||
goog.testing.fs.FileReader.prototype.getError = function() {
|
||||
return this.error_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileReader#abort}
|
||||
*/
|
||||
goog.testing.fs.FileReader.prototype.abort = function() {
|
||||
if (this.readyState_ != goog.fs.FileReader.ReadyState.LOADING) {
|
||||
var msg = 'aborting read';
|
||||
throw new goog.fs.Error(goog.fs.Error.ErrorCode.INVALID_STATE, msg);
|
||||
}
|
||||
|
||||
this.aborted_ = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileReader#getResult}
|
||||
* @return {*} The result of the file read.
|
||||
*/
|
||||
goog.testing.fs.FileReader.prototype.getResult = function() {
|
||||
if (this.readyState_ != goog.fs.FileReader.ReadyState.DONE) {
|
||||
return undefined;
|
||||
}
|
||||
if (this.error_) {
|
||||
return undefined;
|
||||
}
|
||||
if (this.returnType_ == goog.testing.fs.FileReader.ReturnType.TEXT) {
|
||||
return this.blob_.toString();
|
||||
} else if (this.returnType_ ==
|
||||
goog.testing.fs.FileReader.ReturnType.ARRAY_BUFFER) {
|
||||
return this.blob_.toArrayBuffer();
|
||||
} else if (this.returnType_ ==
|
||||
goog.testing.fs.FileReader.ReturnType.BINARY_STRING) {
|
||||
return this.blob_.toString();
|
||||
} else if (this.returnType_ ==
|
||||
goog.testing.fs.FileReader.ReturnType.DATA_URL) {
|
||||
return this.blob_.toDataUrl();
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Fires the read events.
|
||||
* @param {!goog.testing.fs.Blob} blob The blob to read from.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.FileReader.prototype.read_ = function(blob) {
|
||||
this.blob_ = blob;
|
||||
if (this.readyState_ == goog.fs.FileReader.ReadyState.LOADING) {
|
||||
var msg = 'reading file';
|
||||
throw new goog.fs.Error(goog.fs.Error.ErrorCode.INVALID_STATE, msg);
|
||||
}
|
||||
|
||||
this.readyState_ = goog.fs.FileReader.ReadyState.LOADING;
|
||||
goog.Timer.callOnce(function() {
|
||||
if (this.aborted_) {
|
||||
this.abort_(blob.size);
|
||||
return;
|
||||
}
|
||||
|
||||
this.progressEvent_(goog.fs.FileReader.EventType.LOAD_START, 0, blob.size);
|
||||
this.progressEvent_(goog.fs.FileReader.EventType.LOAD, blob.size / 2,
|
||||
blob.size);
|
||||
this.progressEvent_(goog.fs.FileReader.EventType.LOAD, blob.size,
|
||||
blob.size);
|
||||
this.readyState_ = goog.fs.FileReader.ReadyState.DONE;
|
||||
this.progressEvent_(goog.fs.FileReader.EventType.LOAD, blob.size,
|
||||
blob.size);
|
||||
this.progressEvent_(goog.fs.FileReader.EventType.LOAD_END, blob.size,
|
||||
blob.size);
|
||||
}, 0, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileReader#readAsBinaryString}
|
||||
* @param {!goog.testing.fs.Blob} blob The blob to read.
|
||||
*/
|
||||
goog.testing.fs.FileReader.prototype.readAsBinaryString = function(blob) {
|
||||
this.returnType_ = goog.testing.fs.FileReader.ReturnType.BINARY_STRING;
|
||||
this.read_(blob);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileReader#readAsArrayBuffer}
|
||||
* @param {!goog.testing.fs.Blob} blob The blob to read.
|
||||
*/
|
||||
goog.testing.fs.FileReader.prototype.readAsArrayBuffer = function(blob) {
|
||||
this.returnType_ = goog.testing.fs.FileReader.ReturnType.ARRAY_BUFFER;
|
||||
this.read_(blob);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileReader#readAsText}
|
||||
* @param {!goog.testing.fs.Blob} blob The blob to read.
|
||||
* @param {string=} opt_encoding The name of the encoding to use.
|
||||
*/
|
||||
goog.testing.fs.FileReader.prototype.readAsText = function(blob, opt_encoding) {
|
||||
this.returnType_ = goog.testing.fs.FileReader.ReturnType.TEXT;
|
||||
this.read_(blob);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileReader#readAsDataUrl}
|
||||
* @param {!goog.testing.fs.Blob} blob The blob to read.
|
||||
*/
|
||||
goog.testing.fs.FileReader.prototype.readAsDataUrl = function(blob) {
|
||||
this.returnType_ = goog.testing.fs.FileReader.ReturnType.DATA_URL;
|
||||
this.read_(blob);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Abort the current action and emit appropriate events.
|
||||
*
|
||||
* @param {number} total The total data that was to be processed, in bytes.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.FileReader.prototype.abort_ = function(total) {
|
||||
this.error_ = new goog.fs.Error(
|
||||
goog.fs.Error.ErrorCode.ABORT, 'reading file');
|
||||
this.progressEvent_(goog.fs.FileReader.EventType.ERROR, 0, total);
|
||||
this.progressEvent_(goog.fs.FileReader.EventType.ABORT, 0, total);
|
||||
this.readyState_ = goog.fs.FileReader.ReadyState.DONE;
|
||||
this.progressEvent_(goog.fs.FileReader.EventType.LOAD_END, 0, total);
|
||||
this.aborted_ = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Dispatch a progress event.
|
||||
*
|
||||
* @param {goog.fs.FileReader.EventType} type The event type.
|
||||
* @param {number} loaded The number of bytes processed.
|
||||
* @param {number} total The total data that was to be processed, in bytes.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.FileReader.prototype.progressEvent_ = function(type, loaded,
|
||||
total) {
|
||||
this.dispatchEvent(new goog.testing.fs.ProgressEvent(type, loaded, total));
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2011 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 Mock filesystem object.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.fs.FileSystem');
|
||||
|
||||
goog.require('goog.fs.FileSystem');
|
||||
goog.require('goog.testing.fs.DirectoryEntry');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A mock filesystem object.
|
||||
*
|
||||
* @param {string=} opt_name The name of the filesystem.
|
||||
* @constructor
|
||||
* @implements {goog.fs.FileSystem}
|
||||
*/
|
||||
goog.testing.fs.FileSystem = function(opt_name) {
|
||||
/**
|
||||
* The name of the filesystem.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.name_ = opt_name || 'goog.testing.fs.FileSystem';
|
||||
|
||||
/**
|
||||
* The root entry of the filesystem.
|
||||
* @type {!goog.testing.fs.DirectoryEntry}
|
||||
* @private
|
||||
*/
|
||||
this.root_ = new goog.testing.fs.DirectoryEntry(this, null, '', {});
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.fs.FileSystem.prototype.getName = function() {
|
||||
return this.name_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @return {!goog.testing.fs.DirectoryEntry}
|
||||
*/
|
||||
goog.testing.fs.FileSystem.prototype.getRoot = function() {
|
||||
return this.root_;
|
||||
};
|
||||
@@ -0,0 +1,261 @@
|
||||
// Copyright 2011 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 Mock FileWriter object.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.fs.FileWriter');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.fs.Error');
|
||||
goog.require('goog.fs.FileSaver.EventType');
|
||||
goog.require('goog.fs.FileSaver.ReadyState');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.testing.fs.File');
|
||||
goog.require('goog.testing.fs.ProgressEvent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A mock FileWriter object. This emits the same events as
|
||||
* {@link goog.fs.FileSaver} and {@link goog.fs.FileWriter}.
|
||||
*
|
||||
* @param {!goog.testing.fs.FileEntry} fileEntry The file entry to write to.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.testing.fs.FileWriter = function(fileEntry) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* The file entry to which to write.
|
||||
* @type {!goog.testing.fs.FileEntry}
|
||||
* @private
|
||||
*/
|
||||
this.fileEntry_ = fileEntry;
|
||||
|
||||
/**
|
||||
* The file blob to write to.
|
||||
* @type {!goog.testing.fs.File}
|
||||
* @private
|
||||
*/
|
||||
this.file_ = fileEntry.fileSync();
|
||||
|
||||
/**
|
||||
* The current state of the writer.
|
||||
* @type {goog.fs.FileSaver.ReadyState}
|
||||
* @private
|
||||
*/
|
||||
this.readyState_ = goog.fs.FileSaver.ReadyState.INIT;
|
||||
};
|
||||
goog.inherits(goog.testing.fs.FileWriter, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* The most recent error experienced by this writer.
|
||||
* @type {goog.fs.Error}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.FileWriter.prototype.error_;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the current operation has been aborted.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.FileWriter.prototype.aborted_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* The current position in the file.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.FileWriter.prototype.position_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileSaver#getReadyState}
|
||||
* @return {goog.fs.FileSaver.ReadyState} The ready state.
|
||||
*/
|
||||
goog.testing.fs.FileWriter.prototype.getReadyState = function() {
|
||||
return this.readyState_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileSaver#getError}
|
||||
* @return {goog.fs.Error} The error.
|
||||
*/
|
||||
goog.testing.fs.FileWriter.prototype.getError = function() {
|
||||
return this.error_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileWriter#getPosition}
|
||||
* @return {number} The position.
|
||||
*/
|
||||
goog.testing.fs.FileWriter.prototype.getPosition = function() {
|
||||
return this.position_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileWriter#getLength}
|
||||
* @return {number} The length.
|
||||
*/
|
||||
goog.testing.fs.FileWriter.prototype.getLength = function() {
|
||||
return this.file_.size;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileSaver#abort}
|
||||
*/
|
||||
goog.testing.fs.FileWriter.prototype.abort = function() {
|
||||
if (this.readyState_ != goog.fs.FileSaver.ReadyState.WRITING) {
|
||||
var msg = 'aborting save of ' + this.fileEntry_.getFullPath();
|
||||
throw new goog.fs.Error(goog.fs.Error.ErrorCode.INVALID_STATE, msg);
|
||||
}
|
||||
|
||||
this.aborted_ = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileWriter#write}
|
||||
* @param {!goog.testing.fs.Blob} blob The blob to write.
|
||||
*/
|
||||
goog.testing.fs.FileWriter.prototype.write = function(blob) {
|
||||
if (this.readyState_ == goog.fs.FileSaver.ReadyState.WRITING) {
|
||||
var msg = 'writing to ' + this.fileEntry_.getFullPath();
|
||||
throw new goog.fs.Error(goog.fs.Error.ErrorCode.INVALID_STATE, msg);
|
||||
}
|
||||
|
||||
this.readyState_ = goog.fs.FileSaver.ReadyState.WRITING;
|
||||
goog.Timer.callOnce(function() {
|
||||
if (this.aborted_) {
|
||||
this.abort_(blob.size);
|
||||
return;
|
||||
}
|
||||
|
||||
this.progressEvent_(goog.fs.FileSaver.EventType.WRITE_START, 0, blob.size);
|
||||
var fileString = this.file_.toString();
|
||||
this.file_.setDataInternal(
|
||||
fileString.substring(0, this.position_) + blob.toString() +
|
||||
fileString.substring(this.position_ + blob.size, fileString.length));
|
||||
this.position_ += blob.size;
|
||||
|
||||
this.progressEvent_(
|
||||
goog.fs.FileSaver.EventType.WRITE, blob.size, blob.size);
|
||||
this.readyState_ = goog.fs.FileSaver.ReadyState.DONE;
|
||||
this.progressEvent_(
|
||||
goog.fs.FileSaver.EventType.WRITE_END, blob.size, blob.size);
|
||||
}, 0, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileWriter#truncate}
|
||||
* @param {number} size The size to truncate to.
|
||||
*/
|
||||
goog.testing.fs.FileWriter.prototype.truncate = function(size) {
|
||||
if (this.readyState_ == goog.fs.FileSaver.ReadyState.WRITING) {
|
||||
var msg = 'truncating ' + this.fileEntry_.getFullPath();
|
||||
throw new goog.fs.Error(goog.fs.Error.ErrorCode.INVALID_STATE, msg);
|
||||
}
|
||||
|
||||
this.readyState_ = goog.fs.FileSaver.ReadyState.WRITING;
|
||||
goog.Timer.callOnce(function() {
|
||||
if (this.aborted_) {
|
||||
this.abort_(size);
|
||||
return;
|
||||
}
|
||||
|
||||
this.progressEvent_(goog.fs.FileSaver.EventType.WRITE_START, 0, size);
|
||||
|
||||
var fileString = this.file_.toString();
|
||||
if (size > fileString.length) {
|
||||
this.file_.setDataInternal(
|
||||
fileString + goog.string.repeat('\0', size - fileString.length));
|
||||
} else {
|
||||
this.file_.setDataInternal(fileString.substring(0, size));
|
||||
}
|
||||
this.position_ = Math.min(this.position_, size);
|
||||
|
||||
this.progressEvent_(goog.fs.FileSaver.EventType.WRITE, size, size);
|
||||
this.readyState_ = goog.fs.FileSaver.ReadyState.DONE;
|
||||
this.progressEvent_(goog.fs.FileSaver.EventType.WRITE_END, size, size);
|
||||
}, 0, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.FileWriter#seek}
|
||||
* @param {number} offset The offset to seek to.
|
||||
*/
|
||||
goog.testing.fs.FileWriter.prototype.seek = function(offset) {
|
||||
if (this.readyState_ == goog.fs.FileSaver.ReadyState.WRITING) {
|
||||
var msg = 'truncating ' + this.fileEntry_.getFullPath();
|
||||
throw new goog.fs.Error(goog.fs.Error.ErrorCode.INVALID_STATE, msg);
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
this.position_ = Math.max(0, this.file_.size + offset);
|
||||
} else {
|
||||
this.position_ = Math.min(offset, this.file_.size);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Abort the current action and emit appropriate events.
|
||||
*
|
||||
* @param {number} total The total data that was to be processed, in bytes.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.FileWriter.prototype.abort_ = function(total) {
|
||||
this.error_ = new goog.fs.Error(
|
||||
goog.fs.Error.ErrorCode.ABORT, 'saving ' + this.fileEntry_.getFullPath());
|
||||
this.progressEvent_(goog.fs.FileSaver.EventType.ERROR, 0, total);
|
||||
this.progressEvent_(goog.fs.FileSaver.EventType.ABORT, 0, total);
|
||||
this.readyState_ = goog.fs.FileSaver.ReadyState.DONE;
|
||||
this.progressEvent_(goog.fs.FileSaver.EventType.WRITE_END, 0, total);
|
||||
this.aborted_ = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Dispatch a progress event.
|
||||
*
|
||||
* @param {goog.fs.FileSaver.EventType} type The type of the event.
|
||||
* @param {number} loaded The number of bytes processed.
|
||||
* @param {number} total The total data that was to be processed, in bytes.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.FileWriter.prototype.progressEvent_ = function(
|
||||
type, loaded, total) {
|
||||
// On write, update the last modified date to the current (real or mock) time.
|
||||
if (type == goog.fs.FileSaver.EventType.WRITE) {
|
||||
this.file_.lastModifiedDate = new Date(goog.now());
|
||||
}
|
||||
|
||||
this.dispatchEvent(new goog.testing.fs.ProgressEvent(type, loaded, total));
|
||||
};
|
||||
147
float-no-zero/closure-library/closure/goog/testing/fs/fs.js
Normal file
147
float-no-zero/closure-library/closure/goog/testing/fs/fs.js
Normal file
@@ -0,0 +1,147 @@
|
||||
// Copyright 2011 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 Mock implementations of the Closure HTML5 FileSystem wrapper
|
||||
* classes. These implementations are designed to be usable in any browser, so
|
||||
* they use none of the native FileSystem-related objects.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.fs');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.async.Deferred');
|
||||
goog.require('goog.fs');
|
||||
goog.require('goog.testing.fs.Blob');
|
||||
goog.require('goog.testing.fs.FileSystem');
|
||||
|
||||
|
||||
/**
|
||||
* Get a filesystem object. Since these are mocks, there's no difference between
|
||||
* temporary and persistent filesystems.
|
||||
*
|
||||
* @param {number} size Ignored.
|
||||
* @return {!goog.async.Deferred} The deferred
|
||||
* {@link goog.testing.fs.FileSystem}.
|
||||
*/
|
||||
goog.testing.fs.getTemporary = function(size) {
|
||||
var d = new goog.async.Deferred();
|
||||
goog.Timer.callOnce(
|
||||
goog.bind(d.callback, d, new goog.testing.fs.FileSystem()));
|
||||
return d;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get a filesystem object. Since these are mocks, there's no difference between
|
||||
* temporary and persistent filesystems.
|
||||
*
|
||||
* @param {number} size Ignored.
|
||||
* @return {!goog.async.Deferred} The deferred
|
||||
* {@link goog.testing.fs.FileSystem}.
|
||||
*/
|
||||
goog.testing.fs.getPersistent = function(size) {
|
||||
return goog.testing.fs.getTemporary(size);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Which object URLs have been granted for fake blobs.
|
||||
* @type {!Object.<boolean>}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.fs.objectUrls_ = {};
|
||||
|
||||
|
||||
/**
|
||||
* Create a fake object URL for a given fake blob. This can be used as a real
|
||||
* URL, and it can be created and revoked normally.
|
||||
*
|
||||
* @param {!goog.testing.fs.Blob} blob The blob for which to create the URL.
|
||||
* @return {string} The URL.
|
||||
*/
|
||||
goog.testing.fs.createObjectUrl = function(blob) {
|
||||
var url = blob.toDataUrl();
|
||||
goog.testing.fs.objectUrls_[url] = true;
|
||||
return url;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Remove a URL that was created for a fake blob.
|
||||
*
|
||||
* @param {string} url The URL to revoke.
|
||||
*/
|
||||
goog.testing.fs.revokeObjectUrl = function(url) {
|
||||
delete goog.testing.fs.objectUrls_[url];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Return whether or not a URL has been granted for the given blob.
|
||||
*
|
||||
* @param {!goog.testing.fs.Blob} blob The blob to check.
|
||||
* @return {boolean} Whether a URL has been granted.
|
||||
*/
|
||||
goog.testing.fs.isObjectUrlGranted = function(blob) {
|
||||
return (blob.toDataUrl()) in goog.testing.fs.objectUrls_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Concatenates one or more values together and converts them to a fake blob.
|
||||
*
|
||||
* @param {...(string|!goog.testing.fs.Blob)} var_args The values that will make
|
||||
* up the resulting blob.
|
||||
* @return {!goog.testing.fs.Blob} The blob.
|
||||
*/
|
||||
goog.testing.fs.getBlob = function(var_args) {
|
||||
return new goog.testing.fs.Blob(goog.array.map(arguments, String).join(''));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the string value of a fake blob.
|
||||
*
|
||||
* @param {!goog.testing.fs.Blob} blob The blob to convert to a string.
|
||||
* @param {string=} opt_encoding Ignored.
|
||||
* @return {!goog.async.Deferred} The deferred string value of the blob.
|
||||
*/
|
||||
goog.testing.fs.blobToString = function(blob, opt_encoding) {
|
||||
var d = new goog.async.Deferred();
|
||||
goog.Timer.callOnce(goog.bind(d.callback, d, blob.toString()));
|
||||
return d;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Installs goog.testing.fs in place of the standard goog.fs. After calling
|
||||
* this, code that uses goog.fs should work without issue using goog.testing.fs.
|
||||
*
|
||||
* @param {!goog.testing.PropertyReplacer} stubs The property replacer for
|
||||
* stubbing out the original goog.fs functions.
|
||||
*/
|
||||
goog.testing.fs.install = function(stubs) {
|
||||
// Prevent warnings that goog.fs may get optimized away. It's true this is
|
||||
// unsafe in compiled code, but it's only meant for tests.
|
||||
var fs = goog.getObjectByName('goog.fs');
|
||||
stubs.replace(fs, 'getTemporary', goog.testing.fs.getTemporary);
|
||||
stubs.replace(fs, 'getPersistent', goog.testing.fs.getPersistent);
|
||||
stubs.replace(fs, 'createObjectUrl', goog.testing.fs.createObjectUrl);
|
||||
stubs.replace(fs, 'revokeObjectUrl', goog.testing.fs.revokeObjectUrl);
|
||||
stubs.replace(fs, 'getBlob', goog.testing.fs.getBlob);
|
||||
stubs.replace(fs, 'blobToString', goog.testing.fs.blobToString);
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2011 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 Mock ProgressEvent object.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.fs.ProgressEvent');
|
||||
|
||||
goog.require('goog.events.Event');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A mock progress event.
|
||||
*
|
||||
* @param {!goog.fs.FileSaver.EventType|!goog.fs.FileReader.EventType} type
|
||||
* Event type.
|
||||
* @param {number} loaded The number of bytes processed.
|
||||
* @param {number} total The total data that was to be processed, in bytes.
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
goog.testing.fs.ProgressEvent = function(type, loaded, total) {
|
||||
goog.base(this, type);
|
||||
|
||||
/**
|
||||
* The number of bytes processed.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.loaded_ = loaded;
|
||||
|
||||
|
||||
/**
|
||||
* The total data that was to be procesed, in bytes.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.total_ = total;
|
||||
};
|
||||
goog.inherits(goog.testing.fs.ProgressEvent, goog.events.Event);
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.ProgressEvent#isLengthComputable}
|
||||
* @return {boolean} True if the length is known.
|
||||
*/
|
||||
goog.testing.fs.ProgressEvent.prototype.isLengthComputable = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.ProgressEvent#getLoaded}
|
||||
* @return {number} The number of bytes loaded or written.
|
||||
*/
|
||||
goog.testing.fs.ProgressEvent.prototype.getLoaded = function() {
|
||||
return this.loaded_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @see {goog.fs.ProgressEvent#getTotal}
|
||||
* @return {number} The total bytes to load or write.
|
||||
*/
|
||||
goog.testing.fs.ProgressEvent.prototype.getTotal = function() {
|
||||
return this.total_;
|
||||
};
|
||||
@@ -0,0 +1,177 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview Enable mocking of functions not attached to objects
|
||||
* whether they be global / top-level or anonymous methods / closures.
|
||||
*
|
||||
* See the unit tests for usage.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing');
|
||||
goog.provide('goog.testing.FunctionMock');
|
||||
goog.provide('goog.testing.GlobalFunctionMock');
|
||||
goog.provide('goog.testing.MethodMock');
|
||||
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.testing.LooseMock');
|
||||
goog.require('goog.testing.Mock');
|
||||
goog.require('goog.testing.MockInterface');
|
||||
goog.require('goog.testing.PropertyReplacer');
|
||||
goog.require('goog.testing.StrictMock');
|
||||
|
||||
|
||||
/**
|
||||
* Class used to mock a function. Useful for mocking closures and anonymous
|
||||
* callbacks etc. Creates a function object that extends goog.testing.Mock.
|
||||
* @param {string=} opt_functionName The optional name of the function to mock.
|
||||
* Set to '[anonymous mocked function]' if not passed in.
|
||||
* @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
|
||||
* goog.testing.Mock.STRICT. The default is STRICT.
|
||||
* @return {goog.testing.MockInterface} The mocked function.
|
||||
* @suppress {missingProperties} Mocks do not fit in the type system well.
|
||||
*/
|
||||
goog.testing.FunctionMock = function(opt_functionName, opt_strictness) {
|
||||
var fn = function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.splice(0, 0, opt_functionName || '[anonymous mocked function]');
|
||||
return fn.$mockMethod.apply(fn, args);
|
||||
};
|
||||
var base = opt_strictness === goog.testing.Mock.LOOSE ?
|
||||
goog.testing.LooseMock : goog.testing.StrictMock;
|
||||
goog.object.extend(fn, new base({}));
|
||||
|
||||
return /** @type {goog.testing.MockInterface} */ (fn);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Mocks an existing function. Creates a goog.testing.FunctionMock
|
||||
* and registers it in the given scope with the name specified by functionName.
|
||||
* @param {Object} scope The scope of the method to be mocked out.
|
||||
* @param {string} functionName The name of the function we're going to mock.
|
||||
* @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
|
||||
* goog.testing.Mock.STRICT. The default is STRICT.
|
||||
* @return {goog.testing.MockInterface} The mocked method.
|
||||
*/
|
||||
goog.testing.MethodMock = function(scope, functionName, opt_strictness) {
|
||||
if (!(functionName in scope)) {
|
||||
throw Error(functionName + ' is not a property of the given scope.');
|
||||
}
|
||||
|
||||
var fn = goog.testing.FunctionMock(functionName, opt_strictness);
|
||||
|
||||
fn.$propertyReplacer_ = new goog.testing.PropertyReplacer();
|
||||
fn.$propertyReplacer_.set(scope, functionName, fn);
|
||||
fn.$tearDown = goog.testing.MethodMock.$tearDown;
|
||||
|
||||
return fn;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Resets the global function that we mocked back to its original state.
|
||||
* @this {goog.testing.MockInterface}
|
||||
*/
|
||||
goog.testing.MethodMock.$tearDown = function() {
|
||||
this.$propertyReplacer_.reset();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Mocks a global / top-level function. Creates a goog.testing.MethodMock
|
||||
* in the global scope with the name specified by functionName.
|
||||
* @param {string} functionName The name of the function we're going to mock.
|
||||
* @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
|
||||
* goog.testing.Mock.STRICT. The default is STRICT.
|
||||
* @return {goog.testing.MockInterface} The mocked global function.
|
||||
*/
|
||||
goog.testing.GlobalFunctionMock = function(functionName, opt_strictness) {
|
||||
return goog.testing.MethodMock(goog.global, functionName, opt_strictness);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Convenience method for creating a mock for a function.
|
||||
* @param {string=} opt_functionName The optional name of the function to mock
|
||||
* set to '[anonymous mocked function]' if not passed in.
|
||||
* @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
|
||||
* goog.testing.Mock.STRICT. The default is STRICT.
|
||||
* @return {goog.testing.MockInterface} The mocked function.
|
||||
*/
|
||||
goog.testing.createFunctionMock = function(opt_functionName, opt_strictness) {
|
||||
return goog.testing.FunctionMock(opt_functionName, opt_strictness);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Convenience method for creating a mock for a method.
|
||||
* @param {Object} scope The scope of the method to be mocked out.
|
||||
* @param {string} functionName The name of the function we're going to mock.
|
||||
* @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
|
||||
* goog.testing.Mock.STRICT. The default is STRICT.
|
||||
* @return {goog.testing.MockInterface} The mocked global function.
|
||||
*/
|
||||
goog.testing.createMethodMock = function(scope, functionName, opt_strictness) {
|
||||
return goog.testing.MethodMock(scope, functionName, opt_strictness);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Convenience method for creating a mock for a constructor. Copies class
|
||||
* members to the mock.
|
||||
*
|
||||
* <p>When mocking a constructor to return a mocked instance, remember to create
|
||||
* the instance mock before mocking the constructor. If you mock the constructor
|
||||
* first, then the mock framework will be unable to examine the prototype chain
|
||||
* when creating the mock instance.
|
||||
* @param {Object} scope The scope of the constructor to be mocked out.
|
||||
* @param {string} constructorName The name of the constructor we're going to
|
||||
* mock.
|
||||
* @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
|
||||
* goog.testing.Mock.STRICT. The default is STRICT.
|
||||
* @return {goog.testing.MockInterface} The mocked constructor.
|
||||
*/
|
||||
goog.testing.createConstructorMock = function(scope, constructorName,
|
||||
opt_strictness) {
|
||||
var realConstructor = scope[constructorName];
|
||||
var constructorMock = goog.testing.MethodMock(scope, constructorName,
|
||||
opt_strictness);
|
||||
|
||||
// Copy class members from the real constructor to the mock. Do not copy
|
||||
// the closure superClass_ property (see goog.inherits), the built-in
|
||||
// prototype property, or properties added to Function.prototype
|
||||
// (see goog.MODIFY_FUNCTION_PROTOTYPES in closure/base.js).
|
||||
for (var property in realConstructor) {
|
||||
if (property != 'superClass_' &&
|
||||
property != 'prototype' &&
|
||||
realConstructor.hasOwnProperty(property)) {
|
||||
constructorMock[property] = realConstructor[property];
|
||||
}
|
||||
}
|
||||
return constructorMock;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Convenience method for creating a mocks for a global / top-level function.
|
||||
* @param {string} functionName The name of the function we're going to mock.
|
||||
* @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
|
||||
* goog.testing.Mock.STRICT. The default is STRICT.
|
||||
* @return {goog.testing.MockInterface} The mocked global function.
|
||||
*/
|
||||
goog.testing.createGlobalFunctionMock = function(functionName, opt_strictness) {
|
||||
return goog.testing.GlobalFunctionMock(functionName, opt_strictness);
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview Testing utilities for DOM related tests.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.graphics');
|
||||
|
||||
goog.require('goog.graphics.Path.Segment');
|
||||
goog.require('goog.testing.asserts');
|
||||
|
||||
|
||||
/**
|
||||
* Array mapping numeric segment constant to a descriptive character.
|
||||
* @type {Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.graphics.SEGMENT_NAMES_ = function() {
|
||||
var arr = [];
|
||||
arr[goog.graphics.Path.Segment.MOVETO] = 'M';
|
||||
arr[goog.graphics.Path.Segment.LINETO] = 'L';
|
||||
arr[goog.graphics.Path.Segment.CURVETO] = 'C';
|
||||
arr[goog.graphics.Path.Segment.ARCTO] = 'A';
|
||||
arr[goog.graphics.Path.Segment.CLOSE] = 'X';
|
||||
return arr;
|
||||
}();
|
||||
|
||||
|
||||
/**
|
||||
* Test if the given path matches the expected array of commands and parameters.
|
||||
* @param {Array.<string|number>} expected The expected array of commands and
|
||||
* parameters.
|
||||
* @param {goog.graphics.Path} path The path to test against.
|
||||
*/
|
||||
goog.testing.graphics.assertPathEquals = function(expected, path) {
|
||||
var actual = [];
|
||||
path.forEachSegment(function(seg, args) {
|
||||
actual.push(goog.testing.graphics.SEGMENT_NAMES_[seg]);
|
||||
Array.prototype.push.apply(actual, args);
|
||||
});
|
||||
assertEquals(expected.length, actual.length);
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
if (goog.isNumber(expected[i])) {
|
||||
assertTrue(goog.isNumber(actual[i]));
|
||||
assertRoughlyEquals(expected[i], actual[i], 0.01);
|
||||
} else {
|
||||
assertEquals(expected[i], actual[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2013 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 Assert functions that account for locale data changes.
|
||||
*
|
||||
* The locale data gets updated from CLDR (http://cldr.unicode.org/),
|
||||
* and CLDR gets an update about twice per year.
|
||||
* So the locale data are expected to change.
|
||||
* This can make unit tests quite fragile:
|
||||
* assertEquals("Dec 31, 2013, 1:23pm", format);
|
||||
* Now imagine that the decision is made to add a dot after abbreviations,
|
||||
* and a comma between date and time.
|
||||
* The previous assert will fail, because the string is now
|
||||
* "Dec. 31 2013, 1:23pm"
|
||||
*
|
||||
* One option is to not unit test the results of the formatters client side,
|
||||
* and just trust that CLDR and closure/i18n takes care of that.
|
||||
* The other option is to be a more flexible when testing.
|
||||
* This is the role of assertI18nEquals, to centralize all the small
|
||||
* differences between hard-coded values in unit tests and the current result.
|
||||
* It allows some decupling, so that the closure/i18n can be updated without
|
||||
* breaking all the clients using it.
|
||||
* For the example above, this will succeed:
|
||||
* assertI18nEquals("Dec 31, 2013, 1:23pm", "Dec. 31, 2013 1:23pm");
|
||||
* It does this by white-listing, no "guessing" involved.
|
||||
*
|
||||
* But I would say that the best practice is the first option: trust the
|
||||
* library, stop unit-testing it.
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.i18n.asserts');
|
||||
goog.setTestOnly('goog.testing.i18n.asserts');
|
||||
|
||||
goog.require('goog.testing.jsunit');
|
||||
|
||||
|
||||
/**
|
||||
* A map of known tests where locale data changed, but the old values are
|
||||
* still tested for by various clients.
|
||||
* @const {!Object.<string, string>}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.i18n.asserts.EXPECTED_VALUE_MAP_ = {
|
||||
// Data to test the assert itself, old string as key, new string as value
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the two values are "almost equal" from i18n perspective
|
||||
* (based on a manually maintained and validated whitelist).
|
||||
* @param {string} expected The expected value.
|
||||
* @param {string} actual The actual value.
|
||||
*/
|
||||
goog.testing.i18n.asserts.assertI18nEquals = function(expected, actual) {
|
||||
if (expected == actual) {
|
||||
return;
|
||||
}
|
||||
|
||||
var newExpected = goog.testing.i18n.asserts.EXPECTED_VALUE_MAP_[expected];
|
||||
if (newExpected == actual) {
|
||||
return;
|
||||
}
|
||||
|
||||
assertEquals(expected, actual);
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2013 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 Unit tests for goog.testing.i18n.asserts.
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.i18n.assertsTest');
|
||||
goog.setTestOnly('goog.testing.i18n.assertsTest');
|
||||
|
||||
goog.require('goog.testing.i18n.asserts');
|
||||
goog.require('goog.testing.ExpectedFailures');
|
||||
|
||||
|
||||
// Add this mapping for testing only
|
||||
goog.testing.i18n.asserts.EXPECTED_VALUE_MAP_['mappedValue'] = 'newValue';
|
||||
|
||||
var expectedFailures = new goog.testing.ExpectedFailures();
|
||||
|
||||
function tearDown() {
|
||||
expectedFailures.handleTearDown();
|
||||
}
|
||||
|
||||
function testEdgeCases() {
|
||||
// Pass
|
||||
goog.testing.i18n.asserts.assertI18nEquals(null, null);
|
||||
goog.testing.i18n.asserts.assertI18nEquals('', '');
|
||||
|
||||
// Fail
|
||||
expectedFailures.expectFailureFor(true);
|
||||
try {
|
||||
goog.testing.i18n.asserts.assertI18nEquals(null, '');
|
||||
goog.testing.i18n.asserts.assertI18nEquals(null, 'test');
|
||||
goog.testing.i18n.asserts.assertI18nEquals('', null);
|
||||
goog.testing.i18n.asserts.assertI18nEquals('', 'test');
|
||||
goog.testing.i18n.asserts.assertI18nEquals('test', null);
|
||||
goog.testing.i18n.asserts.assertI18nEquals('test', '');
|
||||
} catch (e) {
|
||||
expectedFailures.handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
function testMappingWorks() {
|
||||
// Real equality
|
||||
goog.testing.i18n.asserts.assertI18nEquals('test', 'test');
|
||||
// i18n mapped equality
|
||||
goog.testing.i18n.asserts.assertI18nEquals('mappedValue', 'newValue');
|
||||
|
||||
// Negative testing
|
||||
expectedFailures.expectFailureFor(true);
|
||||
try {
|
||||
goog.testing.i18n.asserts.assertI18nEquals('unmappedValue', 'newValue');
|
||||
} catch (e) {
|
||||
expectedFailures.handleException(e);
|
||||
}
|
||||
}
|
||||
156
float-no-zero/closure-library/closure/goog/testing/jsunit.js
Normal file
156
float-no-zero/closure-library/closure/goog/testing/jsunit.js
Normal file
@@ -0,0 +1,156 @@
|
||||
// Copyright 2007 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 Utilities for working with JsUnit. Writes out the JsUnit file
|
||||
* that needs to be included in every unit test.
|
||||
*
|
||||
* Testing code should not have dependencies outside of goog.testing so as to
|
||||
* reduce the chance of masking missing dependencies.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.jsunit');
|
||||
|
||||
goog.require('goog.testing.TestCase');
|
||||
goog.require('goog.testing.TestRunner');
|
||||
|
||||
|
||||
/**
|
||||
* Base path for JsUnit app files, relative to Closure's base path.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.testing.jsunit.BASE_PATH =
|
||||
'../../third_party/java/jsunit/core/app/';
|
||||
|
||||
|
||||
/**
|
||||
* Filename for the core JS Unit script.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.testing.jsunit.CORE_SCRIPT =
|
||||
goog.testing.jsunit.BASE_PATH + 'jsUnitCore.js';
|
||||
|
||||
|
||||
/**
|
||||
* @define {boolean} If this code is being parsed by JsTestC, we let it disable
|
||||
* the onload handler to avoid running the test in JsTestC.
|
||||
*/
|
||||
goog.define('goog.testing.jsunit.AUTO_RUN_ONLOAD', true);
|
||||
|
||||
|
||||
/**
|
||||
* @define {number} Sets a delay in milliseconds after the window onload event
|
||||
* and running the tests. Used to prevent interference with Selenium and give
|
||||
* tests with asynchronous operations time to finish loading.
|
||||
*/
|
||||
goog.define('goog.testing.jsunit.AUTO_RUN_DELAY_IN_MS', 500);
|
||||
|
||||
|
||||
(function() {
|
||||
// Increases the maximum number of stack frames in Google Chrome from the
|
||||
// default 10 to 50 to get more useful stack traces.
|
||||
Error.stackTraceLimit = 50;
|
||||
|
||||
// Store a reference to the window's timeout so that it can't be overridden
|
||||
// by tests.
|
||||
/** @type {!Function} */
|
||||
var realTimeout = window.setTimeout;
|
||||
|
||||
// Check for JsUnit's test runner (need to check for >2.2 and <=2.2)
|
||||
if (top['JsUnitTestManager'] || top['jsUnitTestManager']) {
|
||||
// Running inside JsUnit so add support code.
|
||||
var path = goog.basePath + goog.testing.jsunit.CORE_SCRIPT;
|
||||
document.write('<script type="text/javascript" src="' +
|
||||
path + '"></' + 'script>');
|
||||
|
||||
} else {
|
||||
|
||||
// Create a test runner.
|
||||
var tr = new goog.testing.TestRunner();
|
||||
|
||||
// Export it so that it can be queried by Selenium and tests that use a
|
||||
// compiled test runner.
|
||||
goog.exportSymbol('G_testRunner', tr);
|
||||
goog.exportSymbol('G_testRunner.initialize', tr.initialize);
|
||||
goog.exportSymbol('G_testRunner.isInitialized', tr.isInitialized);
|
||||
goog.exportSymbol('G_testRunner.isFinished', tr.isFinished);
|
||||
goog.exportSymbol('G_testRunner.isSuccess', tr.isSuccess);
|
||||
goog.exportSymbol('G_testRunner.getReport', tr.getReport);
|
||||
goog.exportSymbol('G_testRunner.getRunTime', tr.getRunTime);
|
||||
goog.exportSymbol('G_testRunner.getNumFilesLoaded', tr.getNumFilesLoaded);
|
||||
goog.exportSymbol('G_testRunner.setStrict', tr.setStrict);
|
||||
goog.exportSymbol('G_testRunner.logTestFailure', tr.logTestFailure);
|
||||
|
||||
// Export debug as a global function for JSUnit compatibility. This just
|
||||
// calls log on the current test case.
|
||||
if (!goog.global['debug']) {
|
||||
goog.exportSymbol('debug', goog.bind(tr.log, tr));
|
||||
}
|
||||
|
||||
// If the application has defined a global error filter, set it now. This
|
||||
// allows users who use a base test include to set the error filter before
|
||||
// the testing code is loaded.
|
||||
if (goog.global['G_errorFilter']) {
|
||||
tr.setErrorFilter(goog.global['G_errorFilter']);
|
||||
}
|
||||
|
||||
// Add an error handler to report errors that may occur during
|
||||
// initialization of the page.
|
||||
var onerror = window.onerror;
|
||||
window.onerror = function(error, url, line) {
|
||||
// Call any existing onerror handlers.
|
||||
if (onerror) {
|
||||
onerror(error, url, line);
|
||||
}
|
||||
if (typeof error == 'object') {
|
||||
// Webkit started passing an event object as the only argument to
|
||||
// window.onerror. It doesn't contain an error message, url or line
|
||||
// number. We therefore log as much info as we can.
|
||||
if (error.target && error.target.tagName == 'SCRIPT') {
|
||||
tr.logError('UNKNOWN ERROR: Script ' + error.target.src);
|
||||
} else {
|
||||
tr.logError('UNKNOWN ERROR: No error information available.');
|
||||
}
|
||||
} else {
|
||||
tr.logError('JS ERROR: ' + error + '\nURL: ' + url + '\nLine: ' + line);
|
||||
}
|
||||
};
|
||||
|
||||
// Create an onload handler, if the test runner hasn't been initialized then
|
||||
// no test has been registered with the test runner by the test file. We
|
||||
// then create a new test case and auto discover any tests in the global
|
||||
// scope. If this code is being parsed by JsTestC, we let it disable the
|
||||
// onload handler to avoid running the test in JsTestC.
|
||||
if (goog.testing.jsunit.AUTO_RUN_ONLOAD) {
|
||||
var onload = window.onload;
|
||||
window.onload = function(e) {
|
||||
// Call any existing onload handlers.
|
||||
if (onload) {
|
||||
onload(e);
|
||||
}
|
||||
// Wait so that we don't interfere with WebDriver.
|
||||
realTimeout(function() {
|
||||
if (!tr.initialized) {
|
||||
var test = new goog.testing.TestCase(document.title);
|
||||
test.autoDiscoverTests();
|
||||
tr.initialize(test);
|
||||
}
|
||||
tr.execute();
|
||||
}, goog.testing.jsunit.AUTO_RUN_DELAY_IN_MS);
|
||||
window.onload = null;
|
||||
};
|
||||
}
|
||||
}
|
||||
})();
|
||||
240
float-no-zero/closure-library/closure/goog/testing/loosemock.js
Normal file
240
float-no-zero/closure-library/closure/goog/testing/loosemock.js
Normal file
@@ -0,0 +1,240 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview This file defines a loose mock implementation.
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.LooseExpectationCollection');
|
||||
goog.provide('goog.testing.LooseMock');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.structs.Map');
|
||||
goog.require('goog.testing.Mock');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This class is an ordered collection of expectations for one method. Since
|
||||
* the loose mock does most of its verification at the time of $verify, this
|
||||
* class is necessary to manage the return/throw behavior when the mock is
|
||||
* being called.
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.LooseExpectationCollection = function() {
|
||||
/**
|
||||
* The list of expectations. All of these should have the same name.
|
||||
* @type {Array.<goog.testing.MockExpectation>}
|
||||
* @private
|
||||
*/
|
||||
this.expectations_ = [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds an expectation to this collection.
|
||||
* @param {goog.testing.MockExpectation} expectation The expectation to add.
|
||||
*/
|
||||
goog.testing.LooseExpectationCollection.prototype.addExpectation =
|
||||
function(expectation) {
|
||||
this.expectations_.push(expectation);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the list of expectations in this collection.
|
||||
* @return {Array.<goog.testing.MockExpectation>} The array of expectations.
|
||||
*/
|
||||
goog.testing.LooseExpectationCollection.prototype.getExpectations = function() {
|
||||
return this.expectations_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This is a mock that does not care about the order of method calls. As a
|
||||
* result, it won't throw exceptions until verify() is called. The only
|
||||
* exception is that if a method is called that has no expectations, then an
|
||||
* exception will be thrown.
|
||||
* @param {Object} objectToMock The object to mock.
|
||||
* @param {boolean=} opt_ignoreUnexpectedCalls Whether to ignore unexpected
|
||||
* calls.
|
||||
* @param {boolean=} opt_mockStaticMethods An optional argument denoting that
|
||||
* a mock should be constructed from the static functions of a class.
|
||||
* @param {boolean=} opt_createProxy An optional argument denoting that
|
||||
* a proxy for the target mock should be created.
|
||||
* @constructor
|
||||
* @extends {goog.testing.Mock}
|
||||
*/
|
||||
goog.testing.LooseMock = function(objectToMock, opt_ignoreUnexpectedCalls,
|
||||
opt_mockStaticMethods, opt_createProxy) {
|
||||
goog.testing.Mock.call(this, objectToMock, opt_mockStaticMethods,
|
||||
opt_createProxy);
|
||||
|
||||
/**
|
||||
* A map of method names to a LooseExpectationCollection for that method.
|
||||
* @type {goog.structs.Map}
|
||||
* @private
|
||||
*/
|
||||
this.$expectations_ = new goog.structs.Map();
|
||||
|
||||
/**
|
||||
* The calls that have been made; we cache them to verify at the end. Each
|
||||
* element is an array where the first element is the name, and the second
|
||||
* element is the arguments.
|
||||
* @type {Array.<Array.<*>>}
|
||||
* @private
|
||||
*/
|
||||
this.$calls_ = [];
|
||||
|
||||
/**
|
||||
* Whether to ignore unexpected calls.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.$ignoreUnexpectedCalls_ = !!opt_ignoreUnexpectedCalls;
|
||||
};
|
||||
goog.inherits(goog.testing.LooseMock, goog.testing.Mock);
|
||||
|
||||
|
||||
/**
|
||||
* A setter for the ignoreUnexpectedCalls field.
|
||||
* @param {boolean} ignoreUnexpectedCalls Whether to ignore unexpected calls.
|
||||
* @return {goog.testing.LooseMock} This mock object.
|
||||
*/
|
||||
goog.testing.LooseMock.prototype.$setIgnoreUnexpectedCalls = function(
|
||||
ignoreUnexpectedCalls) {
|
||||
this.$ignoreUnexpectedCalls_ = ignoreUnexpectedCalls;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.LooseMock.prototype.$recordExpectation = function() {
|
||||
if (!this.$expectations_.containsKey(this.$pendingExpectation.name)) {
|
||||
this.$expectations_.set(this.$pendingExpectation.name,
|
||||
new goog.testing.LooseExpectationCollection());
|
||||
}
|
||||
|
||||
var collection = this.$expectations_.get(this.$pendingExpectation.name);
|
||||
collection.addExpectation(this.$pendingExpectation);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.LooseMock.prototype.$recordCall = function(name, args) {
|
||||
if (!this.$expectations_.containsKey(name)) {
|
||||
if (this.$ignoreUnexpectedCalls_) {
|
||||
return;
|
||||
}
|
||||
this.$throwCallException(name, args);
|
||||
}
|
||||
|
||||
// Start from the beginning of the expectations for this name,
|
||||
// and iterate over them until we find an expectation that matches
|
||||
// and also has calls remaining.
|
||||
var collection = this.$expectations_.get(name);
|
||||
var matchingExpectation = null;
|
||||
var expectations = collection.getExpectations();
|
||||
for (var i = 0; i < expectations.length; i++) {
|
||||
var expectation = expectations[i];
|
||||
if (this.$verifyCall(expectation, name, args)) {
|
||||
matchingExpectation = expectation;
|
||||
if (expectation.actualCalls < expectation.maxCalls) {
|
||||
break;
|
||||
} // else continue and see if we can find something that does match
|
||||
}
|
||||
}
|
||||
if (matchingExpectation == null) {
|
||||
this.$throwCallException(name, args, expectation);
|
||||
}
|
||||
|
||||
matchingExpectation.actualCalls++;
|
||||
if (matchingExpectation.actualCalls > matchingExpectation.maxCalls) {
|
||||
this.$throwException('Too many calls to ' + matchingExpectation.name +
|
||||
'\nExpected: ' + matchingExpectation.maxCalls + ' but was: ' +
|
||||
matchingExpectation.actualCalls);
|
||||
}
|
||||
|
||||
this.$calls_.push([name, args]);
|
||||
return this.$do(matchingExpectation, args);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.LooseMock.prototype.$reset = function() {
|
||||
goog.testing.LooseMock.superClass_.$reset.call(this);
|
||||
|
||||
this.$expectations_ = new goog.structs.Map();
|
||||
this.$calls_ = [];
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.LooseMock.prototype.$replay = function() {
|
||||
goog.testing.LooseMock.superClass_.$replay.call(this);
|
||||
|
||||
// Verify that there are no expectations that can never be reached.
|
||||
// This can't catch every situation, but it is a decent sanity check
|
||||
// and it's similar to the behavior of EasyMock in java.
|
||||
var collections = this.$expectations_.getValues();
|
||||
for (var i = 0; i < collections.length; i++) {
|
||||
var expectations = collections[i].getExpectations();
|
||||
for (var j = 0; j < expectations.length; j++) {
|
||||
var expectation = expectations[j];
|
||||
// If this expectation can be called infinite times, then
|
||||
// check if any subsequent expectation has the exact same
|
||||
// argument list.
|
||||
if (!isFinite(expectation.maxCalls)) {
|
||||
for (var k = j + 1; k < expectations.length; k++) {
|
||||
var laterExpectation = expectations[k];
|
||||
if (laterExpectation.minCalls > 0 &&
|
||||
goog.array.equals(expectation.argumentList,
|
||||
laterExpectation.argumentList)) {
|
||||
var name = expectation.name;
|
||||
var argsString = this.$argumentsAsString(expectation.argumentList);
|
||||
this.$throwException([
|
||||
'Expected call to ', name, ' with arguments ', argsString,
|
||||
' has an infinite max number of calls; can\'t expect an',
|
||||
' identical call later with a positive min number of calls'
|
||||
].join(''));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.LooseMock.prototype.$verify = function() {
|
||||
goog.testing.LooseMock.superClass_.$verify.call(this);
|
||||
var collections = this.$expectations_.getValues();
|
||||
|
||||
for (var i = 0; i < collections.length; i++) {
|
||||
var expectations = collections[i].getExpectations();
|
||||
for (var j = 0; j < expectations.length; j++) {
|
||||
var expectation = expectations[j];
|
||||
if (expectation.actualCalls > expectation.maxCalls) {
|
||||
this.$throwException('Too many calls to ' + expectation.name +
|
||||
'\nExpected: ' + expectation.maxCalls + ' but was: ' +
|
||||
expectation.actualCalls);
|
||||
} else if (expectation.actualCalls < expectation.minCalls) {
|
||||
this.$throwException('Not enough calls to ' + expectation.name +
|
||||
'\nExpected: ' + expectation.minCalls + ' but was: ' +
|
||||
expectation.actualCalls);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2010 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 Mock MessageChannel implementation that can receive fake
|
||||
* messages and test that the right messages are sent.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.testing.messaging.MockMessageChannel');
|
||||
|
||||
goog.require('goog.messaging.AbstractChannel');
|
||||
goog.require('goog.testing.asserts');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class for unit-testing code that communicates over a MessageChannel.
|
||||
* @param {goog.testing.MockControl} mockControl The mock control used to create
|
||||
* the method mock for #send.
|
||||
* @extends {goog.messaging.AbstractChannel}
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.messaging.MockMessageChannel = function(mockControl) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* Whether the channel has been disposed.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.disposed = false;
|
||||
|
||||
mockControl.createMethodMock(this, 'send');
|
||||
};
|
||||
goog.inherits(goog.testing.messaging.MockMessageChannel,
|
||||
goog.messaging.AbstractChannel);
|
||||
|
||||
|
||||
/**
|
||||
* A mock send function. Actually an instance of
|
||||
* {@link goog.testing.FunctionMock}.
|
||||
* @param {string} serviceName The name of the remote service to run.
|
||||
* @param {string|!Object} payload The payload to send to the remote page.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.messaging.MockMessageChannel.prototype.send = function(
|
||||
serviceName, payload) {};
|
||||
|
||||
|
||||
/**
|
||||
* Sets a flag indicating that this is disposed.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.messaging.MockMessageChannel.prototype.dispose = function() {
|
||||
this.disposed = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Mocks the receipt of a message. Passes the payload the appropriate service.
|
||||
* @param {string} serviceName The service to run.
|
||||
* @param {string|!Object} payload The argument to pass to the service.
|
||||
*/
|
||||
goog.testing.messaging.MockMessageChannel.prototype.receive = function(
|
||||
serviceName, payload) {
|
||||
this.deliver(serviceName, payload);
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright 2010 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 A simple mock class for imitating HTML5 MessageEvents.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.messaging.MockMessageEvent');
|
||||
|
||||
goog.require('goog.events.BrowserEvent');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.testing.events');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new fake MessageEvent.
|
||||
*
|
||||
* @param {*} data The data of the message.
|
||||
* @param {string=} opt_origin The origin of the message, for server-sent and
|
||||
* cross-document events.
|
||||
* @param {string=} opt_lastEventId The last event ID, for server-sent events.
|
||||
* @param {Window=} opt_source The proxy for the source window, for
|
||||
* cross-document events.
|
||||
* @param {Array.<MessagePort>=} opt_ports The Array of ports sent with the
|
||||
* message, for cross-document and channel events.
|
||||
* @extends {goog.testing.events.Event}
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.messaging.MockMessageEvent = function(
|
||||
data, opt_origin, opt_lastEventId, opt_source, opt_ports) {
|
||||
goog.base(this, goog.events.EventType.MESSAGE);
|
||||
|
||||
/**
|
||||
* The data of the message.
|
||||
* @type {*}
|
||||
*/
|
||||
this.data = data;
|
||||
|
||||
/**
|
||||
* The origin of the message, for server-sent and cross-document events.
|
||||
* @type {?string}
|
||||
*/
|
||||
this.origin = opt_origin || null;
|
||||
|
||||
/**
|
||||
* The last event ID, for server-sent events.
|
||||
* @type {?string}
|
||||
*/
|
||||
this.lastEventId = opt_lastEventId || null;
|
||||
|
||||
/**
|
||||
* The proxy for the source window, for cross-document events.
|
||||
* @type {Window}
|
||||
*/
|
||||
this.source = opt_source || null;
|
||||
|
||||
/**
|
||||
* The Array of ports sent with the message, for cross-document and channel
|
||||
* events.
|
||||
* @type {Array.<!MessagePort>}
|
||||
*/
|
||||
this.ports = opt_ports || null;
|
||||
};
|
||||
goog.inherits(
|
||||
goog.testing.messaging.MockMessageEvent, goog.testing.events.Event);
|
||||
|
||||
|
||||
/**
|
||||
* Wraps a new fake MessageEvent in a BrowserEvent, like how a real MessageEvent
|
||||
* would be wrapped.
|
||||
*
|
||||
* @param {*} data The data of the message.
|
||||
* @param {string=} opt_origin The origin of the message, for server-sent and
|
||||
* cross-document events.
|
||||
* @param {string=} opt_lastEventId The last event ID, for server-sent events.
|
||||
* @param {Window=} opt_source The proxy for the source window, for
|
||||
* cross-document events.
|
||||
* @param {Array.<MessagePort>=} opt_ports The Array of ports sent with the
|
||||
* message, for cross-document and channel events.
|
||||
* @return {goog.events.BrowserEvent} The wrapping event.
|
||||
*/
|
||||
goog.testing.messaging.MockMessageEvent.wrap = function(
|
||||
data, opt_origin, opt_lastEventId, opt_source, opt_ports) {
|
||||
return new goog.events.BrowserEvent(
|
||||
new goog.testing.messaging.MockMessageEvent(
|
||||
data, opt_origin, opt_lastEventId, opt_source, opt_ports));
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2011 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 A simple dummy class for representing message ports in tests.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.messaging.MockMessagePort');
|
||||
|
||||
goog.require('goog.events.EventTarget');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class for unit-testing code that uses MessagePorts.
|
||||
* @param {*} id An opaque identifier, used because message ports otherwise have
|
||||
* no distinguishing characteristics.
|
||||
* @param {goog.testing.MockControl} mockControl The mock control used to create
|
||||
* the method mock for #postMessage.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.testing.messaging.MockMessagePort = function(id, mockControl) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* An opaque identifier, used because message ports otherwise have no
|
||||
* distinguishing characteristics.
|
||||
* @type {*}
|
||||
*/
|
||||
this.id = id;
|
||||
|
||||
/**
|
||||
* Whether or not the port has been started.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.started = false;
|
||||
|
||||
/**
|
||||
* Whether or not the port has been closed.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.closed = false;
|
||||
|
||||
mockControl.createMethodMock(this, 'postMessage');
|
||||
};
|
||||
goog.inherits(goog.testing.messaging.MockMessagePort, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* A mock postMessage funciton. Actually an instance of
|
||||
* {@link goog.testing.FunctionMock}.
|
||||
* @param {*} message The message to send.
|
||||
* @param {Array.<MessagePort>=} opt_ports Ports to send with the message.
|
||||
*/
|
||||
goog.testing.messaging.MockMessagePort.prototype.postMessage = function(
|
||||
message, opt_ports) {};
|
||||
|
||||
|
||||
/**
|
||||
* Starts the port.
|
||||
*/
|
||||
goog.testing.messaging.MockMessagePort.prototype.start = function() {
|
||||
this.started = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Closes the port.
|
||||
*/
|
||||
goog.testing.messaging.MockMessagePort.prototype.close = function() {
|
||||
this.closed = true;
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright 2011 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 A fake PortNetwork implementation that simply produces
|
||||
* MockMessageChannels for all ports.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.messaging.MockPortNetwork');
|
||||
|
||||
goog.require('goog.messaging.PortNetwork'); // interface
|
||||
goog.require('goog.testing.messaging.MockMessageChannel');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The fake PortNetwork.
|
||||
*
|
||||
* @param {!goog.testing.MockControl} mockControl The mock control for creating
|
||||
* the mock message channels.
|
||||
* @constructor
|
||||
* @implements {goog.messaging.PortNetwork}
|
||||
*/
|
||||
goog.testing.messaging.MockPortNetwork = function(mockControl) {
|
||||
/**
|
||||
* The mock control for creating mock message channels.
|
||||
* @type {!goog.testing.MockControl}
|
||||
* @private
|
||||
*/
|
||||
this.mockControl_ = mockControl;
|
||||
|
||||
/**
|
||||
* The mock ports that have been created.
|
||||
* @type {!Object.<!goog.testing.messaging.MockMessageChannel>}
|
||||
* @private
|
||||
*/
|
||||
this.ports_ = {};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the mock port with the given name.
|
||||
* @param {string} name The name of the port to get.
|
||||
* @return {!goog.testing.messaging.MockMessageChannel} The mock port.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.messaging.MockPortNetwork.prototype.dial = function(name) {
|
||||
if (!(name in this.ports_)) {
|
||||
this.ports_[name] =
|
||||
new goog.testing.messaging.MockMessageChannel(this.mockControl_);
|
||||
}
|
||||
return this.ports_[name];
|
||||
};
|
||||
619
float-no-zero/closure-library/closure/goog/testing/mock.js
Normal file
619
float-no-zero/closure-library/closure/goog/testing/mock.js
Normal file
@@ -0,0 +1,619 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview This file defines base classes used for creating mocks in
|
||||
* JavaScript. The API was inspired by EasyMock.
|
||||
*
|
||||
* The basic API is:
|
||||
* <ul>
|
||||
* <li>Create an object to be mocked
|
||||
* <li>Create a mock object, passing in the above object to the constructor
|
||||
* <li>Set expectations by calling methods on the mock object
|
||||
* <li>Call $replay() on the mock object
|
||||
* <li>Pass the mock to code that will make real calls on it
|
||||
* <li>Call $verify() to make sure that expectations were met
|
||||
* </ul>
|
||||
*
|
||||
* For examples, please see the unit tests for LooseMock and StrictMock.
|
||||
*
|
||||
* Still TODO
|
||||
* implement better (and pluggable) argument matching
|
||||
* Have the exceptions for LooseMock show the number of expected/actual calls
|
||||
* loose and strict mocks share a lot of code - move it to the base class
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.Mock');
|
||||
goog.provide('goog.testing.MockExpectation');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.testing.JsUnitException');
|
||||
goog.require('goog.testing.MockInterface');
|
||||
goog.require('goog.testing.mockmatchers');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This is a class that represents an expectation.
|
||||
* @param {string} name The name of the method for this expectation.
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.MockExpectation = function(name) {
|
||||
/**
|
||||
* The name of the method that is expected to be called.
|
||||
* @type {string}
|
||||
*/
|
||||
this.name = name;
|
||||
|
||||
/**
|
||||
* An array of error messages for expectations not met.
|
||||
* @type {Array}
|
||||
*/
|
||||
this.errorMessages = [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The minimum number of times this method should be called.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.MockExpectation.prototype.minCalls = 1;
|
||||
|
||||
|
||||
/**
|
||||
* The maximum number of times this method should be called.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.MockExpectation.prototype.maxCalls = 1;
|
||||
|
||||
|
||||
/**
|
||||
* The value that this method should return.
|
||||
* @type {*}
|
||||
*/
|
||||
goog.testing.MockExpectation.prototype.returnValue;
|
||||
|
||||
|
||||
/**
|
||||
* The value that will be thrown when the method is called
|
||||
* @type {*}
|
||||
*/
|
||||
goog.testing.MockExpectation.prototype.exceptionToThrow;
|
||||
|
||||
|
||||
/**
|
||||
* The arguments that are expected to be passed to this function
|
||||
* @type {Array.<*>}
|
||||
*/
|
||||
goog.testing.MockExpectation.prototype.argumentList;
|
||||
|
||||
|
||||
/**
|
||||
* The number of times this method is called by real code.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.MockExpectation.prototype.actualCalls = 0;
|
||||
|
||||
|
||||
/**
|
||||
* The number of times this method is called during the verification phase.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.MockExpectation.prototype.verificationCalls = 0;
|
||||
|
||||
|
||||
/**
|
||||
* The function which will be executed when this method is called.
|
||||
* Method arguments will be passed to this function, and return value
|
||||
* of this function will be returned by the method.
|
||||
* @type {Function}
|
||||
*/
|
||||
goog.testing.MockExpectation.prototype.toDo;
|
||||
|
||||
|
||||
/**
|
||||
* Allow expectation failures to include messages.
|
||||
* @param {string} message The failure message.
|
||||
*/
|
||||
goog.testing.MockExpectation.prototype.addErrorMessage = function(message) {
|
||||
this.errorMessages.push(message);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the error messages seen so far.
|
||||
* @return {string} Error messages separated by \n.
|
||||
*/
|
||||
goog.testing.MockExpectation.prototype.getErrorMessage = function() {
|
||||
return this.errorMessages.join('\n');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get how many error messages have been seen so far.
|
||||
* @return {number} Count of error messages.
|
||||
*/
|
||||
goog.testing.MockExpectation.prototype.getErrorMessageCount = function() {
|
||||
return this.errorMessages.length;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The base class for a mock object.
|
||||
* @param {Object|Function} objectToMock The object that should be mocked, or
|
||||
* the constructor of an object to mock.
|
||||
* @param {boolean=} opt_mockStaticMethods An optional argument denoting that
|
||||
* a mock should be constructed from the static functions of a class.
|
||||
* @param {boolean=} opt_createProxy An optional argument denoting that
|
||||
* a proxy for the target mock should be created.
|
||||
* @constructor
|
||||
* @implements {goog.testing.MockInterface}
|
||||
*/
|
||||
goog.testing.Mock = function(objectToMock, opt_mockStaticMethods,
|
||||
opt_createProxy) {
|
||||
if (!goog.isObject(objectToMock) && !goog.isFunction(objectToMock)) {
|
||||
throw new Error('objectToMock must be an object or constructor.');
|
||||
}
|
||||
if (opt_createProxy && !opt_mockStaticMethods &&
|
||||
goog.isFunction(objectToMock)) {
|
||||
/** @constructor */
|
||||
var tempCtor = function() {};
|
||||
goog.inherits(tempCtor, objectToMock);
|
||||
this.$proxy = new tempCtor();
|
||||
} else if (opt_createProxy && opt_mockStaticMethods &&
|
||||
goog.isFunction(objectToMock)) {
|
||||
throw Error('Cannot create a proxy when opt_mockStaticMethods is true');
|
||||
} else if (opt_createProxy && !goog.isFunction(objectToMock)) {
|
||||
throw Error('Must have a constructor to create a proxy');
|
||||
}
|
||||
|
||||
if (goog.isFunction(objectToMock) && !opt_mockStaticMethods) {
|
||||
this.$initializeFunctions_(objectToMock.prototype);
|
||||
} else {
|
||||
this.$initializeFunctions_(objectToMock);
|
||||
}
|
||||
this.$argumentListVerifiers_ = {};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Option that may be passed when constructing function, method, and
|
||||
* constructor mocks. Indicates that the expected calls should be accepted in
|
||||
* any order.
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.Mock.LOOSE = 1;
|
||||
|
||||
|
||||
/**
|
||||
* Option that may be passed when constructing function, method, and
|
||||
* constructor mocks. Indicates that the expected calls should be accepted in
|
||||
* the recorded order only.
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.Mock.STRICT = 0;
|
||||
|
||||
|
||||
/**
|
||||
* This array contains the name of the functions that are part of the base
|
||||
* Object prototype.
|
||||
* Basically a copy of goog.object.PROTOTYPE_FIELDS_.
|
||||
* @const
|
||||
* @type {!Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.Mock.PROTOTYPE_FIELDS_ = [
|
||||
'constructor',
|
||||
'hasOwnProperty',
|
||||
'isPrototypeOf',
|
||||
'propertyIsEnumerable',
|
||||
'toLocaleString',
|
||||
'toString',
|
||||
'valueOf'
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* A proxy for the mock. This can be used for dependency injection in lieu of
|
||||
* the mock if the test requires a strict instanceof check.
|
||||
* @type {Object}
|
||||
*/
|
||||
goog.testing.Mock.prototype.$proxy = null;
|
||||
|
||||
|
||||
/**
|
||||
* Map of argument name to optional argument list verifier function.
|
||||
* @type {Object}
|
||||
*/
|
||||
goog.testing.Mock.prototype.$argumentListVerifiers_;
|
||||
|
||||
|
||||
/**
|
||||
* Whether or not we are in recording mode.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.Mock.prototype.$recording_ = true;
|
||||
|
||||
|
||||
/**
|
||||
* The expectation currently being created. All methods that modify the
|
||||
* current expectation return the Mock object for easy chaining, so this is
|
||||
* where we keep track of the expectation that's currently being modified.
|
||||
* @type {goog.testing.MockExpectation}
|
||||
* @protected
|
||||
*/
|
||||
goog.testing.Mock.prototype.$pendingExpectation;
|
||||
|
||||
|
||||
/**
|
||||
* First exception thrown by this mock; used in $verify.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.Mock.prototype.$threwException_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the functions on the mock object.
|
||||
* @param {Object} objectToMock The object being mocked.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.Mock.prototype.$initializeFunctions_ = function(objectToMock) {
|
||||
// Gets the object properties.
|
||||
var enumerableProperties = goog.object.getKeys(objectToMock);
|
||||
|
||||
// The non enumerable properties are added if they override the ones in the
|
||||
// Object prototype. This is due to the fact that IE8 does not enumerate any
|
||||
// of the prototype Object functions even when overriden and mocking these is
|
||||
// sometimes needed.
|
||||
for (var i = 0; i < goog.testing.Mock.PROTOTYPE_FIELDS_.length; i++) {
|
||||
var prop = goog.testing.Mock.PROTOTYPE_FIELDS_[i];
|
||||
// Look at b/6758711 if you're considering adding ALL properties to ALL
|
||||
// mocks.
|
||||
if (objectToMock[prop] !== Object.prototype[prop]) {
|
||||
enumerableProperties.push(prop);
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the properties to the mock.
|
||||
for (var i = 0; i < enumerableProperties.length; i++) {
|
||||
var prop = enumerableProperties[i];
|
||||
if (typeof objectToMock[prop] == 'function') {
|
||||
this[prop] = goog.bind(this.$mockMethod, this, prop);
|
||||
if (this.$proxy) {
|
||||
this.$proxy[prop] = goog.bind(this.$mockMethod, this, prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Registers a verfifier function to use when verifying method argument lists.
|
||||
* @param {string} methodName The name of the method for which the verifierFn
|
||||
* should be used.
|
||||
* @param {Function} fn Argument list verifier function. Should take 2 argument
|
||||
* arrays as arguments, and return true if they are considered equivalent.
|
||||
* @return {goog.testing.Mock} This mock object.
|
||||
*/
|
||||
goog.testing.Mock.prototype.$registerArgumentListVerifier = function(methodName,
|
||||
fn) {
|
||||
this.$argumentListVerifiers_[methodName] = fn;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The function that replaces all methods on the mock object.
|
||||
* @param {string} name The name of the method being mocked.
|
||||
* @return {*} In record mode, returns the mock object. In replay mode, returns
|
||||
* whatever the creator of the mock set as the return value.
|
||||
*/
|
||||
goog.testing.Mock.prototype.$mockMethod = function(name) {
|
||||
try {
|
||||
// Shift off the name argument so that args contains the arguments to
|
||||
// the mocked method.
|
||||
var args = goog.array.slice(arguments, 1);
|
||||
if (this.$recording_) {
|
||||
this.$pendingExpectation = new goog.testing.MockExpectation(name);
|
||||
this.$pendingExpectation.argumentList = args;
|
||||
this.$recordExpectation();
|
||||
return this;
|
||||
} else {
|
||||
return this.$recordCall(name, args);
|
||||
}
|
||||
} catch (ex) {
|
||||
this.$recordAndThrow(ex);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Records the currently pending expectation, intended to be overridden by a
|
||||
* subclass.
|
||||
* @protected
|
||||
*/
|
||||
goog.testing.Mock.prototype.$recordExpectation = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Records an actual method call, intended to be overridden by a
|
||||
* subclass. The subclass must find the pending expectation and return the
|
||||
* correct value.
|
||||
* @param {string} name The name of the method being called.
|
||||
* @param {Array} args The arguments to the method.
|
||||
* @return {*} The return expected by the mock.
|
||||
* @protected
|
||||
*/
|
||||
goog.testing.Mock.prototype.$recordCall = function(name, args) {
|
||||
return undefined;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* If the expectation expects to throw, this method will throw.
|
||||
* @param {goog.testing.MockExpectation} expectation The expectation.
|
||||
*/
|
||||
goog.testing.Mock.prototype.$maybeThrow = function(expectation) {
|
||||
if (typeof expectation.exceptionToThrow != 'undefined') {
|
||||
throw expectation.exceptionToThrow;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* If this expectation defines a function to be called,
|
||||
* it will be called and its result will be returned.
|
||||
* Otherwise, if the expectation expects to throw, it will throw.
|
||||
* Otherwise, this method will return defined value.
|
||||
* @param {goog.testing.MockExpectation} expectation The expectation.
|
||||
* @param {Array} args The arguments to the method.
|
||||
* @return {*} The return value expected by the mock.
|
||||
*/
|
||||
goog.testing.Mock.prototype.$do = function(expectation, args) {
|
||||
if (typeof expectation.toDo == 'undefined') {
|
||||
this.$maybeThrow(expectation);
|
||||
return expectation.returnValue;
|
||||
} else {
|
||||
return expectation.toDo.apply(this, args);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Specifies a return value for the currently pending expectation.
|
||||
* @param {*} val The return value.
|
||||
* @return {goog.testing.Mock} This mock object.
|
||||
*/
|
||||
goog.testing.Mock.prototype.$returns = function(val) {
|
||||
this.$pendingExpectation.returnValue = val;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Specifies a value for the currently pending expectation to throw.
|
||||
* @param {*} val The value to throw.
|
||||
* @return {goog.testing.Mock} This mock object.
|
||||
*/
|
||||
goog.testing.Mock.prototype.$throws = function(val) {
|
||||
this.$pendingExpectation.exceptionToThrow = val;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Specifies a function to call for currently pending expectation.
|
||||
* Note, that using this method overrides declarations made
|
||||
* using $returns() and $throws() methods.
|
||||
* @param {Function} func The function to call.
|
||||
* @return {goog.testing.Mock} This mock object.
|
||||
*/
|
||||
goog.testing.Mock.prototype.$does = function(func) {
|
||||
this.$pendingExpectation.toDo = func;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Allows the expectation to be called 0 or 1 times.
|
||||
* @return {goog.testing.Mock} This mock object.
|
||||
*/
|
||||
goog.testing.Mock.prototype.$atMostOnce = function() {
|
||||
this.$pendingExpectation.minCalls = 0;
|
||||
this.$pendingExpectation.maxCalls = 1;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Allows the expectation to be called any number of times, as long as it's
|
||||
* called once.
|
||||
* @return {goog.testing.Mock} This mock object.
|
||||
*/
|
||||
goog.testing.Mock.prototype.$atLeastOnce = function() {
|
||||
this.$pendingExpectation.maxCalls = Infinity;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Allows the expectation to be called any number of times.
|
||||
* @return {goog.testing.Mock} This mock object.
|
||||
*/
|
||||
goog.testing.Mock.prototype.$anyTimes = function() {
|
||||
this.$pendingExpectation.minCalls = 0;
|
||||
this.$pendingExpectation.maxCalls = Infinity;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Specifies the number of times the expectation should be called.
|
||||
* @param {number} times The number of times this method will be called.
|
||||
* @return {goog.testing.Mock} This mock object.
|
||||
*/
|
||||
goog.testing.Mock.prototype.$times = function(times) {
|
||||
this.$pendingExpectation.minCalls = times;
|
||||
this.$pendingExpectation.maxCalls = times;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Switches from recording to replay mode.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.Mock.prototype.$replay = function() {
|
||||
this.$recording_ = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Resets the state of this mock object. This clears all pending expectations
|
||||
* without verifying, and puts the mock in recording mode.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.Mock.prototype.$reset = function() {
|
||||
this.$recording_ = true;
|
||||
this.$threwException_ = null;
|
||||
delete this.$pendingExpectation;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Throws an exception and records that an exception was thrown.
|
||||
* @param {string} comment A short comment about the exception.
|
||||
* @param {?string=} opt_message A longer message about the exception.
|
||||
* @throws {Object} JsUnitException object.
|
||||
* @protected
|
||||
*/
|
||||
goog.testing.Mock.prototype.$throwException = function(comment, opt_message) {
|
||||
this.$recordAndThrow(new goog.testing.JsUnitException(comment, opt_message));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Throws an exception and records that an exception was thrown.
|
||||
* @param {Object} ex Exception.
|
||||
* @throws {Object} #ex.
|
||||
* @protected
|
||||
*/
|
||||
goog.testing.Mock.prototype.$recordAndThrow = function(ex) {
|
||||
// If it's an assert exception, record it.
|
||||
if (ex['isJsUnitException']) {
|
||||
var testRunner = goog.global['G_testRunner'];
|
||||
if (testRunner) {
|
||||
var logTestFailureFunction = testRunner['logTestFailure'];
|
||||
if (logTestFailureFunction) {
|
||||
logTestFailureFunction.call(testRunner, ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.$threwException_) {
|
||||
// Only remember first exception thrown.
|
||||
this.$threwException_ = ex;
|
||||
}
|
||||
}
|
||||
throw ex;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Verify that all of the expectations were met. Should be overridden by
|
||||
* subclasses.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.Mock.prototype.$verify = function() {
|
||||
if (this.$threwException_) {
|
||||
throw this.$threwException_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Verifies that a method call matches an expectation.
|
||||
* @param {goog.testing.MockExpectation} expectation The expectation to check.
|
||||
* @param {string} name The name of the called method.
|
||||
* @param {Array.<*>?} args The arguments passed to the mock.
|
||||
* @return {boolean} Whether the call matches the expectation.
|
||||
*/
|
||||
goog.testing.Mock.prototype.$verifyCall = function(expectation, name, args) {
|
||||
if (expectation.name != name) {
|
||||
return false;
|
||||
}
|
||||
var verifierFn =
|
||||
this.$argumentListVerifiers_.hasOwnProperty(expectation.name) ?
|
||||
this.$argumentListVerifiers_[expectation.name] :
|
||||
goog.testing.mockmatchers.flexibleArrayMatcher;
|
||||
|
||||
return verifierFn(expectation.argumentList, args, expectation);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Render the provided argument array to a string to help
|
||||
* clients with debugging tests.
|
||||
* @param {Array.<*>?} args The arguments passed to the mock.
|
||||
* @return {string} Human-readable string.
|
||||
*/
|
||||
goog.testing.Mock.prototype.$argumentsAsString = function(args) {
|
||||
var retVal = [];
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
try {
|
||||
retVal.push(goog.typeOf(args[i]));
|
||||
} catch (e) {
|
||||
retVal.push('[unknown]');
|
||||
}
|
||||
}
|
||||
return '(' + retVal.join(', ') + ')';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Throw an exception based on an incorrect method call.
|
||||
* @param {string} name Name of method called.
|
||||
* @param {Array.<*>?} args Arguments passed to the mock.
|
||||
* @param {goog.testing.MockExpectation=} opt_expectation Expected next call,
|
||||
* if any.
|
||||
*/
|
||||
goog.testing.Mock.prototype.$throwCallException = function(name, args,
|
||||
opt_expectation) {
|
||||
var errorStringBuffer = [];
|
||||
var actualArgsString = this.$argumentsAsString(args);
|
||||
var expectedArgsString = opt_expectation ?
|
||||
this.$argumentsAsString(opt_expectation.argumentList) : '';
|
||||
|
||||
if (opt_expectation && opt_expectation.name == name) {
|
||||
errorStringBuffer.push('Bad arguments to ', name, '().\n',
|
||||
'Actual: ', actualArgsString, '\n',
|
||||
'Expected: ', expectedArgsString, '\n',
|
||||
opt_expectation.getErrorMessage());
|
||||
} else {
|
||||
errorStringBuffer.push('Unexpected call to ', name,
|
||||
actualArgsString, '.');
|
||||
if (opt_expectation) {
|
||||
errorStringBuffer.push('\nNext expected call was to ',
|
||||
opt_expectation.name,
|
||||
expectedArgsString);
|
||||
}
|
||||
}
|
||||
this.$throwException(errorStringBuffer.join(''));
|
||||
};
|
||||
@@ -0,0 +1,578 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview This file defines a factory that can be used to mock and
|
||||
* replace an entire class. This allows for mocks to be used effectively with
|
||||
* "new" instead of having to inject all instances. Essentially, a given class
|
||||
* is replaced with a proxy to either a loose or strict mock. Proxies locate
|
||||
* the appropriate mock based on constructor arguments.
|
||||
*
|
||||
* The usage is:
|
||||
* <ul>
|
||||
* <li>Create a mock with one of the provided methods with a specifc set of
|
||||
* constructor arguments
|
||||
* <li>Set expectations by calling methods on the mock object
|
||||
* <li>Call $replay() on the mock object
|
||||
* <li>Instantiate the object as normal
|
||||
* <li>Call $verify() to make sure that expectations were met
|
||||
* <li>Call reset on the factory to revert all classes back to their original
|
||||
* state
|
||||
* </ul>
|
||||
*
|
||||
* For examples, please see the unit test.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.testing.MockClassFactory');
|
||||
goog.provide('goog.testing.MockClassRecord');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.testing.LooseMock');
|
||||
goog.require('goog.testing.StrictMock');
|
||||
goog.require('goog.testing.TestCase');
|
||||
goog.require('goog.testing.mockmatchers');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A record that represents all the data associated with a mock replacement of
|
||||
* a given class.
|
||||
* @param {Object} namespace The namespace in which the mocked class resides.
|
||||
* @param {string} className The name of the class within the namespace.
|
||||
* @param {Function} originalClass The original class implementation before it
|
||||
* was replaced by a proxy.
|
||||
* @param {Function} proxy The proxy that replaced the original class.
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.MockClassRecord = function(namespace, className, originalClass,
|
||||
proxy) {
|
||||
/**
|
||||
* A standard closure namespace (e.g. goog.foo.bar) that contains the mock
|
||||
* class referenced by this MockClassRecord.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.namespace_ = namespace;
|
||||
|
||||
/**
|
||||
* The name of the class within the provided namespace.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.className_ = className;
|
||||
|
||||
/**
|
||||
* The original class implementation.
|
||||
* @type {Function}
|
||||
* @private
|
||||
*/
|
||||
this.originalClass_ = originalClass;
|
||||
|
||||
/**
|
||||
* The proxy being used as a replacement for the original class.
|
||||
* @type {Function}
|
||||
* @private
|
||||
*/
|
||||
this.proxy_ = proxy;
|
||||
|
||||
/**
|
||||
* A mocks that will be constructed by their argument list. The entries are
|
||||
* objects with the format {'args': args, 'mock': mock}.
|
||||
* @type {Array.<Object>}
|
||||
* @private
|
||||
*/
|
||||
this.instancesByArgs_ = [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A mock associated with the static functions for a given class.
|
||||
* @type {goog.testing.StrictMock|goog.testing.LooseMock|null}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClassRecord.prototype.staticMock_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* A getter for this record's namespace.
|
||||
* @return {Object} The namespace.
|
||||
*/
|
||||
goog.testing.MockClassRecord.prototype.getNamespace = function() {
|
||||
return this.namespace_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A getter for this record's class name.
|
||||
* @return {string} The name of the class referenced by this record.
|
||||
*/
|
||||
goog.testing.MockClassRecord.prototype.getClassName = function() {
|
||||
return this.className_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A getter for the original class.
|
||||
* @return {Function} The original class implementation before mocking.
|
||||
*/
|
||||
goog.testing.MockClassRecord.prototype.getOriginalClass = function() {
|
||||
return this.originalClass_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A getter for the proxy being used as a replacement for the original class.
|
||||
* @return {Function} The proxy.
|
||||
*/
|
||||
goog.testing.MockClassRecord.prototype.getProxy = function() {
|
||||
return this.proxy_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A getter for the static mock.
|
||||
* @return {goog.testing.StrictMock|goog.testing.LooseMock|null} The static
|
||||
* mock associated with this record.
|
||||
*/
|
||||
goog.testing.MockClassRecord.prototype.getStaticMock = function() {
|
||||
return this.staticMock_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A setter for the static mock.
|
||||
* @param {goog.testing.StrictMock|goog.testing.LooseMock} staticMock A mock to
|
||||
* associate with the static functions for the referenced class.
|
||||
*/
|
||||
goog.testing.MockClassRecord.prototype.setStaticMock = function(staticMock) {
|
||||
this.staticMock_ = staticMock;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a new mock instance mapping. The mapping connects a set of function
|
||||
* arguments to a specific mock instance.
|
||||
* @param {Array} args An array of function arguments.
|
||||
* @param {goog.testing.StrictMock|goog.testing.LooseMock} mock A mock
|
||||
* associated with the supplied arguments.
|
||||
*/
|
||||
goog.testing.MockClassRecord.prototype.addMockInstance = function(args, mock) {
|
||||
this.instancesByArgs_.push({args: args, mock: mock});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Finds the mock corresponding to a given argument set. Throws an error if
|
||||
* there is no appropriate match found.
|
||||
* @param {Array} args An array of function arguments.
|
||||
* @return {goog.testing.StrictMock|goog.testing.LooseMock|null} The mock
|
||||
* corresponding to a given argument set.
|
||||
*/
|
||||
goog.testing.MockClassRecord.prototype.findMockInstance = function(args) {
|
||||
for (var i = 0; i < this.instancesByArgs_.length; i++) {
|
||||
var instanceArgs = this.instancesByArgs_[i].args;
|
||||
if (goog.testing.mockmatchers.flexibleArrayMatcher(instanceArgs, args)) {
|
||||
return this.instancesByArgs_[i].mock;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Resets this record by reverting all the mocked classes back to the original
|
||||
* implementation and clearing out the mock instance list.
|
||||
*/
|
||||
goog.testing.MockClassRecord.prototype.reset = function() {
|
||||
this.namespace_[this.className_] = this.originalClass_;
|
||||
this.instancesByArgs_ = [];
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A factory used to create new mock class instances. It is able to generate
|
||||
* both static and loose mocks. The MockClassFactory is a singleton since it
|
||||
* tracks the classes that have been mocked internally.
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.MockClassFactory = function() {
|
||||
if (goog.testing.MockClassFactory.instance_) {
|
||||
return goog.testing.MockClassFactory.instance_;
|
||||
}
|
||||
|
||||
/**
|
||||
* A map from class name -> goog.testing.MockClassRecord.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.mockClassRecords_ = {};
|
||||
|
||||
goog.testing.MockClassFactory.instance_ = this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A singleton instance of the MockClassFactory.
|
||||
* @type {goog.testing.MockClassFactory?}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClassFactory.instance_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The names of the fields that are defined on Object.prototype.
|
||||
* @type {Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClassFactory.PROTOTYPE_FIELDS_ = [
|
||||
'constructor',
|
||||
'hasOwnProperty',
|
||||
'isPrototypeOf',
|
||||
'propertyIsEnumerable',
|
||||
'toLocaleString',
|
||||
'toString',
|
||||
'valueOf'
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Iterates through a namespace to find the name of a given class. This is done
|
||||
* solely to support compilation since string identifiers would break down.
|
||||
* Tests usually aren't compiled, but the functionality is supported.
|
||||
* @param {Object} namespace A javascript namespace (e.g. goog.testing).
|
||||
* @param {Function} classToMock The class whose name should be returned.
|
||||
* @return {string} The name of the class.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClassFactory.prototype.getClassName_ = function(namespace,
|
||||
classToMock) {
|
||||
if (namespace === goog.global) {
|
||||
namespace = goog.testing.TestCase.getGlobals();
|
||||
}
|
||||
for (var prop in namespace) {
|
||||
if (namespace[prop] === classToMock) {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
throw Error('Class is not a part of the given namespace');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether or not a given class has been mocked.
|
||||
* @param {string} className The name of the class.
|
||||
* @return {boolean} Whether or not the given class name has a MockClassRecord.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClassFactory.prototype.classHasMock_ = function(className) {
|
||||
return !!this.mockClassRecords_[className];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a proxy constructor closure. Since this is a constructor, "this"
|
||||
* refers to the local scope of the constructed object thus bind cannot be
|
||||
* used.
|
||||
* @param {string} className The name of the class.
|
||||
* @param {Function} mockFinder A bound function that returns the mock
|
||||
* associated with a class given the constructor's argument list.
|
||||
* @return {Function} A proxy constructor.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClassFactory.prototype.getProxyCtor_ = function(className,
|
||||
mockFinder) {
|
||||
return function() {
|
||||
this.$mock_ = mockFinder(className, arguments);
|
||||
if (!this.$mock_) {
|
||||
// The "arguments" variable is not a proper Array so it must be converted.
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
throw Error('No mock found for ' + className + ' with arguments ' +
|
||||
args.join(', '));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a proxy function for a mock class instance. This function cannot
|
||||
* be used with bind since "this" must refer to the scope of the proxy
|
||||
* constructor.
|
||||
* @param {string} fnName The name of the function that should be proxied.
|
||||
* @return {Function} A proxy function.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClassFactory.prototype.getProxyFunction_ = function(fnName) {
|
||||
return function() {
|
||||
return this.$mock_[fnName].apply(this.$mock_, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Find a mock instance for a given class name and argument list.
|
||||
* @param {string} className The name of the class.
|
||||
* @param {Array} args The argument list to match.
|
||||
* @return {goog.testing.StrictMock|goog.testing.LooseMock} The mock found for
|
||||
* the given argument list.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClassFactory.prototype.findMockInstance_ = function(className,
|
||||
args) {
|
||||
return this.mockClassRecords_[className].findMockInstance(args);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create a proxy class. A proxy will pass functions to the mock for a class.
|
||||
* The proxy class only covers prototype methods. A static mock is not build
|
||||
* simultaneously since it might be strict or loose. The proxy class inherits
|
||||
* from the target class in order to preserve instanceof checks.
|
||||
* @param {Object} namespace A javascript namespace (e.g. goog.testing).
|
||||
* @param {Function} classToMock The class that will be proxied.
|
||||
* @param {string} className The name of the class.
|
||||
* @return {Function} The proxy for provided class.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClassFactory.prototype.createProxy_ = function(namespace,
|
||||
classToMock, className) {
|
||||
var proxy = this.getProxyCtor_(className,
|
||||
goog.bind(this.findMockInstance_, this));
|
||||
var protoToProxy = classToMock.prototype;
|
||||
goog.inherits(proxy, classToMock);
|
||||
|
||||
for (var prop in protoToProxy) {
|
||||
if (goog.isFunction(protoToProxy[prop])) {
|
||||
proxy.prototype[prop] = this.getProxyFunction_(prop);
|
||||
}
|
||||
}
|
||||
|
||||
// For IE the for-in-loop does not contain any properties that are not
|
||||
// enumerable on the prototype object (for example isPrototypeOf from
|
||||
// Object.prototype) and it will also not include 'replace' on objects that
|
||||
// extend String and change 'replace' (not that it is common for anyone to
|
||||
// extend anything except Object).
|
||||
// TODO (arv): Implement goog.object.getIterator and replace this loop.
|
||||
|
||||
goog.array.forEach(goog.testing.MockClassFactory.PROTOTYPE_FIELDS_,
|
||||
function(field) {
|
||||
if (Object.prototype.hasOwnProperty.call(protoToProxy, field)) {
|
||||
proxy.prototype[field] = this.getProxyFunction_(field);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.mockClassRecords_[className] = new goog.testing.MockClassRecord(
|
||||
namespace, className, classToMock, proxy);
|
||||
namespace[className] = proxy;
|
||||
return proxy;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets either a loose or strict mock for a given class based on a set of
|
||||
* arguments.
|
||||
* @param {Object} namespace A javascript namespace (e.g. goog.testing).
|
||||
* @param {Function} classToMock The class that will be mocked.
|
||||
* @param {boolean} isStrict Whether or not the mock should be strict.
|
||||
* @param {goog.array.ArrayLike} ctorArgs The arguments associated with this
|
||||
* instance's constructor.
|
||||
* @return {goog.testing.StrictMock|goog.testing.LooseMock} The mock created
|
||||
* for the provided class.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClassFactory.prototype.getMockClass_ =
|
||||
function(namespace, classToMock, isStrict, ctorArgs) {
|
||||
var className = this.getClassName_(namespace, classToMock);
|
||||
|
||||
// The namespace and classToMock variables should be removed from the
|
||||
// passed in argument stack.
|
||||
ctorArgs = goog.array.slice(ctorArgs, 2);
|
||||
|
||||
if (goog.isFunction(classToMock)) {
|
||||
var mock = isStrict ? new goog.testing.StrictMock(classToMock) :
|
||||
new goog.testing.LooseMock(classToMock);
|
||||
|
||||
if (!this.classHasMock_(className)) {
|
||||
this.createProxy_(namespace, classToMock, className);
|
||||
} else {
|
||||
var instance = this.findMockInstance_(className, ctorArgs);
|
||||
if (instance) {
|
||||
throw Error('Mock instance already created for ' + className +
|
||||
' with arguments ' + ctorArgs.join(', '));
|
||||
}
|
||||
}
|
||||
this.mockClassRecords_[className].addMockInstance(ctorArgs, mock);
|
||||
|
||||
return mock;
|
||||
} else {
|
||||
throw Error('Cannot create a mock class for ' + className +
|
||||
' of type ' + typeof classToMock);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets a strict mock for a given class.
|
||||
* @param {Object} namespace A javascript namespace (e.g. goog.testing).
|
||||
* @param {Function} classToMock The class that will be mocked.
|
||||
* @param {...*} var_args The arguments associated with this instance's
|
||||
* constructor.
|
||||
* @return {goog.testing.StrictMock} The mock created for the provided class.
|
||||
*/
|
||||
goog.testing.MockClassFactory.prototype.getStrictMockClass =
|
||||
function(namespace, classToMock, var_args) {
|
||||
return /** @type {goog.testing.StrictMock} */ (this.getMockClass_(namespace,
|
||||
classToMock, true, arguments));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets a loose mock for a given class.
|
||||
* @param {Object} namespace A javascript namespace (e.g. goog.testing).
|
||||
* @param {Function} classToMock The class that will be mocked.
|
||||
* @param {...*} var_args The arguments associated with this instance's
|
||||
* constructor.
|
||||
* @return {goog.testing.LooseMock} The mock created for the provided class.
|
||||
*/
|
||||
goog.testing.MockClassFactory.prototype.getLooseMockClass =
|
||||
function(namespace, classToMock, var_args) {
|
||||
return /** @type {goog.testing.LooseMock} */ (this.getMockClass_(namespace,
|
||||
classToMock, false, arguments));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates either a loose or strict mock for the static functions of a given
|
||||
* class.
|
||||
* @param {Function} classToMock The class whose static functions will be
|
||||
* mocked. This should be the original class and not the proxy.
|
||||
* @param {string} className The name of the class.
|
||||
* @param {Function} proxy The proxy that will replace the original class.
|
||||
* @param {boolean} isStrict Whether or not the mock should be strict.
|
||||
* @return {goog.testing.StrictMock|goog.testing.LooseMock} The mock created
|
||||
* for the static functions of the provided class.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClassFactory.prototype.createStaticMock_ =
|
||||
function(classToMock, className, proxy, isStrict) {
|
||||
var mock = isStrict ? new goog.testing.StrictMock(classToMock, true) :
|
||||
new goog.testing.LooseMock(classToMock, false, true);
|
||||
|
||||
for (var prop in classToMock) {
|
||||
if (goog.isFunction(classToMock[prop])) {
|
||||
proxy[prop] = goog.bind(mock.$mockMethod, mock, prop);
|
||||
} else if (classToMock[prop] !== classToMock.prototype) {
|
||||
proxy[prop] = classToMock[prop];
|
||||
}
|
||||
}
|
||||
|
||||
this.mockClassRecords_[className].setStaticMock(mock);
|
||||
return mock;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets either a loose or strict mock for the static functions of a given class.
|
||||
* @param {Object} namespace A javascript namespace (e.g. goog.testing).
|
||||
* @param {Function} classToMock The class whose static functions will be
|
||||
* mocked. This should be the original class and not the proxy.
|
||||
* @param {boolean} isStrict Whether or not the mock should be strict.
|
||||
* @return {goog.testing.StrictMock|goog.testing.LooseMock} The mock created
|
||||
* for the static functions of the provided class.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClassFactory.prototype.getStaticMock_ = function(namespace,
|
||||
classToMock, isStrict) {
|
||||
var className = this.getClassName_(namespace, classToMock);
|
||||
|
||||
if (goog.isFunction(classToMock)) {
|
||||
if (!this.classHasMock_(className)) {
|
||||
var proxy = this.createProxy_(namespace, classToMock, className);
|
||||
var mock = this.createStaticMock_(classToMock, className, proxy,
|
||||
isStrict);
|
||||
return mock;
|
||||
}
|
||||
|
||||
if (!this.mockClassRecords_[className].getStaticMock()) {
|
||||
var proxy = this.mockClassRecords_[className].getProxy();
|
||||
var originalClass = this.mockClassRecords_[className].getOriginalClass();
|
||||
var mock = this.createStaticMock_(originalClass, className, proxy,
|
||||
isStrict);
|
||||
return mock;
|
||||
} else {
|
||||
var mock = this.mockClassRecords_[className].getStaticMock();
|
||||
var mockIsStrict = mock instanceof goog.testing.StrictMock;
|
||||
|
||||
if (mockIsStrict != isStrict) {
|
||||
var mockType = mock instanceof goog.testing.StrictMock ? 'strict' :
|
||||
'loose';
|
||||
var requestedType = isStrict ? 'strict' : 'loose';
|
||||
throw Error('Requested a ' + requestedType + ' static mock, but a ' +
|
||||
mockType + ' mock already exists.');
|
||||
}
|
||||
|
||||
return mock;
|
||||
}
|
||||
} else {
|
||||
throw Error('Cannot create a mock for the static functions of ' +
|
||||
className + ' of type ' + typeof classToMock);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets a strict mock for the static functions of a given class.
|
||||
* @param {Object} namespace A javascript namespace (e.g. goog.testing).
|
||||
* @param {Function} classToMock The class whose static functions will be
|
||||
* mocked. This should be the original class and not the proxy.
|
||||
* @return {goog.testing.StrictMock} The mock created for the static functions
|
||||
* of the provided class.
|
||||
*/
|
||||
goog.testing.MockClassFactory.prototype.getStrictStaticMock =
|
||||
function(namespace, classToMock) {
|
||||
return /** @type {goog.testing.StrictMock} */ (this.getStaticMock_(namespace,
|
||||
classToMock, true));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets a loose mock for the static functions of a given class.
|
||||
* @param {Object} namespace A javascript namespace (e.g. goog.testing).
|
||||
* @param {Function} classToMock The class whose static functions will be
|
||||
* mocked. This should be the original class and not the proxy.
|
||||
* @return {goog.testing.LooseMock} The mock created for the static functions
|
||||
* of the provided class.
|
||||
*/
|
||||
goog.testing.MockClassFactory.prototype.getLooseStaticMock =
|
||||
function(namespace, classToMock) {
|
||||
return /** @type {goog.testing.LooseMock} */ (this.getStaticMock_(namespace,
|
||||
classToMock, false));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Resests the factory by reverting all mocked classes to their original
|
||||
* implementations and removing all MockClassRecords.
|
||||
*/
|
||||
goog.testing.MockClassFactory.prototype.reset = function() {
|
||||
goog.object.forEach(this.mockClassRecords_, function(record) {
|
||||
record.reset();
|
||||
});
|
||||
this.mockClassRecords_ = {};
|
||||
};
|
||||
520
float-no-zero/closure-library/closure/goog/testing/mockclock.js
Normal file
520
float-no-zero/closure-library/closure/goog/testing/mockclock.js
Normal file
@@ -0,0 +1,520 @@
|
||||
// Copyright 2007 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 Mock Clock implementation for working with setTimeout,
|
||||
* setInterval, clearTimeout and clearInterval within unit tests.
|
||||
*
|
||||
* Derived from jsUnitMockTimeout.js, contributed to JsUnit by
|
||||
* Pivotal Computer Systems, www.pivotalsf.com
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.MockClock');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.testing.PropertyReplacer');
|
||||
goog.require('goog.testing.events');
|
||||
goog.require('goog.testing.events.Event');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class for unit testing code that uses setTimeout and clearTimeout.
|
||||
*
|
||||
* NOTE: If you are using MockClock to test code that makes use of
|
||||
* goog.fx.Animation, then you must either:
|
||||
*
|
||||
* 1. Install and dispose of the MockClock in setUpPage() and tearDownPage()
|
||||
* respectively (rather than setUp()/tearDown()).
|
||||
*
|
||||
* or
|
||||
*
|
||||
* 2. Ensure that every test clears the animation queue by calling
|
||||
* mockClock.tick(x) at the end of each test function (where `x` is large
|
||||
* enough to complete all animations).
|
||||
*
|
||||
* Otherwise, if any animation is left pending at the time that
|
||||
* MockClock.dispose() is called, that will permanently prevent any future
|
||||
* animations from playing on the page.
|
||||
*
|
||||
* @param {boolean=} opt_autoInstall Install the MockClock at construction time.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
*/
|
||||
goog.testing.MockClock = function(opt_autoInstall) {
|
||||
goog.Disposable.call(this);
|
||||
|
||||
/**
|
||||
* Reverse-order queue of timers to fire.
|
||||
*
|
||||
* The last item of the queue is popped off. Insertion happens from the
|
||||
* right. For example, the expiration times for each element of the queue
|
||||
* might be in the order 300, 200, 200.
|
||||
*
|
||||
* @type {Array.<Object>}
|
||||
* @private
|
||||
*/
|
||||
this.queue_ = [];
|
||||
|
||||
/**
|
||||
* Set of timeouts that should be treated as cancelled.
|
||||
*
|
||||
* Rather than removing cancelled timers directly from the queue, this set
|
||||
* simply marks them as deleted so that they can be ignored when their
|
||||
* turn comes up. The keys are the timeout keys that are cancelled, each
|
||||
* mapping to true.
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.deletedKeys_ = {};
|
||||
|
||||
if (opt_autoInstall) {
|
||||
this.install();
|
||||
}
|
||||
};
|
||||
goog.inherits(goog.testing.MockClock, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* Default wait timeout for mocking requestAnimationFrame (in milliseconds).
|
||||
*
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT = 20;
|
||||
|
||||
|
||||
/**
|
||||
* Count of the number of timeouts made.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.timeoutsMade_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* PropertyReplacer instance which overwrites and resets setTimeout,
|
||||
* setInterval, etc. or null if the MockClock is not installed.
|
||||
* @type {goog.testing.PropertyReplacer}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.replacer_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Map of deleted keys. These keys represents keys that were deleted in a
|
||||
* clearInterval, timeoutid -> object.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.deletedKeys_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The current simulated time in milliseconds.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.nowMillis_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Additional delay between the time a timeout was set to fire, and the time
|
||||
* it actually fires. Useful for testing workarounds for this Firefox 2 bug:
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=291386
|
||||
* May be negative.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.timeoutDelay_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Installs the MockClock by overriding the global object's implementation of
|
||||
* setTimeout, setInterval, clearTimeout and clearInterval.
|
||||
*/
|
||||
goog.testing.MockClock.prototype.install = function() {
|
||||
if (!this.replacer_) {
|
||||
var r = this.replacer_ = new goog.testing.PropertyReplacer();
|
||||
r.set(goog.global, 'setTimeout', goog.bind(this.setTimeout_, this));
|
||||
r.set(goog.global, 'setInterval', goog.bind(this.setInterval_, this));
|
||||
r.set(goog.global, 'setImmediate', goog.bind(this.setImmediate_, this));
|
||||
r.set(goog.global, 'clearTimeout', goog.bind(this.clearTimeout_, this));
|
||||
r.set(goog.global, 'clearInterval', goog.bind(this.clearInterval_, this));
|
||||
|
||||
// Replace the requestAnimationFrame functions.
|
||||
this.replaceRequestAnimationFrame_();
|
||||
|
||||
// PropertyReplacer#set can't be called with renameable functions.
|
||||
this.oldGoogNow_ = goog.now;
|
||||
goog.now = goog.bind(this.getCurrentTime, this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Installs the mocks for requestAnimationFrame and cancelRequestAnimationFrame.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.replaceRequestAnimationFrame_ = function() {
|
||||
var r = this.replacer_;
|
||||
var requestFuncs = ['requestAnimationFrame',
|
||||
'webkitRequestAnimationFrame',
|
||||
'mozRequestAnimationFrame',
|
||||
'oRequestAnimationFrame',
|
||||
'msRequestAnimationFrame'];
|
||||
|
||||
var cancelFuncs = ['cancelRequestAnimationFrame',
|
||||
'webkitCancelRequestAnimationFrame',
|
||||
'mozCancelRequestAnimationFrame',
|
||||
'oCancelRequestAnimationFrame',
|
||||
'msCancelRequestAnimationFrame'];
|
||||
|
||||
for (var i = 0; i < requestFuncs.length; ++i) {
|
||||
if (goog.global && goog.global[requestFuncs[i]]) {
|
||||
r.set(goog.global, requestFuncs[i],
|
||||
goog.bind(this.requestAnimationFrame_, this));
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < cancelFuncs.length; ++i) {
|
||||
if (goog.global && goog.global[cancelFuncs[i]]) {
|
||||
r.set(goog.global, cancelFuncs[i],
|
||||
goog.bind(this.cancelRequestAnimationFrame_, this));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes the MockClock's hooks into the global object's functions and revert
|
||||
* to their original values.
|
||||
*/
|
||||
goog.testing.MockClock.prototype.uninstall = function() {
|
||||
if (this.replacer_) {
|
||||
this.replacer_.reset();
|
||||
this.replacer_ = null;
|
||||
goog.now = this.oldGoogNow_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.MockClock.prototype.disposeInternal = function() {
|
||||
this.uninstall();
|
||||
this.queue_ = null;
|
||||
this.deletedKeys_ = null;
|
||||
goog.testing.MockClock.superClass_.disposeInternal.call(this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Resets the MockClock, removing all timeouts that are scheduled and resets
|
||||
* the fake timer count.
|
||||
*/
|
||||
goog.testing.MockClock.prototype.reset = function() {
|
||||
this.queue_ = [];
|
||||
this.deletedKeys_ = {};
|
||||
this.nowMillis_ = 0;
|
||||
this.timeoutsMade_ = 0;
|
||||
this.timeoutDelay_ = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the amount of time between when a timeout is scheduled to fire and when
|
||||
* it actually fires.
|
||||
* @param {number} delay The delay in milliseconds. May be negative.
|
||||
*/
|
||||
goog.testing.MockClock.prototype.setTimeoutDelay = function(delay) {
|
||||
this.timeoutDelay_ = delay;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} delay The amount of time between when a timeout is
|
||||
* scheduled to fire and when it actually fires, in milliseconds. May
|
||||
* be negative.
|
||||
*/
|
||||
goog.testing.MockClock.prototype.getTimeoutDelay = function() {
|
||||
return this.timeoutDelay_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Increments the MockClock's time by a given number of milliseconds, running
|
||||
* any functions that are now overdue.
|
||||
* @param {number=} opt_millis Number of milliseconds to increment the counter.
|
||||
* If not specified, clock ticks 1 millisecond.
|
||||
* @return {number} Current mock time in milliseconds.
|
||||
*/
|
||||
goog.testing.MockClock.prototype.tick = function(opt_millis) {
|
||||
if (typeof opt_millis != 'number') {
|
||||
opt_millis = 1;
|
||||
}
|
||||
var endTime = this.nowMillis_ + opt_millis;
|
||||
this.runFunctionsWithinRange_(endTime);
|
||||
this.nowMillis_ = endTime;
|
||||
return endTime;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} The number of timeouts that have been scheduled.
|
||||
*/
|
||||
goog.testing.MockClock.prototype.getTimeoutsMade = function() {
|
||||
return this.timeoutsMade_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} The MockClock's current time in milliseconds.
|
||||
*/
|
||||
goog.testing.MockClock.prototype.getCurrentTime = function() {
|
||||
return this.nowMillis_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} timeoutKey The timeout key.
|
||||
* @return {boolean} Whether the timer has been set and not cleared,
|
||||
* independent of the timeout's expiration. In other words, the timeout
|
||||
* could have passed or could be scheduled for the future. Either way,
|
||||
* this function returns true or false depending only on whether the
|
||||
* provided timeoutKey represents a timeout that has been set and not
|
||||
* cleared.
|
||||
*/
|
||||
goog.testing.MockClock.prototype.isTimeoutSet = function(timeoutKey) {
|
||||
return timeoutKey <= this.timeoutsMade_ && !this.deletedKeys_[timeoutKey];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Runs any function that is scheduled before a certain time. Timeouts can
|
||||
* be made to fire early or late if timeoutDelay_ is non-0.
|
||||
* @param {number} endTime The latest time in the range, in milliseconds.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.runFunctionsWithinRange_ = function(
|
||||
endTime) {
|
||||
var adjustedEndTime = endTime - this.timeoutDelay_;
|
||||
|
||||
// Repeatedly pop off the last item since the queue is always sorted.
|
||||
while (this.queue_.length &&
|
||||
this.queue_[this.queue_.length - 1].runAtMillis <= adjustedEndTime) {
|
||||
var timeout = this.queue_.pop();
|
||||
|
||||
if (!(timeout.timeoutKey in this.deletedKeys_)) {
|
||||
// Only move time forwards.
|
||||
this.nowMillis_ = Math.max(this.nowMillis_,
|
||||
timeout.runAtMillis + this.timeoutDelay_);
|
||||
// Call timeout in global scope and pass the timeout key as the argument.
|
||||
timeout.funcToCall.call(goog.global, timeout.timeoutKey);
|
||||
// In case the interval was cleared in the funcToCall
|
||||
if (timeout.recurring) {
|
||||
this.scheduleFunction_(
|
||||
timeout.timeoutKey, timeout.funcToCall, timeout.millis, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Schedules a function to be run at a certain time.
|
||||
* @param {number} timeoutKey The timeout key.
|
||||
* @param {Function} funcToCall The function to call.
|
||||
* @param {number} millis The number of milliseconds to call it in.
|
||||
* @param {boolean} recurring Whether to function call should recur.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.scheduleFunction_ = function(
|
||||
timeoutKey, funcToCall, millis, recurring) {
|
||||
var timeout = {
|
||||
runAtMillis: this.nowMillis_ + millis,
|
||||
funcToCall: funcToCall,
|
||||
recurring: recurring,
|
||||
timeoutKey: timeoutKey,
|
||||
millis: millis
|
||||
};
|
||||
|
||||
goog.testing.MockClock.insert_(timeout, this.queue_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Inserts a timer descriptor into a descending-order queue.
|
||||
*
|
||||
* Later-inserted duplicates appear at lower indices. For example, the
|
||||
* asterisk in (5,4,*,3,2,1) would be the insertion point for 3.
|
||||
*
|
||||
* @param {Object} timeout The timeout to insert, with numerical runAtMillis
|
||||
* property.
|
||||
* @param {Array.<Object>} queue The queue to insert into, with each element
|
||||
* having a numerical runAtMillis property.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.insert_ = function(timeout, queue) {
|
||||
// Although insertion of N items is quadratic, requiring goog.structs.Heap
|
||||
// from a unit test will make tests more prone to breakage. Since unit
|
||||
// tests are normally small, scalability is not a primary issue.
|
||||
|
||||
// Find an insertion point. Since the queue is in reverse order (so we
|
||||
// can pop rather than unshift), and later timers with the same time stamp
|
||||
// should be executed later, we look for the element strictly greater than
|
||||
// the one we are inserting.
|
||||
|
||||
for (var i = queue.length; i != 0; i--) {
|
||||
if (queue[i - 1].runAtMillis > timeout.runAtMillis) {
|
||||
break;
|
||||
}
|
||||
queue[i] = queue[i - 1];
|
||||
}
|
||||
|
||||
queue[i] = timeout;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Maximum 32-bit signed integer.
|
||||
*
|
||||
* Timeouts over this time return immediately in many browsers, due to integer
|
||||
* overflow. Such known browsers include Firefox, Chrome, and Safari, but not
|
||||
* IE.
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.MAX_INT_ = 2147483647;
|
||||
|
||||
|
||||
/**
|
||||
* Schedules a function to be called after {@code millis} milliseconds.
|
||||
* Mock implementation for setTimeout.
|
||||
* @param {Function} funcToCall The function to call.
|
||||
* @param {number} millis The number of milliseconds to call it after.
|
||||
* @return {number} The number of timeouts created.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.setTimeout_ = function(funcToCall, millis) {
|
||||
if (millis > goog.testing.MockClock.MAX_INT_) {
|
||||
throw Error(
|
||||
'Bad timeout value: ' + millis + '. Timeouts over MAX_INT ' +
|
||||
'(24.8 days) cause timeouts to be fired ' +
|
||||
'immediately in most browsers, except for IE.');
|
||||
}
|
||||
this.timeoutsMade_ = this.timeoutsMade_ + 1;
|
||||
this.scheduleFunction_(this.timeoutsMade_, funcToCall, millis, false);
|
||||
return this.timeoutsMade_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Schedules a function to be called every {@code millis} milliseconds.
|
||||
* Mock implementation for setInterval.
|
||||
* @param {Function} funcToCall The function to call.
|
||||
* @param {number} millis The number of milliseconds between calls.
|
||||
* @return {number} The number of timeouts created.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.setInterval_ = function(funcToCall, millis) {
|
||||
this.timeoutsMade_ = this.timeoutsMade_ + 1;
|
||||
this.scheduleFunction_(this.timeoutsMade_, funcToCall, millis, true);
|
||||
return this.timeoutsMade_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Schedules a function to be called when an animation frame is triggered.
|
||||
* Mock implementation for requestAnimationFrame.
|
||||
* @param {Function} funcToCall The function to call.
|
||||
* @return {number} The number of timeouts created.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.requestAnimationFrame_ = function(funcToCall) {
|
||||
return this.setTimeout_(goog.bind(function() {
|
||||
if (funcToCall) {
|
||||
funcToCall(this.getCurrentTime());
|
||||
} else if (goog.global.mozRequestAnimationFrame) {
|
||||
var event = new goog.testing.events.Event('MozBeforePaint', goog.global);
|
||||
event['timeStamp'] = this.getCurrentTime();
|
||||
goog.testing.events.fireBrowserEvent(event);
|
||||
}
|
||||
}, this), goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Schedules a function to be called immediately after the current JS
|
||||
* execution.
|
||||
* Mock implementation for setImmediate.
|
||||
* @param {Function} funcToCall The function to call.
|
||||
* @return {number} The number of timeouts created.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.setImmediate_ = function(funcToCall) {
|
||||
return this.setTimeout_(funcToCall, 0);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears a timeout.
|
||||
* Mock implementation for clearTimeout.
|
||||
* @param {number} timeoutKey The timeout key to clear.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.clearTimeout_ = function(timeoutKey) {
|
||||
// Some common libraries register static state with timers.
|
||||
// This is bad. It leads to all sorts of crazy test problems where
|
||||
// 1) Test A sets up a new mock clock and a static timer.
|
||||
// 2) Test B sets up a new mock clock, but re-uses the static timer
|
||||
// from Test A.
|
||||
// 3) A timeout key from test A gets cleared, breaking a timeout in
|
||||
// Test B.
|
||||
//
|
||||
// For now, we just hackily fail silently if someone tries to clear a timeout
|
||||
// key before we've allocated it.
|
||||
// Ideally, we should throw an exception if we see this happening.
|
||||
//
|
||||
// TODO(user): We might also try allocating timeout ids from a global
|
||||
// pool rather than a local pool.
|
||||
if (this.isTimeoutSet(timeoutKey)) {
|
||||
this.deletedKeys_[timeoutKey] = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears an interval.
|
||||
* Mock implementation for clearInterval.
|
||||
* @param {number} timeoutKey The interval key to clear.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.clearInterval_ = function(timeoutKey) {
|
||||
this.clearTimeout_(timeoutKey);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears a requestAnimationFrame.
|
||||
* Mock implementation for cancelRequestAnimationFrame.
|
||||
* @param {number} timeoutKey The requestAnimationFrame key to clear.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockClock.prototype.cancelRequestAnimationFrame_ =
|
||||
function(timeoutKey) {
|
||||
this.clearTimeout_(timeoutKey);
|
||||
};
|
||||
@@ -0,0 +1,219 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview A MockControl holds a set of mocks for a particular test.
|
||||
* It consolidates calls to $replay, $verify, and $tearDown, which simplifies
|
||||
* the test and helps avoid omissions.
|
||||
*
|
||||
* You can create and control a mock:
|
||||
* var mockFoo = mockControl.addMock(new MyMock(Foo));
|
||||
*
|
||||
* MockControl also exposes some convenience functions that create
|
||||
* controlled mocks for common mocks: StrictMock, LooseMock,
|
||||
* FunctionMock, MethodMock, and GlobalFunctionMock.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.testing.MockControl');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.testing');
|
||||
goog.require('goog.testing.LooseMock');
|
||||
goog.require('goog.testing.MockInterface');
|
||||
goog.require('goog.testing.StrictMock');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Controls a set of mocks. Controlled mocks are replayed, verified, and
|
||||
* cleaned-up at the same time.
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.MockControl = function() {
|
||||
/**
|
||||
* The list of mocks being controlled.
|
||||
* @type {Array.<goog.testing.MockInterface>}
|
||||
* @private
|
||||
*/
|
||||
this.mocks_ = [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes control of this mock.
|
||||
* @param {goog.testing.MockInterface} mock Mock to be controlled.
|
||||
* @return {goog.testing.MockInterface} The same mock passed in,
|
||||
* for convenience.
|
||||
*/
|
||||
goog.testing.MockControl.prototype.addMock = function(mock) {
|
||||
this.mocks_.push(mock);
|
||||
return mock;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls replay on each controlled mock.
|
||||
*/
|
||||
goog.testing.MockControl.prototype.$replayAll = function() {
|
||||
goog.array.forEach(this.mocks_, function(m) {
|
||||
m.$replay();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls reset on each controlled mock.
|
||||
*/
|
||||
goog.testing.MockControl.prototype.$resetAll = function() {
|
||||
goog.array.forEach(this.mocks_, function(m) {
|
||||
m.$reset();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls verify on each controlled mock.
|
||||
*/
|
||||
goog.testing.MockControl.prototype.$verifyAll = function() {
|
||||
goog.array.forEach(this.mocks_, function(m) {
|
||||
m.$verify();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls tearDown on each controlled mock, if necesssary.
|
||||
*/
|
||||
goog.testing.MockControl.prototype.$tearDown = function() {
|
||||
goog.array.forEach(this.mocks_, function(m) {
|
||||
// $tearDown if defined.
|
||||
if (m.$tearDown) {
|
||||
m.$tearDown();
|
||||
}
|
||||
// TODO(user): Somehow determine if verifyAll should have been called
|
||||
// but was not.
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a controlled StrictMock. Passes its arguments through to the
|
||||
* StrictMock constructor.
|
||||
* @param {Object} objectToMock The object to mock.
|
||||
* @param {boolean=} opt_mockStaticMethods An optional argument denoting that
|
||||
* a mock should be constructed from the static functions of a class.
|
||||
* @param {boolean=} opt_createProxy An optional argument denoting that
|
||||
* a proxy for the target mock should be created.
|
||||
* @return {!goog.testing.StrictMock} The mock object.
|
||||
*/
|
||||
goog.testing.MockControl.prototype.createStrictMock = function(
|
||||
objectToMock, opt_mockStaticMethods, opt_createProxy) {
|
||||
var m = new goog.testing.StrictMock(objectToMock, opt_mockStaticMethods,
|
||||
opt_createProxy);
|
||||
this.addMock(m);
|
||||
return m;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a controlled LooseMock. Passes its arguments through to the
|
||||
* LooseMock constructor.
|
||||
* @param {Object} objectToMock The object to mock.
|
||||
* @param {boolean=} opt_ignoreUnexpectedCalls Whether to ignore unexpected
|
||||
* calls.
|
||||
* @param {boolean=} opt_mockStaticMethods An optional argument denoting that
|
||||
* a mock should be constructed from the static functions of a class.
|
||||
* @param {boolean=} opt_createProxy An optional argument denoting that
|
||||
* a proxy for the target mock should be created.
|
||||
* @return {!goog.testing.LooseMock} The mock object.
|
||||
*/
|
||||
goog.testing.MockControl.prototype.createLooseMock = function(
|
||||
objectToMock, opt_ignoreUnexpectedCalls,
|
||||
opt_mockStaticMethods, opt_createProxy) {
|
||||
var m = new goog.testing.LooseMock(objectToMock, opt_ignoreUnexpectedCalls,
|
||||
opt_mockStaticMethods, opt_createProxy);
|
||||
this.addMock(m);
|
||||
return m;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a controlled FunctionMock. Passes its arguments through to the
|
||||
* FunctionMock constructor.
|
||||
* @param {string=} opt_functionName The optional name of the function to mock
|
||||
* set to '[anonymous mocked function]' if not passed in.
|
||||
* @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
|
||||
* goog.testing.Mock.STRICT. The default is STRICT.
|
||||
* @return {goog.testing.MockInterface} The mocked function.
|
||||
*/
|
||||
goog.testing.MockControl.prototype.createFunctionMock = function(
|
||||
opt_functionName, opt_strictness) {
|
||||
var m = goog.testing.createFunctionMock(opt_functionName, opt_strictness);
|
||||
this.addMock(m);
|
||||
return m;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a controlled MethodMock. Passes its arguments through to the
|
||||
* MethodMock constructor.
|
||||
* @param {Object} scope The scope of the method to be mocked out.
|
||||
* @param {string} functionName The name of the function we're going to mock.
|
||||
* @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
|
||||
* goog.testing.Mock.STRICT. The default is STRICT.
|
||||
* @return {goog.testing.MockInterface} The mocked method.
|
||||
*/
|
||||
goog.testing.MockControl.prototype.createMethodMock = function(
|
||||
scope, functionName, opt_strictness) {
|
||||
var m = goog.testing.createMethodMock(scope, functionName, opt_strictness);
|
||||
this.addMock(m);
|
||||
return m;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a controlled MethodMock for a constructor. Passes its arguments
|
||||
* through to the MethodMock constructor. See
|
||||
* {@link goog.testing.createConstructorMock} for details.
|
||||
* @param {Object} scope The scope of the constructor to be mocked out.
|
||||
* @param {string} constructorName The name of the function we're going to mock.
|
||||
* @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
|
||||
* goog.testing.Mock.STRICT. The default is STRICT.
|
||||
* @return {goog.testing.MockInterface} The mocked method.
|
||||
*/
|
||||
goog.testing.MockControl.prototype.createConstructorMock = function(
|
||||
scope, constructorName, opt_strictness) {
|
||||
var m = goog.testing.createConstructorMock(scope, constructorName,
|
||||
opt_strictness);
|
||||
this.addMock(m);
|
||||
return m;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a controlled GlobalFunctionMock. Passes its arguments through to the
|
||||
* GlobalFunctionMock constructor.
|
||||
* @param {string} functionName The name of the function we're going to mock.
|
||||
* @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
|
||||
* goog.testing.Mock.STRICT. The default is STRICT.
|
||||
* @return {goog.testing.MockInterface} The mocked function.
|
||||
*/
|
||||
goog.testing.MockControl.prototype.createGlobalFunctionMock = function(
|
||||
functionName, opt_strictness) {
|
||||
var m = goog.testing.createGlobalFunctionMock(functionName, opt_strictness);
|
||||
this.addMock(m);
|
||||
return m;
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2010 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 An interface that all mocks should share.
|
||||
* @author nicksantos@google.com (Nick Santos)
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.MockInterface');
|
||||
|
||||
|
||||
|
||||
/** @interface */
|
||||
goog.testing.MockInterface = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Write down all the expected functions that have been called on the
|
||||
* mock so far. From here on out, future function calls will be
|
||||
* compared against this list.
|
||||
*/
|
||||
goog.testing.MockInterface.prototype.$replay = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Reset the mock.
|
||||
*/
|
||||
goog.testing.MockInterface.prototype.$reset = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Assert that the expected function calls match the actual calls.
|
||||
*/
|
||||
goog.testing.MockInterface.prototype.$verify = function() {};
|
||||
@@ -0,0 +1,395 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview Matchers to be used with the mock utilities. They allow for
|
||||
* flexible matching by type. Custom matchers can be created by passing a
|
||||
* matcher function into an ArgumentMatcher instance.
|
||||
*
|
||||
* For examples, please see the unit test.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.testing.mockmatchers');
|
||||
goog.provide('goog.testing.mockmatchers.ArgumentMatcher');
|
||||
goog.provide('goog.testing.mockmatchers.IgnoreArgument');
|
||||
goog.provide('goog.testing.mockmatchers.InstanceOf');
|
||||
goog.provide('goog.testing.mockmatchers.ObjectEquals');
|
||||
goog.provide('goog.testing.mockmatchers.RegexpMatch');
|
||||
goog.provide('goog.testing.mockmatchers.SaveArgument');
|
||||
goog.provide('goog.testing.mockmatchers.TypeOf');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.testing.asserts');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A simple interface for executing argument matching. A match in this case is
|
||||
* testing to see if a supplied object fits a given criteria. True is returned
|
||||
* if the given criteria is met.
|
||||
* @param {Function=} opt_matchFn A function that evaluates a given argument
|
||||
* and returns true if it meets a given criteria.
|
||||
* @param {?string=} opt_matchName The name expressing intent as part of
|
||||
* an error message for when a match fails.
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.mockmatchers.ArgumentMatcher =
|
||||
function(opt_matchFn, opt_matchName) {
|
||||
/**
|
||||
* A function that evaluates a given argument and returns true if it meets a
|
||||
* given criteria.
|
||||
* @type {Function}
|
||||
* @private
|
||||
*/
|
||||
this.matchFn_ = opt_matchFn || null;
|
||||
|
||||
/**
|
||||
* A string indicating the match intent (e.g. isBoolean or isString).
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
this.matchName_ = opt_matchName || null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A function that takes a match argument and an optional MockExpectation
|
||||
* which (if provided) will get error information and returns whether or
|
||||
* not it matches.
|
||||
* @param {*} toVerify The argument that should be verified.
|
||||
* @param {goog.testing.MockExpectation?=} opt_expectation The expectation
|
||||
* for this match.
|
||||
* @return {boolean} Whether or not a given argument passes verification.
|
||||
*/
|
||||
goog.testing.mockmatchers.ArgumentMatcher.prototype.matches =
|
||||
function(toVerify, opt_expectation) {
|
||||
if (this.matchFn_) {
|
||||
var isamatch = this.matchFn_(toVerify);
|
||||
if (!isamatch && opt_expectation) {
|
||||
if (this.matchName_) {
|
||||
opt_expectation.addErrorMessage('Expected: ' +
|
||||
this.matchName_ + ' but was: ' + _displayStringForValue(toVerify));
|
||||
} else {
|
||||
opt_expectation.addErrorMessage('Expected: missing mockmatcher' +
|
||||
' description but was: ' +
|
||||
_displayStringForValue(toVerify));
|
||||
}
|
||||
}
|
||||
return isamatch;
|
||||
} else {
|
||||
throw Error('No match function defined for this mock matcher');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that verifies that an argument is an instance of a given class.
|
||||
* @param {Function} ctor The class that will be used for verification.
|
||||
* @constructor
|
||||
* @extends {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.InstanceOf = function(ctor) {
|
||||
goog.testing.mockmatchers.ArgumentMatcher.call(this,
|
||||
function(obj) {
|
||||
return obj instanceof ctor;
|
||||
// NOTE: Browser differences on ctor.toString() output
|
||||
// make using that here problematic. So for now, just let
|
||||
// people know the instanceOf() failed without providing
|
||||
// browser specific details...
|
||||
}, 'instanceOf()');
|
||||
};
|
||||
goog.inherits(goog.testing.mockmatchers.InstanceOf,
|
||||
goog.testing.mockmatchers.ArgumentMatcher);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that verifies that an argument is of a given type (e.g. "object").
|
||||
* @param {string} type The type that a given argument must have.
|
||||
* @constructor
|
||||
* @extends {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.TypeOf = function(type) {
|
||||
goog.testing.mockmatchers.ArgumentMatcher.call(this,
|
||||
function(obj) {
|
||||
return goog.typeOf(obj) == type;
|
||||
}, 'typeOf(' + type + ')');
|
||||
};
|
||||
goog.inherits(goog.testing.mockmatchers.TypeOf,
|
||||
goog.testing.mockmatchers.ArgumentMatcher);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that verifies that an argument matches a given RegExp.
|
||||
* @param {RegExp} regexp The regular expression that the argument must match.
|
||||
* @constructor
|
||||
* @extends {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.RegexpMatch = function(regexp) {
|
||||
goog.testing.mockmatchers.ArgumentMatcher.call(this,
|
||||
function(str) {
|
||||
return regexp.test(str);
|
||||
}, 'match(' + regexp + ')');
|
||||
};
|
||||
goog.inherits(goog.testing.mockmatchers.RegexpMatch,
|
||||
goog.testing.mockmatchers.ArgumentMatcher);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that always returns true. It is useful when the user does not care
|
||||
* for some arguments.
|
||||
* For example: mockFunction('username', 'password', IgnoreArgument);
|
||||
* @constructor
|
||||
* @extends {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.IgnoreArgument = function() {
|
||||
goog.testing.mockmatchers.ArgumentMatcher.call(this,
|
||||
function() {
|
||||
return true;
|
||||
}, 'true');
|
||||
};
|
||||
goog.inherits(goog.testing.mockmatchers.IgnoreArgument,
|
||||
goog.testing.mockmatchers.ArgumentMatcher);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that verifies that the argument is an object that equals the given
|
||||
* expected object, using a deep comparison.
|
||||
* @param {Object} expectedObject An object to match against when
|
||||
* verifying the argument.
|
||||
* @constructor
|
||||
* @extends {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.ObjectEquals = function(expectedObject) {
|
||||
goog.testing.mockmatchers.ArgumentMatcher.call(this,
|
||||
function(matchObject) {
|
||||
assertObjectEquals('Expected equal objects', expectedObject,
|
||||
matchObject);
|
||||
return true;
|
||||
}, 'objectEquals(' + expectedObject + ')');
|
||||
};
|
||||
goog.inherits(goog.testing.mockmatchers.ObjectEquals,
|
||||
goog.testing.mockmatchers.ArgumentMatcher);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.mockmatchers.ObjectEquals.prototype.matches =
|
||||
function(toVerify, opt_expectation) {
|
||||
// Override the default matches implementation to capture the exception thrown
|
||||
// by assertObjectEquals (if any) and add that message to the expectation.
|
||||
try {
|
||||
return goog.testing.mockmatchers.ObjectEquals.superClass_.matches.call(
|
||||
this, toVerify, opt_expectation);
|
||||
} catch (e) {
|
||||
if (opt_expectation) {
|
||||
opt_expectation.addErrorMessage(e.message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that saves the argument that it is verifying so that your unit test
|
||||
* can perform extra tests with this argument later. For example, if the
|
||||
* argument is a callback method, the unit test can then later call this
|
||||
* callback to test the asynchronous portion of the call.
|
||||
* @param {goog.testing.mockmatchers.ArgumentMatcher|Function=} opt_matcher
|
||||
* Argument matcher or matching function that will be used to validate the
|
||||
* argument. By default, argument will always be valid.
|
||||
* @param {?string=} opt_matchName The name expressing intent as part of
|
||||
* an error message for when a match fails.
|
||||
* @constructor
|
||||
* @extends {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.SaveArgument = function(opt_matcher, opt_matchName) {
|
||||
goog.testing.mockmatchers.ArgumentMatcher.call(
|
||||
this, /** @type {Function} */ (opt_matcher), opt_matchName);
|
||||
|
||||
if (opt_matcher instanceof goog.testing.mockmatchers.ArgumentMatcher) {
|
||||
/**
|
||||
* Delegate match requests to this matcher.
|
||||
* @type {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
* @private
|
||||
*/
|
||||
this.delegateMatcher_ = opt_matcher;
|
||||
} else if (!opt_matcher) {
|
||||
this.delegateMatcher_ = goog.testing.mockmatchers.ignoreArgument;
|
||||
}
|
||||
};
|
||||
goog.inherits(goog.testing.mockmatchers.SaveArgument,
|
||||
goog.testing.mockmatchers.ArgumentMatcher);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.mockmatchers.SaveArgument.prototype.matches = function(
|
||||
toVerify, opt_expectation) {
|
||||
this.arg = toVerify;
|
||||
if (this.delegateMatcher_) {
|
||||
return this.delegateMatcher_.matches(toVerify, opt_expectation);
|
||||
}
|
||||
return goog.testing.mockmatchers.SaveArgument.superClass_.matches.call(
|
||||
this, toVerify, opt_expectation);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Saved argument that was verified.
|
||||
* @type {*}
|
||||
*/
|
||||
goog.testing.mockmatchers.SaveArgument.prototype.arg;
|
||||
|
||||
|
||||
/**
|
||||
* An instance of the IgnoreArgument matcher. Returns true for all matches.
|
||||
* @type {goog.testing.mockmatchers.IgnoreArgument}
|
||||
*/
|
||||
goog.testing.mockmatchers.ignoreArgument =
|
||||
new goog.testing.mockmatchers.IgnoreArgument();
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that verifies that an argument is an array.
|
||||
* @type {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.isArray =
|
||||
new goog.testing.mockmatchers.ArgumentMatcher(goog.isArray,
|
||||
'isArray');
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that verifies that an argument is a array-like. A NodeList is an
|
||||
* example of a collection that is very close to an array.
|
||||
* @type {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.isArrayLike =
|
||||
new goog.testing.mockmatchers.ArgumentMatcher(goog.isArrayLike,
|
||||
'isArrayLike');
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that verifies that an argument is a date-like.
|
||||
* @type {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.isDateLike =
|
||||
new goog.testing.mockmatchers.ArgumentMatcher(goog.isDateLike,
|
||||
'isDateLike');
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that verifies that an argument is a string.
|
||||
* @type {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.isString =
|
||||
new goog.testing.mockmatchers.ArgumentMatcher(goog.isString,
|
||||
'isString');
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that verifies that an argument is a boolean.
|
||||
* @type {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.isBoolean =
|
||||
new goog.testing.mockmatchers.ArgumentMatcher(goog.isBoolean,
|
||||
'isBoolean');
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that verifies that an argument is a number.
|
||||
* @type {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.isNumber =
|
||||
new goog.testing.mockmatchers.ArgumentMatcher(goog.isNumber,
|
||||
'isNumber');
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that verifies that an argument is a function.
|
||||
* @type {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.isFunction =
|
||||
new goog.testing.mockmatchers.ArgumentMatcher(goog.isFunction,
|
||||
'isFunction');
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that verifies that an argument is an object.
|
||||
* @type {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.isObject =
|
||||
new goog.testing.mockmatchers.ArgumentMatcher(goog.isObject,
|
||||
'isObject');
|
||||
|
||||
|
||||
/**
|
||||
* A matcher that verifies that an argument is like a DOM node.
|
||||
* @type {goog.testing.mockmatchers.ArgumentMatcher}
|
||||
*/
|
||||
goog.testing.mockmatchers.isNodeLike =
|
||||
new goog.testing.mockmatchers.ArgumentMatcher(goog.dom.isNodeLike,
|
||||
'isNodeLike');
|
||||
|
||||
|
||||
/**
|
||||
* A function that checks to see if an array matches a given set of
|
||||
* expectations. The expectations array can be a mix of ArgumentMatcher
|
||||
* implementations and values. True will be returned if values are identical or
|
||||
* if a matcher returns a positive result.
|
||||
* @param {Array} expectedArr An array of expectations which can be either
|
||||
* values to check for equality or ArgumentMatchers.
|
||||
* @param {Array} arr The array to match.
|
||||
* @param {goog.testing.MockExpectation?=} opt_expectation The expectation
|
||||
* for this match.
|
||||
* @return {boolean} Whether or not the given array matches the expectations.
|
||||
*/
|
||||
goog.testing.mockmatchers.flexibleArrayMatcher =
|
||||
function(expectedArr, arr, opt_expectation) {
|
||||
return goog.array.equals(expectedArr, arr, function(a, b) {
|
||||
var errCount = 0;
|
||||
if (opt_expectation) {
|
||||
errCount = opt_expectation.getErrorMessageCount();
|
||||
}
|
||||
var isamatch = a === b ||
|
||||
a instanceof goog.testing.mockmatchers.ArgumentMatcher &&
|
||||
a.matches(b, opt_expectation);
|
||||
var failureMessage = null;
|
||||
if (!isamatch) {
|
||||
failureMessage = goog.testing.asserts.findDifferences(a, b);
|
||||
isamatch = !failureMessage;
|
||||
}
|
||||
if (!isamatch && opt_expectation) {
|
||||
// If the error count changed, the match sent out an error
|
||||
// message. If the error count has not changed, then
|
||||
// we need to send out an error message...
|
||||
if (errCount == opt_expectation.getErrorMessageCount()) {
|
||||
// Use the _displayStringForValue() from assert.js
|
||||
// for consistency...
|
||||
if (!failureMessage) {
|
||||
failureMessage = 'Expected: ' + _displayStringForValue(a) +
|
||||
' but was: ' + _displayStringForValue(b);
|
||||
}
|
||||
opt_expectation.addErrorMessage(failureMessage);
|
||||
}
|
||||
}
|
||||
return isamatch;
|
||||
});
|
||||
};
|
||||
126
float-no-zero/closure-library/closure/goog/testing/mockrandom.js
Normal file
126
float-no-zero/closure-library/closure/goog/testing/mockrandom.js
Normal file
@@ -0,0 +1,126 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview MockRandom provides a mechanism for specifying a stream of
|
||||
* numbers to expect from calls to Math.random().
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.MockRandom');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class for unit testing code that uses Math.random.
|
||||
*
|
||||
* @param {Array.<number>} sequence The sequence of numbers to return.
|
||||
* @param {boolean=} opt_install Whether to install the MockRandom at
|
||||
* construction time.
|
||||
* @extends {goog.Disposable}
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.MockRandom = function(sequence, opt_install) {
|
||||
goog.Disposable.call(this);
|
||||
|
||||
/**
|
||||
* The sequence of numbers to be returned by calls to random()
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.sequence_ = sequence || [];
|
||||
|
||||
/**
|
||||
* The original Math.random function.
|
||||
* @type {function(): number}
|
||||
* @private
|
||||
*/
|
||||
this.mathRandom_ = Math.random;
|
||||
|
||||
if (opt_install) {
|
||||
this.install();
|
||||
}
|
||||
};
|
||||
goog.inherits(goog.testing.MockRandom, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* Whether this MockRandom has been installed.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockRandom.prototype.installed_;
|
||||
|
||||
|
||||
/**
|
||||
* Installs this MockRandom as the system number generator.
|
||||
*/
|
||||
goog.testing.MockRandom.prototype.install = function() {
|
||||
if (!this.installed_) {
|
||||
Math.random = goog.bind(this.random, this);
|
||||
this.installed_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} The next number in the sequence. If there are no more values
|
||||
* left, this will return a random number.
|
||||
*/
|
||||
goog.testing.MockRandom.prototype.random = function() {
|
||||
return this.hasMoreValues() ? this.sequence_.shift() : this.mathRandom_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether there are more numbers left in the sequence.
|
||||
*/
|
||||
goog.testing.MockRandom.prototype.hasMoreValues = function() {
|
||||
return this.sequence_.length > 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Injects new numbers into the beginning of the sequence.
|
||||
* @param {Array.<number>|number} values Number or array of numbers to inject.
|
||||
*/
|
||||
goog.testing.MockRandom.prototype.inject = function(values) {
|
||||
if (goog.isArray(values)) {
|
||||
this.sequence_ = values.concat(this.sequence_);
|
||||
} else {
|
||||
this.sequence_.splice(0, 0, values);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Uninstalls the MockRandom.
|
||||
*/
|
||||
goog.testing.MockRandom.prototype.uninstall = function() {
|
||||
if (this.installed_) {
|
||||
Math.random = this.mathRandom_;
|
||||
this.installed_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.MockRandom.prototype.disposeInternal = function() {
|
||||
this.uninstall();
|
||||
delete this.sequence_;
|
||||
delete this.mathRandom_;
|
||||
goog.testing.MockRandom.superClass_.disposeInternal.call(this);
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview LooseMock of goog.dom.AbstractRange.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.MockRange');
|
||||
|
||||
goog.require('goog.dom.AbstractRange');
|
||||
goog.require('goog.testing.LooseMock');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* LooseMock of goog.dom.AbstractRange. Useful because the mock framework cannot
|
||||
* simply create a mock out of an abstract class, and cannot create a mock out
|
||||
* of classes that implements __iterator__ because it relies on the default
|
||||
* behavior of iterating through all of an object's properties.
|
||||
* @constructor
|
||||
* @extends {goog.testing.LooseMock}
|
||||
*/
|
||||
goog.testing.MockRange = function() {
|
||||
goog.testing.LooseMock.call(this, goog.testing.MockRange.ConcreteRange_);
|
||||
};
|
||||
goog.inherits(goog.testing.MockRange, goog.testing.LooseMock);
|
||||
|
||||
|
||||
// *** Private helper class ************************************************* //
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Concrete subclass of goog.dom.AbstractRange that simply sets the abstract
|
||||
* method __iterator__ to undefined so that javascript defaults to iterating
|
||||
* through all of the object's properties.
|
||||
* @constructor
|
||||
* @extends {goog.dom.AbstractRange}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockRange.ConcreteRange_ = function() {
|
||||
goog.dom.AbstractRange.call(this);
|
||||
};
|
||||
goog.inherits(goog.testing.MockRange.ConcreteRange_, goog.dom.AbstractRange);
|
||||
|
||||
|
||||
/**
|
||||
* Undefine the iterator so the mock framework can loop through this class'
|
||||
* properties.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.MockRange.ConcreteRange_.prototype.__iterator__ =
|
||||
// This isn't really type-safe.
|
||||
/** @type {?} */ (undefined);
|
||||
@@ -0,0 +1,107 @@
|
||||
// Copyright 2011 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 Provides a JS storage class implementing the HTML5 Storage
|
||||
* interface.
|
||||
*/
|
||||
|
||||
|
||||
goog.require('goog.structs.Map');
|
||||
|
||||
|
||||
goog.provide('goog.testing.MockStorage');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A JS storage instance, implementing the HMTL5 Storage interface.
|
||||
* See http://www.w3.org/TR/webstorage/ for details.
|
||||
*
|
||||
* @constructor
|
||||
* @implements {Storage}
|
||||
*/
|
||||
goog.testing.MockStorage = function() {
|
||||
/**
|
||||
* The underlying storage object.
|
||||
* @type {goog.structs.Map}
|
||||
* @private
|
||||
*/
|
||||
this.store_ = new goog.structs.Map();
|
||||
|
||||
/**
|
||||
* The number of elements in the storage.
|
||||
* @type {number}
|
||||
*/
|
||||
this.length = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets an item to the storage.
|
||||
* @param {string} key Storage key.
|
||||
* @param {*} value Storage value. Must be convertible to string.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.MockStorage.prototype.setItem = function(key, value) {
|
||||
this.store_.set(key, String(value));
|
||||
this.length = this.store_.getCount();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets an item from the storage. The item returned is the "structured clone"
|
||||
* of the value from setItem. In practice this means it's the value cast to a
|
||||
* string.
|
||||
* @param {string} key Storage key.
|
||||
* @return {?string} Storage value for key; null if does not exist.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.MockStorage.prototype.getItem = function(key) {
|
||||
var val = this.store_.get(key);
|
||||
// Enforce that getItem returns string values.
|
||||
return (val != null) ? /** @type {string} */ (val) : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes and item from the storage.
|
||||
* @param {string} key Storage key.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.MockStorage.prototype.removeItem = function(key) {
|
||||
this.store_.remove(key);
|
||||
this.length = this.store_.getCount();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the storage.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.MockStorage.prototype.clear = function() {
|
||||
this.store_.clear();
|
||||
this.length = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the key at the given index.
|
||||
* @param {number} index The index for the key.
|
||||
* @return {?string} Key at the given index, null if not found.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.MockStorage.prototype.key = function(index) {
|
||||
return this.store_.getKeys()[index] || null;
|
||||
};
|
||||
@@ -0,0 +1,141 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview MockUserAgent overrides goog.userAgent.getUserAgentString()
|
||||
* depending on a specified configuration.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.MockUserAgent');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class for unit testing code that uses goog.userAgent.
|
||||
*
|
||||
* @extends {goog.Disposable}
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.MockUserAgent = function() {
|
||||
goog.Disposable.call(this);
|
||||
|
||||
/**
|
||||
* The userAgent string used by goog.userAgent.
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
this.userAgent_ = goog.userAgent.getUserAgentString();
|
||||
|
||||
/**
|
||||
* The original goog.userAgent.getUserAgentString function.
|
||||
* @type {function():?string}
|
||||
* @private
|
||||
*/
|
||||
this.originalUserAgentFunction_ = goog.userAgent.getUserAgentString;
|
||||
|
||||
/**
|
||||
* The navigator object used by goog.userAgent
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.navigator_ = goog.userAgent.getNavigator();
|
||||
|
||||
/**
|
||||
* The original goog.userAgent.getNavigator function
|
||||
* @type {function():Object}
|
||||
* @private
|
||||
*/
|
||||
this.originalNavigatorFunction_ = goog.userAgent.getNavigator;
|
||||
};
|
||||
goog.inherits(goog.testing.MockUserAgent, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* Whether this MockUserAgent has been installed.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.MockUserAgent.prototype.installed_;
|
||||
|
||||
|
||||
/**
|
||||
* Installs this MockUserAgent.
|
||||
*/
|
||||
goog.testing.MockUserAgent.prototype.install = function() {
|
||||
if (!this.installed_) {
|
||||
goog.userAgent.getUserAgentString =
|
||||
goog.bind(this.getUserAgentString, this);
|
||||
goog.userAgent.getNavigator = goog.bind(this.getNavigator, this);
|
||||
this.installed_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {?string} The userAgent set in this class.
|
||||
*/
|
||||
goog.testing.MockUserAgent.prototype.getUserAgentString = function() {
|
||||
return this.userAgent_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} userAgent The desired userAgent string to use.
|
||||
*/
|
||||
goog.testing.MockUserAgent.prototype.setUserAgentString = function(userAgent) {
|
||||
this.userAgent_ = userAgent;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Object} The Navigator set in this class.
|
||||
*/
|
||||
goog.testing.MockUserAgent.prototype.getNavigator = function() {
|
||||
return this.navigator_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Object} navigator The desired Navigator object to use.
|
||||
*/
|
||||
goog.testing.MockUserAgent.prototype.setNavigator = function(navigator) {
|
||||
this.navigator_ = navigator;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Uninstalls the MockUserAgent.
|
||||
*/
|
||||
goog.testing.MockUserAgent.prototype.uninstall = function() {
|
||||
if (this.installed_) {
|
||||
goog.userAgent.getUserAgentString = this.originalUserAgentFunction_;
|
||||
goog.userAgent.getNavigator = this.originalNavigatorFunction_;
|
||||
this.installed_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.MockUserAgent.prototype.disposeInternal = function() {
|
||||
this.uninstall();
|
||||
delete this.userAgent_;
|
||||
delete this.originalUserAgentFunction_;
|
||||
delete this.navigator_;
|
||||
delete this.originalNavigatorFunction_;
|
||||
goog.testing.MockUserAgent.superClass_.disposeInternal.call(this);
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
709
float-no-zero/closure-library/closure/goog/testing/net/xhrio.js
Normal file
709
float-no-zero/closure-library/closure/goog/testing/net/xhrio.js
Normal file
@@ -0,0 +1,709 @@
|
||||
// Copyright 2007 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 Mock of XhrIo for unit testing.
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.net.XhrIo');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom.xml');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.net.ErrorCode');
|
||||
goog.require('goog.net.EventType');
|
||||
goog.require('goog.net.HttpStatus');
|
||||
goog.require('goog.net.XhrIo');
|
||||
goog.require('goog.net.XmlHttp');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.structs.Map');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Mock implementation of goog.net.XhrIo. This doesn't provide a mock
|
||||
* implementation for all cases, but it's not too hard to add them as needed.
|
||||
* @param {goog.testing.TestQueue=} opt_testQueue Test queue for inserting test
|
||||
* events.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.testing.net.XhrIo = function(opt_testQueue) {
|
||||
goog.events.EventTarget.call(this);
|
||||
|
||||
/**
|
||||
* Map of default headers to add to every request, use:
|
||||
* XhrIo.headers.set(name, value)
|
||||
* @type {goog.structs.Map}
|
||||
*/
|
||||
this.headers = new goog.structs.Map();
|
||||
|
||||
/**
|
||||
* Queue of events write to.
|
||||
* @type {goog.testing.TestQueue?}
|
||||
* @private
|
||||
*/
|
||||
this.testQueue_ = opt_testQueue || null;
|
||||
};
|
||||
goog.inherits(goog.testing.net.XhrIo, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Alias this enum here to make mocking of goog.net.XhrIo easier.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.testing.net.XhrIo.ResponseType = goog.net.XhrIo.ResponseType;
|
||||
|
||||
|
||||
/**
|
||||
* All non-disposed instances of goog.testing.net.XhrIo created
|
||||
* by {@link goog.testing.net.XhrIo.send} are in this Array.
|
||||
* @see goog.testing.net.XhrIo.cleanup
|
||||
* @type {Array.<goog.testing.net.XhrIo>}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.sendInstances_ = [];
|
||||
|
||||
|
||||
/**
|
||||
* Returns an Array containing all non-disposed instances of
|
||||
* goog.testing.net.XhrIo created by {@link goog.testing.net.XhrIo.send}.
|
||||
* @return {Array} Array of goog.testing.net.XhrIo instances.
|
||||
*/
|
||||
goog.testing.net.XhrIo.getSendInstances = function() {
|
||||
return goog.testing.net.XhrIo.sendInstances_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Disposes all non-disposed instances of goog.testing.net.XhrIo created by
|
||||
* {@link goog.testing.net.XhrIo.send}.
|
||||
* @see goog.net.XhrIo.cleanup
|
||||
*/
|
||||
goog.testing.net.XhrIo.cleanup = function() {
|
||||
var instances = goog.testing.net.XhrIo.sendInstances_;
|
||||
while (instances.length) {
|
||||
instances.pop().dispose();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates the static XhrIo send method.
|
||||
* @param {string} url Uri to make request to.
|
||||
* @param {Function=} opt_callback Callback function for when request is
|
||||
* complete.
|
||||
* @param {string=} opt_method Send method, default: GET.
|
||||
* @param {string=} opt_content Post data.
|
||||
* @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
|
||||
* request.
|
||||
* @param {number=} opt_timeoutInterval Number of milliseconds after which an
|
||||
* incomplete request will be aborted; 0 means no timeout is set.
|
||||
*/
|
||||
goog.testing.net.XhrIo.send = function(url, opt_callback, opt_method,
|
||||
opt_content, opt_headers,
|
||||
opt_timeoutInterval) {
|
||||
var x = new goog.testing.net.XhrIo();
|
||||
goog.testing.net.XhrIo.sendInstances_.push(x);
|
||||
if (opt_callback) {
|
||||
goog.events.listen(x, goog.net.EventType.COMPLETE, opt_callback);
|
||||
}
|
||||
goog.events.listen(x,
|
||||
goog.net.EventType.READY,
|
||||
goog.partial(goog.testing.net.XhrIo.cleanupSend_, x));
|
||||
if (opt_timeoutInterval) {
|
||||
x.setTimeoutInterval(opt_timeoutInterval);
|
||||
}
|
||||
x.send(url, opt_method, opt_content, opt_headers);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Disposes of the specified goog.testing.net.XhrIo created by
|
||||
* {@link goog.testing.net.XhrIo.send} and removes it from
|
||||
* {@link goog.testing.net.XhrIo.pendingStaticSendInstances_}.
|
||||
* @param {goog.testing.net.XhrIo} XhrIo An XhrIo created by
|
||||
* {@link goog.testing.net.XhrIo.send}.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.cleanupSend_ = function(XhrIo) {
|
||||
XhrIo.dispose();
|
||||
goog.array.remove(goog.testing.net.XhrIo.sendInstances_, XhrIo);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Stores the simulated response headers for the requests which are sent through
|
||||
* this XhrIo.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.responseHeaders_;
|
||||
|
||||
|
||||
/**
|
||||
* Whether MockXhrIo is active.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.active_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Last URI that was requested.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.lastUri_ = '';
|
||||
|
||||
|
||||
/**
|
||||
* Last HTTP method that was requested.
|
||||
* @type {string|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.lastMethod_;
|
||||
|
||||
|
||||
/**
|
||||
* Last POST content that was requested.
|
||||
* @type {string|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.lastContent_;
|
||||
|
||||
|
||||
/**
|
||||
* Additional headers that were requested in the last query.
|
||||
* @type {Object|goog.structs.Map|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.lastHeaders_;
|
||||
|
||||
|
||||
/**
|
||||
* Last error code.
|
||||
* @type {goog.net.ErrorCode}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.lastErrorCode_ =
|
||||
goog.net.ErrorCode.NO_ERROR;
|
||||
|
||||
|
||||
/**
|
||||
* Last error message.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.lastError_ = '';
|
||||
|
||||
|
||||
/**
|
||||
* The response object.
|
||||
* @type {string|Document}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.response_ = '';
|
||||
|
||||
|
||||
/**
|
||||
* Mock ready state.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.readyState_ =
|
||||
goog.net.XmlHttp.ReadyState.UNINITIALIZED;
|
||||
|
||||
|
||||
/**
|
||||
* Number of milliseconds after which an incomplete request will be aborted and
|
||||
* a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no timeout is set.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.timeoutInterval_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Window timeout ID used to cancel the timeout event handler if the request
|
||||
* completes successfully.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.timeoutId_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The requested type for the response. The empty string means use the default
|
||||
* XHR behavior.
|
||||
* @type {goog.net.XhrIo.ResponseType}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.responseType_ =
|
||||
goog.net.XhrIo.ResponseType.DEFAULT;
|
||||
|
||||
|
||||
/**
|
||||
* Whether a "credentialed" request is to be sent (one that is aware of cookies
|
||||
* and authentication) . This is applicable only for cross-domain requests and
|
||||
* more recent browsers that support this part of the HTTP Access Control
|
||||
* standard.
|
||||
*
|
||||
* @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#withcredentials
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.withCredentials_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Whether there's currently an underlying XHR object.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.xhr_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of milliseconds after which an incomplete request will be
|
||||
* aborted, or 0 if no timeout is set.
|
||||
* @return {number} Timeout interval in milliseconds.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getTimeoutInterval = function() {
|
||||
return this.timeoutInterval_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of milliseconds after which an incomplete request will be
|
||||
* aborted and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no
|
||||
* timeout is set.
|
||||
* @param {number} ms Timeout interval in milliseconds; 0 means none.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.setTimeoutInterval = function(ms) {
|
||||
this.timeoutInterval_ = Math.max(0, ms);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Causes timeout events to be fired.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.simulateTimeout = function() {
|
||||
this.lastErrorCode_ = goog.net.ErrorCode.TIMEOUT;
|
||||
this.dispatchEvent(goog.net.EventType.TIMEOUT);
|
||||
this.abort(goog.net.ErrorCode.TIMEOUT);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the desired type for the response. At time of writing, this is only
|
||||
* supported in very recent versions of WebKit (10.0.612.1 dev and later).
|
||||
*
|
||||
* If this is used, the response may only be accessed via {@link #getResponse}.
|
||||
*
|
||||
* @param {goog.net.XhrIo.ResponseType} type The desired type for the response.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.setResponseType = function(type) {
|
||||
this.responseType_ = type;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the desired type for the response.
|
||||
* @return {goog.net.XhrIo.ResponseType} The desired type for the response.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getResponseType = function() {
|
||||
return this.responseType_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether a "credentialed" request that is aware of cookie and
|
||||
* authentication information should be made. This option is only supported by
|
||||
* browsers that support HTTP Access Control. As of this writing, this option
|
||||
* is not supported in IE.
|
||||
*
|
||||
* @param {boolean} withCredentials Whether this should be a "credentialed"
|
||||
* request.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.setWithCredentials =
|
||||
function(withCredentials) {
|
||||
this.withCredentials_ = withCredentials;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets whether a "credentialed" request is to be sent.
|
||||
* @return {boolean} The desired type for the response.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getWithCredentials = function() {
|
||||
return this.withCredentials_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Abort the current XMLHttpRequest
|
||||
* @param {goog.net.ErrorCode=} opt_failureCode Optional error code to use -
|
||||
* defaults to ABORT.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.abort = function(opt_failureCode) {
|
||||
if (this.active_) {
|
||||
try {
|
||||
this.active_ = false;
|
||||
this.lastErrorCode_ = opt_failureCode || goog.net.ErrorCode.ABORT;
|
||||
this.dispatchEvent(goog.net.EventType.COMPLETE);
|
||||
this.dispatchEvent(goog.net.EventType.ABORT);
|
||||
} finally {
|
||||
this.simulateReady();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates the XhrIo send.
|
||||
* @param {string} url Uri to make request too.
|
||||
* @param {string=} opt_method Send method, default: GET.
|
||||
* @param {string=} opt_content Post data.
|
||||
* @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the
|
||||
* request.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.send = function(url, opt_method, opt_content,
|
||||
opt_headers) {
|
||||
if (this.xhr_) {
|
||||
throw Error('[goog.net.XhrIo] Object is active with another request');
|
||||
}
|
||||
|
||||
this.lastUri_ = url;
|
||||
this.lastMethod_ = opt_method || 'GET';
|
||||
this.lastContent_ = opt_content;
|
||||
this.lastHeaders_ = opt_headers;
|
||||
|
||||
if (this.testQueue_) {
|
||||
this.testQueue_.enqueue(['s', url, opt_method, opt_content, opt_headers]);
|
||||
}
|
||||
this.xhr_ = true;
|
||||
this.active_ = true;
|
||||
this.readyState_ = goog.net.XmlHttp.ReadyState.UNINITIALIZED;
|
||||
this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.LOADING);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new XHR object.
|
||||
* @return {XMLHttpRequest|GearsHttpRequest} The newly created XHR object.
|
||||
* @protected
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.createXhr = function() {
|
||||
return goog.net.XmlHttp();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates changing to the new ready state.
|
||||
* @param {number} readyState Ready state to change to.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.simulateReadyStateChange =
|
||||
function(readyState) {
|
||||
if (readyState < this.readyState_) {
|
||||
throw Error('Readystate cannot go backwards');
|
||||
}
|
||||
|
||||
// INTERACTIVE can be dispatched repeatedly as more data is reported.
|
||||
if (readyState == goog.net.XmlHttp.ReadyState.INTERACTIVE &&
|
||||
readyState == this.readyState_) {
|
||||
this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
while (this.readyState_ < readyState) {
|
||||
this.readyState_++;
|
||||
this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE);
|
||||
|
||||
if (this.readyState_ == goog.net.XmlHttp.ReadyState.COMPLETE) {
|
||||
this.active_ = false;
|
||||
this.dispatchEvent(goog.net.EventType.COMPLETE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulate receiving some bytes but the request not fully completing, and
|
||||
* the XHR entering the 'INTERACTIVE' state.
|
||||
* @param {string} partialResponse A string to append to the response text.
|
||||
* @param {Object=} opt_headers Simulated response headers.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.simulatePartialResponse =
|
||||
function(partialResponse, opt_headers) {
|
||||
this.response_ += partialResponse;
|
||||
this.responseHeaders_ = opt_headers || {};
|
||||
this.statusCode_ = 200;
|
||||
this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.INTERACTIVE);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates receiving a response.
|
||||
* @param {number} statusCode Simulated status code.
|
||||
* @param {string|Document|null} response Simulated response.
|
||||
* @param {Object=} opt_headers Simulated response headers.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.simulateResponse = function(statusCode,
|
||||
response, opt_headers) {
|
||||
this.statusCode_ = statusCode;
|
||||
this.response_ = response || '';
|
||||
this.responseHeaders_ = opt_headers || {};
|
||||
|
||||
try {
|
||||
if (this.isSuccess()) {
|
||||
this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.COMPLETE);
|
||||
this.dispatchEvent(goog.net.EventType.SUCCESS);
|
||||
} else {
|
||||
this.lastErrorCode_ = goog.net.ErrorCode.HTTP_ERROR;
|
||||
this.lastError_ = this.getStatusText() + ' [' + this.getStatus() + ']';
|
||||
this.simulateReadyStateChange(goog.net.XmlHttp.ReadyState.COMPLETE);
|
||||
this.dispatchEvent(goog.net.EventType.ERROR);
|
||||
}
|
||||
} finally {
|
||||
this.simulateReady();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Simulates the Xhr is ready for the next request.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.simulateReady = function() {
|
||||
this.active_ = false;
|
||||
this.xhr_ = false;
|
||||
this.dispatchEvent(goog.net.EventType.READY);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether there is an active request.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.isActive = function() {
|
||||
return !!this.xhr_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Has the request completed.
|
||||
* @return {boolean} Whether the request has completed.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.isComplete = function() {
|
||||
return this.readyState_ == goog.net.XmlHttp.ReadyState.COMPLETE;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Has the request compeleted with a success.
|
||||
* @return {boolean} Whether the request compeleted successfully.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.isSuccess = function() {
|
||||
switch (this.getStatus()) {
|
||||
case goog.net.HttpStatus.OK:
|
||||
case goog.net.HttpStatus.NO_CONTENT:
|
||||
case goog.net.HttpStatus.NOT_MODIFIED:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the readystate.
|
||||
* @return {number} goog.net.XmlHttp.ReadyState.*.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getReadyState = function() {
|
||||
return this.readyState_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the status from the Xhr object. Will only return correct result when
|
||||
* called from the context of a callback.
|
||||
* @return {number} Http status.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getStatus = function() {
|
||||
return this.statusCode_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the status text from the Xhr object. Will only return correct result
|
||||
* when called from the context of a callback.
|
||||
* @return {string} Status text.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getStatusText = function() {
|
||||
return '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the last error message.
|
||||
* @return {goog.net.ErrorCode} Last error code.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getLastErrorCode = function() {
|
||||
return this.lastErrorCode_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the last error message.
|
||||
* @return {string} Last error message.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getLastError = function() {
|
||||
return this.lastError_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the last URI that was requested.
|
||||
* @return {string} Last URI.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getLastUri = function() {
|
||||
return this.lastUri_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the last HTTP method that was requested.
|
||||
* @return {string|undefined} Last HTTP method used by send.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getLastMethod = function() {
|
||||
return this.lastMethod_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the last POST content that was requested.
|
||||
* @return {string|undefined} Last POST content or undefined if last request was
|
||||
* a GET.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getLastContent = function() {
|
||||
return this.lastContent_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the headers of the last request.
|
||||
* @return {Object|goog.structs.Map|undefined} Last headers manually set in send
|
||||
* call or undefined if no additional headers were specified.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getLastRequestHeaders = function() {
|
||||
return this.lastHeaders_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the response text from the Xhr object. Will only return correct result
|
||||
* when called from the context of a callback.
|
||||
* @return {string} Result from the server.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getResponseText = function() {
|
||||
return goog.isString(this.response_) ? this.response_ :
|
||||
goog.dom.xml.serialize(this.response_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the response body from the Xhr object. Will only return correct result
|
||||
* when called from the context of a callback.
|
||||
* @return {Object} Binary result from the server or null.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getResponseBody = function() {
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the response and evaluates it as JSON from the Xhr object. Will only
|
||||
* return correct result when called from the context of a callback.
|
||||
* @param {string=} opt_xssiPrefix Optional XSSI prefix string to use for
|
||||
* stripping of the response before parsing. This needs to be set only if
|
||||
* your backend server prepends the same prefix string to the JSON response.
|
||||
* @return {Object} JavaScript object.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getResponseJson = function(opt_xssiPrefix) {
|
||||
var responseText = this.getResponseText();
|
||||
if (opt_xssiPrefix && responseText.indexOf(opt_xssiPrefix) == 0) {
|
||||
responseText = responseText.substring(opt_xssiPrefix.length);
|
||||
}
|
||||
|
||||
return goog.json.parse(responseText);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the response XML from the Xhr object. Will only return correct result
|
||||
* when called from the context of a callback.
|
||||
* @return {Document} Result from the server if it was XML.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getResponseXml = function() {
|
||||
// NOTE(user): I haven't found out how to check in Internet Explorer
|
||||
// whether the response is XML document, so I do it the other way around.
|
||||
return goog.isString(this.response_) ? null : this.response_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the response as the type specificed by {@link #setResponseType}. At time
|
||||
* of writing, this is only supported in very recent versions of WebKit
|
||||
* (10.0.612.1 dev and later).
|
||||
*
|
||||
* @return {*} The response.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getResponse = function() {
|
||||
return this.response_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the value of the response-header with the given name from the Xhr object
|
||||
* Will only return correct result when called from the context of a callback
|
||||
* and the request has completed
|
||||
* @param {string} key The name of the response-header to retrieve.
|
||||
* @return {string|undefined} The value of the response-header named key.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getResponseHeader = function(key) {
|
||||
return this.isComplete() ? this.responseHeaders_[key] : undefined;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the text of all the headers in the response.
|
||||
* Will only return correct result when called from the context of a callback
|
||||
* and the request has completed
|
||||
* @return {string} The string containing all the response headers.
|
||||
*/
|
||||
goog.testing.net.XhrIo.prototype.getAllResponseHeaders = function() {
|
||||
if (!this.isComplete()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var headers = [];
|
||||
goog.object.forEach(this.responseHeaders_, function(value, name) {
|
||||
headers.push(name + ': ' + value);
|
||||
});
|
||||
|
||||
return headers.join('\n');
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright 2011 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 An XhrIo pool that uses a single mock XHR object for testing.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.net.XhrIoPool');
|
||||
|
||||
goog.require('goog.net.XhrIoPool');
|
||||
goog.require('goog.testing.net.XhrIo');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A pool containing a single mock XhrIo object.
|
||||
*
|
||||
* @param {goog.testing.net.XhrIo=} opt_xhr The mock XhrIo object.
|
||||
* @constructor
|
||||
* @extends {goog.net.XhrIoPool}
|
||||
*/
|
||||
goog.testing.net.XhrIoPool = function(opt_xhr) {
|
||||
/**
|
||||
* The mock XhrIo object.
|
||||
* @type {!goog.testing.net.XhrIo}
|
||||
* @private
|
||||
*/
|
||||
this.xhr_ = opt_xhr || new goog.testing.net.XhrIo();
|
||||
|
||||
// Run this after setting xhr_ because xhr_ is used to initialize the pool.
|
||||
goog.base(this, undefined, 1, 1);
|
||||
};
|
||||
goog.inherits(goog.testing.net.XhrIoPool, goog.net.XhrIoPool);
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @suppress {invalidCasts}
|
||||
*/
|
||||
goog.testing.net.XhrIoPool.prototype.createObject = function() {
|
||||
return (/** @type {!goog.net.XhrIo} */ (this.xhr_));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the mock XhrIo used by this pool.
|
||||
*
|
||||
* @return {!goog.testing.net.XhrIo} The mock XhrIo.
|
||||
*/
|
||||
goog.testing.net.XhrIoPool.prototype.getXhr = function() {
|
||||
return this.xhr_;
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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 Helper for passing property names as string literals in
|
||||
* compiled test code.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.ObjectPropertyString');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Object to pass a property name as a string literal and its containing object
|
||||
* when the JSCompiler is rewriting these names. This should only be used in
|
||||
* test code.
|
||||
*
|
||||
* @param {Object} object The containing object.
|
||||
* @param {Object|string} propertyString Property name as a string literal.
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.ObjectPropertyString = function(object, propertyString) {
|
||||
this.object_ = object;
|
||||
this.propertyString_ = /** @type {string} */ (propertyString);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ObjectPropertyString.prototype.object_;
|
||||
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ObjectPropertyString.prototype.propertyString_;
|
||||
|
||||
|
||||
/**
|
||||
* @return {Object} The object.
|
||||
*/
|
||||
goog.testing.ObjectPropertyString.prototype.getObject = function() {
|
||||
return this.object_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The property string.
|
||||
*/
|
||||
goog.testing.ObjectPropertyString.prototype.getPropertyString = function() {
|
||||
return this.propertyString_;
|
||||
};
|
||||
@@ -0,0 +1,190 @@
|
||||
// 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 A table for showing the results of performance testing.
|
||||
*
|
||||
* {@see goog.testing.benchmark} for an easy way to use this functionality.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
* @author nicksantos@google.com (Nick Santos)
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.PerformanceTable');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.testing.PerformanceTimer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A UI widget that runs performance tests and displays the results.
|
||||
* @param {Element} root The element where the table should be attached.
|
||||
* @param {goog.testing.PerformanceTimer=} opt_timer A timer to use for
|
||||
* executing functions and profiling them.
|
||||
* @param {number=} opt_precision Number of digits of precision to include in
|
||||
* results. Defaults to 0.
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.PerformanceTable = function(root, opt_timer, opt_precision) {
|
||||
/**
|
||||
* Where the table should be attached.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.root_ = root;
|
||||
|
||||
/**
|
||||
* Number of digits of precision to include in results.
|
||||
* Defaults to 0.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.precision_ = opt_precision || 0;
|
||||
|
||||
var timer = opt_timer;
|
||||
if (!timer) {
|
||||
timer = new goog.testing.PerformanceTimer();
|
||||
timer.setNumSamples(5);
|
||||
timer.setDiscardOutliers(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* A timer for running the tests.
|
||||
* @type {goog.testing.PerformanceTimer}
|
||||
* @private
|
||||
*/
|
||||
this.timer_ = timer;
|
||||
|
||||
this.initRoot_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.testing.PerformanceTimer} The timer being used.
|
||||
*/
|
||||
goog.testing.PerformanceTable.prototype.getTimer = function() {
|
||||
return this.timer_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Render the initial table.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PerformanceTable.prototype.initRoot_ = function() {
|
||||
this.root_.innerHTML =
|
||||
'<table class="test-results" cellspacing="1">' +
|
||||
' <thead>' +
|
||||
' <tr>' +
|
||||
' <th rowspan="2">Test Description</th>' +
|
||||
' <th rowspan="2">Runs</th>' +
|
||||
' <th colspan="4">Results (ms)</th>' +
|
||||
' </tr>' +
|
||||
' <tr>' +
|
||||
' <th>Average</th>' +
|
||||
' <th>Std Dev</th>' +
|
||||
' <th>Minimum</th>' +
|
||||
' <th>Maximum</th>' +
|
||||
' </tr>' +
|
||||
' </thead>' +
|
||||
' <tbody>' +
|
||||
' </tbody>' +
|
||||
'</table>';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Element} The body of the table.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PerformanceTable.prototype.getTableBody_ = function() {
|
||||
return this.root_.getElementsByTagName(goog.dom.TagName.TBODY)[0];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Round to the specified precision.
|
||||
* @param {number} num The number to round.
|
||||
* @return {string} The rounded number, as a string.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PerformanceTable.prototype.round_ = function(num) {
|
||||
var factor = Math.pow(10, this.precision_);
|
||||
return String(Math.round(num * factor) / factor);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Run the given function with the performance timer, and show the results.
|
||||
* @param {Function} fn The function to run.
|
||||
* @param {string=} opt_desc A description to associate with this run.
|
||||
*/
|
||||
goog.testing.PerformanceTable.prototype.run = function(fn, opt_desc) {
|
||||
this.runTask(
|
||||
new goog.testing.PerformanceTimer.Task(/** @type {function()} */ (fn)),
|
||||
opt_desc);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Run the given task with the performance timer, and show the results.
|
||||
* @param {goog.testing.PerformanceTimer.Task} task The performance timer task
|
||||
* to run.
|
||||
* @param {string=} opt_desc A description to associate with this run.
|
||||
*/
|
||||
goog.testing.PerformanceTable.prototype.runTask = function(task, opt_desc) {
|
||||
var results = this.timer_.runTask(task);
|
||||
this.recordResults(results, opt_desc);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Record a performance timer results object to the performance table. See
|
||||
* {@code goog.testing.PerformanceTimer} for details of the format of this
|
||||
* object.
|
||||
* @param {Object} results The performance timer results object.
|
||||
* @param {string=} opt_desc A description to associate with these results.
|
||||
*/
|
||||
goog.testing.PerformanceTable.prototype.recordResults = function(
|
||||
results, opt_desc) {
|
||||
var average = results['average'];
|
||||
var standardDeviation = results['standardDeviation'];
|
||||
var isSuspicious = average < 0 || standardDeviation > average * .5;
|
||||
var resultsRow = goog.dom.createDom('tr', null,
|
||||
goog.dom.createDom('td', 'test-description',
|
||||
opt_desc || 'No description'),
|
||||
goog.dom.createDom('td', 'test-count', String(results['count'])),
|
||||
goog.dom.createDom('td', 'test-average', this.round_(average)),
|
||||
goog.dom.createDom('td', 'test-standard-deviation',
|
||||
this.round_(standardDeviation)),
|
||||
goog.dom.createDom('td', 'test-minimum', String(results['minimum'])),
|
||||
goog.dom.createDom('td', 'test-maximum', String(results['maximum'])));
|
||||
if (isSuspicious) {
|
||||
resultsRow.className = 'test-suspicious';
|
||||
}
|
||||
this.getTableBody_().appendChild(resultsRow);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Report an error in the table.
|
||||
* @param {*} reason The reason for the error.
|
||||
*/
|
||||
goog.testing.PerformanceTable.prototype.reportError = function(reason) {
|
||||
this.getTableBody_().appendChild(
|
||||
goog.dom.createDom('tr', null,
|
||||
goog.dom.createDom('td', {'class': 'test-error', 'colSpan': 5},
|
||||
String(reason))));
|
||||
};
|
||||
@@ -0,0 +1,405 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview Performance timer.
|
||||
*
|
||||
* {@see goog.testing.benchmark} for an easy way to use this functionality.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.PerformanceTimer');
|
||||
goog.provide('goog.testing.PerformanceTimer.Task');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.async.Deferred');
|
||||
goog.require('goog.math');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a performance timer that runs test functions a number of times to
|
||||
* generate timing samples, and provides performance statistics (minimum,
|
||||
* maximum, average, and standard deviation).
|
||||
* @param {number=} opt_numSamples Number of times to run the test function;
|
||||
* defaults to 10.
|
||||
* @param {number=} opt_timeoutInterval Number of milliseconds after which the
|
||||
* test is to be aborted; defaults to 5 seconds (5,000ms).
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.PerformanceTimer = function(opt_numSamples, opt_timeoutInterval) {
|
||||
/**
|
||||
* Number of times the test function is to be run; defaults to 10.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.numSamples_ = opt_numSamples || 10;
|
||||
|
||||
/**
|
||||
* Number of milliseconds after which the test is to be aborted; defaults to
|
||||
* 5,000ms.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.timeoutInterval_ = opt_timeoutInterval || 5000;
|
||||
|
||||
/**
|
||||
* Whether to discard outliers (i.e. the smallest and the largest values)
|
||||
* from the sample set before computing statistics. Defaults to false.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.discardOutliers_ = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} The number of times the test function will be run.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.prototype.getNumSamples = function() {
|
||||
return this.numSamples_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of times the test function will be run.
|
||||
* @param {number} numSamples Number of times to run the test function.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.prototype.setNumSamples = function(numSamples) {
|
||||
this.numSamples_ = numSamples;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} The number of milliseconds after which the test times out.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.prototype.getTimeoutInterval = function() {
|
||||
return this.timeoutInterval_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of milliseconds after which the test times out.
|
||||
* @param {number} timeoutInterval Timeout interval in ms.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.prototype.setTimeoutInterval = function(
|
||||
timeoutInterval) {
|
||||
this.timeoutInterval_ = timeoutInterval;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether to ignore the smallest and the largest values when computing
|
||||
* stats.
|
||||
* @param {boolean} discard Whether to discard outlier values.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.prototype.setDiscardOutliers = function(discard) {
|
||||
this.discardOutliers_ = discard;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether outlier values are discarded prior to computing
|
||||
* stats.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.prototype.isDiscardOutliers = function() {
|
||||
return this.discardOutliers_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Executes the test function the required number of times (or until the
|
||||
* test run exceeds the timeout interval, whichever comes first). Returns
|
||||
* an object containing the following:
|
||||
* <pre>
|
||||
* {
|
||||
* 'average': average execution time (ms)
|
||||
* 'count': number of executions (may be fewer than expected due to timeout)
|
||||
* 'maximum': longest execution time (ms)
|
||||
* 'minimum': shortest execution time (ms)
|
||||
* 'standardDeviation': sample standard deviation (ms)
|
||||
* 'total': total execution time (ms)
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param {Function} testFn Test function whose performance is to
|
||||
* be measured.
|
||||
* @return {Object} Object containing performance stats.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.prototype.run = function(testFn) {
|
||||
return this.runTask(new goog.testing.PerformanceTimer.Task(
|
||||
/** @type {goog.testing.PerformanceTimer.TestFunction} */ (testFn)));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Executes the test function of the specified task as described in
|
||||
* {@code run}. In addition, if specified, the set up and tear down functions of
|
||||
* the task are invoked before and after each invocation of the test function.
|
||||
* @see goog.testing.PerformanceTimer#run
|
||||
* @param {goog.testing.PerformanceTimer.Task} task A task describing the test
|
||||
* function to invoke.
|
||||
* @return {Object} Object containing performance stats.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.prototype.runTask = function(task) {
|
||||
var samples = [];
|
||||
var testStart = goog.now();
|
||||
var totalRunTime = 0;
|
||||
|
||||
var testFn = task.getTest();
|
||||
var setUpFn = task.getSetUp();
|
||||
var tearDownFn = task.getTearDown();
|
||||
|
||||
for (var i = 0; i < this.numSamples_ && totalRunTime <= this.timeoutInterval_;
|
||||
i++) {
|
||||
setUpFn();
|
||||
var sampleStart = goog.now();
|
||||
testFn();
|
||||
var sampleEnd = goog.now();
|
||||
tearDownFn();
|
||||
samples[i] = sampleEnd - sampleStart;
|
||||
totalRunTime = sampleEnd - testStart;
|
||||
}
|
||||
|
||||
return this.finishTask_(samples);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Finishes the run of a task by creating a result object from samples, in the
|
||||
* format described in {@code run}.
|
||||
* @see goog.testing.PerformanceTimer#run
|
||||
* @return {Object} Object containing performance stats.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PerformanceTimer.prototype.finishTask_ = function(samples) {
|
||||
if (this.discardOutliers_ && samples.length > 2) {
|
||||
goog.array.remove(samples, Math.min.apply(null, samples));
|
||||
goog.array.remove(samples, Math.max.apply(null, samples));
|
||||
}
|
||||
|
||||
return goog.testing.PerformanceTimer.createResults(samples);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Executes the test function of the specified task asynchronously. The test
|
||||
* function is expected to take a callback as input and has to call it to signal
|
||||
* that it's done. In addition, if specified, the setUp and tearDown functions
|
||||
* of the task are invoked before and after each invocation of the test
|
||||
* function. Note that setUp/tearDown functions take a callback as input and
|
||||
* must call this callback when they are done.
|
||||
* @see goog.testing.PerformanceTimer#run
|
||||
* @param {goog.testing.PerformanceTimer.Task} task A task describing the test
|
||||
* function to invoke.
|
||||
* @return {!goog.async.Deferred} The deferred result, eventually an object
|
||||
* containing performance stats.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.prototype.runAsyncTask = function(task) {
|
||||
var samples = [];
|
||||
var testStart = goog.now();
|
||||
|
||||
var testFn = task.getTest();
|
||||
var setUpFn = task.getSetUp();
|
||||
var tearDownFn = task.getTearDown();
|
||||
|
||||
// Note that this uses a separate code path from runTask() because
|
||||
// implementing runTask() in terms of runAsyncTask() could easily cause
|
||||
// a stack overflow if there are many iterations.
|
||||
var result = new goog.async.Deferred();
|
||||
this.runAsyncTaskSample_(testFn, setUpFn, tearDownFn, result, samples,
|
||||
testStart);
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Runs a task once, waits for the test function to complete asynchronously
|
||||
* and starts another run if not enough samples have been collected. Otherwise
|
||||
* finishes this task.
|
||||
* @param {goog.testing.PerformanceTimer.TestFunction} testFn The test function.
|
||||
* @param {goog.testing.PerformanceTimer.TestFunction} setUpFn The set up
|
||||
* function that will be called once before the test function is run.
|
||||
* @param {goog.testing.PerformanceTimer.TestFunction} tearDownFn The set up
|
||||
* function that will be called once after the test function completed.
|
||||
* @param {!goog.async.Deferred} result The deferred result, eventually an
|
||||
* object containing performance stats.
|
||||
* @param {!Array.<number>} samples The time samples from all runs of the test
|
||||
* function so far.
|
||||
* @param {number} testStart The timestamp when the first sample was started.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PerformanceTimer.prototype.runAsyncTaskSample_ = function(testFn,
|
||||
setUpFn, tearDownFn, result, samples, testStart) {
|
||||
var timer = this;
|
||||
timer.handleOptionalDeferred_(setUpFn, function() {
|
||||
var sampleStart = goog.now();
|
||||
timer.handleOptionalDeferred_(testFn, function() {
|
||||
var sampleEnd = goog.now();
|
||||
timer.handleOptionalDeferred_(tearDownFn, function() {
|
||||
samples.push(sampleEnd - sampleStart);
|
||||
var totalRunTime = sampleEnd - testStart;
|
||||
if (samples.length < timer.numSamples_ &&
|
||||
totalRunTime <= timer.timeoutInterval_) {
|
||||
timer.runAsyncTaskSample_(testFn, setUpFn, tearDownFn, result,
|
||||
samples, testStart);
|
||||
} else {
|
||||
result.callback(timer.finishTask_(samples));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Execute a function that optionally returns a deferred object and continue
|
||||
* with the given continuation function only once the deferred object has a
|
||||
* result.
|
||||
* @param {goog.testing.PerformanceTimer.TestFunction} deferredFactory The
|
||||
* function that optionally returns a deferred object.
|
||||
* @param {function()} continuationFunction The function that should be called
|
||||
* after the optional deferred has a result.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PerformanceTimer.prototype.handleOptionalDeferred_ = function(
|
||||
deferredFactory, continuationFunction) {
|
||||
var deferred = deferredFactory();
|
||||
if (deferred) {
|
||||
deferred.addCallback(continuationFunction);
|
||||
} else {
|
||||
continuationFunction();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a performance timer results object by analyzing a given array of
|
||||
* sample timings.
|
||||
* @param {Array.<number>} samples The samples to analyze.
|
||||
* @return {Object} Object containing performance stats.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.createResults = function(samples) {
|
||||
return {
|
||||
'average': goog.math.average.apply(null, samples),
|
||||
'count': samples.length,
|
||||
'maximum': Math.max.apply(null, samples),
|
||||
'minimum': Math.min.apply(null, samples),
|
||||
'standardDeviation': goog.math.standardDeviation.apply(null, samples),
|
||||
'total': goog.math.sum.apply(null, samples)
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A test function whose performance should be measured or a setUp/tearDown
|
||||
* function. It may optionally return a deferred object. If it does so, the
|
||||
* test harness will assume the function is asynchronous and it must signal
|
||||
* that it's done by setting an (empty) result on the deferred object. If the
|
||||
* function doesn't return anything, the test harness will assume it's
|
||||
* synchronous.
|
||||
* @typedef {function():(goog.async.Deferred|undefined)}
|
||||
*/
|
||||
goog.testing.PerformanceTimer.TestFunction;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A task for the performance timer to measure. Callers can specify optional
|
||||
* setUp and tearDown methods to control state before and after each run of the
|
||||
* test function.
|
||||
* @param {goog.testing.PerformanceTimer.TestFunction} test Test function whose
|
||||
* performance is to be measured.
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.PerformanceTimer.Task = function(test) {
|
||||
/**
|
||||
* The test function to time.
|
||||
* @type {goog.testing.PerformanceTimer.TestFunction}
|
||||
* @private
|
||||
*/
|
||||
this.test_ = test;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An optional set up function to run before each invocation of the test
|
||||
* function.
|
||||
* @type {goog.testing.PerformanceTimer.TestFunction}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PerformanceTimer.Task.prototype.setUp_ = goog.nullFunction;
|
||||
|
||||
|
||||
/**
|
||||
* An optional tear down function to run after each invocation of the test
|
||||
* function.
|
||||
* @type {goog.testing.PerformanceTimer.TestFunction}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PerformanceTimer.Task.prototype.tearDown_ = goog.nullFunction;
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.testing.PerformanceTimer.TestFunction} The test function to
|
||||
* time.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.Task.prototype.getTest = function() {
|
||||
return this.test_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Specifies a set up function to be invoked before each invocation of the test
|
||||
* function.
|
||||
* @param {goog.testing.PerformanceTimer.TestFunction} setUp The set up
|
||||
* function.
|
||||
* @return {goog.testing.PerformanceTimer.Task} This task.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.Task.prototype.withSetUp = function(setUp) {
|
||||
this.setUp_ = setUp;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.testing.PerformanceTimer.TestFunction} The set up function or
|
||||
* the default no-op function if none was specified.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.Task.prototype.getSetUp = function() {
|
||||
return this.setUp_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Specifies a tear down function to be invoked after each invocation of the
|
||||
* test function.
|
||||
* @param {goog.testing.PerformanceTimer.TestFunction} tearDown The tear down
|
||||
* function.
|
||||
* @return {goog.testing.PerformanceTimer.Task} This task.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.Task.prototype.withTearDown = function(tearDown) {
|
||||
this.tearDown_ = tearDown;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.testing.PerformanceTimer.TestFunction} The tear down function
|
||||
* or the default no-op function if none was specified.
|
||||
*/
|
||||
goog.testing.PerformanceTimer.Task.prototype.getTearDown = function() {
|
||||
return this.tearDown_;
|
||||
};
|
||||
@@ -0,0 +1,243 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview Helper class for creating stubs for testing.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.PropertyReplacer');
|
||||
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Helper class for stubbing out variables and object properties for unit tests.
|
||||
* This class can change the value of some variables before running the test
|
||||
* cases, and to reset them in the tearDown phase.
|
||||
* See googletest.StubOutForTesting as an analogy in Python:
|
||||
* http://protobuf.googlecode.com/svn/trunk/python/stubout.py
|
||||
*
|
||||
* Example usage:
|
||||
* <pre>var stubs = new goog.testing.PropertyReplacer();
|
||||
*
|
||||
* function setUp() {
|
||||
* // Mock functions used in all test cases.
|
||||
* stubs.set(Math, 'random', function() {
|
||||
* return 4; // Chosen by fair dice roll. Guaranteed to be random.
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* function tearDown() {
|
||||
* stubs.reset();
|
||||
* }
|
||||
*
|
||||
* function testThreeDice() {
|
||||
* // Mock a constant used only in this test case.
|
||||
* stubs.set(goog.global, 'DICE_COUNT', 3);
|
||||
* assertEquals(12, rollAllDice());
|
||||
* }</pre>
|
||||
*
|
||||
* Constraints on altered objects:
|
||||
* <ul>
|
||||
* <li>DOM subclasses aren't supported.
|
||||
* <li>The value of the objects' constructor property must either be equal to
|
||||
* the real constructor or kept untouched.
|
||||
* </ul>
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.PropertyReplacer = function() {
|
||||
/**
|
||||
* Stores the values changed by the set() method in chronological order.
|
||||
* Its items are objects with 3 fields: 'object', 'key', 'value'. The
|
||||
* original value for the given key in the given object is stored under the
|
||||
* 'value' key.
|
||||
* @type {Array.<Object>}
|
||||
* @private
|
||||
*/
|
||||
this.original_ = [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Indicates that a key didn't exist before having been set by the set() method.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PropertyReplacer.NO_SUCH_KEY_ = {};
|
||||
|
||||
|
||||
/**
|
||||
* Tells if the given key exists in the object. Ignores inherited fields.
|
||||
* @param {Object|Function} obj The JavaScript or native object or function
|
||||
* whose key is to be checked.
|
||||
* @param {string} key The key to check.
|
||||
* @return {boolean} Whether the object has the key as own key.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PropertyReplacer.hasKey_ = function(obj, key) {
|
||||
if (!(key in obj)) {
|
||||
return false;
|
||||
}
|
||||
// hasOwnProperty is only reliable with JavaScript objects. It returns false
|
||||
// for built-in DOM attributes.
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
return true;
|
||||
}
|
||||
// In all browsers except Opera obj.constructor never equals to Object if
|
||||
// obj is an instance of a native class. In Opera we have to fall back on
|
||||
// examining obj.toString().
|
||||
if (obj.constructor == Object &&
|
||||
(!goog.userAgent.OPERA ||
|
||||
Object.prototype.toString.call(obj) == '[object Object]')) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
// Firefox hack to consider "className" part of the HTML elements or
|
||||
// "body" part of document. Although they are defined in the prototype of
|
||||
// HTMLElement or Document, accessing them this way throws an exception.
|
||||
// <pre>
|
||||
// var dummy = document.body.constructor.prototype.className
|
||||
// [Exception... "Cannot modify properties of a WrappedNative"]
|
||||
// </pre>
|
||||
var dummy = obj.constructor.prototype[key];
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
return !(key in obj.constructor.prototype);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deletes a key from an object. Sets it to undefined or empty string if the
|
||||
* delete failed.
|
||||
* @param {Object|Function} obj The object or function to delete a key from.
|
||||
* @param {string} key The key to delete.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PropertyReplacer.deleteKey_ = function(obj, key) {
|
||||
try {
|
||||
delete obj[key];
|
||||
// Delete has no effect for built-in properties of DOM nodes in FF.
|
||||
if (!goog.testing.PropertyReplacer.hasKey_(obj, key)) {
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// IE throws TypeError when trying to delete properties of native objects
|
||||
// (e.g. DOM nodes or window), even if they have been added by JavaScript.
|
||||
}
|
||||
|
||||
obj[key] = undefined;
|
||||
if (obj[key] == 'undefined') {
|
||||
// Some properties such as className in IE are always evaluated as string
|
||||
// so undefined will become 'undefined'.
|
||||
obj[key] = '';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds or changes a value in an object while saving its original state.
|
||||
* @param {Object|Function} obj The JavaScript or native object or function to
|
||||
* alter. See the constraints in the class description.
|
||||
* @param {string} key The key to change the value for.
|
||||
* @param {*} value The new value to set.
|
||||
*/
|
||||
goog.testing.PropertyReplacer.prototype.set = function(obj, key, value) {
|
||||
var origValue = goog.testing.PropertyReplacer.hasKey_(obj, key) ? obj[key] :
|
||||
goog.testing.PropertyReplacer.NO_SUCH_KEY_;
|
||||
this.original_.push({object: obj, key: key, value: origValue});
|
||||
obj[key] = value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Changes an existing value in an object to another one of the same type while
|
||||
* saving its original state. The advantage of {@code replace} over {@link #set}
|
||||
* is that {@code replace} protects against typos and erroneously passing tests
|
||||
* after some members have been renamed during a refactoring.
|
||||
* @param {Object|Function} obj The JavaScript or native object or function to
|
||||
* alter. See the constraints in the class description.
|
||||
* @param {string} key The key to change the value for. It has to be present
|
||||
* either in {@code obj} or in its prototype chain.
|
||||
* @param {*} value The new value to set. It has to have the same type as the
|
||||
* original value. The types are compared with {@link goog.typeOf}.
|
||||
* @throws {Error} In case of missing key or type mismatch.
|
||||
*/
|
||||
goog.testing.PropertyReplacer.prototype.replace = function(obj, key, value) {
|
||||
if (!(key in obj)) {
|
||||
throw Error('Cannot replace missing property "' + key + '" in ' + obj);
|
||||
}
|
||||
if (goog.typeOf(obj[key]) != goog.typeOf(value)) {
|
||||
throw Error('Cannot replace property "' + key + '" in ' + obj +
|
||||
' with a value of different type');
|
||||
}
|
||||
this.set(obj, key, value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Builds an object structure for the provided namespace path. Doesn't
|
||||
* overwrite those prefixes of the path that are already objects or functions.
|
||||
* @param {string} path The path to create or alter, e.g. 'goog.ui.Menu'.
|
||||
* @param {*} value The value to set.
|
||||
*/
|
||||
goog.testing.PropertyReplacer.prototype.setPath = function(path, value) {
|
||||
var parts = path.split('.');
|
||||
var obj = goog.global;
|
||||
for (var i = 0; i < parts.length - 1; i++) {
|
||||
var part = parts[i];
|
||||
if (part == 'prototype' && !obj[part]) {
|
||||
throw Error('Cannot set the prototype of ' + parts.slice(0, i).join('.'));
|
||||
}
|
||||
if (!goog.isObject(obj[part]) && !goog.isFunction(obj[part])) {
|
||||
this.set(obj, part, {});
|
||||
}
|
||||
obj = obj[part];
|
||||
}
|
||||
this.set(obj, parts[parts.length - 1], value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deletes the key from the object while saving its original value.
|
||||
* @param {Object|Function} obj The JavaScript or native object or function to
|
||||
* alter. See the constraints in the class description.
|
||||
* @param {string} key The key to delete.
|
||||
*/
|
||||
goog.testing.PropertyReplacer.prototype.remove = function(obj, key) {
|
||||
if (goog.testing.PropertyReplacer.hasKey_(obj, key)) {
|
||||
this.original_.push({object: obj, key: key, value: obj[key]});
|
||||
goog.testing.PropertyReplacer.deleteKey_(obj, key);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Resets all changes made by goog.testing.PropertyReplacer.prototype.set.
|
||||
*/
|
||||
goog.testing.PropertyReplacer.prototype.reset = function() {
|
||||
for (var i = this.original_.length - 1; i >= 0; i--) {
|
||||
var original = this.original_[i];
|
||||
if (original.value == goog.testing.PropertyReplacer.NO_SUCH_KEY_) {
|
||||
goog.testing.PropertyReplacer.deleteKey_(original.object, original.key);
|
||||
} else {
|
||||
original.object[original.key] = original.value;
|
||||
}
|
||||
delete this.original_[i];
|
||||
}
|
||||
this.original_.length = 0;
|
||||
};
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright 2012 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 Test helpers to compare goog.proto2.Messages.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.proto2');
|
||||
|
||||
goog.require('goog.proto2.Message');
|
||||
goog.require('goog.testing.asserts');
|
||||
|
||||
|
||||
/**
|
||||
* Compares two goog.proto2.Message instances of the same type.
|
||||
* @param {!goog.proto2.Message} expected First message.
|
||||
* @param {!goog.proto2.Message} actual Second message.
|
||||
* @param {string} path Path to the messages.
|
||||
* @return {string} A string describing where they differ. Empty string if they
|
||||
* are equal.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.proto2.findDifferences_ = function(expected, actual, path) {
|
||||
var fields = expected.getDescriptor().getFields();
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
var field = fields[i];
|
||||
var newPath = (path ? path + '/' : '') + field.getName();
|
||||
|
||||
if (expected.has(field) && !actual.has(field)) {
|
||||
return newPath + ' should be present';
|
||||
}
|
||||
if (!expected.has(field) && actual.has(field)) {
|
||||
return newPath + ' should not be present';
|
||||
}
|
||||
|
||||
if (expected.has(field)) {
|
||||
var isComposite = field.isCompositeType();
|
||||
|
||||
if (field.isRepeated()) {
|
||||
var expectedCount = expected.countOf(field);
|
||||
var actualCount = actual.countOf(field);
|
||||
if (expectedCount != actualCount) {
|
||||
return newPath + ' should have ' + expectedCount + ' items, ' +
|
||||
'but has ' + actualCount;
|
||||
}
|
||||
|
||||
for (var j = 0; j < expectedCount; j++) {
|
||||
var expectedItem = expected.get(field, j);
|
||||
var actualItem = actual.get(field, j);
|
||||
if (isComposite) {
|
||||
var itemDiff = goog.testing.proto2.findDifferences_(
|
||||
/** @type {!goog.proto2.Message} */ (expectedItem),
|
||||
/** @type {!goog.proto2.Message} */ (actualItem),
|
||||
newPath + '[' + j + ']');
|
||||
if (itemDiff) {
|
||||
return itemDiff;
|
||||
}
|
||||
} else {
|
||||
if (expectedItem != actualItem) {
|
||||
return newPath + '[' + j + '] should be ' + expectedItem +
|
||||
', but was ' + actualItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var expectedValue = expected.get(field);
|
||||
var actualValue = actual.get(field);
|
||||
if (isComposite) {
|
||||
var diff = goog.testing.proto2.findDifferences_(
|
||||
/** @type {!goog.proto2.Message} */ (expectedValue),
|
||||
/** @type {!goog.proto2.Message} */ (actualValue),
|
||||
newPath);
|
||||
if (diff) {
|
||||
return diff;
|
||||
}
|
||||
} else {
|
||||
if (expectedValue != actualValue) {
|
||||
return newPath + ' should be ' + expectedValue + ', but was ' +
|
||||
actualValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Compares two goog.proto2.Message objects. Gives more readable output than
|
||||
* assertObjectEquals on mismatch.
|
||||
* @param {!goog.proto2.Message} expected Expected proto2 message.
|
||||
* @param {!goog.proto2.Message} actual Actual proto2 message.
|
||||
* @param {string=} opt_failureMessage Failure message when the values don't
|
||||
* match.
|
||||
*/
|
||||
goog.testing.proto2.assertEquals = function(expected, actual,
|
||||
opt_failureMessage) {
|
||||
var failureSummary = opt_failureMessage || '';
|
||||
if (!(expected instanceof goog.proto2.Message) ||
|
||||
!(actual instanceof goog.proto2.Message)) {
|
||||
goog.testing.asserts.raiseException(failureSummary,
|
||||
'Bad arguments were passed to goog.testing.proto2.assertEquals');
|
||||
}
|
||||
if (expected.constructor != actual.constructor) {
|
||||
goog.testing.asserts.raiseException(failureSummary,
|
||||
'Message type mismatch: ' + expected.getDescriptor().getFullName() +
|
||||
' != ' + actual.getDescriptor().getFullName());
|
||||
}
|
||||
var diff = goog.testing.proto2.findDifferences_(expected, actual, '');
|
||||
if (diff) {
|
||||
goog.testing.asserts.raiseException(failureSummary, diff);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,179 @@
|
||||
// Copyright 2011 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 PseudoRandom provides a mechanism for generating deterministic
|
||||
* psuedo random numbers based on a seed. Based on the Park-Miller algorithm.
|
||||
* See http://dx.doi.org/10.1145%2F63039.63042 for details.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.PseudoRandom');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class for unit testing code that uses Math.random. Generates deterministic
|
||||
* random numbers.
|
||||
*
|
||||
* @param {number=} opt_seed The seed to use.
|
||||
* @param {boolean=} opt_install Whether to install the PseudoRandom at
|
||||
* construction time.
|
||||
* @extends {goog.Disposable}
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.PseudoRandom = function(opt_seed, opt_install) {
|
||||
goog.Disposable.call(this);
|
||||
|
||||
if (!goog.isDef(opt_seed)) {
|
||||
opt_seed = goog.testing.PseudoRandom.seedUniquifier_++ + goog.now();
|
||||
}
|
||||
this.seed(opt_seed);
|
||||
|
||||
if (opt_install) {
|
||||
this.install();
|
||||
}
|
||||
};
|
||||
goog.inherits(goog.testing.PseudoRandom, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* Helps create a unique seed.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PseudoRandom.seedUniquifier_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Constant used as part of the algorithm.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.PseudoRandom.A = 48271;
|
||||
|
||||
|
||||
/**
|
||||
* Constant used as part of the algorithm. 2^31 - 1.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.PseudoRandom.M = 2147483647;
|
||||
|
||||
|
||||
/**
|
||||
* Constant used as part of the algorithm. It is equal to M / A.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.PseudoRandom.Q = 44488;
|
||||
|
||||
|
||||
/**
|
||||
* Constant used as part of the algorithm. It is equal to M % A.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.PseudoRandom.R = 3399;
|
||||
|
||||
|
||||
/**
|
||||
* Constant used as part of the algorithm to get values from range [0, 1).
|
||||
* @type {number}
|
||||
*/
|
||||
goog.testing.PseudoRandom.ONE_OVER_M_MINUS_ONE =
|
||||
1.0 / (goog.testing.PseudoRandom.M - 1);
|
||||
|
||||
|
||||
/**
|
||||
* The seed of the random sequence and also the next returned value (before
|
||||
* normalization). Must be between 1 and M - 1 (inclusive).
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PseudoRandom.prototype.seed_ = 1;
|
||||
|
||||
|
||||
/**
|
||||
* Whether this PseudoRandom has been installed.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PseudoRandom.prototype.installed_;
|
||||
|
||||
|
||||
/**
|
||||
* The original Math.random function.
|
||||
* @type {function(): number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.PseudoRandom.prototype.mathRandom_;
|
||||
|
||||
|
||||
/**
|
||||
* Installs this PseudoRandom as the system number generator.
|
||||
*/
|
||||
goog.testing.PseudoRandom.prototype.install = function() {
|
||||
if (!this.installed_) {
|
||||
this.mathRandom_ = Math.random;
|
||||
Math.random = goog.bind(this.random, this);
|
||||
this.installed_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.PseudoRandom.prototype.disposeInternal = function() {
|
||||
goog.testing.PseudoRandom.superClass_.disposeInternal.call(this);
|
||||
this.uninstall();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Uninstalls the PseudoRandom.
|
||||
*/
|
||||
goog.testing.PseudoRandom.prototype.uninstall = function() {
|
||||
if (this.installed_) {
|
||||
Math.random = this.mathRandom_;
|
||||
this.installed_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Seed the generator.
|
||||
*
|
||||
* @param {number=} seed The seed to use.
|
||||
*/
|
||||
goog.testing.PseudoRandom.prototype.seed = function(seed) {
|
||||
this.seed_ = seed % (goog.testing.PseudoRandom.M - 1);
|
||||
if (this.seed_ <= 0) {
|
||||
this.seed_ += goog.testing.PseudoRandom.M - 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} The next number in the sequence.
|
||||
*/
|
||||
goog.testing.PseudoRandom.prototype.random = function() {
|
||||
var hi = Math.floor(this.seed_ / goog.testing.PseudoRandom.Q);
|
||||
var lo = this.seed_ % goog.testing.PseudoRandom.Q;
|
||||
var test = goog.testing.PseudoRandom.A * lo -
|
||||
goog.testing.PseudoRandom.R * hi;
|
||||
if (test > 0) {
|
||||
this.seed_ = test;
|
||||
} else {
|
||||
this.seed_ = test + goog.testing.PseudoRandom.M;
|
||||
}
|
||||
return (this.seed_ - 1) * goog.testing.PseudoRandom.ONE_OVER_M_MINUS_ONE;
|
||||
};
|
||||
@@ -0,0 +1,201 @@
|
||||
// Copyright 2010 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 Helper class for recording the calls of a function.
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
* var stubs = new goog.testing.PropertyReplacer();
|
||||
*
|
||||
* function tearDown() {
|
||||
* stubs.reset();
|
||||
* }
|
||||
*
|
||||
* function testShuffle() {
|
||||
* stubs.set(Math, 'random', goog.testing.recordFunction(Math.random));
|
||||
* var arr = shuffle([1, 2, 3, 4, 5]);
|
||||
* assertSameElements([1, 2, 3, 4, 5], arr);
|
||||
* assertEquals(4, Math.random.getCallCount());
|
||||
* }
|
||||
*
|
||||
* function testOpenDialog() {
|
||||
* stubs.set(goog.ui, 'Dialog',
|
||||
* goog.testing.recordConstructor(goog.ui.Dialog));
|
||||
* openConfirmDialog();
|
||||
* var lastDialogInstance = goog.ui.Dialog.getLastCall().getThis();
|
||||
* assertEquals('confirm', lastDialogInstance.getTitle());
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.FunctionCall');
|
||||
goog.provide('goog.testing.recordConstructor');
|
||||
goog.provide('goog.testing.recordFunction');
|
||||
|
||||
|
||||
/**
|
||||
* Wraps the function into another one which calls the inner function and
|
||||
* records its calls. The recorded function will have 3 static methods:
|
||||
* {@code getCallCount}, {@code getCalls} and {@code getLastCall} but won't
|
||||
* inherit the original function's prototype and static fields.
|
||||
*
|
||||
* @param {!Function=} opt_f The function to wrap and record. Defaults to
|
||||
* {@link goog.nullFunction}.
|
||||
* @return {!Function} The wrapped function.
|
||||
*/
|
||||
goog.testing.recordFunction = function(opt_f) {
|
||||
var f = opt_f || goog.nullFunction;
|
||||
var calls = [];
|
||||
|
||||
function recordedFunction() {
|
||||
try {
|
||||
var ret = f.apply(this, arguments);
|
||||
calls.push(new goog.testing.FunctionCall(f, this, arguments, ret, null));
|
||||
return ret;
|
||||
} catch (err) {
|
||||
calls.push(new goog.testing.FunctionCall(f, this, arguments, undefined,
|
||||
err));
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number} Total number of calls.
|
||||
*/
|
||||
recordedFunction.getCallCount = function() {
|
||||
return calls.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {!Array.<!goog.testing.FunctionCall>} All calls of the recorded
|
||||
* function.
|
||||
*/
|
||||
recordedFunction.getCalls = function() {
|
||||
return calls;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {goog.testing.FunctionCall} Last call of the recorded function or
|
||||
* null if it hasn't been called.
|
||||
*/
|
||||
recordedFunction.getLastCall = function() {
|
||||
return calls[calls.length - 1] || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns and removes the last call of the recorded function.
|
||||
* @return {goog.testing.FunctionCall} Last call of the recorded function or
|
||||
* null if it hasn't been called.
|
||||
*/
|
||||
recordedFunction.popLastCall = function() {
|
||||
return calls.pop() || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resets the recorded function and removes all calls.
|
||||
*/
|
||||
recordedFunction.reset = function() {
|
||||
calls.length = 0;
|
||||
};
|
||||
|
||||
return recordedFunction;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as {@link goog.testing.recordFunction} but the recorded function will
|
||||
* have the same prototype and static fields as the original one. It can be
|
||||
* used with constructors.
|
||||
*
|
||||
* @param {!Function} ctor The function to wrap and record.
|
||||
* @return {!Function} The wrapped function.
|
||||
*/
|
||||
goog.testing.recordConstructor = function(ctor) {
|
||||
var recordedConstructor = goog.testing.recordFunction(ctor);
|
||||
recordedConstructor.prototype = ctor.prototype;
|
||||
goog.mixin(recordedConstructor, ctor);
|
||||
return recordedConstructor;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Struct for a single function call.
|
||||
* @param {!Function} func The called function.
|
||||
* @param {!Object} thisContext {@code this} context of called function.
|
||||
* @param {!Arguments} args Arguments of the called function.
|
||||
* @param {*} ret Return value of the function or undefined in case of error.
|
||||
* @param {*} error The error thrown by the function or null if none.
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.FunctionCall = function(func, thisContext, args, ret, error) {
|
||||
this.function_ = func;
|
||||
this.thisContext_ = thisContext;
|
||||
this.arguments_ = Array.prototype.slice.call(args);
|
||||
this.returnValue_ = ret;
|
||||
this.error_ = error;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!Function} The called function.
|
||||
*/
|
||||
goog.testing.FunctionCall.prototype.getFunction = function() {
|
||||
return this.function_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!Object} {@code this} context of called function. It is the same as
|
||||
* the created object if the function is a constructor.
|
||||
*/
|
||||
goog.testing.FunctionCall.prototype.getThis = function() {
|
||||
return this.thisContext_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!Array} Arguments of the called function.
|
||||
*/
|
||||
goog.testing.FunctionCall.prototype.getArguments = function() {
|
||||
return this.arguments_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the nth argument of the called function.
|
||||
* @param {number} index 0-based index of the argument.
|
||||
* @return {*} The argument value or undefined if there is no such argument.
|
||||
*/
|
||||
goog.testing.FunctionCall.prototype.getArgument = function(index) {
|
||||
return this.arguments_[index];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {*} Return value of the function or undefined in case of error.
|
||||
*/
|
||||
goog.testing.FunctionCall.prototype.getReturnValue = function() {
|
||||
return this.returnValue_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {*} The error thrown by the function or null if none.
|
||||
*/
|
||||
goog.testing.FunctionCall.prototype.getError = function() {
|
||||
return this.error_;
|
||||
};
|
||||
@@ -0,0 +1,122 @@
|
||||
// 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 Utility for sharding tests.
|
||||
*
|
||||
* Usage instructions:
|
||||
* <ol>
|
||||
* <li>Instead of writing your large test in foo_test.html, write it in
|
||||
* foo_test_template.html</li>
|
||||
* <li>Add a call to {@code goog.testing.ShardingTestCase.shardByFileName()}
|
||||
* near the top of your test, before any test cases or setup methods.</li>
|
||||
* <li>Symlink foo_test_template.html into different sharded test files
|
||||
* named foo_1of4_test.html, foo_2of4_test.html, etc, using `ln -s`.</li>
|
||||
* <li>Add the symlinks as foo_1of4_test.html.
|
||||
* In perforce, run the command `g4 add foo_1of4_test.html` followed
|
||||
* by `g4 reopen -t symlink foo_1of4_test.html` so that perforce treats the file
|
||||
* as a symlink
|
||||
* </li>
|
||||
* </ol>
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.ShardingTestCase');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.testing.TestCase');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A test case that runs tests in per-file shards.
|
||||
* @param {number} shardIndex Shard index for this page,
|
||||
* <strong>1-indexed</strong>.
|
||||
* @param {number} numShards Number of shards to split up test cases into.
|
||||
* @extends {goog.testing.TestCase}
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.ShardingTestCase = function(shardIndex, numShards, opt_name) {
|
||||
goog.base(this, opt_name);
|
||||
|
||||
goog.asserts.assert(shardIndex > 0, 'Shard index should be positive');
|
||||
goog.asserts.assert(numShards > 0, 'Number of shards should be positive');
|
||||
goog.asserts.assert(shardIndex <= numShards,
|
||||
'Shard index out of bounds');
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.shardIndex_ = shardIndex;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.numShards_ = numShards;
|
||||
};
|
||||
goog.inherits(goog.testing.ShardingTestCase, goog.testing.TestCase);
|
||||
|
||||
|
||||
/**
|
||||
* Whether we've actually partitioned the tests yet. We may execute twice
|
||||
* ('Run again without reloading') without failing.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ShardingTestCase.prototype.sharded_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Installs a runTests global function that goog.testing.JsUnit will use to
|
||||
* run tests, which will run a single shard of the tests present on the page.
|
||||
* @override
|
||||
*/
|
||||
goog.testing.ShardingTestCase.prototype.runTests = function() {
|
||||
if (!this.sharded_) {
|
||||
var numTests = this.getCount();
|
||||
goog.asserts.assert(numTests >= this.numShards_,
|
||||
'Must have at least as many tests as shards!');
|
||||
var shardSize = Math.ceil(numTests / this.numShards_);
|
||||
var startIndex = (this.shardIndex_ - 1) * shardSize;
|
||||
var endIndex = startIndex + shardSize;
|
||||
goog.asserts.assert(this.order == goog.testing.TestCase.Order.SORTED,
|
||||
'Only SORTED order is allowed for sharded tests');
|
||||
this.setTests(this.getTests().slice(startIndex, endIndex));
|
||||
this.sharded_ = true;
|
||||
}
|
||||
|
||||
// Call original runTests method to execute the tests.
|
||||
goog.base(this, 'runTests');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Shards tests based on the test filename. Assumes that the filename is
|
||||
* formatted like 'foo_1of5_test.html'.
|
||||
* @param {string=} opt_name A descriptive name for the test case.
|
||||
*/
|
||||
goog.testing.ShardingTestCase.shardByFileName = function(opt_name) {
|
||||
var path = window.location.pathname;
|
||||
var shardMatch = path.match(/_(\d+)of(\d+)_test\.html/);
|
||||
goog.asserts.assert(shardMatch,
|
||||
'Filename must be of the form "foo_1of5_test.html"');
|
||||
var shardIndex = parseInt(shardMatch[1], 10);
|
||||
var numShards = parseInt(shardMatch[2], 10);
|
||||
|
||||
var testCase = new goog.testing.ShardingTestCase(
|
||||
shardIndex, numShards, opt_name);
|
||||
goog.testing.TestCase.initializeTestRunner(testCase);
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 This module simplifies testing code which uses stateful
|
||||
* singletons. {@code goog.testing.singleton.reset} resets all instances, so
|
||||
* next time when {@code getInstance} is called, a new instance is created.
|
||||
* It's recommended to reset the singletons in {@code tearDown} to prevent
|
||||
* interference between subsequent tests.
|
||||
*
|
||||
* The {@code goog.testing.singleton} functions expect that the goog.DEBUG flag
|
||||
* is enabled, and the tests are either uncompiled or compiled without renaming.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.singleton');
|
||||
|
||||
|
||||
/**
|
||||
* Deletes all singleton instances, so {@code getInstance} will return a new
|
||||
* instance on next call.
|
||||
*/
|
||||
goog.testing.singleton.reset = function() {
|
||||
var singletons = goog.getObjectByName('goog.instantiatedSingletons_');
|
||||
var ctor;
|
||||
while (ctor = singletons.pop()) {
|
||||
delete ctor.instance_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated Please use {@code goog.addSingletonGetter}.
|
||||
*/
|
||||
goog.testing.singleton.addSingletonGetter = goog.addSingletonGetter;
|
||||
555
float-no-zero/closure-library/closure/goog/testing/stacktrace.js
Normal file
555
float-no-zero/closure-library/closure/goog/testing/stacktrace.js
Normal file
@@ -0,0 +1,555 @@
|
||||
// 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 Tools for parsing and pretty printing error stack traces.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.stacktrace');
|
||||
goog.provide('goog.testing.stacktrace.Frame');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class representing one stack frame.
|
||||
* @param {string} context Context object, empty in case of global functions or
|
||||
* if the browser doesn't provide this information.
|
||||
* @param {string} name Function name, empty in case of anonymous functions.
|
||||
* @param {string} alias Alias of the function if available. For example the
|
||||
* function name will be 'c' and the alias will be 'b' if the function is
|
||||
* defined as <code>a.b = function c() {};</code>.
|
||||
* @param {string} args Arguments of the function in parentheses if available.
|
||||
* @param {string} path File path or URL including line number and optionally
|
||||
* column number separated by colons.
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.stacktrace.Frame = function(context, name, alias, args, path) {
|
||||
this.context_ = context;
|
||||
this.name_ = name;
|
||||
this.alias_ = alias;
|
||||
this.args_ = args;
|
||||
this.path_ = path;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The function name or empty string if the function is
|
||||
* anonymous and the object field which it's assigned to is unknown.
|
||||
*/
|
||||
goog.testing.stacktrace.Frame.prototype.getName = function() {
|
||||
return this.name_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the stack frame contains an anonymous function.
|
||||
*/
|
||||
goog.testing.stacktrace.Frame.prototype.isAnonymous = function() {
|
||||
return !this.name_ || this.context_ == '[object Object]';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Brings one frame of the stack trace into a common format across browsers.
|
||||
* @return {string} Pretty printed stack frame.
|
||||
*/
|
||||
goog.testing.stacktrace.Frame.prototype.toCanonicalString = function() {
|
||||
var htmlEscape = goog.testing.stacktrace.htmlEscape_;
|
||||
var deobfuscate = goog.testing.stacktrace.maybeDeobfuscateFunctionName_;
|
||||
|
||||
var canonical = [
|
||||
this.context_ ? htmlEscape(this.context_) + '.' : '',
|
||||
this.name_ ? htmlEscape(deobfuscate(this.name_)) : 'anonymous',
|
||||
htmlEscape(this.args_),
|
||||
this.alias_ ? ' [as ' + htmlEscape(deobfuscate(this.alias_)) + ']' : ''
|
||||
];
|
||||
|
||||
if (this.path_) {
|
||||
canonical.push(' at ');
|
||||
// If Closure Inspector is installed and running, then convert the line
|
||||
// into a source link for displaying the code in Firebug.
|
||||
if (goog.testing.stacktrace.isClosureInspectorActive_()) {
|
||||
var lineNumber = this.path_.match(/\d+$/)[0];
|
||||
canonical.push('<a href="" onclick="CLOSURE_INSPECTOR___.showLine(\'',
|
||||
htmlEscape(this.path_), '\', \'', lineNumber, '\'); return false">',
|
||||
htmlEscape(this.path_), '</a>');
|
||||
} else {
|
||||
canonical.push(htmlEscape(this.path_));
|
||||
}
|
||||
}
|
||||
return canonical.join('');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Maximum number of steps while the call chain is followed.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.MAX_DEPTH_ = 20;
|
||||
|
||||
|
||||
/**
|
||||
* Maximum length of a string that can be matched with a RegExp on
|
||||
* Firefox 3x. Exceeding this approximate length will cause string.match
|
||||
* to exceed Firefox's stack quota. This situation can be encountered
|
||||
* when goog.globalEval is invoked with a long argument; such as
|
||||
* when loading a module.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.MAX_FIREFOX_FRAMESTRING_LENGTH_ = 500000;
|
||||
|
||||
|
||||
/**
|
||||
* RegExp pattern for JavaScript identifiers. We don't support Unicode
|
||||
* identifiers defined in ECMAScript v3.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.IDENTIFIER_PATTERN_ = '[a-zA-Z_$][\\w$]*';
|
||||
|
||||
|
||||
/**
|
||||
* RegExp pattern for function name alias in the Chrome stack trace.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.CHROME_ALIAS_PATTERN_ =
|
||||
'(?: \\[as (' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')\\])?';
|
||||
|
||||
|
||||
/**
|
||||
* RegExp pattern for function names and constructor calls in the Chrome stack
|
||||
* trace.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.CHROME_FUNCTION_NAME_PATTERN_ =
|
||||
'(?:new )?(?:' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ +
|
||||
'|<anonymous>)';
|
||||
|
||||
|
||||
/**
|
||||
* RegExp pattern for function call in the Chrome stack trace.
|
||||
* Creates 3 submatches with context object (optional), function name and
|
||||
* function alias (optional).
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.CHROME_FUNCTION_CALL_PATTERN_ =
|
||||
' (?:(.*?)\\.)?(' + goog.testing.stacktrace.CHROME_FUNCTION_NAME_PATTERN_ +
|
||||
')' + goog.testing.stacktrace.CHROME_ALIAS_PATTERN_;
|
||||
|
||||
|
||||
/**
|
||||
* RegExp pattern for an URL + position inside the file.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.URL_PATTERN_ =
|
||||
'((?:http|https|file)://[^\\s)]+|javascript:.*)';
|
||||
|
||||
|
||||
/**
|
||||
* RegExp pattern for an URL + line number + column number in Chrome.
|
||||
* The URL is either in submatch 1 or submatch 2.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.CHROME_URL_PATTERN_ = ' (?:' +
|
||||
'\\(unknown source\\)' + '|' +
|
||||
'\\(native\\)' + '|' +
|
||||
'\\((?:eval at )?' + goog.testing.stacktrace.URL_PATTERN_ + '\\)' + '|' +
|
||||
goog.testing.stacktrace.URL_PATTERN_ + ')';
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression for parsing one stack frame in Chrome.
|
||||
* @type {!RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.CHROME_STACK_FRAME_REGEXP_ = new RegExp('^ at' +
|
||||
'(?:' + goog.testing.stacktrace.CHROME_FUNCTION_CALL_PATTERN_ + ')?' +
|
||||
goog.testing.stacktrace.CHROME_URL_PATTERN_ + '$');
|
||||
|
||||
|
||||
/**
|
||||
* RegExp pattern for function call in the Firefox stack trace.
|
||||
* Creates 2 submatches with function name (optional) and arguments.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.FIREFOX_FUNCTION_CALL_PATTERN_ =
|
||||
'(' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')?' +
|
||||
'(\\(.*\\))?@';
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression for parsing one stack frame in Firefox.
|
||||
* @type {!RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.FIREFOX_STACK_FRAME_REGEXP_ = new RegExp('^' +
|
||||
goog.testing.stacktrace.FIREFOX_FUNCTION_CALL_PATTERN_ +
|
||||
'(?::0|' + goog.testing.stacktrace.URL_PATTERN_ + ')$');
|
||||
|
||||
|
||||
/**
|
||||
* RegExp pattern for an anonymous function call in an Opera stack frame.
|
||||
* Creates 2 (optional) submatches: the context object and function name.
|
||||
* @type {string}
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.OPERA_ANONYMOUS_FUNCTION_NAME_PATTERN_ =
|
||||
'<anonymous function(?:\\: ' +
|
||||
'(?:(' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ +
|
||||
'(?:\\.' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')*)\\.)?' +
|
||||
'(' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + '))?>';
|
||||
|
||||
|
||||
/**
|
||||
* RegExp pattern for a function call in an Opera stack frame.
|
||||
* Creates 4 (optional) submatches: the function name (if not anonymous),
|
||||
* the aliased context object and function name (if anonymous), and the
|
||||
* function call arguments.
|
||||
* @type {string}
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.OPERA_FUNCTION_CALL_PATTERN_ =
|
||||
'(?:(?:(' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')|' +
|
||||
goog.testing.stacktrace.OPERA_ANONYMOUS_FUNCTION_NAME_PATTERN_ +
|
||||
')(\\(.*\\)))?@';
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression for parsing on stack frame in Opera 11.68+
|
||||
* @type {!RegExp}
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.OPERA_STACK_FRAME_REGEXP_ = new RegExp('^' +
|
||||
goog.testing.stacktrace.OPERA_FUNCTION_CALL_PATTERN_ +
|
||||
goog.testing.stacktrace.URL_PATTERN_ + '?$');
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression for finding the function name in its source.
|
||||
* @type {!RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.FUNCTION_SOURCE_REGEXP_ = new RegExp(
|
||||
'^function (' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')');
|
||||
|
||||
|
||||
/**
|
||||
* RegExp pattern for function call in a IE stack trace. This expression allows
|
||||
* for identifiers like 'Anonymous function', 'eval code', and 'Global code'.
|
||||
* @type {string}
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.IE_FUNCTION_CALL_PATTERN_ = '(' +
|
||||
goog.testing.stacktrace.IDENTIFIER_PATTERN_ + '(?:\\s+\\w+)*)';
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression for parsing a stack frame in IE.
|
||||
* @type {!RegExp}
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.IE_STACK_FRAME_REGEXP_ = new RegExp('^ at ' +
|
||||
goog.testing.stacktrace.IE_FUNCTION_CALL_PATTERN_ +
|
||||
'\\s*\\((eval code:[^)]*|' + goog.testing.stacktrace.URL_PATTERN_ +
|
||||
')\\)?$');
|
||||
|
||||
|
||||
/**
|
||||
* Creates a stack trace by following the call chain. Based on
|
||||
* {@link goog.debug.getStacktrace}.
|
||||
* @return {!Array.<!goog.testing.stacktrace.Frame>} Stack frames.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.followCallChain_ = function() {
|
||||
var frames = [];
|
||||
var fn = arguments.callee.caller;
|
||||
var depth = 0;
|
||||
|
||||
while (fn && depth < goog.testing.stacktrace.MAX_DEPTH_) {
|
||||
var fnString = Function.prototype.toString.call(fn);
|
||||
var match = fnString.match(goog.testing.stacktrace.FUNCTION_SOURCE_REGEXP_);
|
||||
var functionName = match ? match[1] : '';
|
||||
|
||||
var argsBuilder = ['('];
|
||||
if (fn.arguments) {
|
||||
for (var i = 0; i < fn.arguments.length; i++) {
|
||||
var arg = fn.arguments[i];
|
||||
if (i > 0) {
|
||||
argsBuilder.push(', ');
|
||||
}
|
||||
if (goog.isString(arg)) {
|
||||
argsBuilder.push('"', arg, '"');
|
||||
} else {
|
||||
// Some args are mocks, and we don't want to fail from them not having
|
||||
// expected a call to toString, so instead insert a static string.
|
||||
if (arg && arg['$replay']) {
|
||||
argsBuilder.push('goog.testing.Mock');
|
||||
} else {
|
||||
argsBuilder.push(String(arg));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Opera 10 doesn't know the arguments of native functions.
|
||||
argsBuilder.push('unknown');
|
||||
}
|
||||
argsBuilder.push(')');
|
||||
var args = argsBuilder.join('');
|
||||
|
||||
frames.push(new goog.testing.stacktrace.Frame('', functionName, '', args,
|
||||
''));
|
||||
|
||||
/** @preserveTry */
|
||||
try {
|
||||
fn = fn.caller;
|
||||
} catch (e) {
|
||||
break;
|
||||
}
|
||||
depth++;
|
||||
}
|
||||
|
||||
return frames;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parses one stack frame.
|
||||
* @param {string} frameStr The stack frame as string.
|
||||
* @return {goog.testing.stacktrace.Frame} Stack frame object or null if the
|
||||
* parsing failed.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.parseStackFrame_ = function(frameStr) {
|
||||
var m = frameStr.match(goog.testing.stacktrace.CHROME_STACK_FRAME_REGEXP_);
|
||||
if (m) {
|
||||
return new goog.testing.stacktrace.Frame(m[1] || '', m[2] || '', m[3] || '',
|
||||
'', m[4] || m[5] || '');
|
||||
}
|
||||
|
||||
if (frameStr.length >
|
||||
goog.testing.stacktrace.MAX_FIREFOX_FRAMESTRING_LENGTH_) {
|
||||
return goog.testing.stacktrace.parseLongFirefoxFrame_(frameStr);
|
||||
}
|
||||
|
||||
m = frameStr.match(goog.testing.stacktrace.FIREFOX_STACK_FRAME_REGEXP_);
|
||||
if (m) {
|
||||
return new goog.testing.stacktrace.Frame('', m[1] || '', '', m[2] || '',
|
||||
m[3] || '');
|
||||
}
|
||||
|
||||
m = frameStr.match(goog.testing.stacktrace.OPERA_STACK_FRAME_REGEXP_);
|
||||
if (m) {
|
||||
return new goog.testing.stacktrace.Frame(m[2] || '', m[1] || m[3] || '',
|
||||
'', m[4] || '', m[5] || '');
|
||||
}
|
||||
|
||||
m = frameStr.match(goog.testing.stacktrace.IE_STACK_FRAME_REGEXP_);
|
||||
if (m) {
|
||||
return new goog.testing.stacktrace.Frame('', m[1] || '', '', '',
|
||||
m[2] || '');
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parses a long firefox stack frame.
|
||||
* @param {string} frameStr The stack frame as string.
|
||||
* @return {!goog.testing.stacktrace.Frame} Stack frame object.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.parseLongFirefoxFrame_ = function(frameStr) {
|
||||
var firstParen = frameStr.indexOf('(');
|
||||
var lastAmpersand = frameStr.lastIndexOf('@');
|
||||
var lastColon = frameStr.lastIndexOf(':');
|
||||
var functionName = '';
|
||||
if ((firstParen >= 0) && (firstParen < lastAmpersand)) {
|
||||
functionName = frameStr.substring(0, firstParen);
|
||||
}
|
||||
var loc = '';
|
||||
if ((lastAmpersand >= 0) && (lastAmpersand + 1 < lastColon)) {
|
||||
loc = frameStr.substring(lastAmpersand + 1);
|
||||
}
|
||||
var args = '';
|
||||
if ((firstParen >= 0 && lastAmpersand > 0) &&
|
||||
(firstParen < lastAmpersand)) {
|
||||
args = frameStr.substring(firstParen, lastAmpersand);
|
||||
}
|
||||
return new goog.testing.stacktrace.Frame('', functionName, '', args, loc);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Function to deobfuscate function names.
|
||||
* @type {function(string): string}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.deobfuscateFunctionName_;
|
||||
|
||||
|
||||
/**
|
||||
* Sets function to deobfuscate function names.
|
||||
* @param {function(string): string} fn function to deobfuscate function names.
|
||||
*/
|
||||
goog.testing.stacktrace.setDeobfuscateFunctionName = function(fn) {
|
||||
goog.testing.stacktrace.deobfuscateFunctionName_ = fn;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deobfuscates a compiled function name with the function passed to
|
||||
* {@link #setDeobfuscateFunctionName}. Returns the original function name if
|
||||
* the deobfuscator hasn't been set.
|
||||
* @param {string} name The function name to deobfuscate.
|
||||
* @return {string} The deobfuscated function name.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.maybeDeobfuscateFunctionName_ = function(name) {
|
||||
return goog.testing.stacktrace.deobfuscateFunctionName_ ?
|
||||
goog.testing.stacktrace.deobfuscateFunctionName_(name) : name;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the Closure Inspector is active.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.isClosureInspectorActive_ = function() {
|
||||
return Boolean(goog.global['CLOSURE_INSPECTOR___'] &&
|
||||
goog.global['CLOSURE_INSPECTOR___']['supportsJSUnit']);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Escapes the special character in HTML.
|
||||
* @param {string} text Plain text.
|
||||
* @return {string} Escaped text.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.htmlEscape_ = function(text) {
|
||||
return text.replace(/&/g, '&').
|
||||
replace(/</g, '<').
|
||||
replace(/>/g, '>').
|
||||
replace(/"/g, '"');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts the stack frames into canonical format. Chops the beginning and the
|
||||
* end of it which come from the testing environment, not from the test itself.
|
||||
* @param {!Array.<goog.testing.stacktrace.Frame>} frames The frames.
|
||||
* @return {string} Canonical, pretty printed stack trace.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.framesToString_ = function(frames) {
|
||||
// Removes the anonymous calls from the end of the stack trace (they come
|
||||
// from testrunner.js, testcase.js and asserts.js), so the stack trace will
|
||||
// end with the test... method.
|
||||
var lastIndex = frames.length - 1;
|
||||
while (frames[lastIndex] && frames[lastIndex].isAnonymous()) {
|
||||
lastIndex--;
|
||||
}
|
||||
|
||||
// Removes the beginning of the stack trace until the call of the private
|
||||
// _assert function (inclusive), so the stack trace will begin with a public
|
||||
// asserter. Does nothing if _assert is not present in the stack trace.
|
||||
var privateAssertIndex = -1;
|
||||
for (var i = 0; i < frames.length; i++) {
|
||||
if (frames[i] && frames[i].getName() == '_assert') {
|
||||
privateAssertIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var canonical = [];
|
||||
for (var i = privateAssertIndex + 1; i <= lastIndex; i++) {
|
||||
canonical.push('> ');
|
||||
if (frames[i]) {
|
||||
canonical.push(frames[i].toCanonicalString());
|
||||
} else {
|
||||
canonical.push('(unknown)');
|
||||
}
|
||||
canonical.push('\n');
|
||||
}
|
||||
return canonical.join('');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parses the browser's native stack trace.
|
||||
* @param {string} stack Stack trace.
|
||||
* @return {!Array.<goog.testing.stacktrace.Frame>} Stack frames. The
|
||||
* unrecognized frames will be nulled out.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.stacktrace.parse_ = function(stack) {
|
||||
var lines = stack.replace(/\s*$/, '').split('\n');
|
||||
var frames = [];
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
frames.push(goog.testing.stacktrace.parseStackFrame_(lines[i]));
|
||||
}
|
||||
return frames;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Brings the stack trace into a common format across browsers.
|
||||
* @param {string} stack Browser-specific stack trace.
|
||||
* @return {string} Same stack trace in common format.
|
||||
*/
|
||||
goog.testing.stacktrace.canonicalize = function(stack) {
|
||||
var frames = goog.testing.stacktrace.parse_(stack);
|
||||
return goog.testing.stacktrace.framesToString_(frames);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the native stack trace if available otherwise follows the call chain.
|
||||
* @return {string} The stack trace in canonical format.
|
||||
*/
|
||||
goog.testing.stacktrace.get = function() {
|
||||
var stack = '';
|
||||
// IE10 will only create a stack trace when the Error is thrown.
|
||||
// We use null.x() to throw an exception because the closure compiler may
|
||||
// replace "throw" with a function call in an attempt to minimize the binary
|
||||
// size, which in turn has the side effect of adding an unwanted stack frame.
|
||||
try {
|
||||
null.x();
|
||||
} catch (e) {
|
||||
stack = e.stack;
|
||||
}
|
||||
|
||||
var frames = stack ? goog.testing.stacktrace.parse_(stack) :
|
||||
goog.testing.stacktrace.followCallChain_();
|
||||
return goog.testing.stacktrace.framesToString_(frames);
|
||||
};
|
||||
|
||||
|
||||
goog.exportSymbol('setDeobfuscateFunctionName',
|
||||
goog.testing.stacktrace.setDeobfuscateFunctionName);
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2012 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 Provides a fake storage mechanism for testing.
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.storage.FakeMechanism');
|
||||
goog.setTestOnly('goog.testing.storage.FakeMechanism');
|
||||
|
||||
goog.require('goog.storage.mechanism.IterableMechanism');
|
||||
goog.require('goog.structs.Map');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a fake iterable mechanism.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.storage.mechanism.IterableMechanism}
|
||||
*/
|
||||
goog.testing.storage.FakeMechanism = function() {
|
||||
/**
|
||||
* @type {goog.structs.Map}
|
||||
* @private
|
||||
*/
|
||||
this.storage_ = new goog.structs.Map();
|
||||
};
|
||||
goog.inherits(goog.testing.storage.FakeMechanism,
|
||||
goog.storage.mechanism.IterableMechanism);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.storage.FakeMechanism.prototype.set = function(key, value) {
|
||||
this.storage_.set(key, value);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.storage.FakeMechanism.prototype.get = function(key) {
|
||||
return /** @type {?string} */ (
|
||||
this.storage_.get(key, null /* default value */));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.storage.FakeMechanism.prototype.remove = function(key) {
|
||||
this.storage_.remove(key);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.storage.FakeMechanism.prototype.__iterator__ = function(opt_keys) {
|
||||
return this.storage_.__iterator__(opt_keys);
|
||||
};
|
||||
128
float-no-zero/closure-library/closure/goog/testing/strictmock.js
Normal file
128
float-no-zero/closure-library/closure/goog/testing/strictmock.js
Normal file
@@ -0,0 +1,128 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview This file defines a strict mock implementation.
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.StrictMock');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.testing.Mock');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This is a mock that verifies that methods are called in the order that they
|
||||
* are specified during the recording phase. Since it verifies order, it
|
||||
* follows 'fail fast' semantics. If it detects a deviation from the
|
||||
* expectations, it will throw an exception and not wait for verify to be
|
||||
* called.
|
||||
* @param {Object} objectToMock The object to mock.
|
||||
* @param {boolean=} opt_mockStaticMethods An optional argument denoting that
|
||||
* a mock should be constructed from the static functions of a class.
|
||||
* @param {boolean=} opt_createProxy An optional argument denoting that
|
||||
* a proxy for the target mock should be created.
|
||||
* @constructor
|
||||
* @extends {goog.testing.Mock}
|
||||
*/
|
||||
goog.testing.StrictMock = function(objectToMock, opt_mockStaticMethods,
|
||||
opt_createProxy) {
|
||||
goog.testing.Mock.call(this, objectToMock, opt_mockStaticMethods,
|
||||
opt_createProxy);
|
||||
|
||||
/**
|
||||
* An array of MockExpectations.
|
||||
* @type {Array.<goog.testing.MockExpectation>}
|
||||
* @private
|
||||
*/
|
||||
this.$expectations_ = [];
|
||||
};
|
||||
goog.inherits(goog.testing.StrictMock, goog.testing.Mock);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.StrictMock.prototype.$recordExpectation = function() {
|
||||
this.$expectations_.push(this.$pendingExpectation);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.StrictMock.prototype.$recordCall = function(name, args) {
|
||||
if (this.$expectations_.length == 0) {
|
||||
this.$throwCallException(name, args);
|
||||
}
|
||||
|
||||
// If the current expectation has a different name, make sure it was called
|
||||
// enough and then discard it. We're through with it.
|
||||
var currentExpectation = this.$expectations_[0];
|
||||
while (!this.$verifyCall(currentExpectation, name, args)) {
|
||||
|
||||
// This might be an item which has passed its min, and we can now
|
||||
// look past it, or it might be below its min and generate an error.
|
||||
if (currentExpectation.actualCalls < currentExpectation.minCalls) {
|
||||
this.$throwCallException(name, args, currentExpectation);
|
||||
}
|
||||
|
||||
this.$expectations_.shift();
|
||||
if (this.$expectations_.length < 1) {
|
||||
// Nothing left, but this may be a failed attempt to call the previous
|
||||
// item on the list, which may have been between its min and max.
|
||||
this.$throwCallException(name, args, currentExpectation);
|
||||
}
|
||||
currentExpectation = this.$expectations_[0];
|
||||
}
|
||||
|
||||
if (currentExpectation.maxCalls == 0) {
|
||||
this.$throwCallException(name, args);
|
||||
}
|
||||
|
||||
currentExpectation.actualCalls++;
|
||||
// If we hit the max number of calls for this expectation, we're finished
|
||||
// with it.
|
||||
if (currentExpectation.actualCalls == currentExpectation.maxCalls) {
|
||||
this.$expectations_.shift();
|
||||
}
|
||||
|
||||
return this.$do(currentExpectation, args);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.StrictMock.prototype.$reset = function() {
|
||||
goog.testing.StrictMock.superClass_.$reset.call(this);
|
||||
|
||||
goog.array.clear(this.$expectations_);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.testing.StrictMock.prototype.$verify = function() {
|
||||
goog.testing.StrictMock.superClass_.$verify.call(this);
|
||||
|
||||
while (this.$expectations_.length > 0) {
|
||||
var expectation = this.$expectations_[0];
|
||||
if (expectation.actualCalls < expectation.minCalls) {
|
||||
this.$throwException('Missing a call to ' + expectation.name +
|
||||
'\nExpected: ' + expectation.minCalls + ' but was: ' +
|
||||
expectation.actualCalls);
|
||||
|
||||
} else {
|
||||
// Don't need to check max, that's handled when the call is made
|
||||
this.$expectations_.shift();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
// 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 A utility class for making layout assertions. This is a port
|
||||
* of http://go/layoutbot.java
|
||||
* See {@link http://go/layouttesting}.
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.style.layoutasserts');
|
||||
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.testing.asserts');
|
||||
goog.require('goog.testing.style');
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that an element has:
|
||||
* 1 - a CSS rendering the makes the element visible.
|
||||
* 2 - a non-zero width and height.
|
||||
* @param {Element|string} a The element or optionally the comment string.
|
||||
* @param {Element=} opt_b The element when a comment string is present.
|
||||
*/
|
||||
var assertIsVisible = function(a, opt_b) {
|
||||
_validateArguments(1, arguments);
|
||||
var element = nonCommentArg(1, 1, arguments);
|
||||
|
||||
_assert(commentArg(1, arguments),
|
||||
goog.testing.style.isVisible(element) &&
|
||||
goog.testing.style.hasVisibleDimensions(element),
|
||||
'Specified element should be visible.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The counter assertion of assertIsVisible().
|
||||
* @param {Element|string} a The element or optionally the comment string.
|
||||
* @param {Element=} opt_b The element when a comment string is present.
|
||||
*/
|
||||
var assertNotVisible = function(a, opt_b) {
|
||||
_validateArguments(1, arguments);
|
||||
var element = nonCommentArg(1, 1, arguments);
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
_assert(commentArg(1, arguments),
|
||||
!goog.testing.style.isVisible(element) ||
|
||||
!goog.testing.style.hasVisibleDimensions(element),
|
||||
'Specified element should not be visible.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the two specified elements intersect.
|
||||
* @param {Element|string} a The first element or optionally the comment string.
|
||||
* @param {Element} b The second element or the first element if comment string
|
||||
* is present.
|
||||
* @param {Element=} opt_c The second element if comment string is present.
|
||||
*/
|
||||
var assertIntersect = function(a, b, opt_c) {
|
||||
_validateArguments(2, arguments);
|
||||
var element = nonCommentArg(1, 2, arguments);
|
||||
var otherElement = nonCommentArg(2, 2, arguments);
|
||||
|
||||
_assert(commentArg(1, arguments),
|
||||
goog.testing.style.intersects(element, otherElement),
|
||||
'Elements should intersect.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the two specified elements do not intersect.
|
||||
* @param {Element|string} a The first element or optionally the comment string.
|
||||
* @param {Element} b The second element or the first element if comment string
|
||||
* is present.
|
||||
* @param {Element=} opt_c The second element if comment string is present.
|
||||
*/
|
||||
var assertNoIntersect = function(a, b, opt_c) {
|
||||
_validateArguments(2, arguments);
|
||||
var element = nonCommentArg(1, 2, arguments);
|
||||
var otherElement = nonCommentArg(2, 2, arguments);
|
||||
|
||||
_assert(commentArg(1, arguments),
|
||||
!goog.testing.style.intersects(element, otherElement),
|
||||
'Elements should not intersect.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the element must have the specified width.
|
||||
* @param {Element|string} a The first element or optionally the comment string.
|
||||
* @param {Element} b The second element or the first element if comment string
|
||||
* is present.
|
||||
* @param {Element=} opt_c The second element if comment string is present.
|
||||
*/
|
||||
var assertWidth = function(a, b, opt_c) {
|
||||
_validateArguments(2, arguments);
|
||||
var element = nonCommentArg(1, 2, arguments);
|
||||
var width = nonCommentArg(2, 2, arguments);
|
||||
var size = goog.style.getSize(element);
|
||||
var elementWidth = size.width;
|
||||
|
||||
_assert(commentArg(1, arguments),
|
||||
goog.testing.style.layoutasserts.isWithinThreshold_(
|
||||
width, elementWidth, 0 /* tolerance */),
|
||||
'Element should have width ' + width + ' but was ' + elementWidth + '.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the element must have the specified width within the specified
|
||||
* tolerance.
|
||||
* @param {Element|string} a The element or optionally the comment string.
|
||||
* @param {number|Element} b The height or the element if comment string is
|
||||
* present.
|
||||
* @param {number} c The tolerance or the height if comment string is
|
||||
* present.
|
||||
* @param {number=} opt_d The tolerance if comment string is present.
|
||||
*/
|
||||
var assertWidthWithinTolerance = function(a, b, c, opt_d) {
|
||||
_validateArguments(3, arguments);
|
||||
var element = nonCommentArg(1, 3, arguments);
|
||||
var width = nonCommentArg(2, 3, arguments);
|
||||
var tolerance = nonCommentArg(3, 3, arguments);
|
||||
var size = goog.style.getSize(element);
|
||||
var elementWidth = size.width;
|
||||
|
||||
_assert(commentArg(1, arguments),
|
||||
goog.testing.style.layoutasserts.isWithinThreshold_(
|
||||
width, elementWidth, tolerance),
|
||||
'Element width(' + elementWidth + ') should be within given width(' +
|
||||
width + ') with tolerance value of ' + tolerance + '.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the element must have the specified height.
|
||||
* @param {Element|string} a The first element or optionally the comment string.
|
||||
* @param {Element} b The second element or the first element if comment string
|
||||
* is present.
|
||||
* @param {Element=} opt_c The second element if comment string is present.
|
||||
*/
|
||||
var assertHeight = function(a, b, opt_c) {
|
||||
_validateArguments(2, arguments);
|
||||
var element = nonCommentArg(1, 2, arguments);
|
||||
var height = nonCommentArg(2, 2, arguments);
|
||||
var size = goog.style.getSize(element);
|
||||
var elementHeight = size.height;
|
||||
|
||||
_assert(commentArg(1, arguments),
|
||||
goog.testing.style.layoutasserts.isWithinThreshold_(
|
||||
height, elementHeight, 0 /* tolerance */),
|
||||
'Element should have height ' + height + '.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the element must have the specified height within the specified
|
||||
* tolerance.
|
||||
* @param {Element|string} a The element or optionally the comment string.
|
||||
* @param {number|Element} b The height or the element if comment string is
|
||||
* present.
|
||||
* @param {number} c The tolerance or the height if comment string is
|
||||
* present.
|
||||
* @param {number=} opt_d The tolerance if comment string is present.
|
||||
*/
|
||||
var assertHeightWithinTolerance = function(a, b, c, opt_d) {
|
||||
_validateArguments(3, arguments);
|
||||
var element = nonCommentArg(1, 3, arguments);
|
||||
var height = nonCommentArg(2, 3, arguments);
|
||||
var tolerance = nonCommentArg(3, 3, arguments);
|
||||
var size = goog.style.getSize(element);
|
||||
var elementHeight = size.height;
|
||||
|
||||
_assert(commentArg(1, arguments),
|
||||
goog.testing.style.layoutasserts.isWithinThreshold_(
|
||||
height, elementHeight, tolerance),
|
||||
'Element width(' + elementHeight + ') should be within given width(' +
|
||||
height + ') with tolerance value of ' + tolerance + '.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the first element is to the left of the second element.
|
||||
* @param {Element|string} a The first element or optionally the comment string.
|
||||
* @param {Element} b The second element or the first element if comment string
|
||||
* is present.
|
||||
* @param {Element=} opt_c The second element if comment string is present.
|
||||
*/
|
||||
var assertIsLeftOf = function(a, b, opt_c) {
|
||||
_validateArguments(2, arguments);
|
||||
var element = nonCommentArg(1, 2, arguments);
|
||||
var otherElement = nonCommentArg(2, 2, arguments);
|
||||
var elementRect = goog.style.getBounds(element);
|
||||
var otherElementRect = goog.style.getBounds(otherElement);
|
||||
|
||||
_assert(commentArg(1, arguments),
|
||||
elementRect.left < otherElementRect.left,
|
||||
'Elements should be left to right.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the first element is strictly left of the second element.
|
||||
* @param {Element|string} a The first element or optionally the comment string.
|
||||
* @param {Element} b The second element or the first element if comment string
|
||||
* is present.
|
||||
* @param {Element=} opt_c The second element if comment string is present.
|
||||
*/
|
||||
var assertIsStrictlyLeftOf = function(a, b, opt_c) {
|
||||
_validateArguments(2, arguments);
|
||||
var element = nonCommentArg(1, 2, arguments);
|
||||
var otherElement = nonCommentArg(2, 2, arguments);
|
||||
var elementRect = goog.style.getBounds(element);
|
||||
var otherElementRect = goog.style.getBounds(otherElement);
|
||||
|
||||
_assert(commentArg(1, arguments),
|
||||
elementRect.left + elementRect.width < otherElementRect.left,
|
||||
'Elements should be strictly left to right.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the first element is higher than the second element.
|
||||
* @param {Element|string} a The first element or optionally the comment string.
|
||||
* @param {Element} b The second element or the first element if comment string
|
||||
* is present.
|
||||
* @param {Element=} opt_c The second element if comment string is present.
|
||||
*/
|
||||
var assertIsAbove = function(a, b, opt_c) {
|
||||
_validateArguments(2, arguments);
|
||||
var element = nonCommentArg(1, 2, arguments);
|
||||
var otherElement = nonCommentArg(2, 2, arguments);
|
||||
var elementRect = goog.style.getBounds(element);
|
||||
var otherElementRect = goog.style.getBounds(otherElement);
|
||||
|
||||
_assert(commentArg(1, arguments),
|
||||
elementRect.top < otherElementRect.top,
|
||||
'Elements should be top to bottom.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the first element is strictly higher than the second element.
|
||||
* @param {Element|string} a The first element or optionally the comment string.
|
||||
* @param {Element} b The second element or the first element if comment string
|
||||
* is present.
|
||||
* @param {Element=} opt_c The second element if comment string is present.
|
||||
*/
|
||||
var assertIsStrictlyAbove = function(a, b, opt_c) {
|
||||
_validateArguments(2, arguments);
|
||||
var element = nonCommentArg(1, 2, arguments);
|
||||
var otherElement = nonCommentArg(2, 2, arguments);
|
||||
var elementRect = goog.style.getBounds(element);
|
||||
var otherElementRect = goog.style.getBounds(otherElement);
|
||||
|
||||
_assert(commentArg(1, arguments),
|
||||
elementRect.top + elementRect.height < otherElementRect.top,
|
||||
'Elements should be strictly top to bottom.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the first element's bounds contain the bounds of the second
|
||||
* element.
|
||||
* @param {Element|string} a The first element or optionally the comment string.
|
||||
* @param {Element} b The second element or the first element if comment string
|
||||
* is present.
|
||||
* @param {Element=} opt_c The second element if comment string is present.
|
||||
*/
|
||||
var assertContained = function(a, b, opt_c) {
|
||||
_validateArguments(2, arguments);
|
||||
var element = nonCommentArg(1, 2, arguments);
|
||||
var otherElement = nonCommentArg(2, 2, arguments);
|
||||
var elementRect = goog.style.getBounds(element);
|
||||
var otherElementRect = goog.style.getBounds(otherElement);
|
||||
|
||||
_assert(commentArg(1, arguments),
|
||||
elementRect.contains(otherElementRect),
|
||||
'Element should be contained within the other element.');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the difference between val1 and val2 is less than or equal to
|
||||
* the threashold.
|
||||
* @param {number} val1 The first value.
|
||||
* @param {number} val2 The second value.
|
||||
* @param {number} threshold The threshold value.
|
||||
* @return {boolean} Whether or not the the values are within the threshold.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.style.layoutasserts.isWithinThreshold_ = function(
|
||||
val1, val2, threshold) {
|
||||
return Math.abs(val1 - val2) <= threshold;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// Copyright 2011 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 Utilities for inspecting page layout. This is a port of
|
||||
* http://go/layoutbot.java
|
||||
* See {@link http://go/layouttesting}.
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.style');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.math.Rect');
|
||||
goog.require('goog.style');
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether the bounding rectangles of the given elements intersect.
|
||||
* @param {Element} element The first element.
|
||||
* @param {Element} otherElement The second element.
|
||||
* @return {boolean} Whether the bounding rectangles of the given elements
|
||||
* intersect.
|
||||
*/
|
||||
goog.testing.style.intersects = function(element, otherElement) {
|
||||
var elementRect = goog.style.getBounds(element);
|
||||
var otherElementRect = goog.style.getBounds(otherElement);
|
||||
return goog.math.Rect.intersects(elementRect, otherElementRect);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether the element has visible dimensions, i.e. x > 0 && y > 0.
|
||||
* @param {Element} element The element to check.
|
||||
* @return {boolean} Whether the element has visible dimensions.
|
||||
*/
|
||||
goog.testing.style.hasVisibleDimensions = function(element) {
|
||||
var elSize = goog.style.getSize(element);
|
||||
var shortest = elSize.getShortest();
|
||||
if (shortest <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether the CSS style of the element renders it visible.
|
||||
* @param {!Element} element The element to check.
|
||||
* @return {boolean} Whether the CSS style of the element renders it visible.
|
||||
*/
|
||||
goog.testing.style.isVisible = function(element) {
|
||||
var visibilityStyle =
|
||||
goog.testing.style.getAvailableStyle_(element, 'visibility');
|
||||
var displayStyle =
|
||||
goog.testing.style.getAvailableStyle_(element, 'display');
|
||||
|
||||
return (visibilityStyle != 'hidden' && displayStyle != 'none');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Test whether the given element is on screen.
|
||||
* @param {!Element} el The element to test.
|
||||
* @return {boolean} Whether the element is on the screen.
|
||||
*/
|
||||
goog.testing.style.isOnScreen = function(el) {
|
||||
var doc = goog.dom.getDomHelper(el).getDocument();
|
||||
var viewport = goog.style.getVisibleRectForElement(doc.body);
|
||||
var viewportRect = goog.math.Rect.createFromBox(viewport);
|
||||
return goog.dom.contains(doc, el) &&
|
||||
goog.style.getBounds(el).intersects(viewportRect);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This is essentially goog.style.getStyle_. goog.style.getStyle_ is private
|
||||
* and is not a recommended way for general purpose style extractor. For the
|
||||
* purposes of layout testing, we only use this function for retrieving
|
||||
* 'visiblity' and 'display' style.
|
||||
* @param {!Element} element The element to retrieve the style from.
|
||||
* @param {string} style Style property name.
|
||||
* @return {string} Style value.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.style.getAvailableStyle_ = function(element, style) {
|
||||
return goog.style.getComputedStyle(element, style) ||
|
||||
goog.style.getCascadedStyle(element, style) ||
|
||||
goog.style.getStyle(element, style);
|
||||
};
|
||||
1233
float-no-zero/closure-library/closure/goog/testing/testcase.js
Normal file
1233
float-no-zero/closure-library/closure/goog/testing/testcase.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
||||
// Copyright 2007 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 Generic queue for writing unit tests.
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.TestQueue');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generic queue for writing unit tests
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.TestQueue = function() {
|
||||
/**
|
||||
* Events that have accumulated
|
||||
* @type {Array.<Object>}
|
||||
* @private
|
||||
*/
|
||||
this.events_ = [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a new event onto the queue.
|
||||
* @param {Object} event The event to queue.
|
||||
*/
|
||||
goog.testing.TestQueue.prototype.enqueue = function(event) {
|
||||
this.events_.push(event);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the queue is empty.
|
||||
* @return {boolean} Whether the queue is empty.
|
||||
*/
|
||||
goog.testing.TestQueue.prototype.isEmpty = function() {
|
||||
return this.events_.length == 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the next event from the queue. Throws an exception if the queue is
|
||||
* empty.
|
||||
* @param {string=} opt_comment Comment if the queue is empty.
|
||||
* @return {Object} The next event from the queue.
|
||||
*/
|
||||
goog.testing.TestQueue.prototype.dequeue = function(opt_comment) {
|
||||
if (this.isEmpty()) {
|
||||
throw Error('Handler is empty: ' + opt_comment);
|
||||
}
|
||||
return this.events_.shift();
|
||||
};
|
||||
399
float-no-zero/closure-library/closure/goog/testing/testrunner.js
Normal file
399
float-no-zero/closure-library/closure/goog/testing/testrunner.js
Normal file
@@ -0,0 +1,399 @@
|
||||
// Copyright 2007 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 The test runner is a singleton object that is used to execute
|
||||
* a goog.testing.TestCases, display the results, and expose the results to
|
||||
* Selenium for automation. If a TestCase hasn't been registered with the
|
||||
* runner by the time window.onload occurs, the testRunner will try to auto-
|
||||
* discover JsUnit style test pages.
|
||||
*
|
||||
* The hooks for selenium are :-
|
||||
* - Boolean G_testRunner.isFinished()
|
||||
* - Boolean G_testRunner.isSuccess()
|
||||
* - String G_testRunner.getReport()
|
||||
* - number G_testRunner.getRunTime()
|
||||
*
|
||||
* Testing code should not have dependencies outside of goog.testing so as to
|
||||
* reduce the chance of masking missing dependencies.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.TestRunner');
|
||||
|
||||
goog.require('goog.testing.TestCase');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Construct a test runner.
|
||||
*
|
||||
* NOTE(user): This is currently pretty weird, I'm essentially trying to
|
||||
* create a wrapper that the Selenium test can hook into to query the state of
|
||||
* the running test case, while making goog.testing.TestCase general.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
goog.testing.TestRunner = function() {
|
||||
/**
|
||||
* Errors that occurred in the window.
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
this.errors = [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Reference to the active test case.
|
||||
* @type {goog.testing.TestCase?}
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.testCase = null;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the test runner has been initialized yet.
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.initialized = false;
|
||||
|
||||
|
||||
/**
|
||||
* Element created in the document to add test results to.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.logEl_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Function to use when filtering errors.
|
||||
* @type {(function(string))?}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.errorFilter_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Whether an empty test case counts as an error.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.strict_ = true;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the test runner.
|
||||
* @param {goog.testing.TestCase} testCase The test case to initialize with.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.initialize = function(testCase) {
|
||||
if (this.testCase && this.testCase.running) {
|
||||
throw Error('The test runner is already waiting for a test to complete');
|
||||
}
|
||||
this.testCase = testCase;
|
||||
testCase.setTestRunner(this);
|
||||
this.initialized = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* By default, the test runner is strict, and fails if it runs an empty
|
||||
* test case.
|
||||
* @param {boolean} strict Whether the test runner should fail on an empty
|
||||
* test case.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.setStrict = function(strict) {
|
||||
this.strict_ = strict;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the test runner should fail on an empty
|
||||
* test case.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.isStrict = function() {
|
||||
return this.strict_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the test runner is initialized.
|
||||
* Used by Selenium Hooks.
|
||||
* @return {boolean} Whether the test runner is active.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.isInitialized = function() {
|
||||
return this.initialized;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the test runner is finished.
|
||||
* Used by Selenium Hooks.
|
||||
* @return {boolean} Whether the test runner is active.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.isFinished = function() {
|
||||
return this.errors.length > 0 ||
|
||||
this.initialized && !!this.testCase && this.testCase.started &&
|
||||
!this.testCase.running;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the test case didn't fail.
|
||||
* Used by Selenium Hooks.
|
||||
* @return {boolean} Whether the current test returned successfully.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.isSuccess = function() {
|
||||
return !this.hasErrors() && !!this.testCase && this.testCase.isSuccess();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the test case runner has errors that were caught outside of
|
||||
* the test case.
|
||||
* @return {boolean} Whether there were JS errors.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.hasErrors = function() {
|
||||
return this.errors.length > 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs an error that occurred. Used in the case of environment setting up
|
||||
* an onerror handler.
|
||||
* @param {string} msg Error message.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.logError = function(msg) {
|
||||
if (!this.errorFilter_ || this.errorFilter_.call(null, msg)) {
|
||||
this.errors.push(msg);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Log failure in current running test.
|
||||
* @param {Error} ex Exception.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.logTestFailure = function(ex) {
|
||||
var testName = /** @type {string} */ (goog.testing.TestCase.currentTestName);
|
||||
if (this.testCase) {
|
||||
this.testCase.logError(testName, ex);
|
||||
} else {
|
||||
// NOTE: Do not forget to log the original exception raised.
|
||||
throw new Error('Test runner not initialized with a test case. Original ' +
|
||||
'exception: ' + ex.message);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets a function to use as a filter for errors.
|
||||
* @param {function(string)} fn Filter function.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.setErrorFilter = function(fn) {
|
||||
this.errorFilter_ = fn;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a report of the test case that ran.
|
||||
* Used by Selenium Hooks.
|
||||
* @param {boolean=} opt_verbose If true results will include data about all
|
||||
* tests, not just what failed.
|
||||
* @return {string} A report summary of the test.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.getReport = function(opt_verbose) {
|
||||
var report = [];
|
||||
if (this.testCase) {
|
||||
report.push(this.testCase.getReport(opt_verbose));
|
||||
}
|
||||
if (this.errors.length > 0) {
|
||||
report.push('JavaScript errors detected by test runner:');
|
||||
report.push.apply(report, this.errors);
|
||||
report.push('\n');
|
||||
}
|
||||
return report.join('\n');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the amount of time it took for the test to run.
|
||||
* Used by Selenium Hooks.
|
||||
* @return {number} The run time, in milliseconds.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.getRunTime = function() {
|
||||
return this.testCase ? this.testCase.getRunTime() : 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of script files that were loaded in order to run the test.
|
||||
* @return {number} The number of script files.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.getNumFilesLoaded = function() {
|
||||
return this.testCase ? this.testCase.getNumFilesLoaded() : 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Executes a test case and prints the results to the window.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.execute = function() {
|
||||
if (!this.testCase) {
|
||||
throw Error('The test runner must be initialized with a test case before ' +
|
||||
'execute can be called.');
|
||||
}
|
||||
this.testCase.setCompletedCallback(goog.bind(this.onComplete_, this));
|
||||
this.testCase.runTests();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes the results to the document when the test case completes.
|
||||
* @private
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.onComplete_ = function() {
|
||||
var log = this.testCase.getReport(true);
|
||||
if (this.errors.length > 0) {
|
||||
log += '\n' + this.errors.join('\n');
|
||||
}
|
||||
|
||||
if (!this.logEl_) {
|
||||
var el = document.getElementById('closureTestRunnerLog');
|
||||
if (el == null) {
|
||||
el = document.createElement('div');
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
this.logEl_ = el;
|
||||
}
|
||||
|
||||
// Highlight the page to indicate the overall outcome.
|
||||
this.writeLog(log);
|
||||
|
||||
// TODO(user): Make this work with multiple test cases (b/8603638).
|
||||
var runAgainLink = document.createElement('a');
|
||||
runAgainLink.style.display = 'block';
|
||||
runAgainLink.style.fontSize = 'small';
|
||||
runAgainLink.href = '';
|
||||
runAgainLink.onclick = goog.bind(function() {
|
||||
this.execute();
|
||||
return false;
|
||||
}, this);
|
||||
runAgainLink.innerHTML = 'Run again without reloading';
|
||||
this.logEl_.appendChild(runAgainLink);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a nicely formatted log out to the document.
|
||||
* @param {string} log The string to write.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.writeLog = function(log) {
|
||||
var lines = log.split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = lines[i];
|
||||
var color;
|
||||
var isFailOrError = /FAILED/.test(line) || /ERROR/.test(line);
|
||||
if (/PASSED/.test(line)) {
|
||||
color = 'darkgreen';
|
||||
} else if (isFailOrError) {
|
||||
color = 'darkred';
|
||||
} else {
|
||||
color = '#333';
|
||||
}
|
||||
var div = document.createElement('div');
|
||||
if (line.substr(0, 2) == '> ') {
|
||||
// The stack trace may contain links so it has to be interpreted as HTML.
|
||||
div.innerHTML = line;
|
||||
} else {
|
||||
div.appendChild(document.createTextNode(line));
|
||||
}
|
||||
|
||||
if (isFailOrError) {
|
||||
var testNameMatch = /(\S+) (\[[^\]]*] )?: (FAILED|ERROR)/.exec(line);
|
||||
if (testNameMatch) {
|
||||
// Build a URL to run the test individually. If this test was already
|
||||
// part of another subset test, we need to overwrite the old runTests
|
||||
// query parameter. We also need to do this without bringing in any
|
||||
// extra dependencies, otherwise we could mask missing dependency bugs.
|
||||
var newSearch = 'runTests=' + testNameMatch[1];
|
||||
var search = window.location.search;
|
||||
if (search) {
|
||||
var oldTests = /runTests=([^&]*)/.exec(search);
|
||||
if (oldTests) {
|
||||
newSearch = search.substr(0, oldTests.index) +
|
||||
newSearch +
|
||||
search.substr(oldTests.index + oldTests[0].length);
|
||||
} else {
|
||||
newSearch = search + '&' + newSearch;
|
||||
}
|
||||
} else {
|
||||
newSearch = '?' + newSearch;
|
||||
}
|
||||
var href = window.location.href;
|
||||
var hash = window.location.hash;
|
||||
if (hash && hash.charAt(0) != '#') {
|
||||
hash = '#' + hash;
|
||||
}
|
||||
href = href.split('#')[0].split('?')[0] + newSearch + hash;
|
||||
|
||||
// Add the link.
|
||||
var a = document.createElement('A');
|
||||
a.innerHTML = '(run individually)';
|
||||
a.style.fontSize = '0.8em';
|
||||
a.href = href;
|
||||
div.appendChild(document.createTextNode(' '));
|
||||
div.appendChild(a);
|
||||
}
|
||||
}
|
||||
|
||||
div.style.color = color;
|
||||
div.style.font = 'normal 100% monospace';
|
||||
if (i == 0) {
|
||||
// Highlight the first line as a header that indicates the test outcome.
|
||||
div.style.padding = '20px';
|
||||
div.style.marginBottom = '10px';
|
||||
if (isFailOrError) {
|
||||
div.style.border = '5px solid ' + color;
|
||||
div.style.backgroundColor = '#ffeeee';
|
||||
} else {
|
||||
div.style.border = '1px solid black';
|
||||
div.style.backgroundColor = '#eeffee';
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
div.style.whiteSpace = 'pre-wrap';
|
||||
} catch (e) {
|
||||
// NOTE(brenneman): IE raises an exception when assigning to pre-wrap.
|
||||
// Thankfully, it doesn't collapse whitespace when using monospace fonts,
|
||||
// so it will display correctly if we ignore the exception.
|
||||
}
|
||||
|
||||
if (i < 2) {
|
||||
div.style.fontWeight = 'bold';
|
||||
}
|
||||
this.logEl_.appendChild(div);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logs a message to the current test case.
|
||||
* @param {string} s The text to output to the log.
|
||||
*/
|
||||
goog.testing.TestRunner.prototype.log = function(s) {
|
||||
if (this.testCase) {
|
||||
this.testCase.log(s);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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 Additional asserts for testing ControlRenderers.
|
||||
*
|
||||
* @author mkretzschmar@google.com (Martin Kretzschmar)
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.ui.rendererasserts');
|
||||
|
||||
goog.require('goog.testing.asserts');
|
||||
|
||||
|
||||
/**
|
||||
* Assert that a control renderer constructor doesn't call getCssClass.
|
||||
*
|
||||
* @param {?function(new:goog.ui.ControlRenderer)} rendererClassUnderTest The
|
||||
* renderer constructor to test.
|
||||
*/
|
||||
goog.testing.ui.rendererasserts.assertNoGetCssClassCallsInConstructor =
|
||||
function(rendererClassUnderTest) {
|
||||
var getCssClassCalls = 0;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {goog.ui.ControlRenderer}
|
||||
*/
|
||||
function TestControlRenderer() {
|
||||
rendererClassUnderTest.call(this);
|
||||
}
|
||||
goog.inherits(TestControlRenderer, rendererClassUnderTest);
|
||||
|
||||
/** @override */
|
||||
TestControlRenderer.prototype.getCssClass = function() {
|
||||
getCssClassCalls++;
|
||||
return TestControlRenderer.superClass_.getCssClass.call(this);
|
||||
};
|
||||
|
||||
var testControlRenderer = new TestControlRenderer();
|
||||
|
||||
assertEquals('Constructors should not call getCssClass, ' +
|
||||
'getCustomRenderer must be able to override it post construction.',
|
||||
0, getCssClassCalls);
|
||||
};
|
||||
@@ -0,0 +1,176 @@
|
||||
// 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.
|
||||
// All Rights Reserved
|
||||
|
||||
/**
|
||||
* @fileoverview A driver for testing renderers.
|
||||
*
|
||||
* @author nicksantos@google.com (Nick Santos)
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.ui.RendererHarness');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.dom.NodeType');
|
||||
goog.require('goog.testing.asserts');
|
||||
goog.require('goog.testing.dom');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A driver for testing renderers.
|
||||
*
|
||||
* @param {goog.ui.ControlRenderer} renderer A renderer to test.
|
||||
* @param {Element} renderParent The parent of the element where controls will
|
||||
* be rendered.
|
||||
* @param {Element} decorateParent The parent of the element where controls will
|
||||
* be decorated.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
*/
|
||||
goog.testing.ui.RendererHarness = function(renderer, renderParent,
|
||||
decorateParent) {
|
||||
goog.Disposable.call(this);
|
||||
|
||||
/**
|
||||
* The renderer under test.
|
||||
* @type {goog.ui.ControlRenderer}
|
||||
* @private
|
||||
*/
|
||||
this.renderer_ = renderer;
|
||||
|
||||
/**
|
||||
* The parent of the element where controls will be rendered.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.renderParent_ = renderParent;
|
||||
|
||||
/**
|
||||
* The original HTML of the render element.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.renderHtml_ = renderParent.innerHTML;
|
||||
|
||||
/**
|
||||
* Teh parent of the element where controls will be decorated.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.decorateParent_ = decorateParent;
|
||||
|
||||
/**
|
||||
* The original HTML of the decorated element.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.decorateHtml_ = decorateParent.innerHTML;
|
||||
};
|
||||
goog.inherits(goog.testing.ui.RendererHarness, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* A control to create by decoration.
|
||||
* @type {goog.ui.Control}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ui.RendererHarness.prototype.decorateControl_;
|
||||
|
||||
|
||||
/**
|
||||
* A control to create by rendering.
|
||||
* @type {goog.ui.Control}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ui.RendererHarness.prototype.renderControl_;
|
||||
|
||||
|
||||
/**
|
||||
* Whether all the necessary assert methods have been called.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ui.RendererHarness.prototype.verified_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Attach a control and render its DOM.
|
||||
* @param {goog.ui.Control} control A control.
|
||||
* @return {Element} The element created.
|
||||
*/
|
||||
goog.testing.ui.RendererHarness.prototype.attachControlAndRender =
|
||||
function(control) {
|
||||
this.renderControl_ = control;
|
||||
|
||||
control.setRenderer(this.renderer_);
|
||||
control.render(this.renderParent_);
|
||||
return control.getElement();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Attach a control and decorate the element given in the constructor.
|
||||
* @param {goog.ui.Control} control A control.
|
||||
* @return {Element} The element created.
|
||||
*/
|
||||
goog.testing.ui.RendererHarness.prototype.attachControlAndDecorate =
|
||||
function(control) {
|
||||
this.decorateControl_ = control;
|
||||
|
||||
control.setRenderer(this.renderer_);
|
||||
|
||||
var child = this.decorateParent_.firstChild;
|
||||
assertEquals('The decorated node must be an element',
|
||||
goog.dom.NodeType.ELEMENT, child.nodeType);
|
||||
control.decorate(/** @type {Element} */ (child));
|
||||
return control.getElement();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Assert that the rendered element and the decorated element match.
|
||||
*/
|
||||
goog.testing.ui.RendererHarness.prototype.assertDomMatches = function() {
|
||||
assert('Both elements were not generated',
|
||||
!!(this.renderControl_ && this.decorateControl_));
|
||||
goog.testing.dom.assertHtmlMatches(
|
||||
this.renderControl_.getElement().innerHTML,
|
||||
this.decorateControl_.getElement().innerHTML);
|
||||
this.verified_ = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Destroy the harness, verifying that all assertions had been checked.
|
||||
* @override
|
||||
* @protected
|
||||
*/
|
||||
goog.testing.ui.RendererHarness.prototype.disposeInternal = function() {
|
||||
// If the harness was not verified appropriately, throw an exception.
|
||||
assert('Expected assertDomMatches to be called',
|
||||
this.verified_ || !this.renderControl_ || !this.decorateControl_);
|
||||
|
||||
if (this.decorateControl_) {
|
||||
this.decorateControl_.dispose();
|
||||
}
|
||||
if (this.renderControl_) {
|
||||
this.renderControl_.dispose();
|
||||
}
|
||||
|
||||
this.renderParent_.innerHTML = this.renderHtml_;
|
||||
this.decorateParent_.innerHTML = this.decorateHtml_;
|
||||
|
||||
goog.testing.ui.RendererHarness.superClass_.disposeInternal.call(this);
|
||||
};
|
||||
137
float-no-zero/closure-library/closure/goog/testing/ui/style.js
Normal file
137
float-no-zero/closure-library/closure/goog/testing/ui/style.js
Normal file
@@ -0,0 +1,137 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview Tools for testing Closure renderers against static markup
|
||||
* spec pages.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.testing.ui.style');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.testing.asserts');
|
||||
|
||||
|
||||
/**
|
||||
* Uses document.write to add an iFrame to the page with the reference path in
|
||||
* the src attribute. Used for loading an html file containing reference
|
||||
* structures to test against into the page. Should be called within the body of
|
||||
* the jsunit test page.
|
||||
* @param {string} referencePath A path to a reference HTML file.
|
||||
*/
|
||||
goog.testing.ui.style.writeReferenceFrame = function(referencePath) {
|
||||
document.write('<iframe id="reference" name="reference" ' +
|
||||
'src="' + referencePath + '"></iframe>');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a reference to the first element child of a node with the given id
|
||||
* from the page loaded into the reference iFrame. Used to retrieve a particular
|
||||
* reference DOM structure to test against.
|
||||
* @param {string} referenceId The id of a container element for a reference
|
||||
* structure in the reference page.
|
||||
* @return {Node} The root element of the reference structure.
|
||||
*/
|
||||
goog.testing.ui.style.getReferenceNode = function(referenceId) {
|
||||
return goog.dom.getFirstElementChild(
|
||||
window.frames['reference'].document.getElementById(referenceId));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of all element children of a given node.
|
||||
* @param {Node} element The node to get element children of.
|
||||
* @return {Array.<Node>} An array of all the element children.
|
||||
*/
|
||||
goog.testing.ui.style.getElementChildren = function(element) {
|
||||
var first = goog.dom.getFirstElementChild(element);
|
||||
if (!first) {
|
||||
return [];
|
||||
}
|
||||
var children = [first], next;
|
||||
while (next = goog.dom.getNextElementSibling(children[children.length - 1])) {
|
||||
children.push(next);
|
||||
}
|
||||
return children;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Tests whether a given node is a "content" node of a reference structure,
|
||||
* which means it is allowed to have arbitrary children.
|
||||
* @param {Node} element The node to test.
|
||||
* @return {boolean} Whether the given node is a content node or not.
|
||||
*/
|
||||
goog.testing.ui.style.isContentNode = function(element) {
|
||||
return element.className.indexOf('content') != -1;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Tests that the structure, node names, and classes of the given element are
|
||||
* the same as the reference structure with the given id. Throws an error if the
|
||||
* element doesn't have the same nodes at each level of the DOM with the same
|
||||
* classes on each. The test ignores all DOM structure within content nodes.
|
||||
* @param {Node} element The root node of the DOM structure to test.
|
||||
* @param {string} referenceId The id of the container for the reference
|
||||
* structure to test against.
|
||||
*/
|
||||
goog.testing.ui.style.assertStructureMatchesReference = function(element,
|
||||
referenceId) {
|
||||
goog.testing.ui.style.assertStructureMatchesReferenceInner_(element,
|
||||
goog.testing.ui.style.getReferenceNode(referenceId));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A recursive function for comparing structure, node names, and classes between
|
||||
* a test and reference DOM structure. Throws an error if one of these things
|
||||
* doesn't match. Used internally by
|
||||
* {@link goog.testing.ui.style.assertStructureMatchesReference}.
|
||||
* @param {Node} element DOM element to test.
|
||||
* @param {Node} reference DOM element to use as a reference (test against).
|
||||
* @private
|
||||
*/
|
||||
goog.testing.ui.style.assertStructureMatchesReferenceInner_ = function(element,
|
||||
reference) {
|
||||
if (!element && !reference) {
|
||||
return;
|
||||
}
|
||||
assertTrue('Expected two elements.', !!element && !!reference);
|
||||
assertEquals('Expected nodes to have the same nodeName.',
|
||||
element.nodeName, reference.nodeName);
|
||||
var elementClasses = goog.dom.classes.get(element);
|
||||
goog.array.forEach(goog.dom.classes.get(reference), function(referenceClass) {
|
||||
assertContains('Expected test node to have all reference classes.',
|
||||
referenceClass, elementClasses);
|
||||
});
|
||||
// Call assertStructureMatchesReferenceInner_ on all element children
|
||||
// unless this is a content node
|
||||
var elChildren = goog.testing.ui.style.getElementChildren(element),
|
||||
refChildren = goog.testing.ui.style.getElementChildren(reference);
|
||||
if (!goog.testing.ui.style.isContentNode(reference)) {
|
||||
if (elChildren.length != refChildren.length) {
|
||||
assertEquals('Expected same number of children for a non-content node.',
|
||||
elChildren.length, refChildren.length);
|
||||
}
|
||||
for (var i = 0; i < elChildren.length; i++) {
|
||||
goog.testing.ui.style.assertStructureMatchesReferenceInner_(elChildren[i],
|
||||
refChildren[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user