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

@@ -60,14 +60,14 @@ define(
PERSISTENCE_SERVICE_METHODS
);
PERSISTENCE_SERVICE_METHODS.forEach(function (m) {
mockProvider[m].andReturn(fakePromise(true));
mockProvider[m].and.returnValue(fakePromise(true));
});
mockProvider.listSpaces.andReturn(fakePromise([space]));
mockProvider.listSpaces.and.returnValue(fakePromise([space]));
return mockProvider;
});
mockCallback = jasmine.createSpy();
mockQ.all.andCallFake(function (fakePromises) {
mockQ.all.and.callFake(function (fakePromises) {
var result = [];
fakePromises.forEach(function (p) {
p.then(function (v) {

View File

@@ -39,7 +39,7 @@ define(
testPath = "/test/path";
testInterval = 12321; // Some number
mockHttp.get.andReturn(mockPromise);
mockHttp.get.and.returnValue(mockPromise);
indicator = new CouchIndicator(
mockHttp,
@@ -71,7 +71,7 @@ define(
// Nominal just means getting back an object, without
// an error field.
mockPromise.then.mostRecentCall.args[0]({ data: {} });
mockPromise.then.calls.mostRecent().args[0]({ data: {} });
// Verify that these values changed;
// don't test for specific text.
@@ -90,7 +90,7 @@ define(
// Nominal just means getting back an object, with
// an error field.
mockPromise.then.mostRecentCall.args[0](
mockPromise.then.calls.mostRecent().args[0](
{ data: { error: "Uh oh." } }
);
@@ -112,7 +112,7 @@ define(
// Nominal just means getting back an object, without
// an error field.
mockPromise.then.mostRecentCall.args[1]({ data: {} });
mockPromise.then.calls.mostRecent().args[1]({ data: {} });
// Verify that these values changed;
// don't test for specific text.

View File

@@ -47,7 +47,7 @@ define(
mockHttp = jasmine.createSpy("$http");
mockQ = jasmine.createSpyObj("$q", ["when"]);
mockQ.when.andCallFake(mockPromise);
mockQ.when.and.callFake(mockPromise);
// Capture promise results
capture = jasmine.createSpy("capture");
@@ -70,20 +70,21 @@ define(
// would expect, and finally verify that CouchPersistenceProvider's
// return values match what is expected.
it("lists all available documents", function () {
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { rows: [{ id: "a" }, { id: "b" }, { id: "c" }] }
}));
provider.listObjects().then(capture);
expect(mockHttp).toHaveBeenCalledWith({
url: "/test/db/_all_docs", // couch document listing
method: "GET"
method: "GET",
data: undefined
});
expect(capture).toHaveBeenCalledWith(["a", "b", "c"]);
});
it("allows object creation", function () {
var model = { someKey: "some value" };
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_rev": "xyz", "ok": true }
}));
provider.createObject("testSpace", "abc", model).then(capture);
@@ -92,6 +93,8 @@ define(
method: "PUT",
data: {
"_id": "abc",
"_rev": undefined,
"_deleted": undefined,
metadata: jasmine.any(Object),
model: model
}
@@ -101,13 +104,14 @@ define(
it("allows object models to be read back", function () {
var model = { someKey: "some value" };
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_rev": "xyz", "model": model }
}));
provider.readObject("testSpace", "abc").then(capture);
expect(mockHttp).toHaveBeenCalledWith({
url: "/test/db/abc",
method: "GET"
method: "GET",
data: undefined
});
expect(capture).toHaveBeenCalledWith(model);
});
@@ -116,13 +120,13 @@ define(
var model = { someKey: "some value" };
// First do a read to populate rev tags...
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_rev": "xyz", "model": {} }
}));
provider.readObject("testSpace", "abc");
// Now perform an update
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_rev": "uvw", "ok": true }
}));
provider.updateObject("testSpace", "abc", model).then(capture);
@@ -132,6 +136,7 @@ define(
data: {
"_id": "abc",
"_rev": "xyz",
"_deleted": undefined,
metadata: jasmine.any(Object),
model: model
}
@@ -141,13 +146,13 @@ define(
it("allows object deletion", function () {
// First do a read to populate rev tags...
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_rev": "xyz", "model": {} }
}));
provider.readObject("testSpace", "abc");
// Now perform an update
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_rev": "uvw", "ok": true }
}));
provider.deleteObject("testSpace", "abc", {}).then(capture);
@@ -167,7 +172,7 @@ define(
it("reports failure to create objects", function () {
var model = { someKey: "some value" };
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_rev": "xyz", "ok": false }
}));
provider.createObject("testSpace", "abc", model).then(capture);
@@ -176,7 +181,7 @@ define(
it("returns undefined when objects are not found", function () {
// Act like a 404
mockHttp.andReturn({
mockHttp.and.returnValue({
then: function (success, fail) {
return mockPromise(fail());
}

View File

@@ -39,7 +39,7 @@ define(
testPath = "/test/path";
testInterval = 12321; // Some number
mockHttp.get.andReturn(mockPromise);
mockHttp.get.and.returnValue(mockPromise);
indicator = new ElasticIndicator(
mockHttp,
@@ -73,7 +73,7 @@ define(
// Nominal just means getting back an object, without
// an error field.
mockPromise.then.mostRecentCall.args[0]({ data: {} });
mockPromise.then.calls.mostRecent().args[0]({ data: {} });
// Verify that these values changed;
// don't test for specific text.
@@ -92,7 +92,7 @@ define(
// Nominal just means getting back an object, without
// an error field.
mockPromise.then.mostRecentCall.args[1]({ data: {} });
mockPromise.then.calls.mostRecent().args[1]({ data: {} });
// Verify that these values changed;
// don't test for specific text.

View File

@@ -46,8 +46,8 @@ define(
mockHttp = jasmine.createSpy("$http");
mockQ = jasmine.createSpyObj("$q", ["when", "reject"]);
mockQ.when.andCallFake(mockPromise);
mockQ.reject.andCallFake(function (value) {
mockQ.when.and.callFake(mockPromise);
mockQ.reject.and.callFake(function (value) {
return {
then: function (ignored, callback) {
return mockPromise(callback(value));
@@ -84,27 +84,30 @@ define(
it("allows object creation", function () {
var model = { someKey: "some value" };
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 1 }
}));
provider.createObject("testSpace", "abc", model).then(capture);
expect(mockHttp).toHaveBeenCalledWith({
url: "/test/db/abc",
method: "PUT",
data: model
data: model,
params: undefined
});
expect(capture.mostRecentCall.args[0]).toBeTruthy();
expect(capture.calls.mostRecent().args[0]).toBeTruthy();
});
it("allows object models to be read back", function () {
var model = { someKey: "some value" };
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 1, "_source": model }
}));
provider.readObject("testSpace", "abc").then(capture);
expect(mockHttp).toHaveBeenCalledWith({
url: "/test/db/abc",
method: "GET"
method: "GET",
params: undefined,
data: undefined
});
expect(capture).toHaveBeenCalledWith(model);
});
@@ -113,13 +116,13 @@ define(
var model = { someKey: "some value" };
// First do a read to populate rev tags...
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} }
}));
provider.readObject("testSpace", "abc");
// Now perform an update
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 43, "_source": {} }
}));
provider.updateObject("testSpace", "abc", model).then(capture);
@@ -129,31 +132,33 @@ define(
params: { version: 42 },
data: model
});
expect(capture.mostRecentCall.args[0]).toBeTruthy();
expect(capture.calls.mostRecent().args[0]).toBeTruthy();
});
it("allows object deletion", function () {
// First do a read to populate rev tags...
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} }
}));
provider.readObject("testSpace", "abc");
// Now perform an update
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} }
}));
provider.deleteObject("testSpace", "abc", {}).then(capture);
expect(mockHttp).toHaveBeenCalledWith({
url: "/test/db/abc",
method: "DELETE"
method: "DELETE",
params: undefined,
data: undefined
});
expect(capture.mostRecentCall.args[0]).toBeTruthy();
expect(capture.calls.mostRecent().args[0]).toBeTruthy();
});
it("returns undefined when objects are not found", function () {
// Act like a 404
mockHttp.andReturn({
mockHttp.and.returnValue({
then: function (success, fail) {
return mockPromise(fail());
}
@@ -167,13 +172,13 @@ define(
mockErrorCallback = jasmine.createSpy('error');
// First do a read to populate rev tags...
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} }
}));
provider.readObject("testSpace", "abc");
// Now perform an update
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "status": 409, "error": "Revision error..." }
}));
provider.updateObject("testSpace", "abc", model).then(
@@ -190,13 +195,13 @@ define(
mockErrorCallback = jasmine.createSpy('error');
// First do a read to populate rev tags...
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "_id": "abc", "_version": 42, "_source": {} }
}));
provider.readObject("testSpace", "abc");
// Now perform an update
mockHttp.andReturn(mockPromise({
mockHttp.and.returnValue(mockPromise({
data: { "status": 410, "error": "Revision error..." }
}));
provider.updateObject("testSpace", "abc", model).then(

View File

@@ -43,10 +43,10 @@ define([
describe('query', function () {
beforeEach(function () {
spyOn(provider, 'cleanTerm').andReturn('cleanedTerm');
spyOn(provider, 'fuzzyMatchUnquotedTerms').andReturn('fuzzy');
spyOn(provider, 'parseResponse').andReturn('parsedResponse');
$http.andReturn(Promise.resolve({}));
spyOn(provider, 'cleanTerm').and.returnValue('cleanedTerm');
spyOn(provider, 'fuzzyMatchUnquotedTerms').and.returnValue('fuzzy');
spyOn(provider, 'parseResponse').and.returnValue('parsedResponse');
$http.and.returnValue(Promise.resolve({}));
});
it('cleans terms and adds fuzzyness', function () {
@@ -69,40 +69,28 @@ define([
});
it('gracefully fails when http fails', function () {
var promiseChainResolved = false;
$http.andReturn(Promise.reject());
$http.and.returnValue(Promise.reject());
provider
return provider
.query('hello', 10)
.then(function (results) {
expect(results).toEqual({
hits: [],
total: 0
});
promiseChainResolved = true;
});
waitsFor(function () {
return promiseChainResolved;
});
});
it('parses and returns when http succeeds', function () {
var promiseChainResolved = false;
$http.andReturn(Promise.resolve('successResponse'));
$http.and.returnValue(Promise.resolve('successResponse'));
provider
return provider
.query('hello', 10)
.then(function (results) {
expect(provider.parseResponse)
.toHaveBeenCalledWith('successResponse');
expect(results).toBe('parsedResponse');
promiseChainResolved = true;
});
waitsFor(function () {
return promiseChainResolved;
});
});
});

View File

@@ -46,7 +46,7 @@ define(
mockQ = jasmine.createSpyObj("$q", ["when", "reject"]);
mockCallback = jasmine.createSpy('callback');
mockQ.when.andCallFake(mockPromise);
mockQ.when.and.callFake(mockPromise);
provider = new LocalStoragePersistenceProvider(
{ localStorage: testLocalStorage },
@@ -62,10 +62,10 @@ define(
it("lists all available documents", function () {
provider.listObjects(testSpace).then(mockCallback);
expect(mockCallback.mostRecentCall.args[0]).toEqual([]);
expect(mockCallback.calls.mostRecent().args[0]).toEqual([]);
provider.createObject(testSpace, 'abc', { a: 42 });
provider.listObjects(testSpace).then(mockCallback);
expect(mockCallback.mostRecentCall.args[0]).toEqual(['abc']);
expect(mockCallback.calls.mostRecent().args[0]).toEqual(['abc']);
});
it("allows object creation", function () {
@@ -74,7 +74,7 @@ define(
.then(mockCallback);
expect(JSON.parse(testLocalStorage[testSpace]).abc)
.toEqual(model);
expect(mockCallback.mostRecentCall.args[0]).toBeTruthy();
expect(mockCallback.calls.mostRecent().args[0]).toBeTruthy();
});
it("allows object models to be read back", function () {

View File

@@ -53,10 +53,10 @@ define(
'domainObject',
['getCapability', 'useCapability', 'getModel']
);
mockFailure.domainObject.getCapability.andCallFake(function (c) {
mockFailure.domainObject.getCapability.and.callFake(function (c) {
return (c === 'persistence') && mockPersistence;
});
mockFailure.domainObject.getModel.andReturn({ id: id, modified: index });
mockFailure.domainObject.getModel.and.returnValue({ id: id, modified: index });
mockFailure.persistence = mockPersistence;
mockFailure.id = id;
mockFailure.error = { key: Constants.REVISION_ERROR_KEY };
@@ -68,9 +68,9 @@ define(
mockDialogService = jasmine.createSpyObj('dialogService', ['getUserChoice']);
mockFailures = ['a', 'b', 'c'].map(makeMockFailure);
mockPromise = jasmine.createSpyObj('promise', ['then']);
mockDialogService.getUserChoice.andReturn(mockPromise);
mockQ.all.andReturn(mockPromise);
mockPromise.then.andReturn(mockPromise);
mockDialogService.getUserChoice.and.returnValue(mockPromise);
mockQ.all.and.returnValue(mockPromise);
mockPromise.then.and.returnValue(mockPromise);
handler = new PersistenceFailureHandler(mockQ, mockDialogService);
});
@@ -80,10 +80,10 @@ define(
});
it("overwrites on request", function () {
mockQ.all.andReturn(asPromise([]));
mockQ.all.and.returnValue(asPromise([]));
handler.handle(mockFailures);
// User chooses overwrite
mockPromise.then.mostRecentCall.args[0](Constants.OVERWRITE_KEY);
mockPromise.then.calls.mostRecent().args[0](Constants.OVERWRITE_KEY);
// Should refresh, remutate, and requeue all objects
mockFailures.forEach(function (mockFailure, i) {
expect(mockFailure.persistence.refresh).toHaveBeenCalled();
@@ -93,16 +93,16 @@ define(
jasmine.any(Function),
i // timestamp
);
expect(mockFailure.domainObject.useCapability.mostRecentCall.args[1]())
expect(mockFailure.domainObject.useCapability.calls.mostRecent().args[1]())
.toEqual({ id: mockFailure.id, modified: i });
});
});
it("discards on request", function () {
mockQ.all.andReturn(asPromise([]));
mockQ.all.and.returnValue(asPromise([]));
handler.handle(mockFailures);
// User chooses overwrite
mockPromise.then.mostRecentCall.args[0](false);
mockPromise.then.calls.mostRecent().args[0](false);
// Should refresh, but not remutate, and requeue all objects
mockFailures.forEach(function (mockFailure) {
expect(mockFailure.persistence.refresh).toHaveBeenCalled();

View File

@@ -49,7 +49,7 @@ define(
'persistence-' + id,
['persist', 'refresh']
);
mockPersistence.persist.andReturn(asPromise(true));
mockPersistence.persist.and.returnValue(asPromise(true));
return mockPersistence;
}
@@ -58,7 +58,7 @@ define(
'domainObject-' + id,
['getId']
);
mockDomainObject.getId.andReturn(id);
mockDomainObject.getId.and.returnValue(id);
return mockDomainObject;
}
@@ -73,8 +73,8 @@ define(
mockDomainObjects[id] = makeMockDomainObject(id);
});
mockRejection = jasmine.createSpyObj('rejection', ['then']);
mockQ.all.andReturn(asPromise([]));
mockRejection.then.andCallFake(function (callback, fallback) {
mockQ.all.and.returnValue(asPromise([]));
mockRejection.then.and.callFake(function (callback, fallback) {
return asPromise(fallback({ someKey: "some value" }));
});
handler = new PersistenceQueueHandler(mockQ, mockFailureHandler);
@@ -90,8 +90,8 @@ define(
});
it("handles failures that occur", function () {
mockPersistences.b.persist.andReturn(mockRejection);
mockPersistences.c.persist.andReturn(mockRejection);
mockPersistences.b.persist.and.returnValue(mockRejection);
mockPersistences.c.persist.and.returnValue(mockRejection);
handler.persist(mockPersistences, mockDomainObjects, mockQueue);
expect(mockFailureHandler.handle).toHaveBeenCalledWith([
{
@@ -115,14 +115,14 @@ define(
// This method is needed by PersistenceFailureHandler
// to allow requeuing of objects for persistence when
// Overwrite is chosen.
mockPersistences.b.persist.andReturn(mockRejection);
mockPersistences.b.persist.and.returnValue(mockRejection);
handler.persist(mockPersistences, mockDomainObjects, mockQueue);
// Verify precondition
expect(mockQueue.put).not.toHaveBeenCalled();
// Invoke requeue
mockFailureHandler.handle.mostRecentCall.args[0][0].requeue();
mockFailureHandler.handle.calls.mostRecent().args[0][0].requeue();
// Should have returned the object to the queue
expect(mockQueue.put).toHaveBeenCalledWith(

View File

@@ -40,7 +40,7 @@ define(
'domainObject-' + id,
['getId']
);
mockDomainObject.getId.andReturn(id);
mockDomainObject.getId.and.returnValue(id);
return mockDomainObject;
}
@@ -59,10 +59,10 @@ define(
mockDeferred = jasmine.createSpyObj('deferred', ['resolve']);
mockDeferred.promise = jasmine.createSpyObj('promise', ['then']);
mockPromise = jasmine.createSpyObj('promise', ['then']);
mockQ.defer.andReturn(mockDeferred);
mockTimeout.andReturn({});
mockHandler.persist.andReturn(mockPromise);
mockPromise.then.andReturn(mockPromise);
mockQ.defer.and.returnValue(mockDeferred);
mockTimeout.and.returnValue({});
mockHandler.persist.and.returnValue(mockPromise);
mockPromise.then.and.returnValue(mockPromise);
queue = new PersistenceQueueImpl(
mockQ,
mockTimeout,
@@ -87,7 +87,7 @@ define(
queue.put(makeMockDomainObject('a'), makeMockPersistence('a'));
queue.put(makeMockDomainObject('b'), makeMockPersistence('b'));
queue.put(makeMockDomainObject('c'), makeMockPersistence('c'));
expect(mockTimeout.calls.length).toEqual(1);
expect(mockTimeout.calls.count()).toEqual(1);
});
it("returns a promise", function () {
@@ -99,14 +99,14 @@ define(
// Keep adding objects to the queue between timeouts.
// Should keep scheduling timeouts instead of resolving.
queue.put(makeMockDomainObject('a'), makeMockPersistence('a'));
expect(mockTimeout.calls.length).toEqual(1);
mockTimeout.mostRecentCall.args[0]();
expect(mockTimeout.calls.count()).toEqual(1);
mockTimeout.calls.mostRecent().args[0]();
queue.put(makeMockDomainObject('b'), makeMockPersistence('b'));
expect(mockTimeout.calls.length).toEqual(2);
mockTimeout.mostRecentCall.args[0]();
expect(mockTimeout.calls.count()).toEqual(2);
mockTimeout.calls.mostRecent().args[0]();
queue.put(makeMockDomainObject('c'), makeMockPersistence('c'));
expect(mockTimeout.calls.length).toEqual(3);
mockTimeout.mostRecentCall.args[0]();
expect(mockTimeout.calls.count()).toEqual(3);
mockTimeout.calls.mostRecent().args[0]();
expect(mockHandler.persist).not.toHaveBeenCalled();
});
@@ -115,8 +115,8 @@ define(
queue.put(makeMockDomainObject('a'), makeMockPersistence('a'));
queue.put(makeMockDomainObject('b'), makeMockPersistence('b'));
queue.put(makeMockDomainObject('c'), makeMockPersistence('c'));
mockTimeout.mostRecentCall.args[0]();
mockTimeout.mostRecentCall.args[0]();
mockTimeout.calls.mostRecent().args[0]();
mockTimeout.calls.mostRecent().args[0]();
expect(mockHandler.persist).toHaveBeenCalled();
});
@@ -124,28 +124,28 @@ define(
// Persist some objects
queue.put(makeMockDomainObject('a'), makeMockPersistence('a'));
queue.put(makeMockDomainObject('b'), makeMockPersistence('b'));
mockTimeout.mostRecentCall.args[0]();
mockTimeout.mostRecentCall.args[0]();
expect(mockTimeout.calls.length).toEqual(2);
mockTimeout.calls.mostRecent().args[0]();
mockTimeout.calls.mostRecent().args[0]();
expect(mockTimeout.calls.count()).toEqual(2);
// Adding a new object should not trigger a new timeout,
// because we haven't completed the previous flush
queue.put(makeMockDomainObject('c'), makeMockPersistence('c'));
expect(mockTimeout.calls.length).toEqual(2);
expect(mockTimeout.calls.count()).toEqual(2);
});
it("clears the active flush after it has completed", function () {
// Persist some objects
queue.put(makeMockDomainObject('a'), makeMockPersistence('a'));
queue.put(makeMockDomainObject('b'), makeMockPersistence('b'));
mockTimeout.mostRecentCall.args[0]();
mockTimeout.mostRecentCall.args[0]();
expect(mockTimeout.calls.length).toEqual(2);
mockTimeout.calls.mostRecent().args[0]();
mockTimeout.calls.mostRecent().args[0]();
expect(mockTimeout.calls.count()).toEqual(2);
// Resolve the promise from handler.persist
mockPromise.then.calls[0].args[0](true);
mockPromise.then.calls.all()[0].args[0](true);
// Adding a new object should now trigger a new timeout,
// because we have completed the previous flush
queue.put(makeMockDomainObject('c'), makeMockPersistence('c'));
expect(mockTimeout.calls.length).toEqual(3);
expect(mockTimeout.calls.count()).toEqual(3);
});
});
}

View File

@@ -53,10 +53,10 @@ define(
['getId']
);
mockCapabilityService.getCapabilities.andReturn({
mockCapabilityService.getCapabilities.and.returnValue({
persistence: mockPersistenceConstructor
});
mockPersistenceConstructor.andReturn(mockPersistence);
mockPersistenceConstructor.and.returnValue(mockPersistence);
decorator = new QueuingPersistenceCapabilityDecorator(
mockQueue,