Switched client.js to fully use Promises (#23)

* Made sanity tests run without an internet connection

* Removed onRequest parameter from Client methods

* Switched client.js to fully use Promises

* Fix mock response for login

* Fixed test_helper.js to actually use the configured url
This commit is contained in:
Harrison Healey 2016-10-18 10:46:30 -04:00 committed by GitHub
parent f262b2c90b
commit 543ccb8fe6
11 changed files with 168 additions and 354 deletions

View file

@ -31,19 +31,15 @@ export function emptyError() {
}
export function bindClientFunc(clientFunc, request, success, failure, ...args) {
return (dispatch, getState) => {
function onRequest() {
return dispatch(requestData(request), getState);
}
return async (dispatch, getState) => {
dispatch(requestData(request), getState);
function onSuccess(data) {
return dispatch(requestSuccess(success, data), getState);
}
try {
const data = await clientFunc(...args);
function onFailure(err) {
return dispatch(requestFailure(failure, err), getState);
dispatch(requestSuccess(success, data), getState);
} catch (err) {
dispatch(requestFailure(failure, err));
}
return dispatch(() => clientFunc(...args, onRequest, onSuccess, onFailure), getState);
};
}

View file

@ -3,9 +3,12 @@
const HEADER_AUTH = 'Authorization';
const HEADER_BEARER = 'BEARER';
const HEADER_CONTENT_TYPE = 'Content-Type';
const HEADER_REQUESTED_WITH = 'X-Requested-With';
const HEADER_TOKEN = 'token';
const CONTENT_TYPE_JSON = 'application/json';
export default class Client {
constructor() {
this.teamId = '';
@ -125,126 +128,96 @@ export default class Client {
// General routes
getClientConfig = (onRequest, onSuccess, onFailure) => {
getClientConfig = async () => {
return this.doFetch(
`${this.getGeneralRoute()}/client_props`,
{method: 'get'},
onRequest,
onSuccess,
onFailure
{method: 'get'}
);
}
getPing = (onRequest, onSuccess, onFailure) => {
getPing = async () => {
return this.doFetch(
`${this.getGeneralRoute()}/ping`,
{method: 'get'},
onRequest,
onSuccess,
onFailure
{method: 'get'}
);
}
logClientError = (message, level, onRequest, onSuccess, onFailure) => {
logClientError = async (message, level = 'ERROR') => {
const body = {
message,
level: level || 'ERROR'
level
};
return this.doFetch(
`${this.getGeneralRoute()}/log_client`,
{method: 'post', body},
onRequest,
onSuccess,
onFailure
{method: 'post', body}
);
}
// User routes
createUser = (user, onRequest, onSuccess, onFailure) => {
createUser = async (user) => {
return this.doFetch(
`${this.getUsersRoute()}/create`,
{method: 'post', body: JSON.stringify(user)},
onRequest,
onSuccess,
onFailure
{method: 'post', body: JSON.stringify(user)}
);
}
login = (loginId, password, token, onRequest, onSuccess, onFailure) => {
login = async (loginId, password, token = '') => {
const body = {
login_id: loginId,
password,
token
};
return this.doFetch(
const {response, data} = await this.doFetchWithResponse(
`${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
{method: 'post', body: JSON.stringify(body)}
);
if (response.headers.has(HEADER_TOKEN)) {
this.token = response.headers.get(HEADER_TOKEN);
}
return data;
}
getInitialLoad = (onRequest, onSuccess, onFailure) => {
getInitialLoad = async () => {
return this.doFetch(
`${this.getUsersRoute()}/initial_load`,
{method: 'get'},
onRequest,
onSuccess,
onFailure
{method: 'get'}
);
}
// Team routes
createTeam = (team, onRequest, onSuccess, onFailure) => {
createTeam = async (team) => {
return this.doFetch(
`${this.getTeamsRoute()}/create`,
{method: 'post', body: JSON.stringify(team)},
onRequest,
onSuccess,
onFailure
{method: 'post', body: JSON.stringify(team)}
);
}
fetchTeams = (onRequest, onSuccess, onFailure) => {
fetchTeams = async () => {
return this.doFetch(
`${this.getTeamsRoute()}/all_team_listings`,
{method: 'get'},
onRequest,
onSuccess,
onFailure
{method: 'get'}
);
}
// Channel routes
createChannel = (channel, onRequest, onSuccess, onFailure) => {
createChannel = async (channel) => {
return this.doFetch(
`${this.getChannelsRoute()}/create`,
{method: 'post', body: JSON.stringify(channel)},
onRequest,
onSuccess,
onFailure
{method: 'post', body: JSON.stringify(channel)}
);
}
fetchChannels = (onRequest, onSuccess, onFailure) => {
fetchChannels = async () => {
return this.doFetch(
`${this.getChannelsRoute()}/`,
{method: 'get'},
onRequest,
onSuccess,
onFailure
{method: 'get'}
);
}
@ -259,45 +232,50 @@ export default class Client {
);
}
createPost = (post, onRequest, onSuccess, onFailure) => {
createPost = async (post) => {
return this.doFetch(
`${this.getPostsRoute(post.channel_id)}/create`,
{method: 'post', body: JSON.stringify(post)},
onRequest,
onSuccess,
onFailure
{method: 'post', body: JSON.stringify(post)}
);
}
doFetch = async (url, options, onRequest, onSuccess, onFailure) => {
if (onRequest) {
onRequest();
}
try {
const resp = await fetch(url, this.getOptions(options));
let data;
doFetch = async (url, options) => {
const {data} = await this.doFetchWithResponse(url, options);
const contentType = resp.headers.get('Content-Type') || 'unknown';
if (contentType.match(/application\/json/)) {
data = await resp.json();
} else {
data = await resp.text();
}
if (resp.ok) {
return onSuccess(data, resp);
}
let msg;
if (contentType === 'application/json') {
msg = data.message;
} else {
msg = data;
}
throw new Error(msg);
} catch (err) {
if (this.logToConsole) {
console.log(err); // eslint-disable-line no-console
}
return onFailure(err);
return data;
}
doFetchWithResponse = async (url, options) => {
const response = await fetch(url, this.getOptions(options));
const contentType = response.headers.get(HEADER_CONTENT_TYPE);
const isJson = contentType && contentType.indexOf(CONTENT_TYPE_JSON) !== -1;
let data;
if (isJson) {
data = await response.json();
} else {
data = await response.text();
}
if (response.ok) {
return {
response,
data
};
}
let msg;
if (isJson) {
msg = data.message || '';
} else {
msg = data;
}
if (this.logToConsole) {
console.error(msg); // eslint-disable-line no-console
}
throw new Error(msg);
}
}

View file

@ -10,7 +10,7 @@ import TestHelper from 'test_helper.js';
describe('Actions.General', () => {
it('getClientConfig', (done) => {
TestHelper.initClient(Client, () => {
TestHelper.initBasic(Client).then(() => {
const store = configureStore();
store.subscribe(() => {
@ -36,7 +36,7 @@ describe('Actions.General', () => {
});
it('getPing', (done) => {
TestHelper.initClient(Client, () => {
TestHelper.initBasic(Client).then(() => {
const store = configureStore();
store.subscribe(() => {
@ -54,7 +54,7 @@ describe('Actions.General', () => {
});
it('getPing - Invalid URL', (done) => {
TestHelper.initClient(Client, () => {
TestHelper.initBasic(Client).then(() => {
const store = configureStore();
store.subscribe(() => {

View file

@ -1,7 +1,6 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import assert from 'assert';
import fetchMock from 'fetch_mock';
import TestHelper from 'test_helper.js';
@ -35,25 +34,9 @@ fetchMock.post(/\/log_client/, {
});
describe('Client', () => {
it('doFetch', (done) => {
it('doFetch', async () => {
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));
}
);
return client.doFetch(`${client.getGeneralRoute()}/ping`, {});
});
});

View file

@ -6,24 +6,14 @@ import assert from 'assert';
import TestHelper from 'test_helper.js';
describe('Client.Channel', () => {
it('createChannel', (done) => {
TestHelper.initBasic(({client, team}) => {
const channel = TestHelper.fakeChannel(team.id);
it('createChannel', async () => {
const {client, team} = await TestHelper.initBasic();
const channel = TestHelper.fakeChannel(team.id);
client.createChannel(
channel,
null,
(data) => {
assert.ok(data.id, 'id is empty');
assert.equal(data.name, channel.name, 'name doesn\'t match');
assert.equal(data.team_id, channel.team_id, 'team id doesn\'t match');
const rchannel = await client.createChannel(channel);
done();
},
(err) => {
done(new Error(err));
}
);
});
assert.ok(rchannel.id, 'id is empty');
assert.equal(rchannel.name, channel.name, 'name doesn\'t match');
assert.equal(rchannel.team_id, channel.team_id, 'team id doesn\'t match');
});
});

View file

@ -5,71 +5,43 @@ import assert from 'assert';
import TestHelper from 'test_helper.js';
describe('Client.General', () => {
it('getClientConfig', (done) => {
TestHelper.initBasic(({client}) => {
client.getClientConfig(
null,
(data) => {
assert.ok(data.Version);
assert.ok(data.BuildNumber);
assert.ok(data.BuildDate);
assert.ok(data.BuildHash);
describe('Client.General', function() {
it('getClientConfig', async () => {
const {client} = await TestHelper.initBasic();
done();
},
(err) => {
done(new Error(err));
}
);
});
const clientConfig = await client.getClientConfig('foo');
assert.ok(clientConfig.Version);
assert.ok(clientConfig.BuildNumber);
assert.ok(clientConfig.BuildDate);
assert.ok(clientConfig.BuildHash);
});
it('getPing', (done) => {
TestHelper.initBasic(({client}) => {
client.getPing(
null,
() => {
done();
},
(err) => {
done(new Error(err));
}
);
});
it('getPing', async () => {
const {client} = await TestHelper.initBasic();
await client.getPing();
});
it('getPing - Invalid URL', (done) => {
TestHelper.initBasic(({client}) => {
client.setUrl('https://example.com/fake/url');
it('getPing - Invalid URL', async () => {
const {client} = await TestHelper.initBasic();
client.setUrl('https://example.com/fake/url');
client.getPing(
null,
() => {
done(new Error('ping should\'ve failed'));
},
() => {
done();
}
);
});
let errored;
try {
await client.getPing();
errored = false;
} catch (err) {
errored = true;
}
assert.ok(errored, 'should have errored');
});
it('logClientError', function(done) {
TestHelper.initBasic(({client}) => {
client.logClientError(
'this is a test',
'ERROR',
null,
(data) => {
TestHelper.assertStatusOkay(data);
it('logClientError', async () => {
const {client} = await TestHelper.initBasic();
done();
},
(err) => {
done(new Error(err));
}
);
});
await client.logClientError('this is a test');
});
});

View file

@ -6,22 +6,12 @@ import assert from 'assert';
import TestHelper from 'test_helper.js';
describe('Client.Post', () => {
it('createPost', (done) => {
TestHelper.initBasic(({client, channel}) => {
const post = TestHelper.fakePost(channel.id);
it('createPost', async () => {
const {client, channel} = await TestHelper.initBasic();
const post = TestHelper.fakePost(channel.id);
client.createPost(
post,
null,
(data) => {
assert.ok(data.id, 'id is empty');
const rpost = await client.createPost(post);
done();
},
(err) => {
done(new Error(err));
}
);
});
assert.ok(rpost.id, 'id is empty');
});
});

View file

@ -6,22 +6,13 @@ import assert from 'assert';
import TestHelper from 'test_helper.js';
describe('Client.Team', () => {
it('createTeam', (done) => {
it('createTeam', async () => {
const client = TestHelper.createClient();
const team = TestHelper.fakeTeam();
client.createTeam(
team,
null,
(data) => {
assert.equal(data.id.length > 0, true);
assert.equal(data.name, team.name);
const rteam = await client.createTeam(team);
done();
},
(err) => {
done(new Error(err));
}
);
assert.equal(rteam.id.length > 0, true);
assert.equal(rteam.name, team.name);
});
});

View file

@ -6,70 +6,37 @@ import assert from 'assert';
import TestHelper from 'test_helper.js';
describe('Client.User', () => {
it('createUser', (done) => {
it('createUser', async () => {
const client = TestHelper.createClient();
const user = TestHelper.fakeUser();
client.createUser(
user,
null,
(data) => {
assert.ok(data.id, 'id is empty');
assert.equal(data.email, user.email, 'email addresses aren\'t equal');
const ruser = await client.createUser(user);
done();
},
(err) => {
done(new Error(err));
}
);
assert.ok(ruser.id, 'id is empty');
assert.equal(ruser.email, user.email, 'email addresses aren\'t equal');
});
it('login', (done) => {
it('login', async () => {
const client = TestHelper.createClient();
const user = TestHelper.fakeUser();
client.createUser(
user,
null,
() => {
client.login(
user.email,
user.password,
'',
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');
user.password = 'password';
await client.createUser(user);
done();
},
(err) => {
done(new Error(err));
}
);
},
(err) => {
done(new Error(err));
}
);
const ruser = await client.login(user.email, user.password);
assert.ok(ruser.id, 'id is empty');
assert.equal(ruser.email, user.email, 'email addresses aren\'t equal');
assert.ok(!ruser.password, 'returned password should be empty');
assert.ok(client.token, 'token is empty');
});
it('getInitialLoad', (done) => {
TestHelper.initBasic(({client, user}) => {
client.getInitialLoad(
null,
(data) => {
assert.ok(data.user.id.length, 'id is empty');
assert.equal(data.user.id, user.id, 'user ids don\'t match');
it('getInitialLoad', async () => {
const {client, user} = await TestHelper.initBasic();
done();
},
(err) => {
done(new Error(err));
}
);
});
const data = await client.getInitialLoad();
assert.ok(data.user.id.length, 'id is empty');
assert.equal(data.user.id, user.id, 'user ids don\'t match');
});
});

View file

@ -14,15 +14,20 @@ describe('Sanity test', () => {
Promise.resolve(true).then(() => {
done();
}).catch((err) => {
done.fail(err);
done(err);
});
});
it('async/await', async () => {
await Promise.resolve(true);
});
it('fetch', (done) => {
fetch('http://example.com').then(() => {
done();
}).catch((err) => {
done.fail(err);
}).catch(() => {
// No internet connection, but fetch still returned at least
done();
});
});
});

View file

@ -130,7 +130,6 @@ fetchMock.post(/\/users\/login$/, (url, opts) => {
token: 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
},
body: {...fakeUserRespBody,
...reqBody,
email: reqBody.login_id
}
};
@ -244,83 +243,26 @@ class TestHelper {
};
}
initClient = (client, callback) => {
initBasic = async (client = this.createClient()) => {
client.setUrl(Config.DefaultServerUrl);
this.basicClient = client;
client.createUser(
this.fakeUser(),
null,
(user) => {
this.basicUser = user;
this.basicUser = await client.createUser(this.fakeUser());
await client.login(this.basicUser.email, PASSWORD);
client.login(
user.email,
PASSWORD,
'',
null,
() => {
client.createTeam(
this.fakeTeam(),
null,
(team) => {
this.basicTeam = team;
this.basicTeam = await client.createTeam(this.fakeTeam());
client.setTeamId(this.basicTeam.id);
client.setTeamId(team.id);
this.basicChannel = await client.createChannel(this.fakeChannel(this.basicTeam.id));
this.basicPost = await client.createPost(this.fakePost(this.basicChannel.id));
client.createChannel(
this.fakeChannel(this.basicTeam.id),
null,
(channel) => {
this.basicChannel = channel;
client.createPost(
this.fakePost(this.basicChannel.id),
null,
(post) => {
this.basicPost = post;
callback({
client,
user: this.basicUser,
team: this.basicTeam,
channel: this.basicChannel,
post: this.basicPost
});
},
(err) => {
console.error(err);
throw err;
}
);
},
(err) => {
console.error(err);
throw err;
}
);
},
(err) => {
console.error(err);
throw err;
}
);
},
(err) => {
console.error(err);
throw err;
}
);
},
(err) => {
throw err;
}
);
}
initBasic = (callback) => {
this.basicClient = this.createClient();
this.initClient(this.basicClient, callback);
return {
client: this.basicClient,
user: this.basicUser,
team: this.basicTeam,
channel: this.basicChannel,
post: this.basicPost
};
}
}