Merge branch '15557-rerun-workflow' into main. Closes #15557
[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.goToPath(`/collections/${testCollection.uuid}`);
44
45             cy.get('[data-cy=collection-panel-options-btn]').click();
46             cy.get('[data-cy=context-menu]').contains('Open with 3rd party client').click();
47             cy.get('[data-cy=download-button').click();
48
49             const filename = path.join(downloadsFolder, `${testCollection.name}.duck`);
50
51             cy.readFile(filename, { timeout: 15000 })
52                 .then((body) => {
53                     const childrenCollection = Array.prototype.slice.call(Cypress.$(body).find('dict')[0].children);
54                     const map = {};
55                     let i, j = 2;
56
57                     for (i=0; i < childrenCollection.length; i += j) {
58                       map[childrenCollection[i].outerText] = childrenCollection[i + 1].outerText;
59                     }
60
61                     cy.get('#simple-tabpanel-0').find('a')
62                         .then((a) => {
63                             const [host, port] = a.text().split('@')[1].split('/')[0].split(':');
64                             expect(map['Protocol']).to.equal('davs');
65                             expect(map['UUID']).to.equal(testCollection.uuid);
66                             expect(map['Username']).to.equal(activeUser.user.username);
67                             expect(map['Port']).to.equal(port);
68                             expect(map['Hostname']).to.equal(host);
69                             if (map['Path']) {
70                                 expect(map['Path']).to.equal(`/c=${testCollection.uuid}`);
71                             }
72                         });
73                 })
74                 .then(() => cy.task('clearDownload', { filename }));
75         });
76     });
77
78     it('attempts to use a preexisting name creating or updating a collection', function() {
79         const name = `Test collection ${Math.floor(Math.random() * 999999)}`;
80         cy.createCollection(adminUser.token, {
81             name: name,
82             owner_uuid: activeUser.user.uuid,
83             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
84         });
85         cy.loginAs(activeUser);
86         cy.goToPath(`/projects/${activeUser.user.uuid}`);
87         cy.get('[data-cy=breadcrumb-first]').should('contain', 'Projects');
88         cy.get('[data-cy=breadcrumb-last]').should('not.exist');
89         // Attempt to create new collection with a duplicate name
90         cy.get('[data-cy=side-panel-button]').click();
91         cy.get('[data-cy=side-panel-new-collection]').click();
92         cy.get('[data-cy=form-dialog]')
93             .should('contain', 'New collection')
94             .within(() => {
95                 cy.get('[data-cy=name-field]').within(() => {
96                     cy.get('input').type(name);
97                 });
98                 cy.get('[data-cy=form-submit-btn]').click();
99             });
100         // Error message should display, allowing editing the name
101         cy.get('[data-cy=form-dialog]').should('exist')
102             .and('contain', 'Collection with the same name already exists')
103             .within(() => {
104                 cy.get('[data-cy=name-field]').within(() => {
105                     cy.get('input').type(' renamed');
106                 });
107                 cy.get('[data-cy=form-submit-btn]').click();
108             });
109         cy.get('[data-cy=form-dialog]').should('not.exist');
110         // Attempt to rename the collection with the duplicate name
111         cy.get('[data-cy=collection-panel-options-btn]').click();
112         cy.get('[data-cy=context-menu]').contains('Edit collection').click();
113         cy.get('[data-cy=form-dialog]')
114             .should('contain', 'Edit Collection')
115             .within(() => {
116                 cy.get('[data-cy=name-field]').within(() => {
117                     cy.get('input')
118                         .type('{selectall}{backspace}')
119                         .type(name);
120                 });
121                 cy.get('[data-cy=form-submit-btn]').click();
122             });
123         cy.get('[data-cy=form-dialog]').should('exist')
124             .and('contain', 'Collection with the same name already exists');
125     });
126
127     it('uses the property editor (from edit dialog) with vocabulary terms', function () {
128         cy.createCollection(adminUser.token, {
129             name: `Test collection ${Math.floor(Math.random() * 999999)}`,
130             owner_uuid: activeUser.user.uuid,
131             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
132         })
133             .as('testCollection').then(function () {
134                 cy.loginAs(activeUser);
135                 cy.goToPath(`/collections/${this.testCollection.uuid}`);
136
137                 cy.get('[data-cy=collection-info-panel')
138                     .should('contain', this.testCollection.name)
139                     .and('not.contain', 'Color: Magenta');
140
141                 cy.get('[data-cy=collection-panel-options-btn]').click();
142                 cy.get('[data-cy=context-menu]').contains('Edit collection').click();
143                 cy.get('[data-cy=form-dialog]').should('contain', 'Properties');
144
145                 // Key: Color (IDTAGCOLORS) - Value: Magenta (IDVALCOLORS3)
146                 cy.get('[data-cy=resource-properties-form]').within(() => {
147                     cy.get('[data-cy=property-field-key]').within(() => {
148                         cy.get('input').type('Color');
149                     });
150                     cy.get('[data-cy=property-field-value]').within(() => {
151                         cy.get('input').type('Magenta');
152                     });
153                     cy.root().submit();
154                 });
155                 // Confirm proper vocabulary labels are displayed on the UI.
156                 cy.get('[data-cy=form-dialog]').should('contain', 'Color: Magenta');
157                 cy.get('[data-cy=form-dialog]').contains('Save').click();
158                 cy.get('[data-cy=form-dialog]').should('not.exist');
159                 // Confirm proper vocabulary IDs were saved on the backend.
160                 cy.doRequest('GET', `/arvados/v1/collections/${this.testCollection.uuid}`)
161                     .its('body').as('collection')
162                     .then(function () {
163                         expect(this.collection.properties.IDTAGCOLORS).to.equal('IDVALCOLORS3');
164                     });
165                 // Confirm the property is displayed on the UI.
166                 cy.get('[data-cy=collection-info-panel')
167                     .should('contain', this.testCollection.name)
168                     .and('contain', 'Color: Magenta');
169             });
170     });
171
172     it('uses the editor (from details panel) with vocabulary terms', function () {
173         cy.createCollection(adminUser.token, {
174             name: `Test collection ${Math.floor(Math.random() * 999999)}`,
175             owner_uuid: activeUser.user.uuid,
176             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
177         })
178             .as('testCollection').then(function () {
179                 cy.loginAs(activeUser);
180                 cy.goToPath(`/collections/${this.testCollection.uuid}`);
181
182                 cy.get('[data-cy=collection-info-panel')
183                     .should('contain', this.testCollection.name)
184                     .and('not.contain', 'Color: Magenta')
185                     .and('not.contain', 'Size: S');
186                 cy.get('[data-cy=additional-info-icon]').click();
187
188                 cy.get('[data-cy=details-panel]').within(() => {
189                     cy.get('[data-cy=details-panel-edit-btn]').click();
190                 });
191                 cy.get('[data-cy=form-dialog').contains('Edit Collection');
192
193                 // Key: Color (IDTAGCOLORS) - Value: Magenta (IDVALCOLORS3)
194                 cy.get('[data-cy=resource-properties-form]').within(() => {
195                     cy.get('[data-cy=property-field-key]').within(() => {
196                         cy.get('input').type('Color');
197                     });
198                     cy.get('[data-cy=property-field-value]').within(() => {
199                         cy.get('input').type('Magenta');
200                     });
201                     cy.root().submit();
202                 });
203                 // Confirm proper vocabulary labels are displayed on the UI.
204                 cy.get('[data-cy=form-dialog]')
205                     .should('contain', 'Color: Magenta');
206
207                 // Case-insensitive on-blur auto-selection test
208                 // Key: Size (IDTAGSIZES) - Value: Small (IDVALSIZES2)
209                 cy.get('[data-cy=resource-properties-form]').within(() => {
210                     cy.get('[data-cy=property-field-key]').within(() => {
211                         cy.get('input').type('sIzE');
212                     });
213                     cy.get('[data-cy=property-field-value]').within(() => {
214                         cy.get('input').type('sMaLL');
215                     });
216                     // Cannot "type()" TAB on Cypress so let's click another field
217                     // to trigger the onBlur event.
218                     cy.get('[data-cy=property-field-key]').click();
219                     cy.root().submit();
220                 });
221                 // Confirm proper vocabulary labels are displayed on the UI.
222                 cy.get('[data-cy=form-dialog]')
223                     .should('contain', 'Size: S');
224
225                 cy.get('[data-cy=form-dialog]').contains('Save').click();
226                 cy.get('[data-cy=form-dialog]').should('not.exist');
227
228                 // Confirm proper vocabulary IDs were saved on the backend.
229                 cy.doRequest('GET', `/arvados/v1/collections/${this.testCollection.uuid}`)
230                     .its('body').as('collection')
231                     .then(function () {
232                         expect(this.collection.properties.IDTAGCOLORS).to.equal('IDVALCOLORS3');
233                         expect(this.collection.properties.IDTAGSIZES).to.equal('IDVALSIZES2');
234                     });
235
236                 // Confirm properties display on the UI.
237                 cy.get('[data-cy=collection-info-panel')
238                     .should('contain', this.testCollection.name)
239                     .and('contain', 'Color: Magenta')
240                     .and('contain', 'Size: S');
241             });
242     });
243
244     it('shows collection by URL', function () {
245         cy.loginAs(activeUser);
246         [true, false].map(function (isWritable) {
247             // Using different file names to avoid test flakyness: the second iteration
248             // on this loop may pass an assertion from the first iteration by looking
249             // for the same file name.
250             const fileName = isWritable ? 'bar' : 'foo';
251             const subDirName = 'subdir';
252             cy.createGroup(adminUser.token, {
253                 name: 'Shared project',
254                 group_class: 'project',
255             }).as('sharedGroup').then(function () {
256                 // Creates the collection using the admin token so we can set up
257                 // a bogus manifest text without block signatures.
258                 cy.doRequest('GET', '/arvados/v1/config', null, null)
259                     .its('body').should((clusterConfig) => {
260                       expect(clusterConfig.Collections, "clusterConfig").to.have.property("TrustAllContent", true);
261                       expect(clusterConfig.Services, "clusterConfig").to.have.property("WebDAV").have.property("ExternalURL");
262                       expect(clusterConfig.Services, "clusterConfig").to.have.property("WebDAVDownload").have.property("ExternalURL");
263                       const inlineUrl = clusterConfig.Services.WebDAV.ExternalURL !== ""
264                           ? clusterConfig.Services.WebDAV.ExternalURL
265                           : clusterConfig.Services.WebDAVDownload.ExternalURL;
266                       expect(inlineUrl).to.not.contain("*");
267                     })
268                     .createCollection(adminUser.token, {
269                       name: 'Test collection',
270                       owner_uuid: this.sharedGroup.uuid,
271                       properties: { someKey: 'someValue' },
272                       manifest_text: `. 37b51d194a7513e45b56f6524f2d51f2+3 0:3:${fileName}\n./${subDirName} 37b51d194a7513e45b56f6524f2d51f2+3 0:3:${fileName}\n`
273                     })
274                     .as('testCollection').then(function () {
275                         // Share the group with active user.
276                         cy.createLink(adminUser.token, {
277                             name: isWritable ? 'can_write' : 'can_read',
278                             link_class: 'permission',
279                             head_uuid: this.sharedGroup.uuid,
280                             tail_uuid: activeUser.user.uuid
281                         })
282                         cy.goToPath(`/collections/${this.testCollection.uuid}`);
283
284                         // Check that name & uuid are correct.
285                         cy.get('[data-cy=collection-info-panel]')
286                             .should('contain', this.testCollection.name)
287                             .and('contain', this.testCollection.uuid)
288                             .and('not.contain', 'This is an old version');
289                         // Check for the read-only icon
290                         cy.get('[data-cy=read-only-icon]').should(`${isWritable ? 'not.' : ''}exist`);
291                         // Check that both read and write operations are available on
292                         // the 'More options' menu.
293                         cy.get('[data-cy=collection-panel-options-btn]')
294                             .click()
295                         cy.get('[data-cy=context-menu]')
296                             .should('contain', 'Add to favorites')
297                             .and(`${isWritable ? '' : 'not.'}contain`, 'Edit collection');
298                         cy.get('body').click(); // Collapse the menu avoiding details panel expansion
299                         cy.get('[data-cy=collection-info-panel]')
300                             .should('contain', 'someKey: someValue')
301                             .and('not.contain', 'anotherKey: anotherValue');
302                         // Check that the file listing show both read & write operations
303                         cy.waitForDom().get('[data-cy=collection-files-panel]').within(() => {
304                             cy.get('[data-cy=collection-files-right-panel]', { timeout: 5000 })
305                                 .should('contain', fileName);
306                             if (isWritable) {
307                                 cy.get('[data-cy=upload-button]')
308                                     .should(`${isWritable ? '' : 'not.'}contain`, 'Upload data');
309                             }
310                         });
311                         // Test context menus
312                         cy.get('[data-cy=collection-files-panel]')
313                             .contains(fileName).rightclick();
314                         cy.get('[data-cy=context-menu]')
315                             .should('contain', 'Download')
316                             .and('contain', 'Open in new tab')
317                             .and('contain', 'Copy to clipboard')
318                             .and(`${isWritable ? '' : 'not.'}contain`, 'Rename')
319                             .and(`${isWritable ? '' : 'not.'}contain`, 'Remove');
320                         cy.get('body').click(); // Collapse the menu
321                         cy.get('[data-cy=collection-files-panel]')
322                             .contains(subDirName).rightclick();
323                         cy.get('[data-cy=context-menu]')
324                             .should('not.contain', 'Download')
325                             .and('contain', 'Open in new tab')
326                             .and('contain', 'Copy to clipboard')
327                             .and(`${isWritable ? '' : 'not.'}contain`, 'Rename')
328                             .and(`${isWritable ? '' : 'not.'}contain`, 'Remove');
329                         cy.get('body').click(); // Collapse the menu
330                         // File/dir item 'more options' button
331                         cy.get('[data-cy=file-item-options-btn')
332                             .first()
333                             .click()
334                         cy.get('[data-cy=context-menu]')
335                             .should(`${isWritable ? '' : 'not.'}contain`, 'Remove');
336                         cy.get('body').click(); // Collapse the menu
337                         // Hamburger 'more options' menu button
338                         cy.get('[data-cy=collection-files-panel-options-btn]')
339                             .click()
340                         cy.get('[data-cy=context-menu]')
341                             .should('contain', 'Select all')
342                             .click()
343                         cy.get('[data-cy=collection-files-panel-options-btn]')
344                             .click()
345                         cy.get('[data-cy=context-menu]')
346                             .should(`${isWritable ? '' : 'not.'}contain`, 'Remove selected')
347                         cy.get('body').click(); // Collapse the menu
348                     })
349             })
350         })
351     })
352
353     it('renames a file using valid names', function () {
354         function eachPair(lst, func){
355             for(var i=0; i < lst.length - 1; i++){
356                 func(lst[i], lst[i + 1])
357             }
358         }
359         // Creates the collection using the admin token so we can set up
360         // a bogus manifest text without block signatures.
361         cy.createCollection(adminUser.token, {
362             name: `Test collection ${Math.floor(Math.random() * 999999)}`,
363             owner_uuid: activeUser.user.uuid,
364             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
365         })
366             .as('testCollection').then(function () {
367                 cy.loginAs(activeUser);
368                 cy.goToPath(`/collections/${this.testCollection.uuid}`);
369
370                 const names = [
371                     'bar', // initial name already set
372                     '&',
373                     'foo',
374                     '&amp;',
375                     'I ❤️ ⛵️',
376                     '...',
377                     '#..',
378                     'some name with whitespaces',
379                     'some name with #2',
380                     'is this name legal? I hope it is',
381                     'some_file.pdf#',
382                     'some_file.pdf?',
383                     '?some_file.pdf',
384                     'some%file.pdf',
385                     'some%2Ffile.pdf',
386                     'some%22file.pdf',
387                     'some%20file.pdf',
388                     "G%C3%BCnter's%20file.pdf",
389                     'table%&?*2',
390                     'bar' // make sure we can go back to the original name as a last step
391                 ];
392                 eachPair(names, (from, to) => {
393                     cy.waitForDom().get('[data-cy=collection-files-panel]')
394                         .contains(`${from}`).rightclick();
395                     cy.get('[data-cy=context-menu]')
396                         .contains('Rename')
397                         .click();
398                     cy.get('[data-cy=form-dialog]')
399                         .should('contain', 'Rename')
400                         .within(() => {
401                             cy.get('input')
402                                 .type('{selectall}{backspace}')
403                                 .type(to, { parseSpecialCharSequences: false });
404                         });
405                     cy.get('[data-cy=form-submit-btn]').click();
406                     cy.get('[data-cy=collection-files-panel]')
407                         .should('not.contain', `${from}`)
408                         .and('contain', `${to}`);
409                 })
410             });
411     });
412
413     it('renames a file to a different directory', function () {
414         // Creates the collection using the admin token so we can set up
415         // a bogus manifest text without block signatures.
416         cy.createCollection(adminUser.token, {
417             name: `Test collection ${Math.floor(Math.random() * 999999)}`,
418             owner_uuid: activeUser.user.uuid,
419             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
420         })
421             .as('testCollection').then(function () {
422                 cy.loginAs(activeUser);
423                 cy.goToPath(`/collections/${this.testCollection.uuid}`);
424
425                 ['subdir', 'G%C3%BCnter\'s%20file', 'table%&?*2'].forEach((subdir) => {
426                     cy.waitForDom().get('[data-cy=collection-files-panel]')
427                         .contains('bar').rightclick();
428                     cy.get('[data-cy=context-menu]')
429                         .contains('Rename')
430                         .click();
431                     cy.get('[data-cy=form-dialog]')
432                         .should('contain', 'Rename')
433                         .within(() => {
434                             cy.get('input').type(`{selectall}{backspace}${subdir}/foo`);
435                         });
436                     cy.get('[data-cy=form-submit-btn]').click();
437                     cy.get('[data-cy=collection-files-panel]')
438                         .should('not.contain', 'bar')
439                         .and('contain', subdir);
440                     cy.get('[data-cy=collection-files-panel]').contains(subdir).click();
441
442                     // Rename 'subdir/foo' to 'bar'
443                     cy.wait(1000);
444                     cy.get('[data-cy=collection-files-panel]')
445                         .contains('foo').rightclick();
446                     cy.get('[data-cy=context-menu]')
447                         .contains('Rename')
448                         .click();
449                     cy.get('[data-cy=form-dialog]')
450                         .should('contain', 'Rename')
451                         .within(() => {
452                             cy.get('input')
453                                 .should('have.value', `${subdir}/foo`)
454                                 .type(`{selectall}{backspace}bar`);
455                         });
456                     cy.get('[data-cy=form-submit-btn]').click();
457
458                     cy.get('[data-cy=collection-files-panel]')
459                         .contains('Home')
460                         .click();
461
462                     cy.wait(2000);
463                     cy.get('[data-cy=collection-files-panel]')
464                         .should('contain', subdir) // empty dir kept
465                         .and('contain', 'bar');
466
467                     cy.get('[data-cy=collection-files-panel]')
468                         .contains(subdir).rightclick();
469                     cy.get('[data-cy=context-menu]')
470                         .contains('Remove')
471                         .click();
472                     cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
473                 });
474             });
475     });
476
477     it('shows collection owner', () => {
478         cy.createCollection(adminUser.token, {
479             name: `Test collection ${Math.floor(Math.random() * 999999)}`,
480             owner_uuid: activeUser.user.uuid,
481             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
482         })
483             .as('testCollection').then((testCollection) => {
484                 cy.loginAs(activeUser);
485                 cy.goToPath(`/collections/${testCollection.uuid}`);
486                 cy.wait(5000);
487                 cy.get('[data-cy=collection-info-panel]').contains(`Collection User`);
488             });
489     });
490
491     it('tries to rename a file with illegal names', function () {
492         // Creates the collection using the admin token so we can set up
493         // a bogus manifest text without block signatures.
494         cy.createCollection(adminUser.token, {
495             name: `Test collection ${Math.floor(Math.random() * 999999)}`,
496             owner_uuid: activeUser.user.uuid,
497             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
498         })
499             .as('testCollection').then(function () {
500                 cy.loginAs(activeUser);
501                 cy.goToPath(`/collections/${this.testCollection.uuid}`);
502
503                 const illegalNamesFromUI = [
504                     ['.', "Name cannot be '.' or '..'"],
505                     ['..', "Name cannot be '.' or '..'"],
506                     ['', 'This field is required'],
507                     [' ', 'Leading/trailing whitespaces not allowed'],
508                     [' foo', 'Leading/trailing whitespaces not allowed'],
509                     ['foo ', 'Leading/trailing whitespaces not allowed'],
510                     ['//foo', 'Empty dir name not allowed']
511                 ]
512                 illegalNamesFromUI.forEach(([name, errMsg]) => {
513                     cy.get('[data-cy=collection-files-panel]')
514                         .contains('bar').rightclick();
515                     cy.get('[data-cy=context-menu]')
516                         .contains('Rename')
517                         .click();
518                     cy.get('[data-cy=form-dialog]')
519                         .should('contain', 'Rename')
520                         .within(() => {
521                             cy.get('input').type(`{selectall}{backspace}${name}`);
522                         });
523                     cy.get('[data-cy=form-dialog]')
524                         .should('contain', 'Rename')
525                         .within(() => {
526                             cy.contains(`${errMsg}`);
527                         });
528                     cy.get('[data-cy=form-cancel-btn]').click();
529                 })
530             });
531     });
532
533     it('can correctly display old versions', function () {
534         const colName = `Versioned Collection ${Math.floor(Math.random() * 999999)}`;
535         let colUuid = '';
536         let oldVersionUuid = '';
537         // Make sure no other collections with this name exist
538         cy.doRequest('GET', '/arvados/v1/collections', null, {
539             filters: `[["name", "=", "${colName}"]]`,
540             include_old_versions: true
541         })
542             .its('body.items').as('collections')
543             .then(function () {
544                 expect(this.collections).to.be.empty;
545             });
546         // Creates the collection using the admin token so we can set up
547         // a bogus manifest text without block signatures.
548         cy.createCollection(adminUser.token, {
549             name: colName,
550             owner_uuid: activeUser.user.uuid,
551             preserve_version: true,
552             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
553         })
554             .as('originalVersion').then(function () {
555                 // Change the file name to create a new version.
556                 cy.updateCollection(adminUser.token, this.originalVersion.uuid, {
557                     manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo\n"
558                 })
559                 colUuid = this.originalVersion.uuid;
560             });
561         // Confirm that there are 2 versions of the collection
562         cy.doRequest('GET', '/arvados/v1/collections', null, {
563             filters: `[["name", "=", "${colName}"]]`,
564             include_old_versions: true
565         })
566             .its('body.items').as('collections')
567             .then(function () {
568                 expect(this.collections).to.have.lengthOf(2);
569                 this.collections.map(function (aCollection) {
570                     expect(aCollection.current_version_uuid).to.equal(colUuid);
571                     if (aCollection.uuid !== aCollection.current_version_uuid) {
572                         oldVersionUuid = aCollection.uuid;
573                     }
574                 });
575                 // Check the old version displays as what it is.
576                 cy.loginAs(activeUser)
577                 cy.goToPath(`/collections/${oldVersionUuid}`);
578
579                 cy.get('[data-cy=collection-info-panel]').should('contain', 'This is an old version');
580                 cy.get('[data-cy=read-only-icon]').should('exist');
581                 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
582                 cy.get('[data-cy=collection-files-panel]').should('contain', 'bar');
583             });
584     });
585
586     it('views & edits storage classes data', function () {
587         const colName= `Test Collection ${Math.floor(Math.random() * 999999)}`;
588         cy.createCollection(adminUser.token, {
589             name: colName,
590             owner_uuid: activeUser.user.uuid,
591             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:some-file\n",
592         }).as('collection').then(function () {
593             expect(this.collection.storage_classes_desired).to.deep.equal(['default'])
594
595             cy.loginAs(activeUser)
596             cy.goToPath(`/collections/${this.collection.uuid}`);
597
598             // Initial check: it should show the 'default' storage class
599             cy.get('[data-cy=collection-info-panel]')
600                 .should('contain', 'Storage classes')
601                 .and('contain', 'default')
602                 .and('not.contain', 'foo')
603                 .and('not.contain', 'bar');
604             // Edit collection: add storage class 'foo'
605             cy.get('[data-cy=collection-panel-options-btn]').click();
606             cy.get('[data-cy=context-menu]').contains('Edit collection').click();
607             cy.get('[data-cy=form-dialog]')
608                 .should('contain', 'Edit Collection')
609                 .and('contain', 'Storage classes')
610                 .and('contain', 'default')
611                 .and('contain', 'foo')
612                 .and('contain', 'bar')
613                 .within(() => {
614                     cy.get('[data-cy=checkbox-foo]').click();
615                 });
616             cy.get('[data-cy=form-submit-btn]').click();
617             cy.get('[data-cy=collection-info-panel]')
618                 .should('contain', 'default')
619                 .and('contain', 'foo')
620                 .and('not.contain', 'bar');
621             cy.doRequest('GET', `/arvados/v1/collections/${this.collection.uuid}`)
622                 .its('body').as('updatedCollection')
623                 .then(function () {
624                     expect(this.updatedCollection.storage_classes_desired).to.deep.equal(['default', 'foo']);
625                 });
626             // Edit collection: remove storage class 'default'
627             cy.get('[data-cy=collection-panel-options-btn]').click();
628             cy.get('[data-cy=context-menu]').contains('Edit collection').click();
629             cy.get('[data-cy=form-dialog]')
630                 .should('contain', 'Edit Collection')
631                 .and('contain', 'Storage classes')
632                 .and('contain', 'default')
633                 .and('contain', 'foo')
634                 .and('contain', 'bar')
635                 .within(() => {
636                     cy.get('[data-cy=checkbox-default]').click();
637                 });
638             cy.get('[data-cy=form-submit-btn]').click();
639             cy.get('[data-cy=collection-info-panel]')
640                 .should('not.contain', 'default')
641                 .and('contain', 'foo')
642                 .and('not.contain', 'bar');
643             cy.doRequest('GET', `/arvados/v1/collections/${this.collection.uuid}`)
644                 .its('body').as('updatedCollection')
645                 .then(function () {
646                     expect(this.updatedCollection.storage_classes_desired).to.deep.equal(['foo']);
647                 });
648         })
649     });
650
651     it('moves a collection to a different project', function () {
652         const collName = `Test Collection ${Math.floor(Math.random() * 999999)}`;
653         const projName = `Test Project ${Math.floor(Math.random() * 999999)}`;
654         const fileName = `Test_File_${Math.floor(Math.random() * 999999)}`;
655
656         cy.createCollection(adminUser.token, {
657             name: collName,
658             owner_uuid: activeUser.user.uuid,
659             manifest_text: `. 37b51d194a7513e45b56f6524f2d51f2+3 0:3:${fileName}\n`,
660         }).as('testCollection');
661         cy.createGroup(adminUser.token, {
662             name: projName,
663             group_class: 'project',
664             owner_uuid: activeUser.user.uuid,
665         }).as('testProject');
666
667         cy.getAll('@testCollection', '@testProject')
668             .then(function ([testCollection, testProject]) {
669                 cy.loginAs(activeUser);
670                 cy.goToPath(`/collections/${testCollection.uuid}`);
671                 cy.get('[data-cy=collection-files-panel]').should('contain', fileName);
672                 cy.get('[data-cy=collection-info-panel]')
673                     .should('not.contain', projName)
674                     .and('not.contain', testProject.uuid);
675                 cy.get('[data-cy=collection-panel-options-btn]').click();
676                 cy.get('[data-cy=context-menu]').contains('Move to').click();
677                 cy.get('[data-cy=form-dialog]')
678                     .should('contain', 'Move to')
679                     .within(() => {
680                         // must use .then to avoid selecting instead of expanding https://github.com/cypress-io/cypress/issues/5529
681                         cy.get('[data-cy=projects-tree-home-tree-picker]')
682                             .find('i')
683                             .then(el => el.click());
684                         cy.get('[data-cy=projects-tree-home-tree-picker]')
685                             .contains(projName)
686                             .click();
687                     });
688                 cy.get('[data-cy=form-submit-btn]').click();
689                 cy.get('[data-cy=snackbar]')
690                     .contains('Collection has been moved')
691                 cy.get('[data-cy=collection-info-panel]')
692                     .contains(projName).and('contain', testProject.uuid);
693                 // Double check that the collection is in the project
694                 cy.goToPath(`/projects/${testProject.uuid}`);
695                 cy.waitForDom().get('[data-cy=project-panel]').should('contain', collName);
696             });
697     });
698
699     it('automatically updates the collection UI contents without using the Refresh button', function () {
700         const collName = `Test Collection ${Math.floor(Math.random() * 999999)}`;
701         const fileName = 'foobar'
702
703         cy.createCollection(adminUser.token, {
704             name: collName,
705             owner_uuid: activeUser.user.uuid,
706         }).as('testCollection');
707
708         cy.getAll('@testCollection').then(function ([testCollection]) {
709             cy.loginAs(activeUser);
710             cy.goToPath(`/collections/${testCollection.uuid}`);
711             cy.get('[data-cy=collection-files-panel]').should('contain', 'This collection is empty');
712             cy.get('[data-cy=collection-files-panel]').should('not.contain', fileName);
713             cy.get('[data-cy=collection-info-panel]').should('contain', collName);
714
715             cy.updateCollection(adminUser.token, testCollection.uuid, {
716                 name: `${collName + ' updated'}`,
717                 manifest_text: `. 37b51d194a7513e45b56f6524f2d51f2+3 0:3:${fileName}\n`,
718             }).as('updatedCollection');
719             cy.getAll('@updatedCollection').then(function ([updatedCollection]) {
720                 expect(updatedCollection.name).to.equal(`${collName + ' updated'}`);
721                 cy.get('[data-cy=collection-info-panel]').should('contain', updatedCollection.name);
722                 cy.get('[data-cy=collection-files-panel]').should('contain', fileName);
723             });
724         });
725     })
726
727     it('makes a copy of an existing collection', function() {
728         const collName = `Test Collection ${Math.floor(Math.random() * 999999)}`;
729         const copyName = `Copy of: ${collName}`;
730
731         cy.createCollection(adminUser.token, {
732             name: collName,
733             owner_uuid: activeUser.user.uuid,
734             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:some-file\n",
735         }).as('collection').then(function () {
736             cy.loginAs(activeUser)
737             cy.goToPath(`/collections/${this.collection.uuid}`);
738             cy.get('[data-cy=collection-files-panel]')
739                 .should('contain', 'some-file');
740             cy.get('[data-cy=collection-panel-options-btn]').click();
741             cy.get('[data-cy=context-menu]').contains('Make a copy').click();
742             cy.get('[data-cy=form-dialog]')
743                 .should('contain', 'Make a copy')
744                 .within(() => {
745                     cy.get('[data-cy=projects-tree-home-tree-picker]')
746                         .contains('Projects')
747                         .click();
748                     cy.get('[data-cy=form-submit-btn]').click();
749                 });
750             cy.get('[data-cy=snackbar]')
751                 .contains('Collection has been copied.')
752             cy.get('[data-cy=snackbar-goto-action]').click();
753             cy.get('[data-cy=project-panel]')
754                 .contains(copyName).click();
755             cy.get('[data-cy=collection-files-panel]')
756                 .should('contain', 'some-file');
757         });
758     });
759
760     it('uses the collection version browser to view a previous version', function () {
761         const colName = `Test Collection ${Math.floor(Math.random() * 999999)}`;
762
763         // Creates the collection using the admin token so we can set up
764         // a bogus manifest text without block signatures.
765         cy.createCollection(adminUser.token, {
766             name: colName,
767             owner_uuid: activeUser.user.uuid,
768             preserve_version: true,
769             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n"
770         })
771             .as('collection').then(function () {
772                 // Visit collection, check basic information
773                 cy.loginAs(activeUser)
774                 cy.goToPath(`/collections/${this.collection.uuid}`);
775
776                 cy.get('[data-cy=collection-info-panel]').should('not.contain', 'This is an old version');
777                 cy.get('[data-cy=read-only-icon]').should('not.exist');
778                 cy.get('[data-cy=collection-version-number]').should('contain', '1');
779                 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
780                 cy.get('[data-cy=collection-files-panel]').should('contain', 'foo').and('contain', 'bar');
781
782                 // Modify collection, expect version number change
783                 cy.get('[data-cy=collection-files-panel]').contains('foo').rightclick();
784                 cy.get('[data-cy=context-menu]').contains('Remove').click();
785                 cy.get('[data-cy=confirmation-dialog]').should('contain', 'Removing file');
786                 cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
787                 cy.get('[data-cy=collection-version-number]').should('contain', '2');
788                 cy.get('[data-cy=collection-files-panel]').should('not.contain', 'foo').and('contain', 'bar');
789
790                 // Click on version number, check version browser. Click on past version.
791                 cy.get('[data-cy=collection-version-browser]').should('not.exist');
792                 cy.get('[data-cy=collection-version-number]').contains('2').click();
793                 cy.get('[data-cy=collection-version-browser]')
794                     .should('contain', 'Nr').and('contain', 'Size').and('contain', 'Date')
795                     .within(() => {
796                         // Version 1: 6 bytes in size
797                         cy.get('[data-cy=collection-version-browser-select-1]')
798                             .should('contain', '1')
799                             .and('contain', '6 B')
800                             .and('contain', adminUser.user.uuid);
801                         // Version 2: 3 bytes in size (one file removed)
802                         cy.get('[data-cy=collection-version-browser-select-2]')
803                             .should('contain', '2')
804                             .and('contain', '3 B')
805                             .and('contain', activeUser.user.full_name);
806                         cy.get('[data-cy=collection-version-browser-select-3]')
807                             .should('not.exist');
808                         cy.get('[data-cy=collection-version-browser-select-1]')
809                             .click();
810                     });
811                 cy.get('[data-cy=collection-info-panel]').should('contain', 'This is an old version');
812                 cy.get('[data-cy=read-only-icon]').should('exist');
813                 cy.get('[data-cy=collection-version-number]').should('contain', '1');
814                 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
815                 cy.get('[data-cy=collection-files-panel]')
816                     .should('contain', 'foo').and('contain', 'bar');
817
818                 // Check that only old collection action are available on context menu
819                 cy.get('[data-cy=collection-panel-options-btn]').click();
820                 cy.get('[data-cy=context-menu]')
821                     .should('contain', 'Restore version')
822                     .and('not.contain', 'Add to favorites');
823                 cy.get('body').click(); // Collapse the menu avoiding details panel expansion
824
825                 // Click on "head version" link, confirm that it's the latest version.
826                 cy.get('[data-cy=collection-info-panel]').contains('head version').click();
827                 cy.get('[data-cy=collection-info-panel]')
828                     .should('not.contain', 'This is an old version');
829                 cy.get('[data-cy=read-only-icon]').should('not.exist');
830                 cy.get('[data-cy=collection-version-number]').should('contain', '2');
831                 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
832                 cy.get('[data-cy=collection-files-panel]').
833                     should('not.contain', 'foo').and('contain', 'bar');
834
835                 // Check that old collection action isn't available on context menu
836                 cy.get('[data-cy=collection-panel-options-btn]').click()
837                 cy.get('[data-cy=context-menu]').should('not.contain', 'Restore version')
838                 cy.get('body').click(); // Collapse the menu avoiding details panel expansion
839
840                 // Make another change, confirm new version.
841                 cy.get('[data-cy=collection-panel-options-btn]').click();
842                 cy.get('[data-cy=context-menu]').contains('Edit collection').click();
843                 cy.get('[data-cy=form-dialog]')
844                     .should('contain', 'Edit Collection')
845                     .within(() => {
846                         // appends some text
847                         cy.get('input').first().type(' renamed');
848                     });
849                 cy.get('[data-cy=form-submit-btn]').click();
850                 cy.get('[data-cy=collection-info-panel]')
851                     .should('not.contain', 'This is an old version');
852                 cy.get('[data-cy=read-only-icon]').should('not.exist');
853                 cy.get('[data-cy=collection-version-number]').should('contain', '3');
854                 cy.get('[data-cy=collection-info-panel]').should('contain', colName + ' renamed');
855                 cy.get('[data-cy=collection-files-panel]')
856                     .should('not.contain', 'foo').and('contain', 'bar');
857                 cy.get('[data-cy=collection-version-browser-select-3]')
858                     .should('contain', '3').and('contain', '3 B');
859
860                 // Check context menus on version browser
861                 cy.get('[data-cy=collection-version-browser-select-3]').rightclick()
862                 cy.get('[data-cy=context-menu]')
863                     .should('contain', 'Add to favorites')
864                     .and('contain', 'Make a copy')
865                     .and('contain', 'Edit collection');
866                 cy.get('body').click();
867                 // (and now an old version...)
868                 cy.get('[data-cy=collection-version-browser-select-1]').rightclick()
869                 cy.get('[data-cy=context-menu]')
870                     .should('not.contain', 'Add to favorites')
871                     .and('contain', 'Make a copy')
872                     .and('not.contain', 'Edit collection');
873                 cy.get('body').click();
874
875                 // Restore first version
876                 cy.get('[data-cy=collection-version-browser]').within(() => {
877                     cy.get('[data-cy=collection-version-browser-select-1]').click();
878                 });
879                 cy.get('[data-cy=collection-panel-options-btn]').click()
880                 cy.get('[data-cy=context-menu]').contains('Restore version').click();
881                 cy.get('[data-cy=confirmation-dialog]').should('contain', 'Restore version');
882                 cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
883                 cy.get('[data-cy=collection-info-panel]')
884                     .should('not.contain', 'This is an old version');
885                 cy.get('[data-cy=collection-version-number]').should('contain', '4');
886                 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
887                 cy.get('[data-cy=collection-files-panel]')
888                     .should('contain', 'foo').and('contain', 'bar');
889             });
890     });
891
892     it('creates collection from selected files of another collection', () => {
893         cy.createCollection(adminUser.token, {
894             name: `Test Collection ${Math.floor(Math.random() * 999999)}`,
895             owner_uuid: activeUser.user.uuid,
896             preserve_version: true,
897             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n"
898         })
899             .as('collection').then(function () {
900                 // Visit collection, check basic information
901                 cy.loginAs(activeUser)
902                 cy.goToPath(`/collections/${this.collection.uuid}`);
903
904                 cy.get('[data-cy=collection-files-panel]').within(() => {
905                     cy.get('input[type=checkbox]').first().click();
906                 });
907
908                 cy.get('[data-cy=collection-files-panel-options-btn]').click();
909                 cy.get('[data-cy=context-menu]').contains('Create a new collection with selected').click();
910
911                 cy.get('[data-cy=form-dialog]').contains('Projects').click();
912
913                 cy.get('[data-cy=form-submit-btn]').click();
914
915                 cy.waitForDom().get('.layout-pane-primary', { timeout: 12000 }).contains('Projects').click();
916
917                 cy.get('main').contains(`Files extracted from: ${this.collection.name}`).should('exist');
918             });
919     });
920
921     it('creates new collection with properties on home project', function () {
922         cy.loginAs(activeUser);
923         cy.goToPath(`/projects/${activeUser.user.uuid}`);
924         cy.get('[data-cy=breadcrumb-first]').should('contain', 'Projects');
925         cy.get('[data-cy=breadcrumb-last]').should('not.exist');
926         // Create new collection
927         cy.get('[data-cy=side-panel-button]').click();
928         cy.get('[data-cy=side-panel-new-collection]').click();
929         // Name between brackets tests bugfix #17582
930         const collName = `[Test collection (${Math.floor(999999 * Math.random())})]`;
931
932         // Select a storage class.
933         cy.get('[data-cy=form-dialog]')
934             .should('contain', 'New collection')
935             .and('contain', 'Storage classes')
936             .and('contain', 'default')
937             .and('contain', 'foo')
938             .and('contain', 'bar')
939             .within(() => {
940                 cy.get('[data-cy=parent-field]').within(() => {
941                     cy.get('input').should('have.value', 'Home project');
942                 });
943                 cy.get('[data-cy=name-field]').within(() => {
944                     cy.get('input').type(collName);
945                 });
946                 cy.get('[data-cy=checkbox-foo]').click();
947             })
948
949         // Add a property.
950         // Key: Color (IDTAGCOLORS) - Value: Magenta (IDVALCOLORS3)
951         cy.get('[data-cy=form-dialog]').should('not.contain', 'Color: Magenta');
952         cy.get('[data-cy=resource-properties-form]').within(() => {
953             cy.get('[data-cy=property-field-key]').within(() => {
954                 cy.get('input').type('Color');
955             });
956             cy.get('[data-cy=property-field-value]').within(() => {
957                 cy.get('input').type('Magenta');
958             });
959             cy.root().submit();
960         });
961         // Confirm proper vocabulary labels are displayed on the UI.
962         cy.get('[data-cy=form-dialog]').should('contain', 'Color: Magenta');
963
964         // Value field should not complain about being required just after
965         // adding a new property. See #19732
966         cy.get('[data-cy=form-dialog]').should('not.contain', 'This field is required');
967
968         cy.get('[data-cy=form-submit-btn]').click();
969         // Confirm that the user was taken to the newly created collection
970         cy.get('[data-cy=form-dialog]').should('not.exist');
971         cy.get('[data-cy=breadcrumb-first]').should('contain', 'Projects');
972         cy.get('[data-cy=breadcrumb-last]').should('contain', collName);
973         cy.get('[data-cy=collection-info-panel]')
974             .should('contain', 'default')
975             .and('contain', 'foo')
976             .and('contain', 'Color: Magenta')
977             .and('not.contain', 'bar');
978         // Confirm that the collection's properties has the real values.
979         cy.doRequest('GET', '/arvados/v1/collections', null, {
980             filters: `[["name", "=", "${collName}"]]`,
981         })
982         .its('body.items').as('collections')
983         .then(function() {
984             expect(this.collections).to.have.lengthOf(1);
985             expect(this.collections[0].properties).to.have.property(
986                 'IDTAGCOLORS', 'IDVALCOLORS3');
987         });
988     });
989
990     it('shows responsible person for collection if available', () => {
991         cy.createCollection(adminUser.token, {
992             name: `Test collection ${Math.floor(Math.random() * 999999)}`,
993             owner_uuid: activeUser.user.uuid,
994             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
995         }).as('testCollection1');
996
997         cy.createCollection(adminUser.token, {
998             name: `Test collection ${Math.floor(Math.random() * 999999)}`,
999             owner_uuid: adminUser.user.uuid,
1000             manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
1001         }).as('testCollection2').then(function (testCollection2) {
1002             cy.shareWith(adminUser.token, activeUser.user.uuid, testCollection2.uuid, 'can_write');
1003         });
1004
1005         cy.getAll('@testCollection1', '@testCollection2')
1006             .then(function ([testCollection1, testCollection2]) {
1007                 cy.loginAs(activeUser);
1008
1009                 cy.goToPath(`/collections/${testCollection1.uuid}`);
1010                 cy.get('[data-cy=responsible-person-wrapper]')
1011                     .contains(activeUser.user.uuid);
1012
1013                 cy.goToPath(`/collections/${testCollection2.uuid}`);
1014                 cy.get('[data-cy=responsible-person-wrapper]')
1015                     .contains(adminUser.user.uuid);
1016             });
1017     });
1018
1019     describe('file upload', () => {
1020         beforeEach(() => {
1021             cy.createCollection(adminUser.token, {
1022                 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
1023                 owner_uuid: activeUser.user.uuid,
1024                 manifest_text: "./subdir 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo\n. 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
1025             }).as('testCollection1');
1026         });
1027
1028         it('uploads a file and checks the collection UI to be fresh', () => {
1029             cy.getAll('@testCollection1')
1030                 .then(function([testCollection1]) {
1031                     cy.loginAs(activeUser);
1032                     cy.goToPath(`/collections/${testCollection1.uuid}`);
1033                     cy.get('[data-cy=upload-button]').click();
1034                     cy.get('[data-cy=collection-files-panel]')
1035                         .contains('5mb_a.bin').should('not.exist');
1036                     cy.get('[data-cy=collection-file-count]').should('contain', '2');
1037                     cy.fixture('files/5mb.bin', 'base64').then(content => {
1038                         cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_a.bin');
1039                         cy.get('[data-cy=form-submit-btn]').click();
1040                         cy.get('[data-cy=form-submit-btn]').should('not.exist');
1041                         cy.get('[data-cy=collection-files-panel]')
1042                             .contains('5mb_a.bin').should('exist');
1043                         cy.get('[data-cy=collection-file-count]').should('contain', '3');
1044
1045                         cy.get('[data-cy=collection-files-panel]').contains('subdir').click();
1046                         cy.get('[data-cy=upload-button]').click();
1047                         cy.fixture('files/5mb.bin', 'base64').then(content => {
1048                             cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_b.bin');
1049                             cy.get('[data-cy=form-submit-btn]').click();
1050                             cy.get('[data-cy=form-submit-btn]').should('not.exist');
1051                             cy.get('[data-cy=collection-files-right-panel]')
1052                                  .contains('5mb_b.bin').should('exist');
1053                         });
1054                     });
1055                 });
1056         });
1057
1058         it('allows to cancel running upload', () => {
1059             cy.getAll('@testCollection1')
1060                 .then(function([testCollection1]) {
1061                     cy.loginAs(activeUser);
1062
1063                     cy.goToPath(`/collections/${testCollection1.uuid}`);
1064
1065                     cy.get('[data-cy=upload-button]').click();
1066
1067                     cy.fixture('files/5mb.bin', 'base64').then(content => {
1068                         cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_a.bin');
1069                         cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_b.bin');
1070
1071                         cy.get('[data-cy=form-submit-btn]').click();
1072
1073                         cy.get('button').contains('Cancel').click();
1074
1075                         cy.get('[data-cy=form-submit-btn]').should('not.exist');
1076                     });
1077                 });
1078         });
1079
1080         it('allows to cancel single file from the running upload', () => {
1081             cy.getAll('@testCollection1')
1082                 .then(function([testCollection1]) {
1083                     cy.loginAs(activeUser);
1084
1085                     cy.goToPath(`/collections/${testCollection1.uuid}`);
1086
1087                     cy.get('[data-cy=upload-button]').click();
1088
1089                     cy.fixture('files/5mb.bin', 'base64').then(content => {
1090                         cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_a.bin');
1091                         cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_b.bin');
1092
1093                         cy.get('[data-cy=form-submit-btn]').click();
1094
1095                         cy.get('button[aria-label=Remove]').eq(1).click();
1096
1097                         cy.get('[data-cy=form-submit-btn]').should('not.exist');
1098
1099                         cy.get('[data-cy=collection-files-panel]').contains('5mb_a.bin').should('exist');
1100                     });
1101                 });
1102         });
1103
1104         it('allows to cancel all files from the running upload', () => {
1105             cy.getAll('@testCollection1')
1106                 .then(function([testCollection1]) {
1107                     cy.loginAs(activeUser);
1108
1109                     cy.goToPath(`/collections/${testCollection1.uuid}`);
1110
1111                     // Confirm initial collection state.
1112                     cy.get('[data-cy=collection-files-panel]')
1113                         .contains('bar').should('exist');
1114                     cy.get('[data-cy=collection-files-panel]')
1115                         .contains('5mb_a.bin').should('not.exist');
1116                     cy.get('[data-cy=collection-files-panel]')
1117                         .contains('5mb_b.bin').should('not.exist');
1118
1119                     cy.get('[data-cy=upload-button]').click();
1120
1121                     cy.fixture('files/5mb.bin', 'base64').then(content => {
1122                         cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_a.bin');
1123                         cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_b.bin');
1124
1125                         cy.get('[data-cy=form-submit-btn]').click();
1126
1127                         cy.get('button[aria-label=Remove]').should('exist');
1128                         cy.get('button[aria-label=Remove]')
1129                             .click({ multiple: true, force: true });
1130
1131                         cy.get('[data-cy=form-submit-btn]').should('not.exist');
1132
1133                         // Confirm final collection state.
1134                         cy.get('[data-cy=collection-files-panel]')
1135                             .contains('bar').should('exist');
1136                         // The following fails, but doesn't seem to happen
1137                         // in the real world. Maybe there's a race between
1138                         // the PUT request finishing and the 'Remove' button
1139                         // dissapearing, because sometimes just one of the 2
1140                         // files gets uploaded.
1141                         // Maybe this will be needed to simulate a slow network:
1142                         // https://docs.cypress.io/api/commands/intercept#Convenience-functions-1
1143                         // cy.get('[data-cy=collection-files-panel]')
1144                         //     .contains('5mb_a.bin').should('not.exist');
1145                         // cy.get('[data-cy=collection-files-panel]')
1146                         //     .contains('5mb_b.bin').should('not.exist');
1147                     });
1148                 });
1149         });
1150     });
1151 })