Commit ec3d81f3 authored by Kyle Keating's avatar Kyle Keating

Add assert-methods.js module

- This is used to patch in helper methods into the global assert object such as `rejects()` for testing.
- The new rejects method gives tests the ability to easily test error results in async methods that return a promise.
- Replace all async try/catch tests with assert.rejects
Signed-off-by: 's avatarKyle Keating <kkeating@hypothes.is>
parent b802467d
...@@ -244,12 +244,7 @@ describe('annotationsService', () => { ...@@ -244,12 +244,7 @@ describe('annotationsService', () => {
fakeApi.annotation.delete.rejects(new Error('Annotation does not exist')); fakeApi.annotation.delete.rejects(new Error('Annotation does not exist'));
const annot = fixtures.defaultAnnotation(); const annot = fixtures.defaultAnnotation();
try { await assert.rejects(svc.delete(annot), 'Annotation does not exist');
await svc.delete(annot);
} catch (e) {
/* Ignored */
}
assert.notCalled(fakeStore.removeAnnotations); assert.notCalled(fakeStore.removeAnnotations);
}); });
}); });
...@@ -271,12 +266,7 @@ describe('annotationsService', () => { ...@@ -271,12 +266,7 @@ describe('annotationsService', () => {
fakeApi.annotation.flag.rejects(new Error('Annotation does not exist')); fakeApi.annotation.flag.rejects(new Error('Annotation does not exist'));
const annot = fixtures.defaultAnnotation(); const annot = fixtures.defaultAnnotation();
try { await assert.rejects(svc.flag(annot), 'Annotation does not exist');
await svc.flag(annot);
} catch (e) {
/* Ignored */
}
assert.notCalled(fakeStore.updateFlagStatus); assert.notCalled(fakeStore.updateFlagStatus);
}); });
}); });
......
// Expose the sinon assertions. // Expose the sinon assertions.
sinon.assert.expose(assert, { prefix: null }); sinon.assert.expose(assert, { prefix: null });
// Patch extra assert helper methods
import patch from '../../test-util/assert-methods';
patch(assert);
import 'angular'; import 'angular';
import 'angular-mocks'; import 'angular-mocks';
......
import { assertPromiseIsRejected } from '../../../shared/test/promise-util';
import { fetchConfig, $imports } from '../fetch-config'; import { fetchConfig, $imports } from '../fetch-config';
describe('sidebar.util.fetch-config', () => { describe('sidebar.util.fetch-config', () => {
...@@ -108,18 +107,18 @@ describe('sidebar.util.fetch-config', () => { ...@@ -108,18 +107,18 @@ describe('sidebar.util.fetch-config', () => {
}); });
}); });
it('rejects if sidebar is top frame', () => { it('rejects if sidebar is top frame', async () => {
fakeWindow.parent = fakeWindow; fakeWindow.parent = fakeWindow;
fakeWindow.top = fakeWindow; fakeWindow.top = fakeWindow;
await assert.rejects(
const config = fetchConfig({}, fakeWindow); fetchConfig({}, fakeWindow),
return assertPromiseIsRejected(config, 'Client is top frame'); 'Client is top frame'
);
}); });
it('rejects if fetching config fails', () => { it('rejects if fetching config fails', async () => {
fakeJsonRpc.call.returns(Promise.reject(new Error('Nope'))); fakeJsonRpc.call.returns(Promise.reject(new Error('Nope')));
const config = fetchConfig({}, fakeWindow); await assert.rejects(fetchConfig({}, fakeWindow), 'Nope');
return assertPromiseIsRejected(config, 'Nope');
}); });
it('returns config from ancestor frame', async () => { it('returns config from ancestor frame', async () => {
...@@ -210,15 +209,10 @@ describe('sidebar.util.fetch-config', () => { ...@@ -210,15 +209,10 @@ describe('sidebar.util.fetch-config', () => {
ancestorLevel: 10, // The top window is only 2 levels high ancestorLevel: 10, // The top window is only 2 levels high
}, },
}); });
try { await assert.rejects(
await fetchConfig({}, fakeWindow); fetchConfig({}, fakeWindow),
throw new Error('Failed to catch error'); /The target parent frame has exceeded the ancestor tree|Try reducing the/g
} catch (e) { );
assert.equal(
e.message,
'The target parent frame has exceeded the ancestor tree. Try reducing the `requestConfigFromFrame.ancestorLevel` value in the `hypothesisConfig`'
);
}
}); });
it('creates a merged config when the RPC requests returns the host config', async () => { it('creates a merged config when the RPC requests returns the host config', async () => {
...@@ -235,9 +229,7 @@ describe('sidebar.util.fetch-config', () => { ...@@ -235,9 +229,7 @@ describe('sidebar.util.fetch-config', () => {
it('rejects if fetching config fails` ', async () => { it('rejects if fetching config fails` ', async () => {
fakeJsonRpc.call.rejects(new Error('Nope')); fakeJsonRpc.call.rejects(new Error('Nope'));
const appConfig = { foo: 'bar', appType: 'via' }; const appConfig = { foo: 'bar', appType: 'via' };
await assert.rejects(fetchConfig(appConfig, fakeWindow), 'Nope');
const result = fetchConfig(appConfig, fakeWindow);
return assertPromiseIsRejected(result, 'Nope');
}); });
it('returns the `groups` array with the initial host config request', async () => { it('returns the `groups` array with the initial host config request', async () => {
...@@ -278,12 +270,10 @@ describe('sidebar.util.fetch-config', () => { ...@@ -278,12 +270,10 @@ describe('sidebar.util.fetch-config', () => {
fakeJsonRpc.call.onFirstCall().resolves({ foo: 'baz' }); // host config fakeJsonRpc.call.onFirstCall().resolves({ foo: 'baz' }); // host config
fakeJsonRpc.call.onSecondCall().rejects(); // requestGroups fakeJsonRpc.call.onSecondCall().rejects(); // requestGroups
const result = await fetchConfig(appConfig, fakeWindow); const result = await fetchConfig(appConfig, fakeWindow);
try { await assert.rejects(
await result.services[0].groups; result.services[0].groups,
throw new Error('Failed to catch error'); 'Unable to fetch groups'
} catch (e) { );
assert.equal(e.message, 'Unable to fetch groups');
}
}); });
}); });
...@@ -299,15 +289,10 @@ describe('sidebar.util.fetch-config', () => { ...@@ -299,15 +289,10 @@ describe('sidebar.util.fetch-config', () => {
// missing ancestorLevel // missing ancestorLevel
}, },
}); });
try { await assert.rejects(
await fetchConfig({}, fakeWindow); fetchConfig({}, fakeWindow),
throw new Error('Failed to catch error'); 'Improper `requestConfigFromFrame` object. Both `ancestorLevel` and `origin` need to be specified'
} catch (e) { );
assert.equal(
e.message,
'Improper `requestConfigFromFrame` object. Both `ancestorLevel` and `origin` need to be specified'
);
}
}); });
it('missing origin', async () => { it('missing origin', async () => {
...@@ -317,15 +302,10 @@ describe('sidebar.util.fetch-config', () => { ...@@ -317,15 +302,10 @@ describe('sidebar.util.fetch-config', () => {
ancestorLevel: 2, ancestorLevel: 2,
}, },
}); });
try { await assert.rejects(
await fetchConfig({}, fakeWindow); fetchConfig({}, fakeWindow),
throw new Error('Failed to catch error'); 'Improper `requestConfigFromFrame` object. Both `ancestorLevel` and `origin` need to be specified'
} catch (e) { );
assert.equal(
e.message,
'Improper `requestConfigFromFrame` object. Both `ancestorLevel` and `origin` need to be specified'
);
}
}); });
}); });
}); });
......
/**
* Add-on for async exception testing.
*
* The `promiseResult` is awaited, and then upon any exception, the resulting
* error message is matched against the `errorMessage` string or regex. This
* method throws an error if `promiseResult` does not throw an error or will
* throw an assertion error if the caught error message differs from the
* `errorMessage` parameter.
*
* e.g. await rejects(someAsyncFunction(), /expected error/g);
*
* @param {Promise} promiseResult - The returned promise a function to test
* @param errorMessage {RegEx|String} - A string or regex that matches the error
* which is expected to be thrown.
*/
const rejects = async (promiseResult, errorMessage) => {
try {
await promiseResult;
const error = new Error();
error.name = 'ErrorNotCaught';
throw error;
} catch (e) {
if (e.name === 'ErrorNotCaught') {
throw new Error(
'Expected to catch the rejected promise but it was not thrown'
);
}
if (errorMessage instanceof RegExp) {
assert.isTrue(errorMessage.test(e.message));
} else {
assert.equal(errorMessage, e.message);
}
}
};
/**
* Patches the assert object with additional custom helper methods
* defined in this module.
*
* @param {Object} assert - global assertion object.
*/
export default function patch(assert) {
assert.rejects = rejects;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment