Skip to content

Commit

Permalink
Merge pull request #217 from nextcloud/enhancement/noid/hash_service
Browse files Browse the repository at this point in the history
Enhancement/noid/hash service
  • Loading branch information
tcitworld authored Dec 10, 2016
2 parents 91c762d + 632c84d commit 4926429
Show file tree
Hide file tree
Showing 2 changed files with 220 additions and 0 deletions.
90 changes: 90 additions & 0 deletions js/app/service/hashService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* Calendar App
*
* @author Georg Ehrke
* @copyright 2016 Georg Ehrke <[email protected]>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/

app.service('HashService', function ($location) {
'use strict';

const context = {
callbacks: {}
};

/**
* register a handler for a certain hash id
* @param {string} id
* @param {function} callback
*/
this.register = (id, callback) => {
if (context.callbacks[id]) {
throw new Error('A callback for this id was already registered in the HashService');
}

context.callbacks[id] = callback;
};

/**
* unregister a handler for a certain hash
* @param {string} id
*/
this.unregister = (id) => {
delete context.callbacks[id];
};

/**
* calls a registered handler if necessary
*/
this.call = () => {
let hash = $location.hash();

if (!hash || hash === '') {
// nothing to do
return false;
}

if (hash.startsWith('#')) {
hash = hash.substr(1);
}

// the hashes must comply with the following convention
// #id?param1=value1&param2=value2& ... &paramN=valueN
// e.g.:
// #go_to_date?date=2016-12-31
// #go_to_event?calendar=work&uri=Nextcloud-23as123asf12.ics
// #search?term=foobar
// #subscribe_to_webcal?url=https%3A%2F%2Fwww.foo.bar%2F
//
// hashes without a question mark after the id will be ignored

if (!hash.includes('?')) {
return false;
}

const questionMarkPosition = hash.indexOf('?');
const identifier = hash.substr(0, questionMarkPosition);
const parameters = hash.substr(questionMarkPosition + 1);

if (context.callbacks[identifier]) {
context.callbacks[identifier](parameters);
return true;
} else {
return false;
}
};
});
130 changes: 130 additions & 0 deletions tests/js/unit/services/hashServiceSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* Calendar App
*
* @author Georg Ehrke
* @copyright 2016 Georg Ehrke <[email protected]>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/

describe('HashService', () => {
'use strict';

let HashService, $location;

beforeEach(module('Calendar', function ($provide) {
$location = {};
$location.hash = jasmine.createSpy();

$provide.value('$location', $location);
}));

beforeEach(inject(function (_HashService_) {
HashService = _HashService_;
}));

it ('should not allow an identifier to be registered twice', () => {
const callback1 = jasmine.createSpy();
const callback2 = jasmine.createSpy();
const callback3 = jasmine.createSpy();

HashService.register('fancy_id', callback1);
HashService.register('another_fancy_id', callback2);
expect(() => HashService.register('fancy_id', callback3)).toThrowError(Error, 'A callback for this id was already registered in the HashService');
});

it ('should allow identifiers to be unregistered', () => {
const callback1 = jasmine.createSpy();
const callback2 = jasmine.createSpy();
const callback3 = jasmine.createSpy();

HashService.register('fancy_id', callback1);
HashService.register('another_fancy_id', callback2);
HashService.unregister('fancy_id');
HashService.register('fancy_id', callback3);
});

it ('should return false when the hash is undefined or empty', () => {
const callback1 = jasmine.createSpy();
const callback2 = jasmine.createSpy();

HashService.register('fancy_id', callback1);
HashService.register('another_fancy_id', callback2);

$location.hash.and.returnValue(undefined);
expect(HashService.call()).toEqual(false);
expect($location.hash).toHaveBeenCalled();

$location.hash.and.returnValue(null);
expect(HashService.call()).toEqual(false);
expect($location.hash).toHaveBeenCalled();

$location.hash.and.returnValue('');
expect(HashService.call()).toEqual(false);
expect($location.hash).toHaveBeenCalled();
});

it ('should return call registered callbacks', () => {
const callback1 = jasmine.createSpy();
const callback2 = jasmine.createSpy();

HashService.register('fancy_id', callback1);
HashService.register('another_fancy_id', callback2);

$location.hash.and.returnValue('fancy_id?param1=value1&param2=value2');
expect(HashService.call()).toEqual(true);
expect(callback1).toHaveBeenCalledWith('param1=value1&param2=value2');
expect(callback2).not.toHaveBeenCalled();
});

it ('should handle hashes beginning with #', () => {
const callback1 = jasmine.createSpy();
const callback2 = jasmine.createSpy();

HashService.register('fancy_id', callback1);
HashService.register('another_fancy_id', callback2);

$location.hash.and.returnValue('#fancy_id?param1=value1&param2=value2');
expect(HashService.call()).toEqual(true);
expect(callback1).toHaveBeenCalledWith('param1=value1&param2=value2');
expect(callback2).not.toHaveBeenCalled();
});

it ('should return false when no registered callbacks are available', () => {
const callback1 = jasmine.createSpy();
const callback2 = jasmine.createSpy();

HashService.register('fancy_id', callback1);
HashService.register('another_fancy_id', callback2);

$location.hash.and.returnValue('super_fancy_id?param1=value1&param2=value2');
expect(HashService.call()).toEqual(false);
expect(callback1).not.toHaveBeenCalled();
expect(callback2).not.toHaveBeenCalled();
});

it ('should return false when hash contains no ?', () => {
const callback1 = jasmine.createSpy();
const callback2 = jasmine.createSpy();

HashService.register('fancy_id', callback1);
HashService.register('another_fancy_id', callback2);

$location.hash.and.returnValue('fancy_id');
expect(HashService.call()).toEqual(false);
expect(callback1).not.toHaveBeenCalled();
expect(callback2).not.toHaveBeenCalled();
});
});

0 comments on commit 4926429

Please sign in to comment.