Skip to content

Commit 232630c

Browse files
authored
Merge pull request topcoder-platform#1 from liuliquan/settings-profile
Settings profile
2 parents a0133c9 + 2802d4e commit 232630c

31 files changed

+3080
-16
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ __coverage__
44
dist
55
node_modules
66
_auto_doc_
7+
.vscode

__tests__/__snapshots__/index.js.snap

+54
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ Object {
4848
"getGroupsDone": [Function],
4949
"getGroupsInit": [Function],
5050
},
51+
"lookup": Object {
52+
"getApprovedSkills": [Function],
53+
},
5154
"memberTasks": Object {
5255
"dropAll": [Function],
5356
"getDone": [Function],
@@ -64,19 +67,49 @@ Object {
6467
"getStatsInit": [Function],
6568
},
6669
"profile": Object {
70+
"addSkillDone": [Function],
71+
"addSkillInit": [Function],
72+
"addWebLinkDone": [Function],
73+
"addWebLinkInit": [Function],
74+
"deletePhotoDone": [Function],
75+
"deletePhotoInit": [Function],
76+
"deleteWebLinkDone": [Function],
77+
"deleteWebLinkInit": [Function],
6778
"getAchievementsDone": [Function],
6879
"getAchievementsInit": [Function],
80+
"getActiveChallengesCountDone": [Function],
81+
"getActiveChallengesCountInit": [Function],
82+
"getCredentialDone": [Function],
83+
"getCredentialInit": [Function],
84+
"getEmailPreferencesDone": [Function],
85+
"getEmailPreferencesInit": [Function],
6986
"getExternalAccountsDone": [Function],
7087
"getExternalAccountsInit": [Function],
7188
"getExternalLinksDone": [Function],
7289
"getExternalLinksInit": [Function],
7390
"getInfoDone": [Function],
7491
"getInfoInit": [Function],
92+
"getLinkedAccountsDone": [Function],
93+
"getLinkedAccountsInit": [Function],
7594
"getSkillsDone": [Function],
7695
"getSkillsInit": [Function],
7796
"getStatsDone": [Function],
7897
"getStatsInit": [Function],
98+
"hideSkillDone": [Function],
99+
"hideSkillInit": [Function],
100+
"linkExternalAccountDone": [Function],
101+
"linkExternalAccountInit": [Function],
79102
"loadProfile": [Function],
103+
"saveEmailPreferencesDone": [Function],
104+
"saveEmailPreferencesInit": [Function],
105+
"unlinkExternalAccountDone": [Function],
106+
"unlinkExternalAccountInit": [Function],
107+
"updatePasswordDone": [Function],
108+
"updatePasswordInit": [Function],
109+
"updateProfileDone": [Function],
110+
"updateProfileInit": [Function],
111+
"uploadPhotoDone": [Function],
112+
"uploadPhotoInit": [Function],
80113
},
81114
"reviewOpportunity": Object {
82115
"cancelApplicationsDone": [Function],
@@ -159,13 +192,29 @@ Object {
159192
"default": undefined,
160193
"mockAction": [Function],
161194
},
195+
"reducerFactories": Object {
196+
"authFactory": [Function],
197+
"challengeFactory": [Function],
198+
"directFactory": [Function],
199+
"errorsFactory": [Function],
200+
"groupsFactory": [Function],
201+
"lookupFactory": [Function],
202+
"memberTasksFactory": [Function],
203+
"membersFactory": [Function],
204+
"mySubmissionsManagementFactory": [Function],
205+
"profileFactory": [Function],
206+
"reviewOpportunityFactory": [Function],
207+
"statsFactory": [Function],
208+
"termsFactory": [Function],
209+
},
162210
"reducerFactory": [Function],
163211
"reducers": Object {
164212
"auth": [Function],
165213
"challenge": [Function],
166214
"direct": [Function],
167215
"errors": [Function],
168216
"groups": [Function],
217+
"lookup": [Function],
169218
"memberTasks": [Function],
170219
"members": [Function],
171220
"mySubmissionsManagement": [Function],
@@ -209,6 +258,10 @@ Object {
209258
"default": undefined,
210259
"getService": [Function],
211260
},
261+
"lookup": Object {
262+
"default": undefined,
263+
"getService": [Function],
264+
},
212265
"members": Object {
213266
"default": undefined,
214267
"getService": [Function],
@@ -242,6 +295,7 @@ Object {
242295
"Spec Review": "Specification Review",
243296
},
244297
"getApiResponsePayloadV3": [Function],
298+
"looseEqual": [Function],
245299
},
246300
"time": Object {
247301
"default": undefined,

__tests__/actions/lookup.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import * as LookupService from 'services/lookup';
2+
import actions from 'actions/lookup';
3+
4+
const tag = {
5+
domain: 'SKILLS',
6+
id: 251,
7+
name: 'Jekyll',
8+
status: 'APPROVED',
9+
};
10+
11+
// Mock services
12+
const mockLookupService = {
13+
getTags: jest.fn().mockReturnValue(Promise.resolve([tag])),
14+
};
15+
LookupService.getService = jest.fn().mockReturnValue(mockLookupService);
16+
17+
18+
describe('lookup.getApprovedSkills', () => {
19+
const a = actions.lookup.getApprovedSkills();
20+
21+
test('has expected type', () => {
22+
expect(a.type).toEqual('LOOKUP/GET_APPROVED_SKILLS');
23+
});
24+
25+
test('Approved skills should be returned', () =>
26+
a.payload.then((res) => {
27+
expect(res).toEqual([tag]);
28+
expect(mockLookupService.getTags).toBeCalled();
29+
}));
30+
});

__tests__/actions/profile.js

+252
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
import * as ChallengesService from 'services/challenges';
2+
import * as MembersService from 'services/members';
3+
import * as UserService from 'services/user';
4+
5+
import actions from 'actions/profile';
6+
7+
const handle = 'tcscoder';
8+
const tokenV3 = 'tokenV3';
9+
const profile = { userId: 12345, handle };
10+
const skill = { tagId: 123, tagName: 'Node.js' };
11+
const weblink = 'https://www.google.com';
12+
const linkedAccounts = [{
13+
providerType: 'github',
14+
social: true,
15+
userId: '623633',
16+
}];
17+
18+
// Mock services
19+
const mockChanllengesService = {
20+
getUserChallenges: jest.fn().mockReturnValue(Promise.resolve({ totalCount: 3 })),
21+
getUserMarathonMatches: jest.fn().mockReturnValue(Promise.resolve({ totalCount: 5 })),
22+
};
23+
ChallengesService.getService = jest.fn().mockReturnValue(mockChanllengesService);
24+
25+
const mockMembersService = {
26+
getPresignedUrl: jest.fn().mockReturnValue(Promise.resolve()),
27+
uploadFileToS3: jest.fn().mockReturnValue(Promise.resolve()),
28+
updateMemberPhoto: jest.fn().mockReturnValue(Promise.resolve('url-of-photo')),
29+
updateMemberProfile: jest.fn().mockReturnValue(Promise.resolve(profile)),
30+
addSkill: jest.fn().mockReturnValue(Promise.resolve({ skills: [skill] })),
31+
hideSkill: jest.fn().mockReturnValue(Promise.resolve({ skills: [] })),
32+
addWebLink: jest.fn().mockReturnValue(Promise.resolve(weblink)),
33+
deleteWebLink: jest.fn().mockReturnValue(Promise.resolve(weblink)),
34+
};
35+
MembersService.getService = jest.fn().mockReturnValue(mockMembersService);
36+
37+
const mockUserService = {
38+
linkExternalAccount: jest.fn().mockReturnValue(Promise.resolve(linkedAccounts[0])),
39+
unlinkExternalAccount: jest.fn().mockReturnValue(Promise.resolve('unlinked')),
40+
getLinkedAccounts: jest.fn().mockReturnValue(Promise.resolve({ profiles: linkedAccounts })),
41+
getCredential: jest.fn().mockReturnValue(Promise.resolve({ credential: { hasPassword: true } })),
42+
getEmailPreferences:
43+
jest.fn().mockReturnValue(Promise.resolve({ subscriptions: { TOPCODER_NL_DATA: true } })),
44+
saveEmailPreferences:
45+
jest.fn().mockReturnValue(Promise.resolve({ subscriptions: { TOPCODER_NL_DATA: true } })),
46+
updatePassword: jest.fn().mockReturnValue(Promise.resolve({ update: true })),
47+
};
48+
UserService.getService = jest.fn().mockReturnValue(mockUserService);
49+
50+
51+
describe('profile.getActiveChallengesCountDone', () => {
52+
const a = actions.profile.getActiveChallengesCountDone(handle, tokenV3);
53+
54+
test('has expected type', () => {
55+
expect(a.type).toBe('PROFILE/GET_ACTIVE_CHALLENGES_COUNT_DONE');
56+
});
57+
58+
test('Sum of challenges and marathon matches should be returned', () =>
59+
a.payload.then((res) => {
60+
expect(res).toBe(8);
61+
expect(mockChanllengesService.getUserChallenges).toBeCalled();
62+
expect(mockChanllengesService.getUserMarathonMatches).toBeCalled();
63+
}));
64+
});
65+
66+
describe('profile.uploadPhotoDone', () => {
67+
const a = actions.profile.uploadPhotoDone(handle, tokenV3);
68+
69+
test('has expected type', () => {
70+
expect(a.type).toBe('PROFILE/UPLOAD_PHOTO_DONE');
71+
});
72+
73+
test('Photo URL should be returned', () =>
74+
a.payload.then((res) => {
75+
expect(res).toEqual({
76+
handle,
77+
photoURL: 'url-of-photo',
78+
});
79+
expect(mockMembersService.getPresignedUrl).toBeCalled();
80+
expect(mockMembersService.uploadFileToS3).toBeCalled();
81+
expect(mockMembersService.updateMemberPhoto).toBeCalled();
82+
}));
83+
});
84+
85+
describe('profile.updateProfileDone', () => {
86+
const a = actions.profile.updateProfileDone(profile, tokenV3);
87+
88+
test('has expected type', () => {
89+
expect(a.type).toBe('PROFILE/UPDATE_PROFILE_DONE');
90+
});
91+
92+
test('Profile should be updated', () =>
93+
a.payload.then((res) => {
94+
expect(res).toEqual(profile);
95+
expect(mockMembersService.updateMemberProfile).toBeCalled();
96+
}));
97+
});
98+
99+
describe('profile.addSkillDone', () => {
100+
const a = actions.profile.addSkillDone(handle, tokenV3, skill);
101+
102+
test('has expected type', () => {
103+
expect(a.type).toBe('PROFILE/ADD_SKILL_DONE');
104+
});
105+
106+
test('Skill should be added', () =>
107+
a.payload.then((res) => {
108+
expect(res).toEqual({ skills: [skill], handle, skill });
109+
expect(mockMembersService.addSkill).toBeCalled();
110+
}));
111+
});
112+
113+
describe('profile.hideSkillDone', () => {
114+
const a = actions.profile.hideSkillDone(handle, tokenV3, skill);
115+
116+
test('has expected type', () => {
117+
expect(a.type).toBe('PROFILE/HIDE_SKILL_DONE');
118+
});
119+
120+
test('Skill should be removed', () =>
121+
a.payload.then((res) => {
122+
expect(res).toEqual({ skills: [], handle, skill });
123+
expect(mockMembersService.hideSkill).toBeCalled();
124+
}));
125+
});
126+
127+
describe('profile.addWebLinkDone', () => {
128+
const a = actions.profile.addWebLinkDone(handle, tokenV3, weblink);
129+
130+
test('has expected type', () => {
131+
expect(a.type).toBe('PROFILE/ADD_WEB_LINK_DONE');
132+
});
133+
134+
test('Web link should be added', () =>
135+
a.payload.then((res) => {
136+
expect(res).toEqual({ data: weblink, handle });
137+
expect(mockMembersService.addWebLink).toBeCalled();
138+
}));
139+
});
140+
141+
describe('profile.deleteWebLinkDone', () => {
142+
const a = actions.profile.deleteWebLinkDone(handle, tokenV3, weblink);
143+
144+
test('has expected type', () => {
145+
expect(a.type).toBe('PROFILE/DELETE_WEB_LINK_DONE');
146+
});
147+
148+
test('Web link should be deleted', () =>
149+
a.payload.then((res) => {
150+
expect(res).toEqual({ data: weblink, handle });
151+
expect(mockMembersService.deleteWebLink).toBeCalled();
152+
}));
153+
});
154+
155+
describe('profile.linkExternalAccountDone', () => {
156+
const a = actions.profile.linkExternalAccountDone(profile, tokenV3, 'github');
157+
158+
test('has expected type', () => {
159+
expect(a.type).toBe('PROFILE/LINK_EXTERNAL_ACCOUNT_DONE');
160+
});
161+
162+
test('External account should be linked', () =>
163+
a.payload.then((res) => {
164+
expect(res).toEqual({ data: linkedAccounts[0], handle });
165+
expect(mockUserService.linkExternalAccount).toBeCalled();
166+
}));
167+
});
168+
169+
describe('profile.unlinkExternalAccountDone', () => {
170+
const a = actions.profile.unlinkExternalAccountDone(profile, tokenV3, 'github');
171+
172+
test('has expected type', () => {
173+
expect(a.type).toBe('PROFILE/UNLINK_EXTERNAL_ACCOUNT_DONE');
174+
});
175+
176+
test('External account should be unlinked', () =>
177+
a.payload.then((res) => {
178+
expect(res).toEqual({ handle, providerType: 'github' });
179+
expect(mockUserService.unlinkExternalAccount).toBeCalled();
180+
}));
181+
});
182+
183+
describe('profile.getLinkedAccountsDone', () => {
184+
const a = actions.profile.getLinkedAccountsDone(profile, tokenV3);
185+
186+
test('has expected type', () => {
187+
expect(a.type).toBe('PROFILE/GET_LINKED_ACCOUNTS_DONE');
188+
});
189+
190+
test('Linked account should be returned', () =>
191+
a.payload.then((res) => {
192+
expect(res).toEqual({ profiles: linkedAccounts });
193+
expect(mockUserService.getLinkedAccounts).toBeCalled();
194+
}));
195+
});
196+
197+
describe('profile.getCredentialDone', () => {
198+
const a = actions.profile.getCredentialDone(profile, tokenV3);
199+
200+
test('has expected type', () => {
201+
expect(a.type).toBe('PROFILE/GET_CREDENTIAL_DONE');
202+
});
203+
204+
test('Credential should be returned', () =>
205+
a.payload.then((res) => {
206+
expect(res).toEqual({ credential: { hasPassword: true } });
207+
expect(mockUserService.getCredential).toBeCalled();
208+
}));
209+
});
210+
211+
describe('profile.getEmailPreferencesDone', () => {
212+
const a = actions.profile.getEmailPreferencesDone(profile, tokenV3);
213+
214+
test('has expected type', () => {
215+
expect(a.type).toBe('PROFILE/GET_EMAIL_PREFERENCES_DONE');
216+
});
217+
218+
test('Email preferences should be returned', () =>
219+
a.payload.then((res) => {
220+
expect(res).toEqual({ subscriptions: { TOPCODER_NL_DATA: true } });
221+
expect(mockUserService.getEmailPreferences).toBeCalled();
222+
}));
223+
});
224+
225+
describe('profile.saveEmailPreferencesDone', () => {
226+
const a = actions.profile.saveEmailPreferencesDone(profile, tokenV3, {});
227+
228+
test('has expected type', () => {
229+
expect(a.type).toBe('PROFILE/SAVE_EMAIL_PREFERENCES_DONE');
230+
});
231+
232+
test('Email preferences should be updated', () =>
233+
a.payload.then((res) => {
234+
expect(res).toEqual({ handle, data: { subscriptions: { TOPCODER_NL_DATA: true } } });
235+
expect(mockUserService.saveEmailPreferences).toBeCalled();
236+
}));
237+
});
238+
239+
describe('profile.updatePasswordDone', () => {
240+
const a = actions.profile.updatePasswordDone(profile, tokenV3, 'newPassword', 'oldPassword');
241+
242+
test('has expected type', () => {
243+
expect(a.type).toBe('PROFILE/UPDATE_PASSWORD_DONE');
244+
});
245+
246+
test('User password should be updated', () =>
247+
a.payload.then((res) => {
248+
expect(res).toEqual({ handle, data: { update: true } });
249+
expect(mockUserService.updatePassword).toBeCalled();
250+
}));
251+
});
252+

0 commit comments

Comments
 (0)