1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 const path = require('path');
7 describe('Collection panel tests', 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;
22 cy.getUser('collectionuser1', 'Collection', 'User', false, true)
23 .as('activeUser').then(function () {
24 activeUser = this.activeUser;
27 downloadsFolder = Cypress.config('downloadsFolder');
30 beforeEach(function () {
32 cy.clearLocalStorage();
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"
41 .as('testCollection').then(function (testCollection) {
42 cy.loginAs(activeUser);
43 cy.goToPath(`/collections/${testCollection.uuid}`);
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();
49 const filename = path.join(downloadsFolder, `${testCollection.name}.duck`);
51 cy.readFile(filename, { timeout: 15000 })
53 const childrenCollection = Array.prototype.slice.call(Cypress.$(body).find('dict')[0].children);
57 for (i=0; i < childrenCollection.length; i += j) {
58 map[childrenCollection[i].outerText] = childrenCollection[i + 1].outerText;
61 cy.get('#simple-tabpanel-0').find('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);
70 expect(map['Path']).to.equal(`/c=${testCollection.uuid}`);
74 .then(() => cy.task('clearDownload', { filename }));
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, {
82 owner_uuid: activeUser.user.uuid,
83 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
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')
95 cy.get('[data-cy=name-field]').within(() => {
96 cy.get('input').type(name);
98 cy.get('[data-cy=form-submit-btn]').click();
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')
104 cy.get('[data-cy=name-field]').within(() => {
105 cy.get('input').type(' renamed');
107 cy.get('[data-cy=form-submit-btn]').click();
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')
116 cy.get('[data-cy=name-field]').within(() => {
118 .type('{selectall}{backspace}')
121 cy.get('[data-cy=form-submit-btn]').click();
123 cy.get('[data-cy=form-dialog]').should('exist')
124 .and('contain', 'Collection with the same name already exists');
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"
133 .as('testCollection').then(function () {
134 cy.loginAs(activeUser);
135 cy.goToPath(`/collections/${this.testCollection.uuid}`);
137 cy.get('[data-cy=collection-info-panel')
138 .should('contain', this.testCollection.name)
139 .and('not.contain', 'Color: Magenta');
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');
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');
150 cy.get('[data-cy=property-field-value]').within(() => {
151 cy.get('input').type('Magenta');
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')
163 expect(this.collection.properties.IDTAGCOLORS).to.equal('IDVALCOLORS3');
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');
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"
178 .as('testCollection').then(function () {
179 cy.loginAs(activeUser);
180 cy.goToPath(`/collections/${this.testCollection.uuid}`);
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();
188 cy.get('[data-cy=details-panel]').within(() => {
189 cy.get('[data-cy=details-panel-edit-btn]').click();
191 cy.get('[data-cy=form-dialog').contains('Edit Collection');
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');
198 cy.get('[data-cy=property-field-value]').within(() => {
199 cy.get('input').type('Magenta');
203 // Confirm proper vocabulary labels are displayed on the UI.
204 cy.get('[data-cy=form-dialog]')
205 .should('contain', 'Color: Magenta');
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');
213 cy.get('[data-cy=property-field-value]').within(() => {
214 cy.get('input').type('sMaLL');
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();
221 // Confirm proper vocabulary labels are displayed on the UI.
222 cy.get('[data-cy=form-dialog]')
223 .should('contain', 'Size: S');
225 cy.get('[data-cy=form-dialog]').contains('Save').click();
226 cy.get('[data-cy=form-dialog]').should('not.exist');
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')
232 expect(this.collection.properties.IDTAGCOLORS).to.equal('IDVALCOLORS3');
233 expect(this.collection.properties.IDTAGSIZES).to.equal('IDVALSIZES2');
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');
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("*");
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`
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
282 cy.goToPath(`/collections/${this.testCollection.uuid}`);
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]')
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);
307 cy.get('[data-cy=upload-button]')
308 .should(`${isWritable ? '' : 'not.'}contain`, 'Upload data');
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')
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]')
340 cy.get('[data-cy=context-menu]')
341 .should('contain', 'Select all')
343 cy.get('[data-cy=collection-files-panel-options-btn]')
345 cy.get('[data-cy=context-menu]')
346 .should(`${isWritable ? '' : 'not.'}contain`, 'Remove selected')
347 cy.get('body').click(); // Collapse the menu
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])
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"
366 .as('testCollection').then(function () {
367 cy.loginAs(activeUser);
368 cy.goToPath(`/collections/${this.testCollection.uuid}`);
371 'bar', // initial name already set
378 'some name with whitespaces',
380 'is this name legal? I hope it is',
388 "G%C3%BCnter's%20file.pdf",
390 'bar' // make sure we can go back to the original name as a last step
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]')
398 cy.get('[data-cy=form-dialog]')
399 .should('contain', 'Rename')
402 .type('{selectall}{backspace}')
403 .type(to, { parseSpecialCharSequences: false });
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}`);
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"
421 .as('testCollection').then(function () {
422 cy.loginAs(activeUser);
423 cy.goToPath(`/collections/${this.testCollection.uuid}`);
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]')
431 cy.get('[data-cy=form-dialog]')
432 .should('contain', 'Rename')
434 cy.get('input').type(`{selectall}{backspace}${subdir}/foo`);
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();
442 // Rename 'subdir/foo' to 'bar'
444 cy.get('[data-cy=collection-files-panel]')
445 .contains('foo').rightclick();
446 cy.get('[data-cy=context-menu]')
449 cy.get('[data-cy=form-dialog]')
450 .should('contain', 'Rename')
453 .should('have.value', `${subdir}/foo`)
454 .type(`{selectall}{backspace}bar`);
456 cy.get('[data-cy=form-submit-btn]').click();
458 // need to wait for dialog to dismiss
459 cy.get('[data-cy=form-dialog]').should('not.exist');
461 cy.waitForDom().get('[data-cy=collection-files-panel]')
466 cy.get('[data-cy=collection-files-panel]')
467 .should('contain', subdir) // empty dir kept
468 .and('contain', 'bar');
470 cy.get('[data-cy=collection-files-panel]')
471 .contains(subdir).rightclick();
472 cy.get('[data-cy=context-menu]')
475 cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
476 cy.get('[data-cy=form-dialog]').should('not.exist');
481 it('shows collection owner', () => {
482 cy.createCollection(adminUser.token, {
483 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
484 owner_uuid: activeUser.user.uuid,
485 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
487 .as('testCollection').then((testCollection) => {
488 cy.loginAs(activeUser);
489 cy.goToPath(`/collections/${testCollection.uuid}`);
491 cy.get('[data-cy=collection-info-panel]').contains(`Collection User`);
495 it('tries to rename a file with illegal names', function () {
496 // Creates the collection using the admin token so we can set up
497 // a bogus manifest text without block signatures.
498 cy.createCollection(adminUser.token, {
499 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
500 owner_uuid: activeUser.user.uuid,
501 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
503 .as('testCollection').then(function () {
504 cy.loginAs(activeUser);
505 cy.goToPath(`/collections/${this.testCollection.uuid}`);
507 const illegalNamesFromUI = [
508 ['.', "Name cannot be '.' or '..'"],
509 ['..', "Name cannot be '.' or '..'"],
510 ['', 'This field is required'],
511 [' ', 'Leading/trailing whitespaces not allowed'],
512 [' foo', 'Leading/trailing whitespaces not allowed'],
513 ['foo ', 'Leading/trailing whitespaces not allowed'],
514 ['//foo', 'Empty dir name not allowed']
516 illegalNamesFromUI.forEach(([name, errMsg]) => {
517 cy.get('[data-cy=collection-files-panel]')
518 .contains('bar').rightclick();
519 cy.get('[data-cy=context-menu]')
522 cy.get('[data-cy=form-dialog]')
523 .should('contain', 'Rename')
525 cy.get('input').type(`{selectall}{backspace}${name}`);
527 cy.get('[data-cy=form-dialog]')
528 .should('contain', 'Rename')
530 cy.contains(`${errMsg}`);
532 cy.get('[data-cy=form-cancel-btn]').click();
537 it('can correctly display old versions', function () {
538 const colName = `Versioned Collection ${Math.floor(Math.random() * 999999)}`;
540 let oldVersionUuid = '';
541 // Make sure no other collections with this name exist
542 cy.doRequest('GET', '/arvados/v1/collections', null, {
543 filters: `[["name", "=", "${colName}"]]`,
544 include_old_versions: true
546 .its('body.items').as('collections')
548 expect(this.collections).to.be.empty;
550 // Creates the collection using the admin token so we can set up
551 // a bogus manifest text without block signatures.
552 cy.createCollection(adminUser.token, {
554 owner_uuid: activeUser.user.uuid,
555 preserve_version: true,
556 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
558 .as('originalVersion').then(function () {
559 // Change the file name to create a new version.
560 cy.updateCollection(adminUser.token, this.originalVersion.uuid, {
561 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo\n"
563 colUuid = this.originalVersion.uuid;
565 // Confirm that there are 2 versions of the collection
566 cy.doRequest('GET', '/arvados/v1/collections', null, {
567 filters: `[["name", "=", "${colName}"]]`,
568 include_old_versions: true
570 .its('body.items').as('collections')
572 expect(this.collections).to.have.lengthOf(2);
573 this.collections.map(function (aCollection) {
574 expect(aCollection.current_version_uuid).to.equal(colUuid);
575 if (aCollection.uuid !== aCollection.current_version_uuid) {
576 oldVersionUuid = aCollection.uuid;
579 // Check the old version displays as what it is.
580 cy.loginAs(activeUser)
581 cy.goToPath(`/collections/${oldVersionUuid}`);
583 cy.get('[data-cy=collection-info-panel]').should('contain', 'This is an old version');
584 cy.get('[data-cy=read-only-icon]').should('exist');
585 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
586 cy.get('[data-cy=collection-files-panel]').should('contain', 'bar');
590 it('views & edits storage classes data', function () {
591 const colName= `Test Collection ${Math.floor(Math.random() * 999999)}`;
592 cy.createCollection(adminUser.token, {
594 owner_uuid: activeUser.user.uuid,
595 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:some-file\n",
596 }).as('collection').then(function () {
597 expect(this.collection.storage_classes_desired).to.deep.equal(['default'])
599 cy.loginAs(activeUser)
600 cy.goToPath(`/collections/${this.collection.uuid}`);
602 // Initial check: it should show the 'default' storage class
603 cy.get('[data-cy=collection-info-panel]')
604 .should('contain', 'Storage classes')
605 .and('contain', 'default')
606 .and('not.contain', 'foo')
607 .and('not.contain', 'bar');
608 // Edit collection: add storage class 'foo'
609 cy.get('[data-cy=collection-panel-options-btn]').click();
610 cy.get('[data-cy=context-menu]').contains('Edit collection').click();
611 cy.get('[data-cy=form-dialog]')
612 .should('contain', 'Edit Collection')
613 .and('contain', 'Storage classes')
614 .and('contain', 'default')
615 .and('contain', 'foo')
616 .and('contain', 'bar')
618 cy.get('[data-cy=checkbox-foo]').click();
620 cy.get('[data-cy=form-submit-btn]').click();
621 cy.get('[data-cy=collection-info-panel]')
622 .should('contain', 'default')
623 .and('contain', 'foo')
624 .and('not.contain', 'bar');
625 cy.doRequest('GET', `/arvados/v1/collections/${this.collection.uuid}`)
626 .its('body').as('updatedCollection')
628 expect(this.updatedCollection.storage_classes_desired).to.deep.equal(['default', 'foo']);
630 // Edit collection: remove storage class 'default'
631 cy.get('[data-cy=collection-panel-options-btn]').click();
632 cy.get('[data-cy=context-menu]').contains('Edit collection').click();
633 cy.get('[data-cy=form-dialog]')
634 .should('contain', 'Edit Collection')
635 .and('contain', 'Storage classes')
636 .and('contain', 'default')
637 .and('contain', 'foo')
638 .and('contain', 'bar')
640 cy.get('[data-cy=checkbox-default]').click();
642 cy.get('[data-cy=form-submit-btn]').click();
643 cy.get('[data-cy=collection-info-panel]')
644 .should('not.contain', 'default')
645 .and('contain', 'foo')
646 .and('not.contain', 'bar');
647 cy.doRequest('GET', `/arvados/v1/collections/${this.collection.uuid}`)
648 .its('body').as('updatedCollection')
650 expect(this.updatedCollection.storage_classes_desired).to.deep.equal(['foo']);
655 it('moves a collection to a different project', function () {
656 const collName = `Test Collection ${Math.floor(Math.random() * 999999)}`;
657 const projName = `Test Project ${Math.floor(Math.random() * 999999)}`;
658 const fileName = `Test_File_${Math.floor(Math.random() * 999999)}`;
660 cy.createCollection(adminUser.token, {
662 owner_uuid: activeUser.user.uuid,
663 manifest_text: `. 37b51d194a7513e45b56f6524f2d51f2+3 0:3:${fileName}\n`,
664 }).as('testCollection');
665 cy.createGroup(adminUser.token, {
667 group_class: 'project',
668 owner_uuid: activeUser.user.uuid,
669 }).as('testProject');
671 cy.getAll('@testCollection', '@testProject')
672 .then(function ([testCollection, testProject]) {
673 cy.loginAs(activeUser);
674 cy.goToPath(`/collections/${testCollection.uuid}`);
675 cy.get('[data-cy=collection-files-panel]').should('contain', fileName);
676 cy.get('[data-cy=collection-info-panel]')
677 .should('not.contain', projName)
678 .and('not.contain', testProject.uuid);
679 cy.get('[data-cy=collection-panel-options-btn]').click();
680 cy.get('[data-cy=context-menu]').contains('Move to').click();
681 cy.get('[data-cy=form-dialog]')
682 .should('contain', 'Move to')
684 // must use .then to avoid selecting instead of expanding https://github.com/cypress-io/cypress/issues/5529
685 cy.get('[data-cy=projects-tree-home-tree-picker]')
687 .then(el => el.click());
688 cy.get('[data-cy=projects-tree-home-tree-picker]')
692 cy.get('[data-cy=form-submit-btn]').click();
693 cy.get('[data-cy=snackbar]')
694 .contains('Collection has been moved')
695 cy.get('[data-cy=collection-info-panel]')
696 .contains(projName).and('contain', testProject.uuid);
697 // Double check that the collection is in the project
698 cy.goToPath(`/projects/${testProject.uuid}`);
699 cy.waitForDom().get('[data-cy=project-panel]').should('contain', collName);
703 it('automatically updates the collection UI contents without using the Refresh button', function () {
704 const collName = `Test Collection ${Math.floor(Math.random() * 999999)}`;
706 cy.createCollection(adminUser.token, {
708 owner_uuid: activeUser.user.uuid,
709 }).as('testCollection');
711 cy.getAll('@testCollection').then(function ([testCollection]) {
712 cy.loginAs(activeUser);
721 cy.goToPath(`/collections/${testCollection.uuid}`);
722 cy.get('[data-cy=collection-files-panel]').should('contain', 'This collection is empty');
723 cy.get('[data-cy=collection-files-panel]').should('not.contain', files[0]);
724 cy.get('[data-cy=collection-info-panel]').should('contain', collName);
726 files.map((fileName, i, files) => {
727 cy.updateCollection(adminUser.token, testCollection.uuid, {
728 name: `${collName + ' updated'}`,
729 manifest_text: fileName ? `. 37b51d194a7513e45b56f6524f2d51f2+3 0:3:${fileName}\n` : "",
730 }).as('updatedCollection');
731 cy.getAll('@updatedCollection').then(function ([updatedCollection]) {
732 expect(updatedCollection.name).to.equal(`${collName + ' updated'}`);
733 cy.get('[data-cy=collection-info-panel]').should('contain', updatedCollection.name);
735 ? cy.get('[data-cy=collection-files-panel]').should('contain', fileName)
736 : cy.get('[data-cy=collection-files-panel]').should('not.contain', files[i-1]);;
743 it('makes a copy of an existing collection', function() {
744 const collName = `Test Collection ${Math.floor(Math.random() * 999999)}`;
745 const copyName = `Copy of: ${collName}`;
747 cy.createCollection(adminUser.token, {
749 owner_uuid: activeUser.user.uuid,
750 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:some-file\n",
751 }).as('collection').then(function () {
752 cy.loginAs(activeUser)
753 cy.goToPath(`/collections/${this.collection.uuid}`);
754 cy.get('[data-cy=collection-files-panel]')
755 .should('contain', 'some-file');
756 cy.get('[data-cy=collection-panel-options-btn]').click();
757 cy.get('[data-cy=context-menu]').contains('Make a copy').click();
758 cy.get('[data-cy=form-dialog]')
759 .should('contain', 'Make a copy')
761 cy.get('[data-cy=projects-tree-home-tree-picker]')
762 .contains('Projects')
764 cy.get('[data-cy=form-submit-btn]').click();
766 cy.get('[data-cy=snackbar]')
767 .contains('Collection has been copied.')
768 cy.get('[data-cy=snackbar-goto-action]').click();
769 cy.get('[data-cy=project-panel]')
770 .contains(copyName).click();
771 cy.get('[data-cy=collection-files-panel]')
772 .should('contain', 'some-file');
776 it('uses the collection version browser to view a previous version', function () {
777 const colName = `Test Collection ${Math.floor(Math.random() * 999999)}`;
779 // Creates the collection using the admin token so we can set up
780 // a bogus manifest text without block signatures.
781 cy.createCollection(adminUser.token, {
783 owner_uuid: activeUser.user.uuid,
784 preserve_version: true,
785 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n"
787 .as('collection').then(function () {
788 // Visit collection, check basic information
789 cy.loginAs(activeUser)
790 cy.goToPath(`/collections/${this.collection.uuid}`);
792 cy.get('[data-cy=collection-info-panel]').should('not.contain', 'This is an old version');
793 cy.get('[data-cy=read-only-icon]').should('not.exist');
794 cy.get('[data-cy=collection-version-number]').should('contain', '1');
795 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
796 cy.get('[data-cy=collection-files-panel]').should('contain', 'foo').and('contain', 'bar');
798 // Modify collection, expect version number change
799 cy.get('[data-cy=collection-files-panel]').contains('foo').rightclick();
800 cy.get('[data-cy=context-menu]').contains('Remove').click();
801 cy.get('[data-cy=confirmation-dialog]').should('contain', 'Removing file');
802 cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
803 cy.get('[data-cy=collection-version-number]').should('contain', '2');
804 cy.get('[data-cy=collection-files-panel]').should('not.contain', 'foo').and('contain', 'bar');
806 // Click on version number, check version browser. Click on past version.
807 cy.get('[data-cy=collection-version-browser]').should('not.exist');
808 cy.get('[data-cy=collection-version-number]').contains('2').click();
809 cy.get('[data-cy=collection-version-browser]')
810 .should('contain', 'Nr').and('contain', 'Size').and('contain', 'Date')
812 // Version 1: 6 bytes in size
813 cy.get('[data-cy=collection-version-browser-select-1]')
814 .should('contain', '1')
815 .and('contain', '6 B')
816 .and('contain', adminUser.user.uuid);
817 // Version 2: 3 bytes in size (one file removed)
818 cy.get('[data-cy=collection-version-browser-select-2]')
819 .should('contain', '2')
820 .and('contain', '3 B')
821 .and('contain', activeUser.user.full_name);
822 cy.get('[data-cy=collection-version-browser-select-3]')
823 .should('not.exist');
824 cy.get('[data-cy=collection-version-browser-select-1]')
827 cy.get('[data-cy=collection-info-panel]').should('contain', 'This is an old version');
828 cy.get('[data-cy=read-only-icon]').should('exist');
829 cy.get('[data-cy=collection-version-number]').should('contain', '1');
830 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
831 cy.get('[data-cy=collection-files-panel]')
832 .should('contain', 'foo').and('contain', 'bar');
834 // Check that only old collection action are available on context menu
835 cy.get('[data-cy=collection-panel-options-btn]').click();
836 cy.get('[data-cy=context-menu]')
837 .should('contain', 'Restore version')
838 .and('not.contain', 'Add to favorites');
839 cy.get('body').click(); // Collapse the menu avoiding details panel expansion
841 // Click on "head version" link, confirm that it's the latest version.
842 cy.get('[data-cy=collection-info-panel]').contains('head version').click();
843 cy.get('[data-cy=collection-info-panel]')
844 .should('not.contain', 'This is an old version');
845 cy.get('[data-cy=read-only-icon]').should('not.exist');
846 cy.get('[data-cy=collection-version-number]').should('contain', '2');
847 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
848 cy.get('[data-cy=collection-files-panel]').
849 should('not.contain', 'foo').and('contain', 'bar');
851 // Check that old collection action isn't available on context menu
852 cy.get('[data-cy=collection-panel-options-btn]').click()
853 cy.get('[data-cy=context-menu]').should('not.contain', 'Restore version')
854 cy.get('body').click(); // Collapse the menu avoiding details panel expansion
856 // Make another change, confirm new version.
857 cy.get('[data-cy=collection-panel-options-btn]').click();
858 cy.get('[data-cy=context-menu]').contains('Edit collection').click();
859 cy.get('[data-cy=form-dialog]')
860 .should('contain', 'Edit Collection')
863 cy.get('input').first().type(' renamed');
865 cy.get('[data-cy=form-submit-btn]').click();
866 cy.get('[data-cy=collection-info-panel]')
867 .should('not.contain', 'This is an old version');
868 cy.get('[data-cy=read-only-icon]').should('not.exist');
869 cy.get('[data-cy=collection-version-number]').should('contain', '3');
870 cy.get('[data-cy=collection-info-panel]').should('contain', colName + ' renamed');
871 cy.get('[data-cy=collection-files-panel]')
872 .should('not.contain', 'foo').and('contain', 'bar');
873 cy.get('[data-cy=collection-version-browser-select-3]')
874 .should('contain', '3').and('contain', '3 B');
876 // Check context menus on version browser
877 cy.get('[data-cy=collection-version-browser-select-3]').rightclick()
878 cy.get('[data-cy=context-menu]')
879 .should('contain', 'Add to favorites')
880 .and('contain', 'Make a copy')
881 .and('contain', 'Edit collection');
882 cy.get('body').click();
883 // (and now an old version...)
884 cy.get('[data-cy=collection-version-browser-select-1]').rightclick()
885 cy.get('[data-cy=context-menu]')
886 .should('not.contain', 'Add to favorites')
887 .and('contain', 'Make a copy')
888 .and('not.contain', 'Edit collection');
889 cy.get('body').click();
891 // Restore first version
892 cy.get('[data-cy=collection-version-browser]').within(() => {
893 cy.get('[data-cy=collection-version-browser-select-1]').click();
895 cy.get('[data-cy=collection-panel-options-btn]').click()
896 cy.get('[data-cy=context-menu]').contains('Restore version').click();
897 cy.get('[data-cy=confirmation-dialog]').should('contain', 'Restore version');
898 cy.get('[data-cy=confirmation-dialog-ok-btn]').click();
899 cy.get('[data-cy=collection-info-panel]')
900 .should('not.contain', 'This is an old version');
901 cy.get('[data-cy=collection-version-number]').should('contain', '4');
902 cy.get('[data-cy=collection-info-panel]').should('contain', colName);
903 cy.get('[data-cy=collection-files-panel]')
904 .should('contain', 'foo').and('contain', 'bar');
908 it('copies selected files into new collection', () => {
909 cy.createCollection(adminUser.token, {
910 name: `Test Collection ${Math.floor(Math.random() * 999999)}`,
911 owner_uuid: activeUser.user.uuid,
912 preserve_version: true,
913 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n"
915 .as('collection').then(function () {
916 // Visit collection, check basic information
917 cy.loginAs(activeUser)
918 cy.goToPath(`/collections/${this.collection.uuid}`);
920 cy.get('[data-cy=collection-files-panel]').within(() => {
921 cy.get('input[type=checkbox]').first().click();
924 cy.get('[data-cy=collection-files-panel-options-btn]').click();
925 cy.get('[data-cy=context-menu]').contains('Copy selected into new collection').click();
927 cy.get('[data-cy=form-dialog]').contains('Projects').click();
929 cy.get('[data-cy=form-submit-btn]').click();
931 cy.waitForDom().get('.layout-pane-primary', { timeout: 12000 }).contains('Projects').click();
933 cy.get('main').contains(`Files extracted from: ${this.collection.name}`).click();
934 cy.get('[data-cy=collection-files-panel]')
935 .and('contain', 'bar');
939 it('copies selected files into existing collection', () => {
940 cy.createCollection(adminUser.token, {
941 name: `Test Collection ${Math.floor(Math.random() * 999999)}`,
942 owner_uuid: activeUser.user.uuid,
943 preserve_version: true,
944 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n"
945 }).as('sourceCollection')
947 cy.createCollection(adminUser.token, {
948 name: `Destination Collection ${Math.floor(Math.random() * 999999)}`,
949 owner_uuid: activeUser.user.uuid,
950 preserve_version: true,
952 }).as('destinationCollection');
954 cy.getAll('@sourceCollection', '@destinationCollection').then(function ([sourceCollection, destinationCollection]) {
955 // Visit collection, check basic information
956 cy.loginAs(activeUser)
957 cy.goToPath(`/collections/${sourceCollection.uuid}`);
959 cy.get('[data-cy=collection-files-panel]').within(() => {
960 cy.get('input[type=checkbox]').first().click();
963 cy.get('[data-cy=collection-files-panel-options-btn]').click();
964 cy.get('[data-cy=context-menu]').contains('Copy selected into existing collection').click();
966 cy.get('[data-cy=form-dialog]').contains(destinationCollection.name).click();
968 cy.get('[data-cy=form-submit-btn]').click();
971 cy.goToPath(`/collections/${destinationCollection.uuid}`);
973 cy.get('main').contains(destinationCollection.name).should('exist');
974 cy.get('[data-cy=collection-files-panel]')
975 .and('contain', 'bar');
979 it('copies selected files into separate collections', () => {
980 cy.createCollection(adminUser.token, {
981 name: `Test Collection ${Math.floor(Math.random() * 999999)}`,
982 owner_uuid: activeUser.user.uuid,
983 preserve_version: true,
984 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n"
985 }).as('sourceCollection')
987 cy.getAll('@sourceCollection').then(function ([sourceCollection]) {
988 // Visit collection, check basic information
989 cy.loginAs(activeUser)
990 cy.goToPath(`/collections/${sourceCollection.uuid}`);
992 cy.get('[data-cy=collection-files-panel]').within(() => {
993 cy.get('input[type=checkbox]').click({multiple: true});
996 cy.get('[data-cy=collection-files-panel-options-btn]').click();
997 cy.get('[data-cy=context-menu]').contains('Copy selected into separate collections').click();
999 cy.get('[data-cy=form-dialog]').contains('Projects').click();
1000 cy.get('[data-cy=form-submit-btn]').click();
1002 cy.waitForDom().get('.layout-pane-primary', { timeout: 12000 }).contains('Projects').click();
1004 // cy.goToPath(`/collections/${destinationCollection.uuid}`);
1006 cy.get('main').contains(`File copied from collection ${sourceCollection.name}/bar`).click();
1007 cy.get('[data-cy=collection-files-panel]')
1008 .and('contain', 'bar');
1012 it('moves selected files into new collection', () => {
1013 cy.createCollection(adminUser.token, {
1014 name: `Test Collection ${Math.floor(Math.random() * 999999)}`,
1015 owner_uuid: activeUser.user.uuid,
1016 preserve_version: true,
1017 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n"
1019 .as('collection').then(function () {
1020 // Visit collection, check basic information
1021 cy.loginAs(activeUser)
1022 cy.goToPath(`/collections/${this.collection.uuid}`);
1024 cy.get('[data-cy=collection-files-panel]').within(() => {
1025 cy.get('input[type=checkbox]').first().click();
1028 cy.get('[data-cy=collection-files-panel-options-btn]').click();
1029 cy.get('[data-cy=context-menu]').contains('Move selected into new collection').click();
1031 cy.get('[data-cy=form-dialog]').contains('Projects').click();
1033 cy.get('[data-cy=form-submit-btn]').click();
1035 cy.waitForDom().get('.layout-pane-primary', { timeout: 12000 }).contains('Projects').click();
1037 cy.get('main').contains(`Files moved from: ${this.collection.name}`).click();
1038 cy.get('[data-cy=collection-files-panel]')
1039 .and('contain', 'bar');
1043 it('moves selected files into existing collection', () => {
1044 cy.createCollection(adminUser.token, {
1045 name: `Test Collection ${Math.floor(Math.random() * 999999)}`,
1046 owner_uuid: activeUser.user.uuid,
1047 preserve_version: true,
1048 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n"
1049 }).as('sourceCollection')
1051 cy.createCollection(adminUser.token, {
1052 name: `Destination Collection ${Math.floor(Math.random() * 999999)}`,
1053 owner_uuid: activeUser.user.uuid,
1054 preserve_version: true,
1056 }).as('destinationCollection');
1058 cy.getAll('@sourceCollection', '@destinationCollection').then(function ([sourceCollection, destinationCollection]) {
1059 // Visit collection, check basic information
1060 cy.loginAs(activeUser)
1061 cy.goToPath(`/collections/${sourceCollection.uuid}`);
1063 cy.get('[data-cy=collection-files-panel]').within(() => {
1064 cy.get('input[type=checkbox]').first().click();
1067 cy.get('[data-cy=collection-files-panel-options-btn]').click();
1068 cy.get('[data-cy=context-menu]').contains('Move selected into existing collection').click();
1070 cy.get('[data-cy=form-dialog]').contains(destinationCollection.name).click();
1072 cy.get('[data-cy=form-submit-btn]').click();
1075 cy.goToPath(`/collections/${destinationCollection.uuid}`);
1077 cy.get('main').contains(destinationCollection.name).should('exist');
1078 cy.get('[data-cy=collection-files-panel]')
1079 .and('contain', 'bar');
1083 it('moves selected files into separate collections', () => {
1084 cy.createCollection(adminUser.token, {
1085 name: `Test Collection ${Math.floor(Math.random() * 999999)}`,
1086 owner_uuid: activeUser.user.uuid,
1087 preserve_version: true,
1088 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n"
1089 }).as('sourceCollection')
1091 cy.getAll('@sourceCollection').then(function ([sourceCollection]) {
1092 // Visit collection, check basic information
1093 cy.loginAs(activeUser)
1094 cy.goToPath(`/collections/${sourceCollection.uuid}`);
1096 cy.get('[data-cy=collection-files-panel]').within(() => {
1097 cy.get('input[type=checkbox]').click({multiple: true});
1100 cy.get('[data-cy=collection-files-panel-options-btn]').click();
1101 cy.get('[data-cy=context-menu]').contains('Move selected into separate collections').click();
1103 cy.get('[data-cy=form-dialog]').contains('Projects').click();
1104 cy.get('[data-cy=form-submit-btn]').click();
1106 cy.waitForDom().get('.layout-pane-primary', { timeout: 12000 }).contains('Projects').click();
1108 cy.get('main').contains(`File moved from collection ${sourceCollection.name}/bar`).click();
1109 cy.get('[data-cy=collection-files-panel]')
1110 .and('contain', 'bar');
1114 it('creates new collection with properties on home project', function () {
1115 cy.loginAs(activeUser);
1116 cy.goToPath(`/projects/${activeUser.user.uuid}`);
1117 cy.get('[data-cy=breadcrumb-first]').should('contain', 'Projects');
1118 cy.get('[data-cy=breadcrumb-last]').should('not.exist');
1119 // Create new collection
1120 cy.get('[data-cy=side-panel-button]').click();
1121 cy.get('[data-cy=side-panel-new-collection]').click();
1122 // Name between brackets tests bugfix #17582
1123 const collName = `[Test collection (${Math.floor(999999 * Math.random())})]`;
1125 // Select a storage class.
1126 cy.get('[data-cy=form-dialog]')
1127 .should('contain', 'New collection')
1128 .and('contain', 'Storage classes')
1129 .and('contain', 'default')
1130 .and('contain', 'foo')
1131 .and('contain', 'bar')
1133 cy.get('[data-cy=parent-field]').within(() => {
1134 cy.get('input').should('have.value', 'Home project');
1136 cy.get('[data-cy=name-field]').within(() => {
1137 cy.get('input').type(collName);
1139 cy.get('[data-cy=checkbox-foo]').click();
1143 // Key: Color (IDTAGCOLORS) - Value: Magenta (IDVALCOLORS3)
1144 cy.get('[data-cy=form-dialog]').should('not.contain', 'Color: Magenta');
1145 cy.get('[data-cy=resource-properties-form]').within(() => {
1146 cy.get('[data-cy=property-field-key]').within(() => {
1147 cy.get('input').type('Color');
1149 cy.get('[data-cy=property-field-value]').within(() => {
1150 cy.get('input').type('Magenta');
1154 // Confirm proper vocabulary labels are displayed on the UI.
1155 cy.get('[data-cy=form-dialog]').should('contain', 'Color: Magenta');
1157 // Value field should not complain about being required just after
1158 // adding a new property. See #19732
1159 cy.get('[data-cy=form-dialog]').should('not.contain', 'This field is required');
1161 cy.get('[data-cy=form-submit-btn]').click();
1162 // Confirm that the user was taken to the newly created collection
1163 cy.get('[data-cy=form-dialog]').should('not.exist');
1164 cy.get('[data-cy=breadcrumb-first]').should('contain', 'Projects');
1165 cy.get('[data-cy=breadcrumb-last]').should('contain', collName);
1166 cy.get('[data-cy=collection-info-panel]')
1167 .should('contain', 'default')
1168 .and('contain', 'foo')
1169 .and('contain', 'Color: Magenta')
1170 .and('not.contain', 'bar');
1171 // Confirm that the collection's properties has the real values.
1172 cy.doRequest('GET', '/arvados/v1/collections', null, {
1173 filters: `[["name", "=", "${collName}"]]`,
1175 .its('body.items').as('collections')
1177 expect(this.collections).to.have.lengthOf(1);
1178 expect(this.collections[0].properties).to.have.property(
1179 'IDTAGCOLORS', 'IDVALCOLORS3');
1183 it('shows responsible person for collection if available', () => {
1184 cy.createCollection(adminUser.token, {
1185 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
1186 owner_uuid: activeUser.user.uuid,
1187 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
1188 }).as('testCollection1');
1190 cy.createCollection(adminUser.token, {
1191 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
1192 owner_uuid: adminUser.user.uuid,
1193 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
1194 }).as('testCollection2').then(function (testCollection2) {
1195 cy.shareWith(adminUser.token, activeUser.user.uuid, testCollection2.uuid, 'can_write');
1198 cy.getAll('@testCollection1', '@testCollection2')
1199 .then(function ([testCollection1, testCollection2]) {
1200 cy.loginAs(activeUser);
1202 cy.goToPath(`/collections/${testCollection1.uuid}`);
1203 cy.get('[data-cy=responsible-person-wrapper]')
1204 .contains(activeUser.user.uuid);
1206 cy.goToPath(`/collections/${testCollection2.uuid}`);
1207 cy.get('[data-cy=responsible-person-wrapper]')
1208 .contains(adminUser.user.uuid);
1212 describe('file upload', () => {
1214 cy.createCollection(adminUser.token, {
1215 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
1216 owner_uuid: activeUser.user.uuid,
1217 manifest_text: "./subdir 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo\n. 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
1218 }).as('testCollection1');
1221 it('uploads a file and checks the collection UI to be fresh', () => {
1222 cy.getAll('@testCollection1')
1223 .then(function([testCollection1]) {
1224 cy.loginAs(activeUser);
1225 cy.goToPath(`/collections/${testCollection1.uuid}`);
1226 cy.get('[data-cy=upload-button]').click();
1227 cy.get('[data-cy=collection-files-panel]')
1228 .contains('5mb_a.bin').should('not.exist');
1229 cy.get('[data-cy=collection-file-count]').should('contain', '2');
1230 cy.fixture('files/5mb.bin', 'base64').then(content => {
1231 cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_a.bin');
1232 cy.get('[data-cy=form-submit-btn]').click();
1233 cy.get('[data-cy=form-submit-btn]').should('not.exist');
1234 cy.get('[data-cy=collection-files-panel]')
1235 .contains('5mb_a.bin').should('exist');
1236 cy.get('[data-cy=collection-file-count]').should('contain', '3');
1238 cy.get('[data-cy=collection-files-panel]').contains('subdir').click();
1239 cy.get('[data-cy=upload-button]').click();
1240 cy.fixture('files/5mb.bin', 'base64').then(content => {
1241 cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_b.bin');
1242 cy.get('[data-cy=form-submit-btn]').click();
1243 cy.waitForDom().get('[data-cy=form-submit-btn]').should('not.exist');
1244 // subdir gets unselected, I think this is a bug but
1245 // for the time being let's just make sure the test works.
1246 cy.get('[data-cy=collection-files-panel]').contains('subdir').click();
1247 cy.waitForDom().get('[data-cy=collection-files-right-panel]')
1248 .contains('5mb_b.bin').should('exist');
1254 it('allows to cancel running upload', () => {
1255 cy.getAll('@testCollection1')
1256 .then(function([testCollection1]) {
1257 cy.loginAs(activeUser);
1259 cy.goToPath(`/collections/${testCollection1.uuid}`);
1261 cy.get('[data-cy=upload-button]').click();
1263 cy.fixture('files/5mb.bin', 'base64').then(content => {
1264 cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_a.bin');
1265 cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_b.bin');
1267 cy.get('[data-cy=form-submit-btn]').click();
1269 cy.get('button').contains('Cancel').click();
1271 cy.get('[data-cy=form-submit-btn]').should('not.exist');
1276 it('allows to cancel single file from the running upload', () => {
1277 cy.getAll('@testCollection1')
1278 .then(function([testCollection1]) {
1279 cy.loginAs(activeUser);
1281 cy.goToPath(`/collections/${testCollection1.uuid}`);
1283 cy.get('[data-cy=upload-button]').click();
1285 cy.fixture('files/5mb.bin', 'base64').then(content => {
1286 cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_a.bin');
1287 cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_b.bin');
1289 cy.get('[data-cy=form-submit-btn]').click();
1291 cy.get('button[aria-label=Remove]').eq(1).click();
1293 cy.get('[data-cy=form-submit-btn]').should('not.exist');
1295 cy.get('[data-cy=collection-files-panel]').contains('5mb_a.bin').should('exist');
1300 it('allows to cancel all files from the running upload', () => {
1301 cy.getAll('@testCollection1')
1302 .then(function([testCollection1]) {
1303 cy.loginAs(activeUser);
1305 cy.goToPath(`/collections/${testCollection1.uuid}`);
1307 // Confirm initial collection state.
1308 cy.get('[data-cy=collection-files-panel]')
1309 .contains('bar').should('exist');
1310 cy.get('[data-cy=collection-files-panel]')
1311 .contains('5mb_a.bin').should('not.exist');
1312 cy.get('[data-cy=collection-files-panel]')
1313 .contains('5mb_b.bin').should('not.exist');
1315 cy.get('[data-cy=upload-button]').click();
1317 cy.fixture('files/5mb.bin', 'base64').then(content => {
1318 cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_a.bin');
1319 cy.get('[data-cy=drag-and-drop]').upload(content, '5mb_b.bin');
1321 cy.get('[data-cy=form-submit-btn]').click();
1323 cy.get('button[aria-label=Remove]').should('exist');
1324 cy.get('button[aria-label=Remove]')
1325 .click({ multiple: true, force: true });
1327 cy.get('[data-cy=form-submit-btn]').should('not.exist');
1329 // Confirm final collection state.
1330 cy.get('[data-cy=collection-files-panel]')
1331 .contains('bar').should('exist');
1332 // The following fails, but doesn't seem to happen
1333 // in the real world. Maybe there's a race between
1334 // the PUT request finishing and the 'Remove' button
1335 // dissapearing, because sometimes just one of the 2
1336 // files gets uploaded.
1337 // Maybe this will be needed to simulate a slow network:
1338 // https://docs.cypress.io/api/commands/intercept#Convenience-functions-1
1339 // cy.get('[data-cy=collection-files-panel]')
1340 // .contains('5mb_a.bin').should('not.exist');
1341 // cy.get('[data-cy=collection-files-panel]')
1342 // .contains('5mb_b.bin').should('not.exist');