From 559ea98840bbbee9f5fda3e478c1284e45d7f00a Mon Sep 17 00:00:00 2001 From: Harrison Healey Date: Tue, 11 Oct 2016 15:50:08 -0400 Subject: [PATCH] Added some more client APIs and refactored the client so it doesn't return a function --- package.json | 2 +- src/actions/helpers.js | 8 +-- src/client/client.js | 133 +++++++++++++++++++----------------- test/client.test.js | 30 ++++++++ test/client_general.test.js | 40 ++++++----- test/client_team.test.js | 27 ++++++++ test/client_user.test.js | 58 ++++++++++++++++ test/test_helper.js | 87 +++++++++++++++++------ 8 files changed, 276 insertions(+), 109 deletions(-) create mode 100644 test/client.test.js create mode 100644 test/client_team.test.js create mode 100644 test/client_user.test.js diff --git a/package.json b/package.json index 275697cf5..9fc4d9e9d 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "remote-redux-devtools-on-debugger": "0.6.2" }, "scripts": { - "check": "eslint --ext \".jsx\" --ignore-pattern node_modules --quiet .", + "check": "eslint --ext \".js\" --ignore-pattern node_modules --quiet .", "start": "node node_modules/react-native/local-cli/cli.js start", "test": "mocha --compilers js:babel-register **/*.test.js", "postinstall": "remotedev-debugger --hostname localhost --port 5678 --injectserver" diff --git a/src/actions/helpers.js b/src/actions/helpers.js index 4409b7779..422117fa5 100644 --- a/src/actions/helpers.js +++ b/src/actions/helpers.js @@ -33,17 +33,17 @@ export function emptyError() { export function bindClientFunc(clientFunc, request, success, failure, ...args) { return (dispatch, getState) => { function onRequest() { - dispatch(requestData(request), getState); + dispatch(() => requestData(request), getState); } function onSuccess(data) { - dispatch(requestSuccess(success, data), getState); + dispatch(() => requestSuccess(success, data), getState); } function onFailure(err) { - dispatch(requestFailure(failure, err), getState); + dispatch(() => requestFailure(failure, err), getState); } - return dispatch(clientFunc(onRequest, onSuccess, onFailure, ...args), getState); + return dispatch(() => clientFunc(onRequest, onSuccess, onFailure, ...args), getState); }; } \ No newline at end of file diff --git a/src/client/client.js b/src/client/client.js index bd0a9a3a3..0959cab09 100644 --- a/src/client/client.js +++ b/src/client/client.js @@ -6,11 +6,11 @@ import 'isomorphic-fetch'; const HEADER_AUTH = 'Authorization'; const HEADER_BEARER = 'BEARER'; const HEADER_REQUESTED_WITH = 'X-Requested-With'; +const HEADER_TOKEN = 'token'; export default class Client { constructor() { this.teamId = ''; - this.serverVersion = ''; // ?? this.logToConsole = false; this.token = ''; this.url = ''; @@ -26,10 +26,6 @@ export default class Client { this.url = url; } - setAcceptLanguage(locale) { - this.defaultHeaders['Accept-Language'] = locale; - } - setTeamId(id) { this.teamId = id; } @@ -42,10 +38,6 @@ export default class Client { return this.teamId; } - getServerVersion() { - return this.serverVersion; - } - getBaseRoute() { return `${this.url}${this.urlVersion}`; } @@ -114,27 +106,21 @@ export default class Client { return `${this.url}${this.urlVersion}/users/${userId}`; } - setTranslations(messages) { - this.translations = messages; - } - enableLogErrorsToConsole(enabled) { this.logToConsole = enabled; } - useHeaderToken() { - this.useToken = true; - if (this.token !== '') { - this.defaultHeaders[HEADER_AUTH] = `${HEADER_BEARER} ${this.token}`; - } - } - getOptions(options) { + const headers = { + [HEADER_REQUESTED_WITH]: 'XMLHttpRequest' + }; + + if (this.token) { + headers[HEADER_AUTH] = `${HEADER_BEARER} ${this.token}`; + } + return { - headers: { - [HEADER_AUTH]: this.token, - [HEADER_REQUESTED_WITH]: 'XMLHttpRequest' - }, + headers, ...options }; } @@ -178,26 +164,37 @@ export default class Client { // User routes - // login(onRequest, onSuccess, onFailure, loginId, password, token) { - // const body = { - // login_id: loginId, - // password, - // token - // }; + createUser = (onRequest, onSuccess, onFailure, user) => { + return this.doFetch( + `${this.getUsersRoute()}/create`, + {method: 'post', body: JSON.stringify(user)}, + onRequest, + onSuccess, + onFailure + ); + } - // return this.doFetch( - // `${this.getUsersRoute()}/login`, - // {method: 'post', body}, - // onRequest, - // (data, response) => { - // console.log(response.headers); - // // if (response.headers.) + login = (onRequest, onSuccess, onFailure, loginId, password, token = null) => { + const body = { + login_id: loginId, + password, + token + }; - // onSuccess(data, response); - // }, - // onFailure - // ); - // } + return this.doFetch( + `${this.getUsersRoute()}/login`, + {method: 'post', body: JSON.stringify(body)}, + onRequest, + (data, response) => { + if (response.headers.has(HEADER_TOKEN)) { + this.token = response.headers.get(HEADER_TOKEN); + } + + onSuccess(data, response); + }, + onFailure + ); + } // getInitialLoad(success, error) { // request. @@ -208,29 +205,41 @@ export default class Client { // end(this.handleResponse.bind(this, 'getInitialLoad', success, error)); // } + // Team routes + + createTeam = (onRequest, onSuccess, onFailure, team) => { + return this.doFetch( + `${this.getTeamsRoute()}/create`, + {method: 'post', body: JSON.stringify(team)}, + onRequest, + onSuccess, + onFailure + ); + } + doFetch = (url, options, onRequest, onSuccess, onFailure) => { - return () => { + if (onRequest) { onRequest(); + } - return fetch(url, this.getOptions(options)).then( - (response) => { - return response.json().then((data) => ({data, response})); - }).then(({data, response}) => { - if (!response.ok) { - return Promise.reject(data); - } - - return onSuccess(data, response); - }).catch((err) => { - // TODO errors that return non-json data get sent here - - if (this.logToConsole) { - console.log(err); // eslint-disable-line no-console - } - - onFailure(err); + return fetch(url, this.getOptions(options)).then( + (response) => { + return response.json().then((data) => ({data, response})); + }).then(({data, response}) => { + if (!response.ok) { + return Promise.reject(data); } - ); - }; + + return onSuccess(data, response); + }).catch((err) => { + // TODO errors that return non-json data get sent here + + if (this.logToConsole) { + console.log(err); // eslint-disable-line no-console + } + + onFailure(err); + } + ); } } \ No newline at end of file diff --git a/test/client.test.js b/test/client.test.js new file mode 100644 index 000000000..49226d524 --- /dev/null +++ b/test/client.test.js @@ -0,0 +1,30 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import assert from 'assert'; + +import TestHelper from './test_helper.js'; + +describe('Client', () => { + it('doFetch', (done) => { + const client = TestHelper.createClient(); + + let onRequestCalled = false; + + client.doFetch( + `${client.getGeneralRoute()}/ping`, + {}, + () => { + onRequestCalled = true; + }, + () => { + assert.ok(onRequestCalled, 'onSuccess called before onRequest'); + + done(); + }, + (err) => { + done(new Error(err)); + } + ); + }); +}); diff --git a/test/client_general.test.js b/test/client_general.test.js index c3b7e636c..016b31f40 100644 --- a/test/client_general.test.js +++ b/test/client_general.test.js @@ -8,7 +8,8 @@ import TestHelper from './test_helper.js'; describe('Client.General', () => { it('General.getClientConfig', (done) => { TestHelper.initBasic(({client}) => { - const {onRequest, onSuccess, onFailure} = TestHelper.assertOnRequestHappensFirst( + client.getClientConfig( + null, (data) => { assert.ok(data.Version); assert.ok(data.BuildNumber); @@ -21,14 +22,13 @@ describe('Client.General', () => { done(new Error(err)); } ); - - client.getClientConfig(onRequest, onSuccess, onFailure)(); }); }); it('General.getPing', (done) => { TestHelper.initBasic(({client}) => { - const {onRequest, onSuccess, onFailure} = TestHelper.assertOnRequestHappensFirst( + client.getPing( + null, () => { done(); }, @@ -36,14 +36,14 @@ describe('Client.General', () => { done(new Error(err)); } ); - - client.getPing(onRequest, onSuccess, onFailure)(); }); }); it('General.getPing - Invalid URL', (done) => { TestHelper.initBasic(({client}) => { - const {onRequest, onSuccess, onFailure} = TestHelper.assertOnRequestHappensFirst( + client.setUrl('https://example.com/fake/url'); + client.getPing( + null, () => { done(new Error('ping should\'ve failed')); }, @@ -51,24 +51,22 @@ describe('Client.General', () => { done(); } ); - - client.setUrl('https://example.com/fake/url'); - client.getPing(onRequest, onSuccess, onFailure)(); }); }); // it('General.logClientError', function(done) { - // const {onRequest, onSuccess, onFailure} = TestHelper.assertOnRequestHappensFirst( - // (data) => { - // TestHelper.assertStatusOkay(data); + // TestHelper.initBasic(({client}) => { + // client.logClientError( + // null, + // (data) => { + // TestHelper.assertStatusOkay(data); - // done(); - // }, - // (err) => { - // done(new Error(err)); - // } - // ); - - // Client.logClientError(onRequest, onSuccess, onFailure)(); + // done(); + // }, + // (err) => { + // done(new Error(err)); + // } + // )(); + // }); // }); }); diff --git a/test/client_team.test.js b/test/client_team.test.js new file mode 100644 index 000000000..76e149bb9 --- /dev/null +++ b/test/client_team.test.js @@ -0,0 +1,27 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import assert from 'assert'; + +import TestHelper from './test_helper.js'; + +describe('Client.Team', () => { + it('createTeam', (done) => { + const client = TestHelper.createClient(); + const team = TestHelper.fakeTeam(); + + client.createTeam( + null, + (data) => { + assert.equal(data.id.length > 0, true); + assert.equal(data.name, team.name); + + done(); + }, + (err) => { + done(new Error(err)); + }, + team + ); + }); +}); diff --git a/test/client_user.test.js b/test/client_user.test.js new file mode 100644 index 000000000..9c0e94f94 --- /dev/null +++ b/test/client_user.test.js @@ -0,0 +1,58 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import assert from 'assert'; + +import TestHelper from './test_helper.js'; + +describe('Client.User', () => { + it('createUser', (done) => { + const client = TestHelper.createClient(); + const user = TestHelper.fakeUser(); + + client.createUser( + null, + (data) => { + assert.ok(data.id, 'id is empty'); + assert.equal(data.email, user.email, 'email addresses aren\'t equal'); + + done(); + }, + (err) => { + done(new Error(err)); + }, + user + ); + }); + + it('login', (done) => { + const client = TestHelper.createClient(); + const user = TestHelper.fakeUser(); + + client.createUser( + null, + () => { + client.login( + null, + (data) => { + assert.ok(data.id, 'id is empty'); + assert.equal(data.email, user.email, 'email addresses aren\'t equal'); + assert.ok(client.token, 'token is empty'); + + done(); + }, + (err) => { + done(new Error(err)); + }, + user.email, + user.password + ); + }, + (err) => { + console.log(err); + done(new Error(err)); + }, + user + ); + }); +}); diff --git a/test/test_helper.js b/test/test_helper.js index a4c768cbe..33bb42d28 100644 --- a/test/test_helper.js +++ b/test/test_helper.js @@ -6,32 +6,36 @@ import assert from 'assert'; import Client from 'client/client.js'; class TestHelper { - assertOnRequestHappensFirst(onSuccess, onFailure) { - let hasReceivedOnRequest = false; - - return { - onRequest: () => { - hasReceivedOnRequest = true; - }, - onSuccess: (response, data) => { - assert(hasReceivedOnRequest); - - onSuccess(response, data); - }, - onFailure: (err) => { - assert(hasReceivedOnRequest); - - onFailure(err); - } - }; + constructor() { + this.basicClient = null; } - assertStatusOkay(data) { + assertStatusOkay = (data) => { assert(data); assert(data.status === 'OK'); } - createClient() { + generateId = () => { + // Implementation taken from http://stackoverflow.com/a/2117523 + let id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; + + id = id.replace(/[xy]/g, (c) => { + const r = Math.floor(Math.random() * 16); + + let v; + if (c === 'x') { + v = r; + } else { + v = (r & 0x3) | 0x8; + } + + return v.toString(16); + }); + + return 'uid' + id; + } + + createClient = () => { const client = new Client(); client.setUrl('http://localhost:8065'); @@ -39,7 +43,48 @@ class TestHelper { return client; } - initBasic(callback) { + fakeEmail = () => { + return 'success' + this.generateId() + '@simulator.amazonses.com'; + } + + fakeUser = () => { + return { + email: this.fakeEmail(), + allow_marketing: true, + password: 'password1', + username: this.generateId() + }; + } + + fakeTeam = () => { + const name = this.generateId(); + + return { + name, + display_name: `Unit Test ${name}`, + type: 'O', + email: this.fakeEmail(), + allowed_domains: '' + }; + } + + fakeChannel = () => { + const name = this.generateId(); + + return { + name, + display_name: `Unit Test ${name}`, + type: 'O' + }; + } + + fakePost = () => { + return { + message: `Unit Test ${this.generateId()}` + }; + } + + initBasic = (callback) => { const client = this.createClient(); callback({client});