17415: Fixed port and hostname, added proper test
[arvados-workbench2.git] / cypress / integration / collection.spec.js
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 const path = require('path');
6
7 describe('Collection panel tests', function () {
8     let activeUser;
9     let adminUser;
10     let downloadsFolder;
11
12     before(function () {
13         // Only set up common users once. These aren't set up as aliases because
14         // aliases are cleaned up after every test. Also it doesn't make sense
15         // to set the same users on beforeEach() over and over again, so we
16         // separate a little from Cypress' 'Best Practices' here.
17         cy.getUser('admin', 'Admin', 'User', true, true)
18             .as('adminUser').then(function () {
19                 adminUser = this.adminUser;
20             }
21             );
22         cy.getUser('collectionuser1', 'Collection', 'User', false, true)
23             .as('activeUser').then(function () {
24                 activeUser = this.activeUser;
25             }
26             );
27         downloadsFolder = Cypress.config('downloadsFolder');
28     });
29
30     beforeEach(function () {
31         cy.clearCookies();
32         cy.clearLocalStorage();
33     });
34
35     it('allows to download mountain duck config for a collection', () => {
36         cy.createCollection(adminUser.token, {
37             name: `Test collection ${Math.floor(Math.random() * 999999)}`,
38             owner_uuid: activeUser.user.uuid,
39             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
40         })
41         .as('testCollection').then(function (testCollection) {
42             cy.loginAs(activeUser);
43             cy.doSearch(`${testCollection.uuid}`);
44
45             cy.get('[data-cy=collection-panel-options-btn]').click();
46             cy.get('[data-cy=context-menu]').contains('Open as network folder or S3 bucket').click();
47             cy.get('[data-cy=download-button').click();
48
49             const filename = path.join(downloadsFolder, `${testCollection.name}.duck`);
50             const expectedValue = '<?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEplistPUBLIC"-//Apple//DTDPLIST1.0//EN""http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plistversion="1.0"><dict><key>Protocol</key><string>davs</string><key>Provider</key><string>iterateGmbH</string><key>UUID</key><string>zzzzz-4zz18-oehuaangyo2timv</string><key>Hostname</key><string>0.0.0.0</string><key>Port</key><string>40041</string><key>Username</key><string>collectionuser1</string><key>Labels</key><array></array></dict></plist>';
51
52             cy.readFile(filename, { timeout: 15000 })
53                 .then((str) => {
54                     expect(str.replaceAll(' ', '').replaceAll('\n', ''), expectedValue);
55                 })
56                 .then(() => cy.task('clearDownload', { filename }));
57         });
58     });
59
60     it('uses the property editor with vocabulary terms', function () {
61         cy.createCollection(adminUser.token, {
62             name: `Test collection ${Math.floor(Math.random() * 999999)}`,
63             owner_uuid: activeUser.user.uuid,
64             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
65         })
66             .as('testCollection').then(function () {
67                 cy.loginAs(activeUser);
68                 cy.doSearch(`${this.testCollection.uuid}`);
69
70                 // Key: Color (IDTAGCOLORS) - Value: Magenta (IDVALCOLORS3)
71                 cy.get('[data-cy=resource-properties-form]').within(() => {
72                     cy.get('[data-cy=property-field-key]').within(() => {
73                         cy.get('input').type('Color');
74                     });
75                     cy.get('[data-cy=property-field-value]').within(() => {
76                         cy.get('input').type('Magenta');
77                     });
78                     cy.root().submit();
79                 });
80                 // Confirm proper vocabulary labels are displayed on the UI.
81                 cy.get('[data-cy=collection-properties-panel]')
82                     .should('contain', 'Color')
83                     .and('contain', 'Magenta');
84                 // Confirm proper vocabulary IDs were saved on the backend.
85                 cy.doRequest('GET', `/arvados/v1/collections/${this.testCollection.uuid}`)
86                     .its('body').as('collection')
87                     .then(function () {
88                         expect(this.collection.properties).to.deep.equal(
89                             { IDTAGCOLORS: 'IDVALCOLORS3' });
90                     });
91             });
92     });
93
94     it('shows collection by URL', function () {
95         cy.loginAs(activeUser);
96         [true, false].map(function (isWritable) {
97             // Using different file names to avoid test flakyness: the second iteration
98             // on this loop may pass an assertion from the first iteration by looking
99             // for the same file name.
100             const fileName = isWritable ? 'bar' : 'foo';
101             cy.createGroup(adminUser.token, {
102                 name: 'Shared project',
103                 group_class: 'project',
104             }).as('sharedGroup').then(function () {
105                 // Creates the collection using the admin token so we can set up
106                 // a bogus manifest text without block signatures.
107                 cy.createCollection(adminUser.token, {
108                     name: 'Test collection',
109                     owner_uuid: this.sharedGroup.uuid,
110                     properties: { someKey: 'someValue' },
111                     manifest_text: `. 37b51d194a7513e45b56f6524f2d51f2+3 0:3:${fileName}\n`
112                 })
113                     .as('testCollection').then(function () {
114                         // Share the group with active user.
115                         cy.createLink(adminUser.token, {
116                             name: isWritable ? 'can_write' : 'can_read',
117                             link_class: 'permission',
118                             head_uuid: this.sharedGroup.uuid,
119                             tail_uuid: activeUser.user.uuid
120                         })
121                         cy.doSearch(`${this.testCollection.uuid}`);
122
123                         // Check that name & uuid are correct.
124                         cy.get('[data-cy=collection-info-panel]')
125                             .should('contain', this.testCollection.name)
126                             .and('contain', this.testCollection.uuid)
127                             .and('not.contain', 'This is an old version');
128                         // Check for the read-only icon
129                         cy.get('[data-cy=read-only-icon]').should(`${isWritable ? 'not.' : ''}exist`);
130                         // Check that both read and write operations are available on
131                         // the 'More options' menu.
132                         cy.get('[data-cy=collection-panel-options-btn]')
133                             .click()
134                         cy.get('[data-cy=context-menu]')
135                             .should('contain', 'Add to favorites')
136                             .and(`${isWritable ? '' : 'not.'}contain`, 'Edit collection');
137                         cy.get('body').click(); // Collapse the menu avoiding details panel expansion
138                         cy.get('[data-cy=collection-properties-panel]')
139                             .should('contain', 'someKey')
140                             .and('contain', 'someValue')
141                             .and('not.contain', 'anotherKey')
142                             .and('not.contain', 'anotherValue')
143                         if (isWritable === true) {
144                             // Check that properties can be added.
145                             cy.get('[data-cy=resource-properties-form]').within(() => {
146                                 cy.get('[data-cy=property-field-key]').within(() => {
147                                     cy.get('input').type('anotherKey');
148                                 });
149                                 cy.get('[data-cy=property-field-value]').within(() => {
150                                     cy.get('input').type('anotherValue');
151                                 });
152                                 cy.root().submit();
153                             })
154                             cy.get('[data-cy=collection-properties-panel]')
155                                 .should('contain', 'anotherKey')
156                                 .and('contain', 'anotherValue')
157                         } else {
158                             // Properties form shouldn't be displayed.
159                             cy.get('[data-cy=resource-properties-form]').should('not.exist');
160                         }
161                         // Check that the file listing show both read & write operations
162                         cy.get('[data-cy=collection-files-panel]').within(() => {
163                             cy.root().should('contain', fileName);
164                             if (isWritable) {
165                                 cy.get('[data-cy=upload-button]')
166                                     .should(`${isWritable ? '' : 'not.'}contain`, 'Upload data');
167                             }
168                         });
169                         cy.get('[data-cy=collection-files-panel]')
170                             .contains(fileName).rightclick({ force: true });
171                         cy.get('[data-cy=context-menu]')
172                             .should('contain', 'Download')
173                             .and('contain', 'Open in new tab')
174                             .and('contain', 'Copy to clipboard')
175                             .and(`${isWritable ? '' : 'not.'}contain`, 'Rename')
176                             .and(`${isWritable ? '' : 'not.'}contain`, 'Remove');
177                         cy.get('body').click(); // Collapse the menu
178                         // Hamburger 'more options' menu button
179                         cy.get('[data-cy=collection-files-panel-options-btn]')
180                             .click()
181                         cy.get('[data-cy=context-menu]')
182                             .should('contain', 'Select all')
183                             .click()
184                         cy.get('[data-cy=collection-files-panel-options-btn]')
185                             .click()
186                         cy.get('[data-cy=context-menu]')
187                             // .should('contain', 'Download selected')
188                             .should(`${isWritable ? '' : 'not.'}contain`, 'Remove selected')
189                         cy.get('body').click(); // Collapse the menu
190                         // File item 'more options' button
191                         cy.get('[data-cy=file-item-options-btn')
192                             .click()
193                         cy.get('[data-cy=context-menu]')
194                             .should('contain', 'Download')
195                             .and(`${isWritable ? '' : 'not.'}contain`, 'Remove');
196                         cy.get('body').click(); // Collapse the menu
197                     })
198             })
199         })
200     })
201
202     it('renames a file using valid names', function () {
203         function eachPair(lst, func){
204             for(var i=0; i < lst.length - 1; i++){
205                 func(lst[i], lst[i + 1])
206             }
207         }
208         // Creates the collection using the admin token so we can set up
209         // a bogus manifest text without block signatures.
210         cy.createCollection(adminUser.token, {
211             name: `Test collection ${Math.floor(Math.random() * 999999)}`,
212             owner_uuid: activeUser.user.uuid,
213             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
214         })
215             .as('testCollection').then(function () {
216                 cy.loginAs(activeUser);
217                 cy.doSearch(`${this.testCollection.uuid}`);
218
219                 const names = [
220                     'bar', // initial name already set
221                     '&',
222                     'foo',
223                     '&amp;',
224                     'I ❤️ ⛵️',
225                     '...',
226                     '#..',
227                     'some name with whitespaces',
228                     'some name with #2',
229                     'is this name legal? I hope it is',
230                     'some_file.pdf#',
231                     'some_file.pdf?',
232                     '?some_file.pdf',
233                     'some%file.pdf',
234                     'some%2Ffile.pdf',
235                     'some%22file.pdf',
236                     'some%20file.pdf',
237                     "G%C3%BCnter's%20file.pdf",
238                     'table%&?*2',
239                     'bar' // make sure we can go back to the original name as a last step
240                 ];
241                 eachPair(names, (from, to) => {
242                     cy.get('[data-cy=collection-files-panel]')
243                         .contains(`${from}`).rightclick();
244                     cy.get('[data-cy=context-menu]')
245                         .contains('Rename')
246                         .click();
247                     cy.get('[data-cy=form-dialog]')
248                         .should('contain', 'Rename')
249                         .within(() => {
250                             cy.get('input').type(`{selectall}{backspace}${to}`);
251                         });
252                     cy.get('[data-cy=form-submit-btn]').click();
253                     cy.get('[data-cy=collection-files-panel]')
254                         .should('not.contain', `${from}`)
255                         .and('contain', `${to}`);
256                 })
257             });
258     });
259
260     it('renames a file to a different directory', function () {
261         // Creates the collection using the admin token so we can set up
262         // a bogus manifest text without block signatures.
263         cy.createCollection(adminUser.token, {
264             name: `Test collection ${Math.floor(Math.random() * 999999)}`,
265             owner_uuid: activeUser.user.uuid,
266             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
267         })
268             .as('testCollection').then(function () {
269                 cy.loginAs(activeUser);
270                 cy.doSearch(`${this.testCollection.uuid}`);
271
272                 ['subdir', 'G%C3%BCnter\'s%20file', 'table%&?*2'].forEach((subdir) => {
273                     cy.get('[data-cy=collection-files-panel]')
274                         .contains('bar').rightclick({force: true});
275                     cy.get('[data-cy=context-menu]')
276                         .contains('Rename')
277                         .click();
278                     cy.get('[data-cy=form-dialog]')
279                         .should('contain', 'Rename')
280                         .within(() => {
281                             cy.get('input').type(`{selectall}{backspace}${subdir}/foo`);
282                         });
283                     cy.get('[data-cy=form-submit-btn]').click();
284                     cy.get('[data-cy=collection-files-panel]')
285                         .should('not.contain', 'bar')
286                         .and('contain', subdir);
287                     // Look for the "arrow icon" and expand the "subdir" directory.
288                     cy.get('[data-cy=virtual-file-tree] > div > i').click();
289                     // Rename 'subdir/foo' to 'foo'
290                     cy.get('[data-cy=collection-files-panel]')
291                         .contains('foo').rightclick();
292                     cy.get('[data-cy=context-menu]')
293                         .contains('Rename')
294                         .click();
295                     cy.get('[data-cy=form-dialog]')
296                         .should('contain', 'Rename')
297                         .within(() => {
298                             cy.get('input')
299                                 .should('have.value', `${subdir}/foo`)
300                                 .type(`{selectall}{backspace}bar`);
301                         });
302                     cy.get('[data-cy=form-submit-btn]').click();
303                     cy.get('[data-cy=collection-files-panel]')
304                         .should('contain', subdir) // empty dir kept
305                         .and('contain', 'bar');
306
307                     cy.get('[data-cy=collection-files-panel]')
308                         .contains(subdir).rightclick();
309                     cy.get('[data-cy=context-menu]')
310                         .contains('Remove')
311                         .click();
312                     cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
313                 });
314             });
315     });
316
317     it('tries to rename a file with illegal names', function () {
318         // Creates the collection using the admin token so we can set up
319         // a bogus manifest text without block signatures.
320         cy.createCollection(adminUser.token, {
321             name: `Test collection ${Math.floor(Math.random() * 999999)}`,
322             owner_uuid: activeUser.user.uuid,
323             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
324         })
325             .as('testCollection').then(function () {
326                 cy.loginAs(activeUser);
327                 cy.doSearch(`${this.testCollection.uuid}`);
328
329                 const illegalNamesFromUI = [
330                     ['.', "Name cannot be '.' or '..'"],
331                     ['..', "Name cannot be '.' or '..'"],
332                     ['', 'This field is required'],
333                     [' ', 'Leading/trailing whitespaces not allowed'],
334                     [' foo', 'Leading/trailing whitespaces not allowed'],
335                     ['foo ', 'Leading/trailing whitespaces not allowed'],
336                     ['//foo', 'Empty dir name not allowed']
337                 ]
338                 illegalNamesFromUI.forEach(([name, errMsg]) => {
339                     cy.get('[data-cy=collection-files-panel]')
340                         .contains('bar').rightclick();
341                     cy.get('[data-cy=context-menu]')
342                         .contains('Rename')
343                         .click();
344                     cy.get('[data-cy=form-dialog]')
345                         .should('contain', 'Rename')
346                         .within(() => {
347                             cy.get('input').type(`{selectall}{backspace}${name}`);
348                         });
349                     cy.get('[data-cy=form-dialog]')
350                         .should('contain', 'Rename')
351                         .within(() => {
352                             cy.contains(`${errMsg}`);
353                         });
354                     cy.get('[data-cy=form-cancel-btn]').click();
355                 })
356             });
357     });
358
359     it('can correctly display old versions', function () {
360         const colName = `Versioned Collection ${Math.floor(Math.random() * 999999)}`;
361         let colUuid = '';
362         let oldVersionUuid = '';
363         // Make sure no other collections with this name exist
364         cy.doRequest('GET', '/arvados/v1/collections', null, {
365             filters: `[["name", "=", "${colName}"]]`,
366             include_old_versions: true
367         })
368             .its('body.items').as('collections')
369             .then(function () {
370                 expect(this.collections).to.be.empty;
371             });
372         // Creates the collection using the admin token so we can set up
373         // a bogus manifest text without block signatures.
374         cy.createCollection(adminUser.token, {
375             name: colName,
376             owner_uuid: activeUser.user.uuid,
377             preserve_version: true,
378             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
379         })
380             .as('originalVersion').then(function () {
381                 // Change the file name to create a new version.
382                 cy.updateCollection(adminUser.token, this.originalVersion.uuid, {
383                     manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo\n"
384                 })
385                 colUuid = this.originalVersion.uuid;
386             });
387         // Confirm that there are 2 versions of the collection
388         cy.doRequest('GET', '/arvados/v1/collections', null, {
389             filters: `[["name", "=", "${colName}"]]`,
390             include_old_versions: true
391         })
392             .its('body.items').as('collections')
393             .then(function () {
394                 expect(this.collections).to.have.lengthOf(2);
395                 this.collections.map(function (aCollection) {
396                     expect(aCollection.current_version_uuid).to.equal(colUuid);
397                     if (aCollection.uuid !== aCollection.current_version_uuid) {
398                         oldVersionUuid = aCollection.uuid;
399                     }
400                 });
401                 // Check the old version displays as what it is.
402                 cy.loginAs(activeUser)
403                 cy.doSearch(`${oldVersionUuid}`);
404
405                 cy.get('[data-cy=collection-info-panel]').should('contain', 'This is an old version');
406                 cy.get('[data-cy=read-only-icon]').should('exist');
407                 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
408                 cy.get('[data-cy=collection-files-panel]').should('contain', 'bar');
409             });
410     });
411
412     it('uses the collection version browser to view a previous version', function () {
413         const colName = `Test Collection ${Math.floor(Math.random() * 999999)}`;
414
415         // Creates the collection using the admin token so we can set up
416         // a bogus manifest text without block signatures.
417         cy.createCollection(adminUser.token, {
418             name: colName,
419             owner_uuid: activeUser.user.uuid,
420             preserve_version: true,
421             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n"
422         })
423             .as('collection').then(function () {
424                 // Visit collection, check basic information
425                 cy.loginAs(activeUser)
426                 cy.doSearch(`${this.collection.uuid}`);
427
428                 cy.get('[data-cy=collection-info-panel]').should('not.contain', 'This is an old version');
429                 cy.get('[data-cy=read-only-icon]').should('not.exist');
430                 cy.get('[data-cy=collection-version-number]').should('contain', '1');
431                 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
432                 cy.get('[data-cy=collection-files-panel]').should('contain', 'foo').and('contain', 'bar');
433
434                 // Modify collection, expect version number change
435                 cy.get('[data-cy=collection-files-panel]').contains('foo').rightclick();
436                 cy.get('[data-cy=context-menu]').contains('Remove').click();
437                 cy.get('[data-cy=confirmation-dialog]').should('contain', 'Removing file');
438                 cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
439                 cy.get('[data-cy=collection-version-number]').should('contain', '2');
440                 cy.get('[data-cy=collection-files-panel]').should('not.contain', 'foo').and('contain', 'bar');
441
442                 // Click on version number, check version browser. Click on past version.
443                 cy.get('[data-cy=collection-version-browser]').should('not.exist');
444                 cy.get('[data-cy=collection-version-number]').contains('2').click();
445                 cy.get('[data-cy=collection-version-browser]')
446                     .should('contain', 'Nr').and('contain', 'Size').and('contain', 'Date')
447                     .within(() => {
448                         // Version 1: 6 bytes in size
449                         cy.get('[data-cy=collection-version-browser-select-1]')
450                             .should('contain', '1').and('contain', '6 B');
451                         // Version 2: 3 bytes in size (one file removed)
452                         cy.get('[data-cy=collection-version-browser-select-2]')
453                             .should('contain', '2').and('contain', '3 B');
454                         cy.get('[data-cy=collection-version-browser-select-3]')
455                             .should('not.exist');
456                         cy.get('[data-cy=collection-version-browser-select-1]')
457                             .click();
458                     });
459                 cy.get('[data-cy=collection-info-panel]').should('contain', 'This is an old version');
460                 cy.get('[data-cy=read-only-icon]').should('exist');
461                 cy.get('[data-cy=collection-version-number]').should('contain', '1');
462                 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
463                 cy.get('[data-cy=collection-files-panel]')
464                     .should('contain', 'foo').and('contain', 'bar');
465
466                 // Check that only old collection action are available on context menu
467                 cy.get('[data-cy=collection-panel-options-btn]').click();
468                 cy.get('[data-cy=context-menu]')
469                     .should('contain', 'Restore version')
470                     .and('not.contain', 'Add to favorites');
471                 cy.get('body').click(); // Collapse the menu avoiding details panel expansion
472
473                 // Click on "head version" link, confirm that it's the latest version.
474                 cy.get('[data-cy=collection-info-panel]').contains('head version').click();
475                 cy.get('[data-cy=collection-info-panel]')
476                     .should('not.contain', 'This is an old version');
477                 cy.get('[data-cy=read-only-icon]').should('not.exist');
478                 cy.get('[data-cy=collection-version-number]').should('contain', '2');
479                 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
480                 cy.get('[data-cy=collection-files-panel]').
481                     should('not.contain', 'foo').and('contain', 'bar');
482
483                 // Check that old collection action isn't available on context menu
484                 cy.get('[data-cy=collection-panel-options-btn]').click()
485                 cy.get('[data-cy=context-menu]').should('not.contain', 'Restore version')
486                 cy.get('body').click(); // Collapse the menu avoiding details panel expansion
487
488                 // Make another change, confirm new version.
489                 cy.get('[data-cy=collection-panel-options-btn]').click();
490                 cy.get('[data-cy=context-menu]').contains('Edit collection').click();
491                 cy.get('[data-cy=form-dialog]')
492                     .should('contain', 'Edit Collection')
493                     .within(() => {
494                         // appends some text
495                         cy.get('input').first().type(' renamed');
496                     });
497                 cy.get('[data-cy=form-submit-btn]').click();
498                 cy.get('[data-cy=collection-info-panel]')
499                     .should('not.contain', 'This is an old version');
500                 cy.get('[data-cy=read-only-icon]').should('not.exist');
501                 cy.get('[data-cy=collection-version-number]').should('contain', '3');
502                 cy.get('[data-cy=collection-info-panel]').should('contain', colName + ' renamed');
503                 cy.get('[data-cy=collection-files-panel]')
504                     .should('not.contain', 'foo').and('contain', 'bar');
505                 cy.get('[data-cy=collection-version-browser-select-3]')
506                     .should('contain', '3').and('contain', '3 B');
507
508                 // Check context menus on version browser
509                 cy.get('[data-cy=collection-version-browser-select-3]').rightclick()
510                 cy.get('[data-cy=context-menu]')
511                     .should('contain', 'Add to favorites')
512                     .and('contain', 'Make a copy')
513                     .and('contain', 'Edit collection');
514                 cy.get('body').click();
515                 // (and now an old version...)
516                 cy.get('[data-cy=collection-version-browser-select-1]').rightclick()
517                 cy.get('[data-cy=context-menu]')
518                     .should('not.contain', 'Add to favorites')
519                     .and('contain', 'Make a copy')
520                     .and('not.contain', 'Edit collection');
521                 cy.get('body').click();
522
523                 // Restore first version
524                 cy.get('[data-cy=collection-version-browser]').within(() => {
525                     cy.get('[data-cy=collection-version-browser-select-1]').click();
526                 });
527                 cy.get('[data-cy=collection-panel-options-btn]').click()
528                 cy.get('[data-cy=context-menu]').contains('Restore version').click();
529                 cy.get('[data-cy=confirmation-dialog]').should('contain', 'Restore version');
530                 cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
531                 cy.get('[data-cy=collection-info-panel]')
532                     .should('not.contain', 'This is an old version');
533                 cy.get('[data-cy=collection-version-number]').should('contain', '4');
534                 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
535                 cy.get('[data-cy=collection-files-panel]')
536                     .should('contain', 'foo').and('contain', 'bar');
537             });
538     });
539
540     it('creates new collection on home project', function () {
541         cy.loginAs(activeUser);
542         cy.doSearch(`${activeUser.user.uuid}`);
543         cy.get('[data-cy=breadcrumb-first]').should('contain', 'Projects');
544         cy.get('[data-cy=breadcrumb-last]').should('not.exist');
545         // Create new collection
546         cy.get('[data-cy=side-panel-button]').click();
547         cy.get('[data-cy=side-panel-new-collection]').click();
548         const collName = `Test collection (${Math.floor(999999 * Math.random())})`;
549         cy.get('[data-cy=form-dialog]')
550             .should('contain', 'New collection')
551             .within(() => {
552                 cy.get('[data-cy=parent-field]').within(() => {
553                     cy.get('input').should('have.value', 'Home project');
554                 })
555                 cy.get('[data-cy=name-field]').within(() => {
556                     cy.get('input').type(collName);
557                 })
558             })
559         cy.get('[data-cy=form-submit-btn]').click();
560         // Confirm that the user was taken to the newly created thing
561         cy.get('[data-cy=form-dialog]').should('not.exist');
562         cy.get('[data-cy=breadcrumb-first]').should('contain', 'Projects');
563         cy.get('[data-cy=breadcrumb-last]').should('contain', collName);
564     });
565 })