Unverified Commit 70749111 authored by Kyle Keating's avatar Kyle Keating Committed by GitHub

Merge pull request #1493 from hypothesis/rpc-server-actions

Add changeFocusModeUser action
parents e9400300 6551b4b5
'use strict';
/**
* Return the mapped methods that can be called remotely via this server.
*
* @param {Object} store - The global store
* @return {Object}
*/
const registeredMethods = store => {
return {
changeFocusModeUser: store.changeFocusModeUser,
};
};
/**
* Begin responding to JSON-RPC requests from frames on other origins.
*
......@@ -15,13 +28,14 @@
* http://www.jsonrpc.org/specification
*
* The only part that we support so far is receiving JSON-RPC 2.0 requests (not
* notifications) without any parameters and sending back a successful
* response. Notifications (JSON-RPC calls that don't require a response),
* method parameters, and error responses are not yet supported.
* notifications) and sending back a successful "ok" response.
*
* All methods called upon must be mapped in the `registeredMethods` function.
*/
// @ngInject
function start(store, settings, $window) {
const methods = registeredMethods(store);
$window.addEventListener('message', function receiveMessage(event) {
let allowedOrigins = settings.rpcAllowedOrigins || [];
......@@ -38,28 +52,25 @@ function start(store, settings, $window) {
/** Return a JSON-RPC response to the given JSON-RPC request object. */
function jsonRpcResponse(request) {
// The set of methods that clients can call.
let methods = {
searchUris: store.searchUris,
};
let method = methods[request.method];
const method = methods[request.method];
let response = {
// Return an error response if the method name is not registered with
// registeredMethods.
if (method === undefined) {
return {
jsonrpc: '2.0',
id: request.id,
error: { code: -32601, message: 'Method not found' },
};
}
if (method) {
response.result = method();
// Call the method and return the result response.
if (request.params) {
method(...request.params);
} else {
response.error = {
code: -32601,
message: 'Method not found',
};
method();
}
return response;
return { jsonrpc: '2.0', result: 'ok', id: request.id };
}
}
......
......@@ -8,6 +8,12 @@
* - The current filter query
*/
/**
* @typedef User
* @property {string} username - Unique user's username
* @property {string} displayName - User's display name
*/
'use strict';
const { createSelector } = require('reselect');
......@@ -114,7 +120,7 @@ function init(settings) {
selectedTab: TAB_DEFAULT,
focusMode: {
enabled: settings.hasOwnProperty('focus'), // readonly
enabled: settings.hasOwnProperty('focus'),
focused: true,
// Copy over the focus confg from settings object
config: { ...(settings.focus ? settings.focus : {}) },
......@@ -162,6 +168,19 @@ const update = {
};
},
CHANGE_FOCUS_MODE_USER: function(state, action) {
return {
focusMode: {
...state.focusMode,
enabled: true,
focused: true,
config: {
user: { ...action.user },
},
},
};
},
SET_FORCE_VISIBLE: function(state, action) {
return { forceVisible: action.forceVisible };
},
......@@ -344,6 +363,18 @@ function setFocusModeFocused(focused) {
};
}
/**
* Changes the focused user and sets focused enabled to `true`.
*
* @param {User} user - The user to focus on
*/
function changeFocusModeUser(user) {
return {
type: actions.CHANGE_FOCUS_MODE_USER,
user,
};
}
/** Sets the sort key for the annotation list. */
function setSortKey(key) {
return {
......@@ -469,6 +500,7 @@ module.exports = {
setCollapsed: setCollapsed,
setFilterQuery: setFilterQuery,
setFocusModeFocused: setFocusModeFocused,
changeFocusModeUser: changeFocusModeUser,
setForceVisible: setForceVisible,
setSortKey: setSortKey,
toggleSelectedAnnotations: toggleSelectedAnnotations,
......
......@@ -201,6 +201,20 @@ describe('sidebar/store/modules/selection', () => {
});
});
describe('changeFocusModeUser()', function() {
it('sets the focused user and enables focus mode', function() {
store.setFocusModeFocused(false);
store.changeFocusModeUser({
username: 'testuser',
displayName: 'Test User',
});
assert.equal(store.focusModeUsername(), 'testuser');
assert.equal(store.focusModeUserPrettyName(), 'Test User');
assert.equal(store.focusModeFocused(), true);
assert.equal(store.focusModeEnabled(), true);
});
});
describe('setFocusModeFocused()', function() {
it('sets the focus mode to enabled', function() {
store.setFocusModeFocused(true);
......
......@@ -12,7 +12,7 @@ describe('crossOriginRPC', function() {
beforeEach(function() {
fakeStore = {
searchUris: sinon.stub().returns('THE_SEARCH_URIS'),
changeFocusModeUser: sinon.stub(),
};
fakeWindow = {
......@@ -46,28 +46,74 @@ describe('crossOriginRPC', function() {
assert.isTrue(fakeWindow.addEventListener.calledWith('message'));
});
it('sends a response with the result from the called method', function() {
it('sends a response with the "ok" result', function() {
crossOriginRPC.server.start(fakeStore, settings, fakeWindow);
postMessage({
data: { method: 'searchUris', id: 42 },
data: { method: 'changeFocusModeUser', id: 42 },
origin: 'https://allowed1.com',
source: source,
});
assert.isTrue(source.postMessage.calledOnce);
assert.isTrue(
source.postMessage.calledWithExactly(
{
jsonrpc: '2.0',
id: 42,
result: 'THE_SEARCH_URIS',
result: 'ok',
},
'https://allowed1.com'
)
);
});
it('calls the registered method with the provided params', function() {
crossOriginRPC.server.start(fakeStore, settings, fakeWindow);
postMessage({
data: {
method: 'changeFocusModeUser',
id: 42,
params: ['one', 'two'],
},
origin: 'https://allowed1.com',
source: source,
});
assert.isTrue(
fakeStore.changeFocusModeUser.calledWithExactly('one', 'two')
);
});
it('calls the registered method with no params', function() {
crossOriginRPC.server.start(fakeStore, settings, fakeWindow);
postMessage({
data: {
method: 'changeFocusModeUser',
id: 42,
},
origin: 'https://allowed1.com',
source: source,
});
assert.isTrue(fakeStore.changeFocusModeUser.calledWithExactly());
});
it('does not call the unregistered method', function() {
crossOriginRPC.server.start(fakeStore, settings, fakeWindow);
postMessage({
data: {
method: 'unregisteredMethod',
id: 42,
},
origin: 'https://allowed1.com',
source: source,
});
assert.isTrue(fakeStore.changeFocusModeUser.notCalled);
});
[
{},
{ rpcAllowedOrigins: [] },
......@@ -78,7 +124,7 @@ describe('crossOriginRPC', function() {
postMessage({
origin: 'https://notallowed.com',
data: { method: 'searchUris', id: 42 },
data: { method: 'changeFocusModeUser', id: 42 },
source: source,
});
......
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