1// Copyright (C) 2019 Storj Labs, Inc.
2// See LICENSE for copying information.
3
4import Vuex from 'vuex';
5
6import { ProjectMembersApiGql } from '@/api/projectMembers';
7import { ProjectsApiGql } from '@/api/projects';
8import { makeProjectMembersModule, PROJECT_MEMBER_MUTATIONS } from '@/store/modules/projectMembers';
9import { makeProjectsModule } from '@/store/modules/projects';
10import { SortDirection } from '@/types/common';
11import { ProjectMember, ProjectMemberOrderBy, ProjectMembersPage } from '@/types/projectMembers';
12import { Project } from '@/types/projects';
13import { PM_ACTIONS } from '@/utils/constants/actionNames';
14import { createLocalVue } from '@vue/test-utils';
15
16const projectsApi = new ProjectsApiGql();
17const projectsModule = makeProjectsModule(projectsApi);
18const selectedProject = new Project();
19selectedProject.id = '1';
20projectsModule.state.selectedProject = selectedProject;
21
22const FIRST_PAGE = 1;
23const TEST_ERROR = 'testError';
24const UNREACHABLE_ERROR = 'should be unreachable';
25
26const Vue = createLocalVue();
27const pmApi = new ProjectMembersApiGql();
28const projectMembersModule = makeProjectMembersModule(pmApi);
29
30Vue.use(Vuex);
31
32const store = new Vuex.Store<{
33    projectsModule: typeof projectsModule.state,
34    projectMembersModule: typeof projectMembersModule.state,
35}>({modules: { projectsModule, projectMembersModule }});
36const state = store.state.projectMembersModule;
37const date = new Date(0);
38const projectMember1 = new ProjectMember('testFullName1', 'testShortName1', 'test1@example.com', date, '1');
39const projectMember2 = new ProjectMember('testFullName2', 'testShortName2', 'test2@example.com', date, '2');
40
41describe('mutations', () => {
42    it('fetch project members', function () {
43        const testProjectMembersPage = new ProjectMembersPage();
44        testProjectMembersPage.projectMembers = [projectMember1];
45        testProjectMembersPage.totalCount = 1;
46        testProjectMembersPage.pageCount = 1;
47
48        store.commit(PROJECT_MEMBER_MUTATIONS.FETCH, testProjectMembersPage);
49
50        expect(state.page.projectMembers.length).toBe(1);
51        expect(state.page.search).toBe('');
52        expect(state.page.order).toBe(ProjectMemberOrderBy.NAME);
53        expect(state.page.orderDirection).toBe(SortDirection.ASCENDING);
54        expect(state.page.limit).toBe(6);
55        expect(state.page.pageCount).toBe(1);
56        expect(state.page.currentPage).toBe(1);
57        expect(state.page.totalCount).toBe(1);
58    });
59
60    it('set project members page', function () {
61        store.commit(PROJECT_MEMBER_MUTATIONS.SET_PAGE, 2);
62
63        expect(state.cursor.page).toBe(2);
64    });
65
66    it('set search query', function () {
67        store.commit(PROJECT_MEMBER_MUTATIONS.SET_SEARCH_QUERY, 'testSearchQuery');
68
69        expect(state.cursor.search).toBe('testSearchQuery');
70    });
71
72    it('set sort order', function () {
73        store.commit(PROJECT_MEMBER_MUTATIONS.CHANGE_SORT_ORDER, ProjectMemberOrderBy.EMAIL);
74
75        expect(state.cursor.order).toBe(ProjectMemberOrderBy.EMAIL);
76    });
77
78    it('set sort direction', function () {
79        store.commit(PROJECT_MEMBER_MUTATIONS.CHANGE_SORT_ORDER_DIRECTION, SortDirection.DESCENDING);
80
81        expect(state.cursor.orderDirection).toBe(SortDirection.DESCENDING);
82    });
83
84    it('toggle selection', function () {
85        const testProjectMembersPage = new ProjectMembersPage();
86        testProjectMembersPage.projectMembers = [projectMember1];
87        testProjectMembersPage.totalCount = 1;
88        testProjectMembersPage.pageCount = 1;
89
90        store.commit(PROJECT_MEMBER_MUTATIONS.TOGGLE_SELECTION, projectMember1);
91
92        expect(state.page.projectMembers[0].isSelected).toBe(true);
93        expect(state.selectedProjectMembersEmails.length).toBe(1);
94
95        store.commit(PROJECT_MEMBER_MUTATIONS.FETCH, testProjectMembersPage);
96
97        expect(state.selectedProjectMembersEmails.length).toBe(1);
98
99        store.commit(PROJECT_MEMBER_MUTATIONS.TOGGLE_SELECTION, projectMember1);
100
101        expect(state.page.projectMembers[0].isSelected).toBe(false);
102        expect(state.selectedProjectMembersEmails.length).toBe(0);
103    });
104
105    it('clear selection', function () {
106        store.commit(PROJECT_MEMBER_MUTATIONS.CLEAR_SELECTION);
107
108        state.page.projectMembers.forEach((pm: ProjectMember) => {
109            expect(pm.isSelected).toBe(false);
110        });
111
112        expect(state.selectedProjectMembersEmails.length).toBe(0);
113    });
114
115    it('clear store', function () {
116        store.commit(PROJECT_MEMBER_MUTATIONS.CLEAR);
117
118        expect(state.cursor.page).toBe(1);
119        expect(state.cursor.search).toBe('');
120        expect(state.cursor.order).toBe(ProjectMemberOrderBy.NAME);
121        expect(state.cursor.orderDirection).toBe(SortDirection.ASCENDING);
122        expect(state.page.projectMembers.length).toBe(0);
123        expect(state.selectedProjectMembersEmails.length).toBe(0);
124    });
125});
126
127describe('actions', () => {
128    beforeEach(() => {
129        jest.resetAllMocks();
130    });
131
132    it('add project members', async function () {
133        jest.spyOn(pmApi, 'add').mockReturnValue(Promise.resolve());
134
135        try {
136            await store.dispatch(PM_ACTIONS.ADD, [projectMember1.user.email]);
137            throw new Error(TEST_ERROR);
138        } catch (err) {
139            expect(err.message).toBe(TEST_ERROR);
140        }
141    });
142
143    it('add project member throws error when api call fails', async function () {
144        jest.spyOn(pmApi, 'add').mockImplementation(() => {
145            throw new Error(TEST_ERROR);
146        });
147
148        const stateDump = state;
149
150        try {
151            await store.dispatch(PM_ACTIONS.ADD, [projectMember1.user.email]);
152        } catch (err) {
153            expect(err.message).toBe(TEST_ERROR);
154            expect(state).toBe(stateDump);
155
156            return;
157        }
158
159        fail(UNREACHABLE_ERROR);
160    });
161
162    it('delete project members', async function () {
163        jest.spyOn(pmApi, 'delete').mockReturnValue(Promise.resolve());
164
165        try {
166            await store.dispatch(PM_ACTIONS.DELETE, [projectMember1.user.email]);
167            throw new Error(TEST_ERROR);
168        } catch (err) {
169            expect(err.message).toBe(TEST_ERROR);
170        }
171    });
172
173    it('delete project member throws error when api call fails', async function () {
174        jest.spyOn(pmApi, 'delete').mockImplementation(() => {
175            throw new Error(TEST_ERROR);
176        });
177
178        const stateDump = state;
179
180        try {
181            await store.dispatch(PM_ACTIONS.DELETE, [projectMember1.user.email]);
182        } catch (err) {
183            expect(err.message).toBe(TEST_ERROR);
184            expect(state).toBe(stateDump);
185
186            return;
187        }
188
189        fail(UNREACHABLE_ERROR);
190    });
191
192    it('fetch project members', async function () {
193        jest.spyOn(pmApi, 'get').mockReturnValue(
194            Promise.resolve(new ProjectMembersPage(
195                [projectMember1],
196                '',
197                ProjectMemberOrderBy.NAME,
198                SortDirection.ASCENDING,
199                6,
200                1,
201                1,
202                1)),
203        );
204
205        await store.dispatch(PM_ACTIONS.FETCH, FIRST_PAGE);
206
207        expect(state.page.projectMembers[0].isSelected).toBe(false);
208        expect(state.page.projectMembers[0].joinedAt).toBe(projectMember1.joinedAt);
209        expect(state.page.projectMembers[0].user.email).toBe(projectMember1.user.email);
210        expect(state.page.projectMembers[0].user.id).toBe(projectMember1.user.id);
211        expect(state.page.projectMembers[0].user.partnerId).toBe(projectMember1.user.partnerId);
212        expect(state.page.projectMembers[0].user.fullName).toBe(projectMember1.user.fullName);
213        expect(state.page.projectMembers[0].user.shortName).toBe(projectMember1.user.shortName);
214    });
215
216    it('fetch project members throws error when api call fails', async function () {
217        jest.spyOn(pmApi, 'get').mockImplementation(() => {
218            throw new Error(TEST_ERROR);
219        });
220
221        const stateDump = state;
222
223        try {
224            await store.dispatch(PM_ACTIONS.FETCH, FIRST_PAGE);
225        } catch (err) {
226            expect(err.message).toBe(TEST_ERROR);
227            expect(state).toBe(stateDump);
228
229            return;
230        }
231
232        fail(UNREACHABLE_ERROR);
233    });
234
235    it('set project members search query', function () {
236        store.dispatch(PM_ACTIONS.SET_SEARCH_QUERY, 'search');
237
238        expect(state.cursor.search).toBe('search');
239    });
240
241    it('set project members sort by', function () {
242        store.dispatch(PM_ACTIONS.SET_SORT_BY, ProjectMemberOrderBy.CREATED_AT);
243
244        expect(state.cursor.order).toBe(ProjectMemberOrderBy.CREATED_AT);
245    });
246
247    it('set sort direction', function () {
248        store.dispatch(PM_ACTIONS.SET_SORT_DIRECTION, SortDirection.DESCENDING);
249
250        expect(state.cursor.orderDirection).toBe(SortDirection.DESCENDING);
251    });
252
253    it('toggle selection', async function () {
254        jest.spyOn(pmApi, 'get').mockReturnValue(
255            Promise.resolve(new ProjectMembersPage(
256                [projectMember1, projectMember2],
257                '',
258                ProjectMemberOrderBy.NAME,
259                SortDirection.ASCENDING,
260                6,
261                1,
262                1,
263                2)),
264        );
265
266        await store.dispatch(PM_ACTIONS.FETCH, FIRST_PAGE);
267        store.dispatch(PM_ACTIONS.TOGGLE_SELECTION, projectMember1);
268
269        expect(state.page.projectMembers[0].isSelected).toBe(true);
270        expect(state.selectedProjectMembersEmails.length).toBe(1);
271
272        store.dispatch(PM_ACTIONS.TOGGLE_SELECTION, projectMember2);
273
274        expect(state.page.projectMembers[1].isSelected).toBe(true);
275        expect(state.selectedProjectMembersEmails.length).toBe(2);
276
277        await store.dispatch(PM_ACTIONS.FETCH, FIRST_PAGE);
278
279        expect(state.page.projectMembers[1].isSelected).toBe(true);
280        expect(state.selectedProjectMembersEmails.length).toBe(2);
281
282        store.dispatch(PM_ACTIONS.TOGGLE_SELECTION, projectMember1);
283
284        expect(state.page.projectMembers[0].isSelected).toBe(false);
285        expect(state.selectedProjectMembersEmails.length).toBe(1);
286    });
287
288    it('clear selection', function () {
289        store.dispatch(PM_ACTIONS.CLEAR_SELECTION);
290
291        state.page.projectMembers.forEach((pm: ProjectMember) => {
292            expect(pm.isSelected).toBe(false);
293        });
294    });
295
296    it('clear store', function () {
297        store.commit(PM_ACTIONS.CLEAR);
298
299        expect(state.cursor.page).toBe(1);
300        expect(state.cursor.search).toBe('');
301        expect(state.cursor.order).toBe(ProjectMemberOrderBy.NAME);
302        expect(state.cursor.orderDirection).toBe(SortDirection.ASCENDING);
303        expect(state.page.projectMembers.length).toBe(0);
304
305        state.page.projectMembers.forEach((pm: ProjectMember) => {
306            expect(pm.isSelected).toBe(false);
307        });
308    });
309});
310
311describe('getters', () => {
312    const selectedProjectMember = new ProjectMember('testFullName2', 'testShortName2', 'test2@example.com', date, '2');
313
314    it('selected project members', function () {
315        const testProjectMembersPage = new ProjectMembersPage();
316        testProjectMembersPage.projectMembers = [selectedProjectMember];
317        testProjectMembersPage.totalCount = 1;
318        testProjectMembersPage.pageCount = 1;
319
320        store.commit(PROJECT_MEMBER_MUTATIONS.FETCH, testProjectMembersPage);
321        store.commit(PROJECT_MEMBER_MUTATIONS.TOGGLE_SELECTION, selectedProjectMember);
322
323        const retrievedProjectMembers = store.getters.selectedProjectMembers;
324
325        expect(retrievedProjectMembers[0].user.id).toBe(selectedProjectMember.user.id);
326    });
327});
328