Update test specs to use Jasmine 3 (#2089)

* Updated Karma and Jasmine versions

* Added DOMObserver class. Supports promise-based testing of DOM changes

Update asynchronous test specs to use promises or done() instead of waitsFor/runs

* Modified ActionCapability to duplicate context object properties as own properties for better object equality comparisons

* Global find + replace to fix syntax issues

* Fixed various issues caused by non-deterministic runtime order of tests in Jasmine 3. Fixed issues caused by changes to determination of object equality

* Addressed review comments

* Resolved merge conflicts with master

* Fixed style errors

* Use spy.calls.count() instead of manually tracking
This commit is contained in:
Andrew Henry
2018-06-29 17:32:59 -07:00
committed by Pete Richards
parent 013eba744d
commit 433dee0314
305 changed files with 2866 additions and 3324 deletions

View File

@@ -48,21 +48,7 @@ define(
// Really just delegates work, can only verify the
// order of calls.
it("calls injected stages in order", function () {
var result;
initializer.runApplication([]).then(function (v) {
result = v;
});
waitsFor(
function () {
return result !== undefined;
},
"promise resolution",
250
);
runs(function () {
return initializer.runApplication([]).then(function (result) {
expect(result).toEqual(
["loader", "resolver", "registrar", "bootstrapper"]
);

View File

@@ -62,11 +62,11 @@ define(
mockDelegate = jasmine.createSpyObj('$delegate', LOG_METHODS);
LOG_METHODS.forEach(function (m) {
mockLog[m].andCallFake(mockMethods[m]);
mockDelegate[m].andCallFake(mockMethods[m]);
mockLog[m].and.callFake(mockMethods[m]);
mockDelegate[m].and.callFake(mockMethods[m]);
});
mockApp.decorator.andCallFake(function (key, decoration) {
mockApp.decorator.and.callFake(function (key, decoration) {
// We only expect $log to be decorated
if (key === '$log' && decoration[0] === '$delegate') {
decoration[1](mockDelegate);

View File

@@ -29,19 +29,11 @@ define(
describe("The bundle loader", function () {
var loader,
mockCallback,
mockHttp,
mockLog,
mockRegistry,
testBundles;
// Used to wait for then-chain resolution;
// native promises may not resolve synchronously,
// even when values are immediately available.
function mockCallbackResolved() {
return mockCallback.calls.length > 0;
}
beforeEach(function () {
testBundles = {
"bundles.json": ["bundle/a", "bundle/b"],
@@ -49,54 +41,45 @@ define(
"bundle/b/bundle.json": {"someValue": 7}
};
mockCallback = jasmine.createSpy("callback");
mockHttp = jasmine.createSpyObj("$http", ["get"]);
mockLog = jasmine.createSpyObj("$log", ["error", "warn", "info", "debug"]);
mockRegistry = jasmine.createSpyObj(
'legacyRegistry',
['list', 'contains', 'get']
);
mockRegistry.list.andReturn([]);
mockRegistry.contains.andReturn(false);
mockRegistry.list.and.returnValue([]);
mockRegistry.contains.and.returnValue(false);
loader = new BundleLoader(mockHttp, mockLog, mockRegistry);
});
it("accepts a JSON file name and loads all bundles", function () {
// Set up; return named bundles
mockHttp.get.andReturn(Promise.resolve({ data: [] }));
mockHttp.get.and.returnValue(Promise.resolve({ data: [] }));
// Call load bundles
loader.loadBundles("test-filename.json").then(mockCallback);
waitsFor(mockCallbackResolved, "then-chain resolution", 100);
runs(function () {
return loader.loadBundles("test-filename.json").then(function (bundles) {
// Should have loaded the file via $http
expect(mockHttp.get).toHaveBeenCalledWith("test-filename.json");
// Should have gotten an empty bundle list
expect(mockCallback).toHaveBeenCalledWith([]);
expect(bundles).toEqual([]);
});
});
it("accepts a list of bundle paths", function () {
// Set up; return named bundles
mockHttp.get.andCallFake(function (name) {
mockHttp.get.and.callFake(function (name) {
return Promise.resolve({data: testBundles[name]});
});
// Call load bundles
loader.loadBundles(["bundle/a", "bundle/b"]).then(mockCallback);
waitsFor(mockCallbackResolved, "then-chain resolution", 100);
runs(function () {
return loader.loadBundles(["bundle/a", "bundle/b"]).then(function (bundles) {
// Should have gotten back two bundles
expect(mockCallback.mostRecentCall.args[0].length).toEqual(2);
expect(bundles.length).toEqual(2);
// They should have the values we expect; don't care about order,
// some map/reduce the summation.
expect(mockCallback.mostRecentCall.args[0].map(function (call) {
expect(bundles.map(function (call) {
return call.getDefinition().someValue;
}).reduce(function (a, b) {
return a + b;
@@ -106,16 +89,12 @@ define(
it("warns about, then ignores, missing bundle declarations", function () {
// File-not-found
mockHttp.get.andReturn(Promise.reject(new Error("test error")));
mockHttp.get.and.returnValue(Promise.reject(new Error("test error")));
// Try and load
loader.loadBundles(["some/bundle"]).then(mockCallback);
waitsFor(mockCallbackResolved, "then-chain resolution", 100);
runs(function () {
// Should have gotten zero bundle
expect(mockCallback.mostRecentCall.args[0].length).toEqual(0);
return loader.loadBundles(["some/bundle"]).then(function (bundles) {
// Should have gotten zero bundles
expect(bundles.length).toEqual(0);
// They should have the values we expect; don't care about order,
// some map/reduce the summation.
@@ -126,16 +105,12 @@ define(
it("warns about, then ignores, malformed bundle declarations", function () {
// File-not-found
mockHttp.get.andReturn(Promise.resolve(["I am not a valid bundle."]));
mockHttp.get.and.returnValue(Promise.resolve(["I am not a valid bundle."]));
// Try and load
loader.loadBundles(["some/bundle"]).then(mockCallback);
waitsFor(mockCallbackResolved, "then-chain resolution", 100);
runs(function () {
return loader.loadBundles(["some/bundle"]).then(function (bundles) {
// Should have gotten zero bundle
expect(mockCallback.mostRecentCall.args[0].length).toEqual(0);
expect(bundles.length).toEqual(0);
// They should have the values we expect; don't care about order,
// some map/reduce the summation.

View File

@@ -64,70 +64,70 @@ define(
it("invokes built-in functions on the app", function () {
// Verify preconditions, invoke, expect to have been called
expect(mockApp.directive.calls.length).toEqual(0);
expect(mockApp.directive.calls.count()).toEqual(0);
customRegistrars.directives([{ key: "a" }, { key: "b" }, { key: "c" }]);
expect(mockApp.directive.calls.length).toEqual(3);
expect(mockApp.directive.calls.count()).toEqual(3);
expect(mockApp.controller.calls.length).toEqual(0);
expect(mockApp.controller.calls.count()).toEqual(0);
customRegistrars.controllers([{ key: "a" }, { key: "b" }, { key: "c" }]);
expect(mockApp.controller.calls.length).toEqual(3);
expect(mockApp.controller.calls.count()).toEqual(3);
expect(mockApp.service.calls.length).toEqual(0);
expect(mockApp.service.calls.count()).toEqual(0);
customRegistrars.services([{ key: "a" }, { key: "b" }, { key: "c" }]);
expect(mockApp.service.calls.length).toEqual(3);
expect(mockApp.service.calls.count()).toEqual(3);
expect(mockApp.constant.calls.length).toEqual(0);
expect(mockApp.constant.calls.count()).toEqual(0);
customRegistrars.constants([{ key: "a", value: "b" }, { key: "b", value: "c" }, { key: "c", value: "d" }]);
expect(mockApp.constant.calls.length).toEqual(3);
expect(mockApp.constant.calls.count()).toEqual(3);
expect(mockApp.run.calls.length).toEqual(0);
expect(mockApp.run.calls.count()).toEqual(0);
customRegistrars.runs([jasmine.createSpy("a"), jasmine.createSpy("a"), jasmine.createSpy("a")]);
expect(mockApp.run.calls.length).toEqual(3);
expect(mockApp.run.calls.count()).toEqual(3);
});
it("warns when keys are not defined, then skips", function () {
// Verify preconditions, invoke, expect to have been called
expect(mockApp.directive.calls.length).toEqual(0);
expect(mockApp.directive.calls.count()).toEqual(0);
customRegistrars.directives([{ key: "a" }, { }, { key: "c" }]);
expect(mockApp.directive.calls.length).toEqual(2);
expect(mockLog.warn.calls.length).toEqual(1);
expect(mockApp.directive.calls.count()).toEqual(2);
expect(mockLog.warn.calls.count()).toEqual(1);
expect(mockApp.controller.calls.length).toEqual(0);
expect(mockApp.controller.calls.count()).toEqual(0);
customRegistrars.controllers([{ }, { }, { key: "c" }]);
expect(mockApp.controller.calls.length).toEqual(1);
expect(mockLog.warn.calls.length).toEqual(3);
expect(mockApp.controller.calls.count()).toEqual(1);
expect(mockLog.warn.calls.count()).toEqual(3);
expect(mockApp.service.calls.length).toEqual(0);
expect(mockApp.service.calls.count()).toEqual(0);
customRegistrars.services([{ }, { }, { }]);
expect(mockApp.service.calls.length).toEqual(0);
expect(mockLog.warn.calls.length).toEqual(6);
expect(mockApp.service.calls.count()).toEqual(0);
expect(mockLog.warn.calls.count()).toEqual(6);
expect(mockApp.constant.calls.length).toEqual(0);
expect(mockApp.constant.calls.count()).toEqual(0);
customRegistrars.constants([{ }, { }, { }]);
expect(mockApp.constant.calls.length).toEqual(0);
expect(mockLog.warn.calls.length).toEqual(9);
expect(mockApp.constant.calls.count()).toEqual(0);
expect(mockLog.warn.calls.count()).toEqual(9);
// Notably, keys are not needed for run calls
});
it("does not re-register duplicate keys", function () {
// Verify preconditions, invoke, expect to have been called
expect(mockApp.directive.calls.length).toEqual(0);
expect(mockApp.directive.calls.count()).toEqual(0);
customRegistrars.directives([{ key: "a" }, { key: "a" }]);
expect(mockApp.directive.calls.length).toEqual(1);
expect(mockApp.directive.calls.count()).toEqual(1);
expect(mockApp.controller.calls.length).toEqual(0);
expect(mockApp.controller.calls.count()).toEqual(0);
customRegistrars.controllers([{ key: "c" }, { key: "c" }, { key: "c" }]);
expect(mockApp.controller.calls.length).toEqual(1);
expect(mockApp.controller.calls.count()).toEqual(1);
expect(mockApp.service.calls.length).toEqual(0);
expect(mockApp.service.calls.count()).toEqual(0);
customRegistrars.services([{ key: "b" }, { key: "b" }]);
expect(mockApp.service.calls.length).toEqual(1);
expect(mockApp.service.calls.count()).toEqual(1);
// None of this should have warned, this is all
// nominal behavior
expect(mockLog.warn.calls.length).toEqual(0);
expect(mockLog.warn.calls.count()).toEqual(0);
});
it("allows routes to be registered", function () {
@@ -151,20 +151,20 @@ define(
customRegistrars.routes(routes);
// Give it the route provider based on its config call
mockApp.config.calls.forEach(function (call) {
mockApp.config.calls.all().forEach(function (call) {
// Invoke the provided callback
call.args[0][1](mockRouteProvider);
});
// The "when" clause should have been mapped to the when method...
expect(mockRouteProvider.when).toHaveBeenCalled();
expect(mockRouteProvider.when.mostRecentCall.args[0]).toEqual("foo");
expect(mockRouteProvider.when.mostRecentCall.args[1].templateUrl)
expect(mockRouteProvider.when.calls.mostRecent().args[0]).toEqual("foo");
expect(mockRouteProvider.when.calls.mostRecent().args[1].templateUrl)
.toEqual("test/bundle/res/templates/test.html");
// ...while the other should have been treated as a default route
expect(mockRouteProvider.otherwise).toHaveBeenCalled();
expect(mockRouteProvider.otherwise.mostRecentCall.args[0].templateUrl)
expect(mockRouteProvider.otherwise.calls.mostRecent().args[0].templateUrl)
.toEqual("test/bundle/res/templates/default.html");
});

View File

@@ -40,7 +40,7 @@ define(
mockSorter = jasmine.createSpyObj("sorter", ["sort"]);
customRegistrars = {};
mockSorter.sort.andCallFake(function (v) {
mockSorter.sort.and.callFake(function (v) {
return v;
});
@@ -59,7 +59,7 @@ define(
it("registers extensions with square brackets, as arrays", function () {
var callbacks = {};
mockApp.factory.andCallFake(function (name, value) {
mockApp.factory.and.callFake(function (name, value) {
callbacks[name] = value[value.length - 1];
});
registrar.registerExtensions({ things: [{}] });
@@ -78,7 +78,7 @@ define(
it("registers empty extension categories when they are needed", function () {
var lengths = {};
mockApp.factory.andCallFake(function (name, value) {
mockApp.factory.and.callFake(function (name, value) {
lengths[name] = value.length;
});
// Nobody has registered tests[], but it looks like an extension dependency,
@@ -100,7 +100,7 @@ define(
var a = { a: 'a' }, b = { b: 'b' }, c = { c: 'c' };
// Fake sorting; just reverse the array
mockSorter.sort.andCallFake(function (v) {
mockSorter.sort.and.callFake(function (v) {
return v.reverse();
});
@@ -109,7 +109,7 @@ define(
// Verify registration interactions occurred in reverse-order
[c, b, a].forEach(function (extension, index) {
expect(mockApp.factory.calls[index].args[1][0]())
expect(mockApp.factory.calls.all()[index].args[1][0]())
.toEqual(extension);
});
});

View File

@@ -61,7 +61,7 @@ define(
);
// Should have been warned exactly twice (for d & e)
expect(mockLog.warn.calls.length).toEqual(2);
expect(mockLog.warn.calls.count()).toEqual(2);
});
});

View File

@@ -40,7 +40,7 @@ define(
mockApp = jasmine.createSpyObj("app", ["service"]);
mockLog = jasmine.createSpyObj("$log", ["error", "warn", "info", "debug"]);
mockApp.service.andCallFake(function (name, value) {
mockApp.service.and.callFake(function (name, value) {
var factory = value[value.length - 1];
registered[name] = {
@@ -195,8 +195,8 @@ define(
expect(mockApp.service).not.toHaveBeenCalled();
// Should have gotten one warning for each skipped component
expect(mockLog.warn.calls.length).toEqual(2);
expect(mockLog.info.calls.length).toEqual(1);
expect(mockLog.warn.calls.count()).toEqual(2);
expect(mockLog.info.calls.count()).toEqual(1);
});
it("warns about and skips aggregators with zero providers", function () {

View File

@@ -47,7 +47,7 @@ define(
["error", "warn", "info", "debug"]
);
mockExtensionResolver.resolve.andReturn(Promise.resolve("a"));
mockExtensionResolver.resolve.and.returnValue(Promise.resolve("a"));
resolver = new BundleResolver(
mockExtensionResolver,
@@ -57,27 +57,11 @@ define(
});
it("invokes the extension resolver for all bundle extensions", function () {
var result;
resolver.resolveBundles([
return resolver.resolveBundles([
new Bundle("x", { extensions: { tests: [{}, {}, {}] } }),
new Bundle("y", { extensions: { tests: [{}, {}], others: [{}, {}] } }),
new Bundle("z", { extensions: { others: [{}] } })
]).then(function (v) {
result = v;
});
waitsFor(
function () {
return result !== undefined;
},
"promise resolution",
250
);
// Should get back the result from the resolver, and
// should be binned by extension category.
runs(function () {
]).then(function (result) {
expect(result.tests).toEqual(["a", "a", "a", "a", "a"]);
expect(result.others).toEqual(["a", "a", "a"]);
});

View File

@@ -46,7 +46,7 @@ define(
["error", "warn", "info", "debug"]
);
mockLoader.load.andReturn(Promise.resolve(Constructor));
mockLoader.load.and.returnValue(Promise.resolve(Constructor));
resolver = new ExtensionResolver(mockLoader, mockLog);
});
@@ -56,22 +56,9 @@ define(
sources: "x",
extensions: { tests: [{ implementation: "y/z.js" }] }
}),
extension = bundle.getExtensions("tests")[0],
result;
extension = bundle.getExtensions("tests")[0];
resolver.resolve(extension).then(function (v) {
result = v;
});
waitsFor(
function () {
return result !== undefined;
},
"promise resolution",
250
);
runs(function () {
return resolver.resolve(extension).then(function (result) {
// Verify that the right file was requested
expect(mockLoader.load).toHaveBeenCalledWith("w/x/y/z.js");
@@ -90,26 +77,13 @@ define(
implementation: "y/z.js"
}] }
}),
extension = bundle.getExtensions("tests")[0],
result;
extension = bundle.getExtensions("tests")[0];
mockLoader.load.andReturn(Promise.reject(new Error("test error")));
resolver.resolve(extension).then(function (v) {
result = v;
});
mockLoader.load.and.returnValue(Promise.reject(new Error("test error")));
waitsFor(
function () {
return result !== undefined;
},
"promise resolution",
250
);
runs(function () {
return resolver.resolve(extension).then(function (result) {
// Should have gotten a warning
expect(mockLog.warn).toHaveBeenCalled();
// We should have resolved to the plain definition from above
expect(typeof result).not.toEqual('function');
expect(result.someOtherKey).toEqual("some other value");
@@ -121,25 +95,11 @@ define(
sources: "x",
extensions: { tests: [{ implementation: "y/z.js" }] }
}),
extension = bundle.getExtensions("tests")[0],
result;
extension = bundle.getExtensions("tests")[0];
resolver.resolve(extension).then(function (v) {
result = v;
});
waitsFor(
function () {
return result !== undefined;
},
"promise resolution",
250
);
runs(function () {
return resolver.resolve(extension).then(function (result) {
// Verify that the right file was requested
expect(mockLoader.load).toHaveBeenCalledWith("w/x/y/z.js");
// We should have resolved to the constructor from above
expect(typeof result).toEqual('function');
expect(result().someKey).toEqual("some value");

View File

@@ -50,28 +50,14 @@ define(
});
it("wraps require results in a Promise that can resolve", function () {
var result;
// Load and get the result
loader.load("xyz.js").then(function (v) {
result = v;
var promise = loader.load("xyz.js").then(function (result) {
expect(result).toEqual("test result");
});
expect(result).toBeUndefined();
required.fulfill("test result");
waitsFor(
function () {
return result !== undefined;
},
"promise resolution",
250
);
runs(function () {
expect(result).toEqual("test result");
});
return promise;
});
it("wraps require results in a Promise that can reject", function () {
@@ -79,28 +65,19 @@ define(
rejection;
// Load and get the result
loader.load("xyz.js").then(
var promise = loader.load("xyz.js").then(
function (v) {
result = v;
},
function (v) {
rejection = v;
}
);
});
expect(result).toBeUndefined();
required.reject("test result");
waitsFor(
function () {
return rejection !== undefined;
},
"promise resolution",
250
);
runs(function () {
return promise.then(function () {
expect(result).toBeUndefined();
expect(rejection).toEqual("test result");
});