diff --git a/frontend/src/services/html-helpers/html-helpers.js b/frontend/src/services/html-helpers/html-helpers.js
index 7956a569c..92d3168b5 100644
--- a/frontend/src/services/html-helpers/html-helpers.js
+++ b/frontend/src/services/html-helpers/html-helpers.js
@@ -11,7 +11,7 @@ export class HtmlHelpers {
.then(
(responseText) => {
const docFrag = document.createRange().createContextualFragment(responseText);
- let idPrefix;
+ let idPrefix = '';
if (!options.keepIds) {
idPrefix = this._getIdPrefix();
this._prefixIds(docFrag, idPrefix);
diff --git a/frontend/src/services/html-helpers/html-helpers.spec.js b/frontend/src/services/html-helpers/html-helpers.spec.js
index cd13b5cac..c77015b26 100644
--- a/frontend/src/services/html-helpers/html-helpers.spec.js
+++ b/frontend/src/services/html-helpers/html-helpers.spec.js
@@ -2,6 +2,7 @@ import { HtmlHelpers } from "./html-helpers";
describe('HtmlHelpers', () => {
let htmlHelpers;
+
beforeEach(() => {
htmlHelpers = new HtmlHelpers();
});
@@ -10,13 +11,17 @@ describe('HtmlHelpers', () => {
expect(htmlHelpers).toBeTruthy();
});
- describe('parseResponse', () => {
- it('should return a promise with idPrefix and element', (done) => {
- const response = {
- text: () => Promise.resolve('
Test
'),
- };
+ describe('parseResponse()', () => {
+ let fakeHttpResponse;
- htmlHelpers.parseResponse(response).then(result => {
+ beforeEach(() => {
+ fakeHttpResponse = {
+ text: () => Promise.resolve('Test
'),
+ };
+ });
+
+ it('should return a promise with idPrefix and element', (done) => {
+ htmlHelpers.parseResponse(fakeHttpResponse).then(result => {
expect(result.idPrefix).toBeDefined();
expect(result.element).toBeDefined();
expect(result.element.textContent).toMatch('Test');
@@ -25,34 +30,25 @@ describe('HtmlHelpers', () => {
});
it('should nudge IDs', (done) => {
- const response = {
- text: () => Promise.resolve('Test
'),
- };
-
- htmlHelpers.parseResponse(response).then(result => {
+ htmlHelpers.parseResponse(fakeHttpResponse).then(result => {
expect(result.idPrefix).toBeDefined();
expect(result.element).toBeDefined();
- const origElement = result.element.querySelector('#test-div');
- expect(origElement).toBeFalsy();
- const nudgedElement = result.element.querySelector('#' + result.idPrefix + 'test-div');
- expect(nudgedElement).toBeTruthy();
+ const elementWithOrigId = result.element.querySelector('#test-div');
+ expect(elementWithOrigId).toBeFalsy();
+ const elementWithNudgedId = result.element.querySelector('#' + result.idPrefix + 'test-div');
+ expect(elementWithNudgedId).toBeTruthy();
done();
});
});
it('should not nudge IDs with option "keepIds"', (done) => {
- const response = {
- text: () => Promise.resolve('Test
'),
- };
const options = { keepIds: true };
- htmlHelpers.parseResponse(response, options).then(result => {
- expect(result.idPrefix).not.toBeDefined();
+ htmlHelpers.parseResponse(fakeHttpResponse, options).then(result => {
+ expect(result.idPrefix).toBe('');
expect(result.element).toBeDefined();
- const origElement = result.element.querySelector('#test-div');
- expect(origElement).toBeTruthy();
- const nudgedElement = result.element.querySelector('#' + result.idPrefix + 'test-div');
- expect(nudgedElement).toBeFalsy();
+ const elementWithOrigId = result.element.querySelector('#test-div');
+ expect(elementWithOrigId).toBeTruthy();
done();
});
});
diff --git a/frontend/src/services/http-client/http-client.spec.js b/frontend/src/services/http-client/http-client.spec.js
new file mode 100644
index 000000000..e61e248f0
--- /dev/null
+++ b/frontend/src/services/http-client/http-client.spec.js
@@ -0,0 +1,116 @@
+import { HttpClient } from "./http-client";
+
+const TEST_URL = 'http://example.com';
+const FAKE_RESPONSE = {
+ data: 'data',
+};
+
+describe('HttpClient', () => {
+ let httpClient;
+
+ beforeEach(() => {
+ httpClient = new HttpClient();
+
+ // setup and spy on fake fetch API
+ spyOn(window, 'fetch').and.returnValue(Promise.resolve(FAKE_RESPONSE));
+ });
+
+ it('should create', () => {
+ expect(httpClient).toBeTruthy();
+ });
+
+ describe('get()', () => {
+ let params;
+
+ beforeEach(() => {
+ params = {
+ url: TEST_URL,
+ };
+ });
+
+ it('should GET the given url', () => {
+ httpClient.get(params);
+ expect(window.fetch).toHaveBeenCalledWith(params.url, jasmine.objectContaining({ method: 'GET' }));
+ });
+
+ it('should return a promise', (done) => {
+ const result = httpClient.get(params);
+ result.then((response) => {
+ expect(response).toEqual(FAKE_RESPONSE);
+ done();
+ });
+ });
+ });
+
+ describe('post()', () => {
+ let params;
+
+ beforeEach(() => {
+ params = {
+ url: TEST_URL,
+ };
+ });
+
+ it('should POST the given url', () => {
+ httpClient.post(params);
+ expect(window.fetch).toHaveBeenCalledWith(params.url, jasmine.objectContaining({ method: 'POST' }));
+ });
+
+ it('should return a promise', (done) => {
+ const result = httpClient.post(params);
+ result.then((response) => {
+ expect(response).toEqual(FAKE_RESPONSE);
+ done();
+ });
+ });
+ });
+
+ describe('Response Interceptors', () => {
+ it('can be added', () => {
+ const interceptor = () => {};
+ expect(httpClient._responseInterceptors.length).toBe(0);
+ httpClient.addResponseInterceptor(interceptor);
+ expect(httpClient._responseInterceptors.length).toBe(1);
+ httpClient.addResponseInterceptor(interceptor);
+ expect(httpClient._responseInterceptors.length).toBe(2);
+ });
+
+ describe('get called', () => {
+ let intercepted1;
+ let intercepted2;
+ const interceptors = {
+ interceptor1: () => intercepted1 = true,
+ interceptor2: () => intercepted2 = true,
+ };
+
+ beforeEach(() => {
+ intercepted1 = false;
+ intercepted2 = false;
+ spyOn(interceptors, 'interceptor1').and.callThrough();
+ spyOn(interceptors, 'interceptor2').and.callThrough();
+ httpClient.addResponseInterceptor(interceptors.interceptor1);
+ httpClient.addResponseInterceptor(interceptors.interceptor2);
+ });
+
+ it('for GET requests', (done) => {
+ httpClient.get({ url: TEST_URL }).then(() => {
+ expect(intercepted1).toBeTruthy();
+ expect(intercepted2).toBeTruthy();
+ expect(interceptors.interceptor1).toHaveBeenCalledWith(FAKE_RESPONSE, jasmine.any(Object));
+ expect(interceptors.interceptor2).toHaveBeenCalledWith(FAKE_RESPONSE, jasmine.any(Object));
+ done();
+ });
+ });
+
+ it('for POST requests', (done) => {
+ httpClient.post({ url: TEST_URL }).then(() => {
+ expect(intercepted1).toBeTruthy();
+ expect(intercepted2).toBeTruthy();
+ expect(interceptors.interceptor1).toHaveBeenCalledWith(FAKE_RESPONSE, jasmine.any(Object));
+ expect(interceptors.interceptor2).toHaveBeenCalledWith(FAKE_RESPONSE, jasmine.any(Object));
+ done();
+ });
+ });
+ });
+ });
+});
diff --git a/frontend/src/services/i18n/i18n.spec.js b/frontend/src/services/i18n/i18n.spec.js
new file mode 100644
index 000000000..76e5348c0
--- /dev/null
+++ b/frontend/src/services/i18n/i18n.spec.js
@@ -0,0 +1,51 @@
+import { I18n } from "./i18n";
+
+describe('I18n', () => {
+ let i18n;
+
+ beforeEach(() => {
+ i18n = new I18n();
+ });
+
+ // helper function
+ function expectTranslation(id, value) {
+ expect(i18n.translations[id]).toMatch(value);
+ }
+
+ it('should create', () => {
+ expect(i18n).toBeTruthy();
+ });
+
+ describe('add()', () => {
+ it('should add the translation', () => {
+ i18n.add('id1', 'translated-id1');
+ expectTranslation('id1', 'translated-id1');
+ });
+ });
+
+ describe('addMany()', () => {
+ it('should add many translations', () => {
+ i18n.addMany({
+ id1: 'translated-id1',
+ id2: 'translated-id2',
+ id3: 'translated-id3',
+ });
+ expectTranslation('id1', 'translated-id1');
+ expectTranslation('id2', 'translated-id2');
+ expectTranslation('id3', 'translated-id3');
+ });
+ });
+
+ describe('get()', () => {
+ it('should return stored translations', () => {
+ i18n.translations.id1 = 'something';
+ expect(i18n.get('id1')).toMatch('something');
+ });
+
+ it('should throw error if translation is missing', () => {
+ expect(() => {
+ i18n.get('id1');
+ }).toThrow();
+ });
+ });
+});
diff --git a/frontend/src/services/util-registry/util-registry.js b/frontend/src/services/util-registry/util-registry.js
index 001bb1e6b..18b656b8c 100644
--- a/frontend/src/services/util-registry/util-registry.js
+++ b/frontend/src/services/util-registry/util-registry.js
@@ -46,10 +46,10 @@ export class UtilRegistry {
}
setApp(appInstance) {
- this.appInstance = appInstance;
+ this._appInstance = appInstance;
}
- setupAll = (scope) => {
+ setupAll(scope) {
if (DEBUG_MODE > 1) {
console.info('registered js utilities:');
console.table(this._registeredUtils);
@@ -70,7 +70,7 @@ export class UtilRegistry {
let utilInstance = null;
try {
- utilInstance = util.setup(element, this.appInstance);
+ utilInstance = util.setup(element, this._appInstance);
} catch(err) {
if (DEBUG_MODE > 0) {
console.warn('Error while trying to initialize a utility!', { util , element, err });
diff --git a/frontend/src/services/util-registry/util-registry.spec.js b/frontend/src/services/util-registry/util-registry.spec.js
new file mode 100644
index 000000000..2e4afedf2
--- /dev/null
+++ b/frontend/src/services/util-registry/util-registry.spec.js
@@ -0,0 +1,126 @@
+import { UtilRegistry } from "./util-registry";
+
+const TEST_UTILS = [{
+ name: 'util1',
+ selector: '#some-id',
+ setup: () => {},
+}, {
+ name: 'util2',
+ selector: '[uw-util]',
+ setup: () => {},
+}];
+
+describe('UtilRegistry', () => {
+ let utilRegistry;
+
+ beforeEach(() => {
+ utilRegistry = new UtilRegistry();
+ });
+
+ it('should create', () => {
+ expect(utilRegistry).toBeTruthy();
+ });
+
+ describe('register()', () => {
+ it('should allow to add utilities', () => {
+ utilRegistry.register(TEST_UTILS[0]);
+
+ const foundUtil = utilRegistry.find(TEST_UTILS[0].name);
+ expect(foundUtil).toEqual(TEST_UTILS[0]);
+ });
+ });
+
+ describe('deregister()', () => {
+ it('should remove util', () => {
+ // register util
+ utilRegistry.register(TEST_UTILS[0]);
+ let foundUtil = utilRegistry.find('util1');
+ expect(foundUtil).toBeTruthy();
+
+ // deregister util
+ utilRegistry.deregister(TEST_UTILS[0].name);
+ foundUtil = utilRegistry.find('util1');
+ expect(foundUtil).toBeFalsy();
+ });
+
+ it('should destroy util instances if requested', () => {
+ pending('TBD');
+ });
+ });
+
+ describe('setup()', () => {
+ it('should catch errors thrown by the utility', () => {
+ spyOn(TEST_UTILS[0], 'setup').and.throwError('some error');
+ expect(() => {
+ utilRegistry.setup(TEST_UTILS[0]);
+ }).not.toThrow();
+ });
+
+ it('should pass the app instance', () => {
+ const scope = document.createElement('div');
+ const utilElement = document.createElement('div');
+ utilElement.id = 'some-id';
+ scope.appendChild(utilElement);
+ const fakeApp = { fn: () => {} };
+ utilRegistry.setApp(fakeApp);
+ spyOn(TEST_UTILS[0], 'setup');
+ utilRegistry.setup(TEST_UTILS[0], scope);
+ expect(TEST_UTILS[0].setup).toHaveBeenCalledWith(utilElement, fakeApp);
+ });
+
+ describe('given no scope', () => {
+ it('should use fallback scope', () => {
+ spyOn(TEST_UTILS[0], 'setup');
+ utilRegistry.setup(TEST_UTILS[0]);
+ expect(TEST_UTILS[0].setup).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('given a scope', () => {
+ let scope;
+ let utilElement1;
+ let utilElement2;
+
+ beforeEach(() => {
+ scope = document.createElement('div');
+ utilElement1 = document.createElement('div');
+ utilElement2 = document.createElement('div');
+ utilElement1.setAttribute('uw-util', '');
+ utilElement2.setAttribute('uw-util', '');
+ scope.appendChild(utilElement1);
+ scope.appendChild(utilElement2);
+ });
+
+ it('should call the utilities\' setup function for each matching element', () => {
+ spyOn(TEST_UTILS[1], 'setup');
+ utilRegistry.setup(TEST_UTILS[1], scope);
+ // 2 matching elements in scope
+ expect(TEST_UTILS[1].setup.calls.count()).toBe(2);
+ expect(TEST_UTILS[1].setup.calls.argsFor(0)).toEqual([utilElement1, undefined]);
+ expect(TEST_UTILS[1].setup.calls.argsFor(1)).toEqual([utilElement2, undefined]);
+ });
+ });
+ });
+
+ describe('setupAll()', () => {
+ it('should setup all the utilities', () => {
+ spyOn(utilRegistry, 'setup');
+ utilRegistry.register(TEST_UTILS[0]);
+ utilRegistry.register(TEST_UTILS[1]);
+ utilRegistry.setupAll();
+
+ expect(utilRegistry.setup.calls.count()).toBe(2);
+ expect(utilRegistry.setup.calls.argsFor(0)).toEqual([TEST_UTILS[0], undefined]);
+ expect(utilRegistry.setup.calls.argsFor(1)).toEqual([TEST_UTILS[1], undefined]);
+ });
+
+ it('should pass the given scope', () => {
+ spyOn(utilRegistry, 'setup');
+ utilRegistry.register(TEST_UTILS[0]);
+ const scope = document.createElement('div');
+ utilRegistry.setupAll(scope);
+
+ expect(utilRegistry.setup).toHaveBeenCalledWith(TEST_UTILS[0], scope);
+ });
+ });
+});
diff --git a/package.json b/package.json
index 9b4914798..494be6bb6 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"yesod:test": "./test.sh",
"frontend:lint": "eslint frontend/src",
"frontend:test": "karma start --conf karma.conf.js",
+ "frontend:test:watch": "karma start --conf karma.conf.js --single-run false",
"frontend:build": "webpack",
"frontend:build:watch": "webpack --watch"
},