20225: Mock group contents service to squelch errors in unit tests
[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
28
29     };
30     const actions: ApiActions = {
31         progressFn: (id: string, working: boolean) => { },
32         errorFn: (id: string, message: string) => { }
33     };
34     let importMocks: any[];
35
36     beforeEach(() => {
37         axiosMock.reset();
38         services = createServices(mockConfig({}), actions, axiosInst);
39         store = configureStore(createBrowserHistory(), services, config);
40         localStorage.clear();
41         importMocks = [];
42     });
43
44     afterEach(() => {
45         importMocks.map(m => m.restore());
46     });
47
48     it('initializes preselected tree picker nodes', async () => {
49         const dispatchMock = jest.fn();
50         const dispatchWrapper = (action: any) => {
51             dispatchMock(action);
52             return store.dispatch(action);
53         };
54
55         const emptyCollectionUuid = "zzzzz-4zz18-000000000000000";
56         const collectionUuid = "zzzzz-4zz18-111111111111111";
57         const parentProjectUuid = "zzzzz-j7d0g-000000000000000";
58         const childCollectionUuid = "zzzzz-4zz18-222222222222222";
59
60         const fakeResources = {
61             [emptyCollectionUuid]: {
62                 kind: ResourceKind.COLLECTION,
63                 ownerUuid: '',
64                 files: [],
65             },
66             [collectionUuid]: {
67                 kind: ResourceKind.COLLECTION,
68                 ownerUuid: '',
69                 files: [{
70                     id: `${collectionUuid}/directory`,
71                     name: "directory",
72                     path: "",
73                     type: CollectionFileType.DIRECTORY,
74                     url: `/c=${collectionUuid}/directory/`,
75                 }]
76             },
77             [parentProjectUuid]: {
78                 kind: ResourceKind.GROUP,
79                 ownerUuid: '',
80             },
81             [childCollectionUuid]: {
82                 kind: ResourceKind.COLLECTION,
83                 ownerUuid: parentProjectUuid,
84                 files: [
85                     {
86                         id: `${childCollectionUuid}/mainDir`,
87                         name: "mainDir",
88                         path: "",
89                         type: CollectionFileType.DIRECTORY,
90                         url: `/c=${childCollectionUuid}/mainDir/`,
91                     },
92                     {
93                         id: `${childCollectionUuid}/mainDir/subDir`,
94                         name: "subDir",
95                         path: "/mainDir",
96                         type: CollectionFileType.DIRECTORY,
97                         url: `/c=${childCollectionUuid}/mainDir/subDir`,
98                     }
99                 ],
100             },
101         };
102
103         services.ancestorsService.ancestors = jest.fn(async (startUuid, endUuid) => {
104             let ancestors: (GroupResource | CollectionResource)[] = [];
105             let uuid = startUuid;
106             while (uuid?.length && fakeResources[uuid]) {
107                 const resource = fakeResources[uuid];
108                 if (resource.kind === ResourceKind.COLLECTION) {
109                     ancestors.unshift({
110                         uuid, kind: resource.kind,
111                         ownerUuid: resource.ownerUuid,
112                     } as CollectionResource);
113                 } else if (resource.kind === ResourceKind.GROUP) {
114                     ancestors.unshift({
115                         uuid, kind: resource.kind,
116                         ownerUuid: resource.ownerUuid,
117                     } as GroupResource);
118                 }
119                 uuid = resource.ownerUuid;
120             }
121             return ancestors;
122         });
123
124         services.collectionService.files = jest.fn(async (uuid): Promise<(CollectionDirectory | CollectionFile)[]> => {
125             return fakeResources[uuid]?.files || [];
126         });
127
128         services.groupsService.contents = jest.fn(async (uuid, args) => {
129             const items = Object.keys(fakeResources).map(uuid => ({...fakeResources[uuid], uuid})).filter(item => item.ownerUuid === uuid);
130             return {items: items as GroupContentsResource[], itemsAvailable: items.length} as ListResults<GroupContentsResource>;
131         });
132
133         const pickerId = "pickerId";
134
135         // When collection preselected
136         await initProjectsTreePicker(pickerId, {
137             selectedItemUuids: [emptyCollectionUuid],
138             includeDirectories: true,
139             includeFiles: false,
140             multi: true,
141         })(dispatchWrapper, store.getState, services);
142
143         // Expect ancestor service to be called
144         expect(services.ancestorsService.ancestors).toHaveBeenCalledWith(emptyCollectionUuid, '');
145         // Expect top level to be expanded and node to be selected
146         expect(store.getState().treePicker["pickerId_shared"][SHARED_PROJECT_ID].expanded).toBe(true);
147         expect(store.getState().treePicker["pickerId_shared"][emptyCollectionUuid].selected).toBe(true);
148
149
150         // When collection subdirectory is preselected
151         await initProjectsTreePicker(pickerId, {
152             selectedItemUuids: [`${collectionUuid}/directory`],
153             includeDirectories: true,
154             includeFiles: false,
155             multi: true,
156         })(dispatchWrapper, store.getState, services);
157
158         // Expect ancestor service to be called
159         expect(services.ancestorsService.ancestors).toHaveBeenCalledWith(collectionUuid, '');
160         // Expect top level to be expanded and node to be selected
161         expect(store.getState().treePicker["pickerId_shared"][SHARED_PROJECT_ID].expanded).toBe(true);
162         expect(store.getState().treePicker["pickerId_shared"][collectionUuid].expanded).toBe(true);
163         expect(store.getState().treePicker["pickerId_shared"][collectionUuid].selected).toBe(false);
164         expect(store.getState().treePicker["pickerId_shared"][`${collectionUuid}/directory`].selected).toBe(true);
165
166
167         // When subdirectory of collection inside project is preselected
168         await initProjectsTreePicker(pickerId, {
169             selectedItemUuids: [`${childCollectionUuid}/mainDir/subDir`],
170             includeDirectories: true,
171             includeFiles: false,
172             multi: true,
173         })(dispatchWrapper, store.getState, services);
174
175         // Expect ancestor service to be called
176         expect(services.ancestorsService.ancestors).toHaveBeenCalledWith(childCollectionUuid, '');
177         // Expect parent project and collection to be expanded
178         expect(store.getState().treePicker["pickerId_shared"][SHARED_PROJECT_ID].expanded).toBe(true);
179         expect(store.getState().treePicker["pickerId_shared"][parentProjectUuid].expanded).toBe(true);
180         expect(store.getState().treePicker["pickerId_shared"][parentProjectUuid].selected).toBe(false);
181         expect(store.getState().treePicker["pickerId_shared"][childCollectionUuid].expanded).toBe(true);
182         expect(store.getState().treePicker["pickerId_shared"][childCollectionUuid].selected).toBe(false);
183         // Expect main directory to be expanded
184         expect(store.getState().treePicker["pickerId_shared"][`${childCollectionUuid}/mainDir`].expanded).toBe(true);
185         expect(store.getState().treePicker["pickerId_shared"][`${childCollectionUuid}/mainDir`].selected).toBe(false);
186         // Expect sub directory to be selected
187         expect(store.getState().treePicker["pickerId_shared"][`${childCollectionUuid}/mainDir/subDir`].expanded).toBe(false);
188         expect(store.getState().treePicker["pickerId_shared"][`${childCollectionUuid}/mainDir/subDir`].selected).toBe(true);
189
190
191     });
192 });