Merge branch '21128-toolbar-context-menu'
[arvados-workbench2.git] / src / store / tree-picker / tree-picker-actions.test.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { ServiceRepository, createServices } from "services/services";
6 import { configureStore, RootStore } from "../store";
7 import { createBrowserHistory } from "history";
8 import { mockConfig } from 'common/config';
9 import { ApiActions } from "services/api/api-actions";
10 import Axios from "axios";
11 import MockAdapter from "axios-mock-adapter";
12 import { ResourceKind } from 'models/resource';
13 import { SHARED_PROJECT_ID, initProjectsTreePicker } from "./tree-picker-actions";
14 import { CollectionResource } from "models/collection";
15 import { GroupResource } from "models/group";
16 import { CollectionDirectory, CollectionFile, CollectionFileType } from "models/collection-file";
17 import { GroupContentsResource } from "services/groups-service/groups-service";
18 import { ListResults } from "services/common-service/common-service";
19
20 describe('tree-picker-actions', () => {
21     const axiosInst = Axios.create({ headers: {} });
22     const axiosMock = new MockAdapter(axiosInst);
23
24     let store: RootStore;
25     let services: ServiceRepository;
26     const config: any = {};
27     const actions: ApiActions = {
28         progressFn: (id: string, working: boolean) => { },
29         errorFn: (id: string, message: string) => { }
30     };
31     let importMocks: any[];
32
33     beforeEach(() => {
34         axiosMock.reset();
35         services = createServices(mockConfig({}), actions, axiosInst);
36         store = configureStore(createBrowserHistory(), services, config);
37         localStorage.clear();
38         importMocks = [];
39     });
40
41     afterEach(() => {
42         importMocks.map(m => m.restore());
43     });
44
45     it('initializes preselected tree picker nodes', async () => {
46         const dispatchMock = jest.fn();
47         const dispatchWrapper = (action: any) => {
48             dispatchMock(action);
49             return store.dispatch(action);
50         };
51
52         const emptyCollectionUuid = "zzzzz-4zz18-000000000000000";
53         const collectionUuid = "zzzzz-4zz18-111111111111111";
54         const parentProjectUuid = "zzzzz-j7d0g-000000000000000";
55         const childCollectionUuid = "zzzzz-4zz18-222222222222222";
56
57         const fakeResources = {
58             [emptyCollectionUuid]: {
59                 kind: ResourceKind.COLLECTION,
60                 ownerUuid: '',
61                 files: [],
62             },
63             [collectionUuid]: {
64                 kind: ResourceKind.COLLECTION,
65                 ownerUuid: '',
66                 files: [{
67                     id: `${collectionUuid}/directory`,
68                     name: "directory",
69                     path: "",
70                     type: CollectionFileType.DIRECTORY,
71                     url: `/c=${collectionUuid}/directory/`,
72                 }]
73             },
74             [parentProjectUuid]: {
75                 kind: ResourceKind.GROUP,
76                 ownerUuid: '',
77             },
78             [childCollectionUuid]: {
79                 kind: ResourceKind.COLLECTION,
80                 ownerUuid: parentProjectUuid,
81                 files: [
82                     {
83                         id: `${childCollectionUuid}/mainDir`,
84                         name: "mainDir",
85                         path: "",
86                         type: CollectionFileType.DIRECTORY,
87                         url: `/c=${childCollectionUuid}/mainDir/`,
88                     },
89                     {
90                         id: `${childCollectionUuid}/mainDir/subDir`,
91                         name: "subDir",
92                         path: "/mainDir",
93                         type: CollectionFileType.DIRECTORY,
94                         url: `/c=${childCollectionUuid}/mainDir/subDir`,
95                     }
96                 ],
97             },
98         };
99
100         services.ancestorsService.ancestors = jest.fn(async (startUuid, endUuid) => {
101             let ancestors: (GroupResource | CollectionResource)[] = [];
102             let uuid = startUuid;
103             while (uuid?.length && fakeResources[uuid]) {
104                 const resource = fakeResources[uuid];
105                 if (resource.kind === ResourceKind.COLLECTION) {
106                     ancestors.unshift({
107                         uuid, kind: resource.kind,
108                         ownerUuid: resource.ownerUuid,
109                     } as CollectionResource);
110                 } else if (resource.kind === ResourceKind.GROUP) {
111                     ancestors.unshift({
112                         uuid, kind: resource.kind,
113                         ownerUuid: resource.ownerUuid,
114                     } as GroupResource);
115                 }
116                 uuid = resource.ownerUuid;
117             }
118             return ancestors;
119         });
120
121         services.collectionService.files = jest.fn(async (uuid): Promise<(CollectionDirectory | CollectionFile)[]> => {
122             return fakeResources[uuid]?.files || [];
123         });
124
125         services.groupsService.contents = jest.fn(async (uuid, args) => {
126             const items = Object.keys(fakeResources).map(uuid => ({...fakeResources[uuid], uuid})).filter(item => item.ownerUuid === uuid);
127             return {items: items as GroupContentsResource[], itemsAvailable: items.length} as ListResults<GroupContentsResource>;
128         });
129
130         const pickerId = "pickerId";
131
132         // When collection preselected
133         await initProjectsTreePicker(pickerId, {
134             selectedItemUuids: [emptyCollectionUuid],
135             includeDirectories: true,
136             includeFiles: false,
137             multi: true,
138         })(dispatchWrapper, store.getState, services);
139
140         // Expect ancestor service to be called
141         expect(services.ancestorsService.ancestors).toHaveBeenCalledWith(emptyCollectionUuid, '');
142         // Expect top level to be expanded and node to be selected
143         expect(store.getState().treePicker["pickerId_shared"][SHARED_PROJECT_ID].expanded).toBe(true);
144         expect(store.getState().treePicker["pickerId_shared"][emptyCollectionUuid].selected).toBe(true);
145
146
147         // When collection subdirectory is preselected
148         await initProjectsTreePicker(pickerId, {
149             selectedItemUuids: [`${collectionUuid}/directory`],
150             includeDirectories: true,
151             includeFiles: false,
152             multi: true,
153         })(dispatchWrapper, store.getState, services);
154
155         // Expect ancestor service to be called
156         expect(services.ancestorsService.ancestors).toHaveBeenCalledWith(collectionUuid, '');
157         // Expect top level to be expanded and node to be selected
158         expect(store.getState().treePicker["pickerId_shared"][SHARED_PROJECT_ID].expanded).toBe(true);
159         expect(store.getState().treePicker["pickerId_shared"][collectionUuid].expanded).toBe(true);
160         expect(store.getState().treePicker["pickerId_shared"][collectionUuid].selected).toBe(false);
161         expect(store.getState().treePicker["pickerId_shared"][`${collectionUuid}/directory`].selected).toBe(true);
162
163
164         // When subdirectory of collection inside project is preselected
165         await initProjectsTreePicker(pickerId, {
166             selectedItemUuids: [`${childCollectionUuid}/mainDir/subDir`],
167             includeDirectories: true,
168             includeFiles: false,
169             multi: true,
170         })(dispatchWrapper, store.getState, services);
171
172         // Expect ancestor service to be called
173         expect(services.ancestorsService.ancestors).toHaveBeenCalledWith(childCollectionUuid, '');
174         // Expect parent project and collection to be expanded
175         expect(store.getState().treePicker["pickerId_shared"][SHARED_PROJECT_ID].expanded).toBe(true);
176         expect(store.getState().treePicker["pickerId_shared"][parentProjectUuid].expanded).toBe(true);
177         expect(store.getState().treePicker["pickerId_shared"][parentProjectUuid].selected).toBe(false);
178         expect(store.getState().treePicker["pickerId_shared"][childCollectionUuid].expanded).toBe(true);
179         expect(store.getState().treePicker["pickerId_shared"][childCollectionUuid].selected).toBe(false);
180         // Expect main directory to be expanded
181         expect(store.getState().treePicker["pickerId_shared"][`${childCollectionUuid}/mainDir`].expanded).toBe(true);
182         expect(store.getState().treePicker["pickerId_shared"][`${childCollectionUuid}/mainDir`].selected).toBe(false);
183         // Expect sub directory to be selected
184         expect(store.getState().treePicker["pickerId_shared"][`${childCollectionUuid}/mainDir/subDir`].expanded).toBe(false);
185         expect(store.getState().treePicker["pickerId_shared"][`${childCollectionUuid}/mainDir/subDir`].selected).toBe(true);
186
187
188     });
189 });