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