Merge branch '21026-sanitize-html'
[arvados.git] / cypress / integration / search.spec.js
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 describe("Search tests", function () {
6     let activeUser;
7     let adminUser;
8
9     before(function () {
10         // Only set up common users once. These aren't set up as aliases because
11         // aliases are cleaned up after every test. Also it doesn't make sense
12         // to set the same users on beforeEach() over and over again, so we
13         // separate a little from Cypress' 'Best Practices' here.
14         cy.getUser("admin", "Admin", "User", true, true)
15             .as("adminUser")
16             .then(function () {
17                 adminUser = this.adminUser;
18             });
19         cy.getUser("collectionuser1", "Collection", "User", false, true)
20             .as("activeUser")
21             .then(function () {
22                 activeUser = this.activeUser;
23             });
24     });
25
26     beforeEach(function () {
27         cy.clearCookies();
28         cy.clearLocalStorage();
29     });
30
31     it("can search for old collection versions", function () {
32         const colName = `Versioned Collection ${Math.floor(Math.random() * Math.floor(999999))}`;
33         let colUuid = "";
34         let oldVersionUuid = "";
35         // Make sure no other collections with this name exist
36         cy.doRequest("GET", "/arvados/v1/collections", null, {
37             filters: `[["name", "=", "${colName}"]]`,
38             include_old_versions: true,
39         })
40             .its("body.items")
41             .as("collections")
42             .then(function () {
43                 expect(this.collections).to.be.empty;
44             });
45         // Creates the collection using the admin token so we can set up
46         // a bogus manifest text without block signatures.
47         cy.createCollection(adminUser.token, {
48             name: colName,
49             owner_uuid: activeUser.user.uuid,
50             preserve_version: true,
51             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
52         })
53             .as("originalVersion")
54             .then(function () {
55                 // Change the file name to create a new version.
56                 cy.updateCollection(adminUser.token, this.originalVersion.uuid, {
57                     manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo\n",
58                 });
59                 colUuid = this.originalVersion.uuid;
60             });
61         // Confirm that there are 2 versions of the collection
62         cy.doRequest("GET", "/arvados/v1/collections", null, {
63             filters: `[["name", "=", "${colName}"]]`,
64             include_old_versions: true,
65         })
66             .its("body.items")
67             .as("collections")
68             .then(function () {
69                 expect(this.collections).to.have.lengthOf(2);
70                 this.collections.map(function (aCollection) {
71                     expect(aCollection.current_version_uuid).to.equal(colUuid);
72                     if (aCollection.uuid !== aCollection.current_version_uuid) {
73                         oldVersionUuid = aCollection.uuid;
74                     }
75                 });
76                 cy.loginAs(activeUser);
77                 const searchQuery = `${colName} type:arvados#collection`;
78                 // Search for only collection's current version
79                 cy.doSearch(`${searchQuery}`);
80                 cy.get("[data-cy=search-results]").should("contain", "head version");
81                 cy.get("[data-cy=search-results]").should("not.contain", "version 1");
82                 // ...and then, include old versions.
83                 cy.doSearch(`${searchQuery} is:pastVersion`);
84                 cy.get("[data-cy=search-results]").should("contain", "head version");
85                 cy.get("[data-cy=search-results]").should("contain", "version 1");
86             });
87     });
88
89     it("can display path of the selected item", function () {
90         const colName = `Collection ${Math.floor(Math.random() * Math.floor(999999))}`;
91
92         // Creates the collection using the admin token so we can set up
93         // a bogus manifest text without block signatures.
94         cy.createCollection(adminUser.token, {
95             name: colName,
96             owner_uuid: activeUser.user.uuid,
97             preserve_version: true,
98             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
99         }).then(function () {
100             cy.loginAs(activeUser);
101
102             cy.doSearch(colName);
103
104             cy.get("[data-cy=search-results]").should("contain", colName);
105
106             cy.get("[data-cy=search-results]").contains(colName).closest("tr").click();
107
108             cy.get("[data-cy=element-path]").should("contain", `/ Projects / ${colName}`);
109         });
110     });
111
112     it("can search items using quotes", function () {
113         const random = Math.floor(Math.random() * Math.floor(999999));
114         const colName = `Collection ${random}`;
115         const colName2 = `Collection test ${random}`;
116
117         // Creates the collection using the admin token so we can set up
118         // a bogus manifest text without block signatures.
119         cy.createCollection(adminUser.token, {
120             name: colName,
121             owner_uuid: activeUser.user.uuid,
122             preserve_version: true,
123             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
124         }).as("collection1");
125
126         cy.createCollection(adminUser.token, {
127             name: colName2,
128             owner_uuid: activeUser.user.uuid,
129             preserve_version: true,
130             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
131         }).as("collection2");
132
133         cy.getAll("@collection1", "@collection2").then(function () {
134             cy.loginAs(activeUser);
135
136             cy.doSearch(colName);
137             cy.get("[data-cy=search-results] table tbody tr").should("have.length", 2);
138
139             cy.doSearch(`"${colName}"`);
140             cy.get("[data-cy=search-results] table tbody tr").should("have.length", 1);
141         });
142     });
143
144     it("can display owner of the item", function () {
145         const colName = `Collection ${Math.floor(Math.random() * Math.floor(999999))}`;
146
147         cy.createCollection(adminUser.token, {
148             name: colName,
149             owner_uuid: activeUser.user.uuid,
150             preserve_version: true,
151             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
152         }).then(function () {
153             cy.loginAs(activeUser);
154
155             cy.doSearch(colName);
156
157             cy.get("[data-cy=search-results]").should("contain", colName);
158
159             cy.get("[data-cy=search-results]")
160                 .contains(colName)
161                 .closest("tr")
162                 .within(() => {
163                     cy.get("p").contains(activeUser.user.uuid).should("contain", activeUser.user.full_name);
164                 });
165         });
166     });
167
168     it("shows search context menu", function () {
169         const colName = `Home Collection ${Math.floor(Math.random() * Math.floor(999999))}`;
170         const federatedColName = `Federated Collection ${Math.floor(Math.random() * Math.floor(999999))}`;
171         const federatedColUuid = "xxxxx-4zz18-000000000000000";
172
173         // Intercept config to insert remote cluster
174         cy.intercept({ method: "GET", hostname: "127.0.0.1", url: "**/arvados/v1/config?nocache=*" }, req => {
175             req.reply(res => {
176                 res.body.RemoteClusters = {
177                     "*": res.body.RemoteClusters["*"],
178                     xxxxx: {
179                         ActivateUsers: true,
180                         Host: "xxxxx.fakecluster.tld",
181                         Insecure: false,
182                         Proxy: true,
183                         Scheme: "",
184                     },
185                 };
186             });
187         });
188
189         // Fake remote cluster config
190         cy.intercept(
191             {
192                 method: "GET",
193                 hostname: "xxxxx.fakecluster.tld",
194                 url: "**/arvados/v1/config",
195             },
196             {
197                 statusCode: 200,
198                 body: {
199                     API: {},
200                     ClusterID: "xxxxx",
201                     Collections: {},
202                     Containers: {},
203                     InstanceTypes: {},
204                     Login: {},
205                     Mail: { SupportEmailAddress: "arvados@example.com" },
206                     RemoteClusters: {
207                         "*": {
208                             ActivateUsers: false,
209                             Host: "",
210                             Insecure: false,
211                             Proxy: false,
212                             Scheme: "https",
213                         },
214                     },
215                     Services: {
216                         Composer: { ExternalURL: "" },
217                         Controller: { ExternalURL: "https://xxxxx.fakecluster.tld:34763/" },
218                         DispatchCloud: { ExternalURL: "" },
219                         DispatchLSF: { ExternalURL: "" },
220                         DispatchSLURM: { ExternalURL: "" },
221                         GitHTTP: { ExternalURL: "https://xxxxx.fakecluster.tld:39105/" },
222                         GitSSH: { ExternalURL: "" },
223                         Health: { ExternalURL: "https://xxxxx.fakecluster.tld:42915/" },
224                         Keepbalance: { ExternalURL: "" },
225                         Keepproxy: { ExternalURL: "https://xxxxx.fakecluster.tld:46773/" },
226                         Keepstore: { ExternalURL: "" },
227                         RailsAPI: { ExternalURL: "" },
228                         WebDAV: { ExternalURL: "https://xxxxx.fakecluster.tld:36041/" },
229                         WebDAVDownload: { ExternalURL: "https://xxxxx.fakecluster.tld:42957/" },
230                         WebShell: { ExternalURL: "" },
231                         Websocket: { ExternalURL: "wss://xxxxx.fakecluster.tld:37121/websocket" },
232                         Workbench1: { ExternalURL: "https://wb1.xxxxx.fakecluster.tld/" },
233                         Workbench2: { ExternalURL: "https://wb2.xxxxx.fakecluster.tld/" },
234                     },
235                     StorageClasses: {
236                         default: { Default: true, Priority: 0 },
237                     },
238                     Users: {},
239                     Volumes: {},
240                     Workbench: {},
241                 },
242             }
243         );
244
245         cy.createCollection(adminUser.token, {
246             name: colName,
247             owner_uuid: activeUser.user.uuid,
248             preserve_version: true,
249             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
250         }).then(function (testCollection) {
251             cy.loginAs(activeUser);
252
253             // Intercept search results to add federated result
254             cy.intercept({ method: "GET", url: "**/arvados/v1/groups/contents?*" }, req => {
255                 req.reply(res => {
256                     res.body.items = [
257                         res.body.items[0],
258                         {
259                             ...res.body.items[0],
260                             uuid: federatedColUuid,
261                             portable_data_hash: "00000000000000000000000000000000+0",
262                             name: federatedColName,
263                             href: res.body.items[0].href.replace(testCollection.uuid, federatedColUuid),
264                         },
265                     ];
266                     res.body.items_available += 1;
267                 });
268             });
269
270             cy.doSearch(colName);
271
272             // Stub new window
273             cy.window().then(win => {
274                 cy.stub(win, "open").as("Open");
275             });
276
277             // Check copy to clipboard
278             cy.get("[data-cy=search-results]").contains(colName).rightclick();
279             cy.get("[data-cy=context-menu]").within(ctx => {
280                 // Check that there are 4 items in the menu
281                 cy.get(ctx).children().should("have.length", 4);
282                 cy.contains("API Details");
283                 cy.contains("Copy to clipboard");
284                 cy.contains("Open in new tab");
285                 cy.contains("View details");
286
287                 cy.contains("Copy to clipboard").click();
288                 cy.waitForDom();
289                 cy.window().then(win =>
290                     win.navigator.clipboard.readText().then(text => {
291                         expect(text).to.match(new RegExp(`/collections/${testCollection.uuid}$`));
292                     })
293                 );
294             });
295
296             // Check open in new tab
297             cy.get("[data-cy=search-results]").contains(colName).rightclick();
298             cy.get("[data-cy=context-menu]").within(() => {
299                 cy.contains("Open in new tab").click();
300                 cy.waitForDom();
301                 cy.get("@Open").should("have.been.calledOnceWith", `${window.location.origin}/collections/${testCollection.uuid}`);
302             });
303
304             // Check federated result copy to clipboard
305             cy.get("[data-cy=search-results]").contains(federatedColName).rightclick();
306             cy.get("[data-cy=context-menu]").within(() => {
307                 cy.contains("Copy to clipboard").click();
308                 cy.waitForDom();
309                 cy.window().then(win =>
310                     win.navigator.clipboard.readText().then(text => {
311                         expect(text).to.equal(`https://wb2.xxxxx.fakecluster.tld/collections/${federatedColUuid}`);
312                     })
313                 );
314             });
315             // Check open in new tab
316             cy.get("[data-cy=search-results]").contains(federatedColName).rightclick();
317             cy.get("[data-cy=context-menu]").within(() => {
318                 cy.contains("Open in new tab").click();
319                 cy.waitForDom();
320                 cy.get("@Open").should("have.been.calledWith", `https://wb2.xxxxx.fakecluster.tld/collections/${federatedColUuid}`);
321             });
322         });
323     });
324 });