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)
20 adminUser = this.adminUser;
22 cy.getUser("collectionuser1", "Collection", "User", false, true)
25 activeUser = this.activeUser;
27 downloadsFolder = Cypress.config("downloadsFolder");
30 beforeEach(function () {
32 cy.clearLocalStorage();
35 it('shows the appropriate buttons in the toolbar', () => {
37 const msButtonTooltips = [
46 'Open with 3rd party client',
51 cy.loginAs(activeUser);
52 const name = `Test collection ${Math.floor(Math.random() * 999999)}`;
53 cy.get("[data-cy=side-panel-button]").click({force: true});
54 cy.get("[data-cy=side-panel-new-collection]").click();
55 cy.get("[data-cy=form-dialog]")
56 .should("contain", "New collection")
58 cy.get("[data-cy=name-field]").within(() => {
59 cy.get("input").type(name);
61 cy.get("[data-cy=form-submit-btn]").click();
63 cy.get("[data-cy=side-panel-tree]").contains("Home Projects").click();
65 cy.get('[data-cy=data-table-row]').contains(name).should('exist').parent().parent().parent().parent().click()
66 cy.get('[data-cy=multiselect-button]').should('have.length', msButtonTooltips.length)
67 for (let i = 0; i < msButtonTooltips.length; i++) {
68 cy.get('[data-cy=multiselect-button]').eq(i).trigger('mouseover');
69 cy.get('body').contains(msButtonTooltips[i]).should('exist')
70 cy.get('[data-cy=multiselect-button]').eq(i).trigger('mouseout');
74 it("allows to download mountain duck config for a collection", () => {
75 cy.createCollection(adminUser.token, {
76 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
77 owner_uuid: activeUser.user.uuid,
78 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
81 .then(function (testCollection) {
82 cy.loginAs(activeUser);
83 cy.goToPath(`/collections/${testCollection.uuid}`);
85 cy.get("[data-cy=collection-panel-options-btn]").click();
86 cy.get("[data-cy=context-menu]").contains("Open with 3rd party client").click();
87 cy.get("[data-cy=download-button").click();
89 const filename = path.join(downloadsFolder, `${testCollection.name}.duck`);
91 cy.readFile(filename, { timeout: 15000 })
93 const childrenCollection = Array.prototype.slice.call(Cypress.$(body).find("dict")[0].children);
98 for (i = 0; i < childrenCollection.length; i += j) {
99 map[childrenCollection[i].outerText] = childrenCollection[i + 1].outerText;
102 cy.get("#simple-tabpanel-0")
105 const [host, port] = a.text().split("@")[1].split("/")[0].split(":");
106 expect(map["Protocol"]).to.equal("davs");
107 expect(map["UUID"]).to.equal(testCollection.uuid);
108 expect(map["Username"]).to.equal(activeUser.user.username);
109 expect(map["Port"]).to.equal(port);
110 expect(map["Hostname"]).to.equal(host);
112 expect(map["Path"]).to.equal(`/c=${testCollection.uuid}`);
116 .then(() => cy.task("clearDownload", { filename }));
120 it("attempts to use a preexisting name creating or updating a collection", function () {
121 const name = `Test collection ${Math.floor(Math.random() * 999999)}`;
122 cy.createCollection(adminUser.token, {
124 owner_uuid: activeUser.user.uuid,
125 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
127 cy.loginAs(activeUser);
128 cy.goToPath(`/projects/${activeUser.user.uuid}`);
129 cy.get("[data-cy=breadcrumb-first]").should("contain", "Projects");
130 cy.get("[data-cy=breadcrumb-last]").should("not.exist");
131 // Attempt to create new collection with a duplicate name
132 cy.get("[data-cy=side-panel-button]").click();
133 cy.get("[data-cy=side-panel-new-collection]").click();
134 cy.get("[data-cy=form-dialog]")
135 .should("contain", "New collection")
137 cy.get("[data-cy=name-field]").within(() => {
138 cy.get("input").type(name);
140 cy.get("[data-cy=form-submit-btn]").click();
142 // Error message should display, allowing editing the name
143 cy.get("[data-cy=form-dialog]")
145 .and("contain", "Collection with the same name already exists")
147 cy.get("[data-cy=name-field]").within(() => {
148 cy.get("input").type(" renamed");
150 cy.get("[data-cy=form-submit-btn]").click();
152 cy.get("[data-cy=form-dialog]").should("not.exist");
153 // Attempt to rename the collection with the duplicate name
154 cy.get("[data-cy=collection-panel-options-btn]").click();
155 cy.get("[data-cy=context-menu]").contains("Edit collection").click();
156 cy.get("[data-cy=form-dialog]")
157 .should("contain", "Edit Collection")
159 cy.get("[data-cy=name-field]").within(() => {
160 cy.get("input").type("{selectall}{backspace}").type(name);
162 cy.get("[data-cy=form-submit-btn]").click();
164 cy.get("[data-cy=form-dialog]").should("exist").and("contain", "Collection with the same name already exists");
169 it("uses the property editor (from edit dialog) with vocabulary terms", function () {
170 cy.createCollection(adminUser.token, {
171 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
172 owner_uuid: activeUser.user.uuid,
173 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
175 .as("testCollection")
177 cy.loginAs(activeUser);
178 cy.goToPath(`/collections/${this.testCollection.uuid}`);
180 cy.get("[data-cy=collection-info-panel").should("contain", this.testCollection.name).and("not.contain", "Color: Magenta");
182 cy.get("[data-cy=collection-panel-options-btn]").click();
183 cy.get("[data-cy=context-menu]").contains("Edit collection").click();
184 cy.get("[data-cy=form-dialog]").should("contain", "Properties");
186 // Key: Color (IDTAGCOLORS) - Value: Magenta (IDVALCOLORS3)
187 cy.get("[data-cy=resource-properties-form]").within(() => {
188 cy.get("[data-cy=property-field-key]").within(() => {
189 cy.get("input").type("Color");
191 cy.get("[data-cy=property-field-value]").within(() => {
192 cy.get("input").type("Magenta");
196 // Confirm proper vocabulary labels are displayed on the UI.
197 cy.get("[data-cy=form-dialog]").should("contain", "Color: Magenta");
198 cy.get("[data-cy=form-dialog]").contains("Save").click();
199 cy.get("[data-cy=form-dialog]").should("not.exist");
200 // Confirm proper vocabulary IDs were saved on the backend.
201 cy.doRequest("GET", `/arvados/v1/collections/${this.testCollection.uuid}`)
205 expect(this.collection.properties.IDTAGCOLORS).to.equal("IDVALCOLORS3");
207 // Confirm the property is displayed on the UI.
208 cy.get("[data-cy=collection-info-panel").should("contain", this.testCollection.name).and("contain", "Color: Magenta");
214 it("uses the editor (from details panel) with vocabulary terms", function () {
215 cy.createCollection(adminUser.token, {
216 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
217 owner_uuid: activeUser.user.uuid,
218 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
220 .as("testCollection")
222 cy.loginAs(activeUser);
223 cy.goToPath(`/collections/${this.testCollection.uuid}`);
225 cy.get("[data-cy=collection-info-panel")
226 .should("contain", this.testCollection.name)
227 .and("not.contain", "Color: Magenta")
228 .and("not.contain", "Size: S");
229 cy.get("[data-cy=additional-info-icon]").click();
231 cy.get("[data-cy=details-panel]").within(() => {
232 cy.get("[data-cy=details-panel-edit-btn]").click();
234 cy.get("[data-cy=form-dialog").contains("Edit Collection");
236 // Key: Color (IDTAGCOLORS) - Value: Magenta (IDVALCOLORS3)
237 cy.get("[data-cy=resource-properties-form]").within(() => {
238 cy.get("[data-cy=property-field-key]").within(() => {
239 cy.get("input").type("Color");
241 cy.get("[data-cy=property-field-value]").within(() => {
242 cy.get("input").type("Magenta");
246 // Confirm proper vocabulary labels are displayed on the UI.
247 cy.get("[data-cy=form-dialog]").should("contain", "Color: Magenta");
249 // Case-insensitive on-blur auto-selection test
250 // Key: Size (IDTAGSIZES) - Value: Small (IDVALSIZES2)
251 cy.get("[data-cy=resource-properties-form]").within(() => {
252 cy.get("[data-cy=property-field-key]").within(() => {
253 cy.get("input").type("sIzE");
255 cy.get("[data-cy=property-field-value]").within(() => {
256 cy.get("input").type("sMaLL");
258 // Cannot "type()" TAB on Cypress so let's click another field
259 // to trigger the onBlur event.
260 cy.get("[data-cy=property-field-key]").click();
263 // Confirm proper vocabulary labels are displayed on the UI.
264 cy.get("[data-cy=form-dialog]").should("contain", "Size: S");
266 cy.get("[data-cy=form-dialog]").contains("Save").click();
267 cy.get("[data-cy=form-dialog]").should("not.exist");
269 // Confirm proper vocabulary IDs were saved on the backend.
270 cy.doRequest("GET", `/arvados/v1/collections/${this.testCollection.uuid}`)
274 expect(this.collection.properties.IDTAGCOLORS).to.equal("IDVALCOLORS3");
275 expect(this.collection.properties.IDTAGSIZES).to.equal("IDVALSIZES2");
278 // Confirm properties display on the UI.
279 cy.get("[data-cy=collection-info-panel")
280 .should("contain", this.testCollection.name)
281 .and("contain", "Color: Magenta")
282 .and("contain", "Size: S");
286 it("shows collection by URL", function () {
287 cy.loginAs(activeUser);
288 [true, false].map(function (isWritable) {
289 // Using different file names to avoid test flakyness: the second iteration
290 // on this loop may pass an assertion from the first iteration by looking
291 // for the same file name.
292 const fileName = isWritable ? "bar" : "foo";
293 const subDirName = "subdir";
294 cy.createGroup(adminUser.token, {
295 name: "Shared project",
296 group_class: "project",
300 // Creates the collection using the admin token so we can set up
301 // a bogus manifest text without block signatures.
302 cy.doRequest("GET", "/arvados/v1/config", null, null)
304 .should(clusterConfig => {
305 expect(clusterConfig.Collections, "clusterConfig").to.have.property("TrustAllContent", true);
306 expect(clusterConfig.Services, "clusterConfig").to.have.property("WebDAV").have.property("ExternalURL");
307 expect(clusterConfig.Services, "clusterConfig").to.have.property("WebDAVDownload").have.property("ExternalURL");
309 clusterConfig.Services.WebDAV.ExternalURL !== ""
310 ? clusterConfig.Services.WebDAV.ExternalURL
311 : clusterConfig.Services.WebDAVDownload.ExternalURL;
312 expect(inlineUrl).to.not.contain("*");
314 .createCollection(adminUser.token, {
315 name: "Test collection",
316 owner_uuid: this.sharedGroup.uuid,
317 properties: { someKey: "someValue" },
318 manifest_text: `. 37b51d194a7513e45b56f6524f2d51f2+3 0:3:${fileName}\n./${subDirName} 37b51d194a7513e45b56f6524f2d51f2+3 0:3:${fileName}\n`,
320 .as("testCollection")
322 // Share the group with active user.
323 cy.createLink(adminUser.token, {
324 name: isWritable ? "can_write" : "can_read",
325 link_class: "permission",
326 head_uuid: this.sharedGroup.uuid,
327 tail_uuid: activeUser.user.uuid,
329 cy.goToPath(`/collections/${this.testCollection.uuid}`);
331 // Check that name & uuid are correct.
332 cy.get("[data-cy=collection-info-panel]")
333 .should("contain", this.testCollection.name)
334 .and("contain", this.testCollection.uuid)
335 .and("not.contain", "This is an old version");
336 // Check for the read-only icon
337 cy.get("[data-cy=read-only-icon]").should(`${isWritable ? "not." : ""}exist`);
338 // Check that both read and write operations are available on
339 // the 'More options' menu.
340 cy.get("[data-cy=collection-panel-options-btn]").click();
341 cy.get("[data-cy=context-menu]")
342 .should("contain", "Add to favorites")
343 .and(`${isWritable ? "" : "not."}contain`, "Edit collection");
344 cy.get("body").click(); // Collapse the menu avoiding details panel expansion
345 cy.get("[data-cy=collection-info-panel]")
346 .should("contain", "someKey: someValue")
347 .and("not.contain", "anotherKey: anotherValue");
348 // Check that the file listing show both read & write operations
350 .get("[data-cy=collection-files-panel]")
352 cy.get("[data-cy=collection-files-right-panel]", { timeout: 5000 }).should("contain", fileName);
354 cy.get("[data-cy=upload-button]").should(`${isWritable ? "" : "not."}contain`, "Upload data");
357 // Test context menus
358 cy.get("[data-cy=collection-files-panel]").contains(fileName).rightclick();
359 cy.get("[data-cy=context-menu]")
360 .should("contain", "Download")
361 .and("contain", "Open in new tab")
362 .and("contain", "Copy to clipboard")
363 .and(`${isWritable ? "" : "not."}contain`, "Rename")
364 .and(`${isWritable ? "" : "not."}contain`, "Remove");
365 cy.get("body").click(); // Collapse the menu
366 cy.get("[data-cy=collection-files-panel]").contains(subDirName).rightclick();
367 cy.get("[data-cy=context-menu]")
368 .should("not.contain", "Download")
369 .and("contain", "Open in new tab")
370 .and("contain", "Copy to clipboard")
371 .and(`${isWritable ? "" : "not."}contain`, "Rename")
372 .and(`${isWritable ? "" : "not."}contain`, "Remove");
373 cy.get("body").click(); // Collapse the menu
374 // File/dir item 'more options' button
375 cy.get("[data-cy=file-item-options-btn").first().click();
376 cy.get("[data-cy=context-menu]").should(`${isWritable ? "" : "not."}contain`, "Remove");
377 cy.get("body").click(); // Collapse the menu
378 // Hamburger 'more options' menu button
379 cy.get("[data-cy=collection-files-panel-options-btn]").click();
380 cy.get("[data-cy=context-menu]").should("contain", "Select all").click();
381 cy.get("[data-cy=collection-files-panel-options-btn]").click();
382 cy.get("[data-cy=context-menu]").should(`${isWritable ? "" : "not."}contain`, "Remove selected");
383 cy.get("body").click(); // Collapse the menu
389 it("renames a file using valid names", function () {
390 function eachPair(lst, func) {
391 for (var i = 0; i < lst.length - 1; i++) {
392 func(lst[i], lst[i + 1]);
395 // Creates the collection using the admin token so we can set up
396 // a bogus manifest text without block signatures.
397 cy.createCollection(adminUser.token, {
398 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
399 owner_uuid: activeUser.user.uuid,
400 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
402 .as("testCollection")
404 cy.loginAs(activeUser);
405 cy.goToPath(`/collections/${this.testCollection.uuid}`);
408 "bar", // initial name already set
415 "some name with whitespaces",
417 "is this name legal? I hope it is",
425 "G%C3%BCnter's%20file.pdf",
427 "bar", // make sure we can go back to the original name as a last step
429 cy.intercept({ method: "PUT", url: "**/arvados/v1/collections/*" }).as("renameRequest");
430 eachPair(names, (from, to) => {
431 cy.waitForDom().get("[data-cy=collection-files-panel]").contains(`${from}`).rightclick();
432 cy.get("[data-cy=context-menu]").contains("Rename").click();
433 cy.get("[data-cy=form-dialog]")
434 .should("contain", "Rename")
436 cy.get("input").type("{selectall}{backspace}").type(to, { parseSpecialCharSequences: false });
438 cy.get("[data-cy=form-submit-btn]").click();
439 cy.wait("@renameRequest");
440 cy.get("[data-cy=collection-files-panel]").should("not.contain", `${from}`).and("contain", `${to}`);
445 it("renames a file to a different directory", function () {
446 // Creates the collection using the admin token so we can set up
447 // a bogus manifest text without block signatures.
448 cy.createCollection(adminUser.token, {
449 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
450 owner_uuid: activeUser.user.uuid,
451 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
453 .as("testCollection")
455 cy.loginAs(activeUser);
456 cy.goToPath(`/collections/${this.testCollection.uuid}`);
458 ["subdir", "G%C3%BCnter's%20file", "table%&?*2"].forEach(subdir => {
459 cy.waitForDom().get("[data-cy=collection-files-panel]").contains("bar").rightclick();
460 cy.get("[data-cy=context-menu]").contains("Rename").click();
461 cy.get("[data-cy=form-dialog]")
462 .should("contain", "Rename")
464 cy.get("input").type(`{selectall}{backspace}${subdir}/foo`);
466 cy.get("[data-cy=form-submit-btn]").click();
467 cy.get("[data-cy=collection-files-panel]").should("not.contain", "bar").and("contain", subdir);
468 cy.get("[data-cy=collection-files-panel]").contains(subdir).click();
470 // Rename 'subdir/foo' to 'bar'
472 cy.get("[data-cy=collection-files-panel]").contains("foo").rightclick();
473 cy.get("[data-cy=context-menu]").contains("Rename").click();
474 cy.get("[data-cy=form-dialog]")
475 .should("contain", "Rename")
477 cy.get("input").should("have.value", `${subdir}/foo`).type(`{selectall}{backspace}bar`);
479 cy.get("[data-cy=form-submit-btn]").click();
481 // need to wait for dialog to dismiss
482 cy.get("[data-cy=form-dialog]").should("not.exist");
484 cy.waitForDom().get("[data-cy=collection-files-panel]").contains("Home").click();
487 cy.get("[data-cy=collection-files-panel]")
488 .should("contain", subdir) // empty dir kept
489 .and("contain", "bar");
491 cy.get("[data-cy=collection-files-panel]").contains(subdir).rightclick();
492 cy.get("[data-cy=context-menu]").contains("Remove").click();
493 cy.get("[data-cy=confirmation-dialog-ok-btn]").click();
494 cy.get("[data-cy=form-dialog]").should("not.exist");
499 it("shows collection owner", () => {
500 cy.createCollection(adminUser.token, {
501 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
502 owner_uuid: activeUser.user.uuid,
503 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
505 .as("testCollection")
506 .then(testCollection => {
507 cy.loginAs(activeUser);
508 cy.goToPath(`/collections/${testCollection.uuid}`);
510 cy.get("[data-cy=collection-info-panel]").contains(`Collection User`);
514 it("tries to rename a file with illegal names", function () {
515 // Creates the collection using the admin token so we can set up
516 // a bogus manifest text without block signatures.
517 cy.createCollection(adminUser.token, {
518 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
519 owner_uuid: activeUser.user.uuid,
520 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
522 .as("testCollection")
524 cy.loginAs(activeUser);
525 cy.goToPath(`/collections/${this.testCollection.uuid}`);
527 const illegalNamesFromUI = [
528 [".", "Name cannot be '.' or '..'"],
529 ["..", "Name cannot be '.' or '..'"],
530 ["", "This field is required"],
531 [" ", "Leading/trailing whitespaces not allowed"],
532 [" foo", "Leading/trailing whitespaces not allowed"],
533 ["foo ", "Leading/trailing whitespaces not allowed"],
534 ["//foo", "Empty dir name not allowed"],
536 illegalNamesFromUI.forEach(([name, errMsg]) => {
537 cy.get("[data-cy=collection-files-panel]").contains("bar").rightclick();
538 cy.get("[data-cy=context-menu]").contains("Rename").click();
539 cy.get("[data-cy=form-dialog]")
540 .should("contain", "Rename")
542 cy.get("input").type(`{selectall}{backspace}${name}`);
544 cy.get("[data-cy=form-dialog]")
545 .should("contain", "Rename")
547 cy.contains(`${errMsg}`);
549 cy.get("[data-cy=form-cancel-btn]").click();
554 it("can correctly display old versions", function () {
555 const colName = `Versioned Collection ${Math.floor(Math.random() * 999999)}`;
557 let oldVersionUuid = "";
558 // Make sure no other collections with this name exist
559 cy.doRequest("GET", "/arvados/v1/collections", null, {
560 filters: `[["name", "=", "${colName}"]]`,
561 include_old_versions: true,
566 expect(this.collections).to.be.empty;
568 // Creates the collection using the admin token so we can set up
569 // a bogus manifest text without block signatures.
570 cy.createCollection(adminUser.token, {
572 owner_uuid: activeUser.user.uuid,
573 preserve_version: true,
574 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
576 .as("originalVersion")
578 // Change the file name to create a new version.
579 cy.updateCollection(adminUser.token, this.originalVersion.uuid, {
580 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo\n",
582 colUuid = this.originalVersion.uuid;
584 // Confirm that there are 2 versions of the collection
585 cy.doRequest("GET", "/arvados/v1/collections", null, {
586 filters: `[["name", "=", "${colName}"]]`,
587 include_old_versions: true,
592 expect(this.collections).to.have.lengthOf(2);
593 this.collections.map(function (aCollection) {
594 expect(aCollection.current_version_uuid).to.equal(colUuid);
595 if (aCollection.uuid !== aCollection.current_version_uuid) {
596 oldVersionUuid = aCollection.uuid;
599 // Check the old version displays as what it is.
600 cy.loginAs(activeUser);
601 cy.goToPath(`/collections/${oldVersionUuid}`);
603 cy.get("[data-cy=collection-info-panel]").should("contain", "This is an old version");
604 cy.get("[data-cy=read-only-icon]").should("exist");
605 cy.get("[data-cy=collection-info-panel]").should("contain", colName);
606 cy.get("[data-cy=collection-files-panel]").should("contain", "bar");
610 it("views & edits storage classes data", function () {
611 const colName = `Test Collection ${Math.floor(Math.random() * 999999)}`;
612 cy.createCollection(adminUser.token, {
614 owner_uuid: activeUser.user.uuid,
615 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:some-file\n",
619 expect(this.collection.storage_classes_desired).to.deep.equal(["default"]);
621 cy.loginAs(activeUser);
622 cy.goToPath(`/collections/${this.collection.uuid}`);
624 // Initial check: it should show the 'default' storage class
625 cy.get("[data-cy=collection-info-panel]")
626 .should("contain", "Storage classes")
627 .and("contain", "default")
628 .and("not.contain", "foo")
629 .and("not.contain", "bar");
630 // Edit collection: add storage class 'foo'
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-foo]").click();
642 cy.get("[data-cy=form-submit-btn]").click();
643 cy.get("[data-cy=collection-info-panel]").should("contain", "default").and("contain", "foo").and("not.contain", "bar");
644 cy.doRequest("GET", `/arvados/v1/collections/${this.collection.uuid}`)
646 .as("updatedCollection")
648 expect(this.updatedCollection.storage_classes_desired).to.deep.equal(["default", "foo"]);
650 // Edit collection: remove storage class 'default'
651 cy.get("[data-cy=collection-panel-options-btn]").click();
652 cy.get("[data-cy=context-menu]").contains("Edit collection").click();
653 cy.get("[data-cy=form-dialog]")
654 .should("contain", "Edit Collection")
655 .and("contain", "Storage classes")
656 .and("contain", "default")
657 .and("contain", "foo")
658 .and("contain", "bar")
660 cy.get("[data-cy=checkbox-default]").click();
662 cy.get("[data-cy=form-submit-btn]").click();
663 cy.get("[data-cy=collection-info-panel]").should("not.contain", "default").and("contain", "foo").and("not.contain", "bar");
664 cy.doRequest("GET", `/arvados/v1/collections/${this.collection.uuid}`)
666 .as("updatedCollection")
668 expect(this.updatedCollection.storage_classes_desired).to.deep.equal(["foo"]);
673 it("moves a collection to a different project", function () {
674 const collName = `Test Collection ${Math.floor(Math.random() * 999999)}`;
675 const projName = `Test Project ${Math.floor(Math.random() * 999999)}`;
676 const fileName = `Test_File_${Math.floor(Math.random() * 999999)}`;
678 cy.createCollection(adminUser.token, {
680 owner_uuid: activeUser.user.uuid,
681 manifest_text: `. 37b51d194a7513e45b56f6524f2d51f2+3 0:3:${fileName}\n`,
682 }).as("testCollection");
683 cy.createGroup(adminUser.token, {
685 group_class: "project",
686 owner_uuid: activeUser.user.uuid,
687 }).as("testProject");
689 cy.getAll("@testCollection", "@testProject").then(function ([testCollection, testProject]) {
690 cy.loginAs(activeUser);
691 cy.goToPath(`/collections/${testCollection.uuid}`);
692 cy.get("[data-cy=collection-files-panel]").should("contain", fileName);
693 cy.get("[data-cy=collection-info-panel]").should("not.contain", projName).and("not.contain", testProject.uuid);
694 cy.get("[data-cy=collection-panel-options-btn]").click();
695 cy.get("[data-cy=context-menu]").contains("Move to").click();
696 cy.get("[data-cy=form-dialog]")
697 .should("contain", "Move to")
699 // must use .then to avoid selecting instead of expanding https://github.com/cypress-io/cypress/issues/5529
700 cy.get("[data-cy=projects-tree-home-tree-picker]")
702 .then(el => el.click());
703 cy.get("[data-cy=projects-tree-home-tree-picker]").contains(projName).click();
705 cy.get("[data-cy=form-submit-btn]").click();
706 cy.get("[data-cy=snackbar]").contains("Collection has been moved");
707 cy.get("[data-cy=collection-info-panel]").contains(projName).and("contain", testProject.uuid);
708 // Double check that the collection is in the project
709 cy.goToPath(`/projects/${testProject.uuid}`);
710 cy.waitForDom().get("[data-cy=project-panel]").should("contain", collName);
714 it("automatically updates the collection UI contents without using the Refresh button", function () {
715 const collName = `Test Collection ${Math.floor(Math.random() * 999999)}`;
717 cy.createCollection(adminUser.token, {
719 owner_uuid: activeUser.user.uuid,
720 }).as("testCollection");
722 cy.getAll("@testCollection").then(function ([testCollection]) {
723 cy.loginAs(activeUser);
725 const files = ["foobar", "anotherFile", "", "finalName"];
727 cy.goToPath(`/collections/${testCollection.uuid}`);
728 cy.get("[data-cy=collection-files-panel]").should("contain", "This collection is empty");
729 cy.get("[data-cy=collection-files-panel]").should("not.contain", files[0]);
730 cy.get("[data-cy=collection-info-panel]").should("contain", collName);
732 files.map((fileName, i, files) => {
733 cy.updateCollection(adminUser.token, testCollection.uuid, {
734 name: `${collName + " updated"}`,
735 manifest_text: fileName ? `. 37b51d194a7513e45b56f6524f2d51f2+3 0:3:${fileName}\n` : "",
736 }).as("updatedCollection");
737 cy.getAll("@updatedCollection").then(function ([updatedCollection]) {
738 expect(updatedCollection.name).to.equal(`${collName + " updated"}`);
739 cy.get("[data-cy=collection-info-panel]").should("contain", updatedCollection.name);
741 ? cy.get("[data-cy=collection-files-panel]").should("contain", fileName)
742 : cy.get("[data-cy=collection-files-panel]").should("not.contain", files[i - 1]);
748 it("makes a copy of an existing collection", function () {
749 const collName = `Test Collection ${Math.floor(Math.random() * 999999)}`;
750 const copyName = `Copy of: ${collName}`;
752 cy.createCollection(adminUser.token, {
754 owner_uuid: activeUser.user.uuid,
755 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:some-file\n",
759 cy.loginAs(activeUser);
760 cy.goToPath(`/collections/${this.collection.uuid}`);
761 cy.get("[data-cy=collection-files-panel]").should("contain", "some-file");
762 cy.get("[data-cy=collection-panel-options-btn]").click();
763 cy.get("[data-cy=context-menu]").contains("Make a copy").click();
764 cy.get("[data-cy=form-dialog]")
765 .should("contain", "Make a copy")
767 cy.get("[data-cy=projects-tree-home-tree-picker]").contains("Projects").click();
768 cy.get("[data-cy=form-submit-btn]").click();
770 cy.get("[data-cy=snackbar]").contains("Collection has been copied.");
771 cy.get("[data-cy=snackbar-goto-action]").click();
772 cy.get("[data-cy=project-panel]").contains(copyName).click();
773 cy.get("[data-cy=collection-files-panel]").should("contain", "some-file");
777 it("uses the collection version browser to view a previous version", function () {
778 const colName = `Test Collection ${Math.floor(Math.random() * 999999)}`;
780 // Creates the collection using the admin token so we can set up
781 // a bogus manifest text without block signatures.
782 cy.createCollection(adminUser.token, {
784 owner_uuid: activeUser.user.uuid,
785 preserve_version: true,
786 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n",
790 // Visit collection, check basic information
791 cy.loginAs(activeUser);
792 cy.goToPath(`/collections/${this.collection.uuid}`);
794 cy.get("[data-cy=collection-info-panel]").should("not.contain", "This is an old version");
795 cy.get("[data-cy=read-only-icon]").should("not.exist");
796 cy.get("[data-cy=collection-version-number]").should("contain", "1");
797 cy.get("[data-cy=collection-info-panel]").should("contain", colName);
798 cy.get("[data-cy=collection-files-panel]").should("contain", "foo").and("contain", "bar");
800 // Modify collection, expect version number change
801 cy.get("[data-cy=collection-files-panel]").contains("foo").rightclick();
802 cy.get("[data-cy=context-menu]").contains("Remove").click();
803 cy.get("[data-cy=confirmation-dialog]").should("contain", "Removing file");
804 cy.get("[data-cy=confirmation-dialog-ok-btn]").click();
805 cy.get("[data-cy=collection-version-number]").should("contain", "2");
806 cy.get("[data-cy=collection-files-panel]").should("not.contain", "foo").and("contain", "bar");
808 // Click on version number, check version browser. Click on past version.
809 cy.get("[data-cy=collection-version-browser]").should("not.exist");
810 cy.get("[data-cy=collection-version-number]").contains("2").click();
811 cy.get("[data-cy=collection-version-browser]")
812 .should("contain", "Nr")
813 .and("contain", "Size")
814 .and("contain", "Date")
816 // Version 1: 6 bytes in size
817 cy.get("[data-cy=collection-version-browser-select-1]")
818 .should("contain", "1")
819 .and("contain", "6 B")
820 .and("contain", adminUser.user.full_name);
821 // Version 2: 3 bytes in size (one file removed)
822 cy.get("[data-cy=collection-version-browser-select-2]")
823 .should("contain", "2")
824 .and("contain", "3 B")
825 .and("contain", activeUser.user.full_name);
826 cy.get("[data-cy=collection-version-browser-select-3]").should("not.exist");
827 cy.get("[data-cy=collection-version-browser-select-1]").click();
829 cy.get("[data-cy=collection-info-panel]").should("contain", "This is an old version");
830 cy.get("[data-cy=read-only-icon]").should("exist");
831 cy.get("[data-cy=collection-version-number]").should("contain", "1");
832 cy.get("[data-cy=collection-info-panel]").should("contain", colName);
833 cy.get("[data-cy=collection-files-panel]").should("contain", "foo").and("contain", "bar");
835 // Check that only old collection action are available on context menu
836 cy.get("[data-cy=collection-panel-options-btn]").click();
837 cy.get("[data-cy=context-menu]").should("contain", "Restore version").and("not.contain", "Add to favorites");
838 cy.get("body").click(); // Collapse the menu avoiding details panel expansion
840 // Click on "head version" link, confirm that it's the latest version.
841 cy.get("[data-cy=collection-info-panel]").contains("head version").click();
842 cy.get("[data-cy=collection-info-panel]").should("not.contain", "This is an old version");
843 cy.get("[data-cy=read-only-icon]").should("not.exist");
844 cy.get("[data-cy=collection-version-number]").should("contain", "2");
845 cy.get("[data-cy=collection-info-panel]").should("contain", colName);
846 cy.get("[data-cy=collection-files-panel]").should("not.contain", "foo").and("contain", "bar");
848 // Check that old collection action isn't available on context menu
849 cy.get("[data-cy=collection-panel-options-btn]").click();
850 cy.get("[data-cy=context-menu]").should("not.contain", "Restore version");
851 cy.get("body").click(); // Collapse the menu avoiding details panel expansion
853 // Make another change, confirm new version.
854 cy.get("[data-cy=collection-panel-options-btn]").click();
855 cy.get("[data-cy=context-menu]").contains("Edit collection").click();
856 cy.get("[data-cy=form-dialog]")
857 .should("contain", "Edit Collection")
860 cy.get("input").first().type(" renamed");
862 cy.get("[data-cy=form-submit-btn]").click();
863 cy.get("[data-cy=collection-info-panel]").should("not.contain", "This is an old version");
864 cy.get("[data-cy=read-only-icon]").should("not.exist");
865 cy.get("[data-cy=collection-version-number]").should("contain", "3");
866 cy.get("[data-cy=collection-info-panel]").should("contain", colName + " renamed");
867 cy.get("[data-cy=collection-files-panel]").should("not.contain", "foo").and("contain", "bar");
868 cy.get("[data-cy=collection-version-browser-select-3]").should("contain", "3").and("contain", "3 B");
870 // Check context menus on version browser
872 cy.get("[data-cy=collection-version-browser-select-3]").rightclick();
873 cy.get("[data-cy=context-menu]")
874 .should("contain", "Add to favorites")
875 .and("contain", "Make a copy")
876 .and("contain", "Edit collection");
877 cy.get("body").click();
878 // (and now an old version...)
879 cy.get("[data-cy=collection-version-browser-select-1]").rightclick();
880 cy.get("[data-cy=context-menu]")
881 .should("not.contain", "Add to favorites")
882 .and("contain", "Make a copy")
883 .and("not.contain", "Edit collection");
884 cy.get("body").click();
886 // Restore first version
887 cy.get("[data-cy=collection-version-browser]").within(() => {
888 cy.get("[data-cy=collection-version-browser-select-1]").click();
890 cy.get("[data-cy=collection-panel-options-btn]").click();
891 cy.get("[data-cy=context-menu]").contains("Restore version").click();
892 cy.get("[data-cy=confirmation-dialog]").should("contain", "Restore version");
893 cy.get("[data-cy=confirmation-dialog-ok-btn]").click();
894 cy.get("[data-cy=collection-info-panel]").should("not.contain", "This is an old version");
895 cy.get("[data-cy=collection-version-number]").should("contain", "4");
896 cy.get("[data-cy=collection-info-panel]").should("contain", colName);
897 cy.get("[data-cy=collection-files-panel]").should("contain", "foo").and("contain", "bar");
901 it("copies selected files into new collection", () => {
902 cy.createCollection(adminUser.token, {
903 name: `Test Collection ${Math.floor(Math.random() * 999999)}`,
904 owner_uuid: activeUser.user.uuid,
905 preserve_version: true,
906 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n",
910 // Visit collection, check basic information
911 cy.loginAs(activeUser);
912 cy.goToPath(`/collections/${this.collection.uuid}`);
914 cy.get("[data-cy=collection-files-panel]").within(() => {
915 cy.get("input[type=checkbox]").first().click();
918 cy.get("[data-cy=collection-files-panel-options-btn]").click();
919 cy.get("[data-cy=context-menu]").contains("Copy selected into new collection").click();
921 cy.get("[data-cy=form-dialog]").contains("Projects").click();
923 cy.get("[data-cy=form-submit-btn]").click();
925 cy.waitForDom().get(".layout-pane-primary", { timeout: 12000 }).contains("Projects").click();
927 cy.waitForDom().get("main").contains(`Files extracted from: ${this.collection.name}`).click();
928 cy.get("[data-cy=collection-files-panel]").and("contain", "bar");
932 it("copies selected files into existing collection", () => {
933 cy.createCollection(adminUser.token, {
934 name: `Test Collection ${Math.floor(Math.random() * 999999)}`,
935 owner_uuid: activeUser.user.uuid,
936 preserve_version: true,
937 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n",
938 }).as("sourceCollection");
940 cy.createCollection(adminUser.token, {
941 name: `Destination Collection ${Math.floor(Math.random() * 999999)}`,
942 owner_uuid: activeUser.user.uuid,
943 preserve_version: true,
945 }).as("destinationCollection");
947 cy.getAll("@sourceCollection", "@destinationCollection").then(function ([sourceCollection, destinationCollection]) {
948 // Visit collection, check basic information
949 cy.loginAs(activeUser);
950 cy.goToPath(`/collections/${sourceCollection.uuid}`);
952 cy.get("[data-cy=collection-files-panel]").within(() => {
953 cy.get("input[type=checkbox]").first().click();
956 cy.get("[data-cy=collection-files-panel-options-btn]").click();
957 cy.get("[data-cy=context-menu]").contains("Copy selected into existing collection").click();
959 cy.get("[data-cy=form-dialog]").contains(destinationCollection.name).click();
961 cy.get("[data-cy=form-submit-btn]").click();
964 cy.goToPath(`/collections/${destinationCollection.uuid}`);
966 cy.get("main").contains(destinationCollection.name).should("exist");
967 cy.get("[data-cy=collection-files-panel]").and("contain", "bar");
971 it("copies selected files into separate collections", () => {
972 cy.createCollection(adminUser.token, {
973 name: `Test Collection ${Math.floor(Math.random() * 999999)}`,
974 owner_uuid: activeUser.user.uuid,
975 preserve_version: true,
976 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n",
977 }).as("sourceCollection");
979 cy.getAll("@sourceCollection").then(function ([sourceCollection]) {
980 // Visit collection, check basic information
981 cy.loginAs(activeUser);
982 cy.goToPath(`/collections/${sourceCollection.uuid}`);
986 .get("[data-cy=collection-files-panel]")
988 cy.get("input[type=checkbox]").first().click();
989 cy.get("input[type=checkbox]").last().click();
992 // Copy to separate collections
993 cy.get("[data-cy=collection-files-panel-options-btn]").click();
994 cy.get("[data-cy=context-menu]").contains("Copy selected into separate collections").click();
995 cy.get("[data-cy=form-dialog]").contains("Projects").click();
996 cy.get("[data-cy=form-submit-btn]").click();
998 // Verify created collections
999 cy.waitForDom().get(".layout-pane-primary", { timeout: 12000 }).contains("Projects").click();
1000 cy.get("main").contains(`File copied from collection ${sourceCollection.name}/foo`).click();
1001 cy.get("[data-cy=collection-files-panel]").and("contain", "foo");
1002 cy.get(".layout-pane-primary").contains("Projects").click();
1003 cy.get("main").contains(`File copied from collection ${sourceCollection.name}/bar`).click();
1004 cy.get("[data-cy=collection-files-panel]").and("contain", "bar");
1006 // Verify separate collection menu items not present when single file selected
1007 // Wait for dom for collection to re-render
1009 .get("[data-cy=collection-files-panel]")
1011 cy.get("input[type=checkbox]").first().click();
1013 cy.get("[data-cy=collection-files-panel-options-btn]").click();
1014 cy.get("[data-cy=context-menu]").should("not.contain", "Copy selected into separate collections");
1015 cy.get("[data-cy=context-menu]").should("not.contain", "Move selected into separate collections");
1019 it("moves selected files into new collection", () => {
1020 cy.createCollection(adminUser.token, {
1021 name: `Test Collection ${Math.floor(Math.random() * 999999)}`,
1022 owner_uuid: activeUser.user.uuid,
1023 preserve_version: true,
1024 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n",
1028 // Visit collection, check basic information
1029 cy.loginAs(activeUser);
1030 cy.goToPath(`/collections/${this.collection.uuid}`);
1032 cy.get("[data-cy=collection-files-panel]").within(() => {
1033 cy.get("input[type=checkbox]").first().click();
1036 cy.get("[data-cy=collection-files-panel-options-btn]").click();
1037 cy.get("[data-cy=context-menu]").contains("Move selected into new collection").click();
1039 cy.get("[data-cy=form-dialog]").contains("Projects").click();
1041 cy.get("[data-cy=form-submit-btn]").click();
1043 cy.waitForDom().get(".layout-pane-primary", { timeout: 12000 }).contains("Projects").click();
1045 cy.get("main").contains(`Files moved from: ${this.collection.name}`).click();
1046 cy.get("[data-cy=collection-files-panel]").and("contain", "bar");
1050 it("moves selected files into existing collection", () => {
1051 cy.createCollection(adminUser.token, {
1052 name: `Test Collection ${Math.floor(Math.random() * 999999)}`,
1053 owner_uuid: activeUser.user.uuid,
1054 preserve_version: true,
1055 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n",
1056 }).as("sourceCollection");
1058 cy.createCollection(adminUser.token, {
1059 name: `Destination Collection ${Math.floor(Math.random() * 999999)}`,
1060 owner_uuid: activeUser.user.uuid,
1061 preserve_version: true,
1063 }).as("destinationCollection");
1065 cy.getAll("@sourceCollection", "@destinationCollection").then(function ([sourceCollection, destinationCollection]) {
1066 // Visit collection, check basic information
1067 cy.loginAs(activeUser);
1068 cy.goToPath(`/collections/${sourceCollection.uuid}`);
1070 cy.get("[data-cy=collection-files-panel]").within(() => {
1071 cy.get("input[type=checkbox]").first().click();
1074 cy.get("[data-cy=collection-files-panel-options-btn]").click();
1075 cy.get("[data-cy=context-menu]").contains("Move selected into existing collection").click();
1077 cy.get("[data-cy=form-dialog]").contains(destinationCollection.name).click();
1079 cy.get("[data-cy=form-submit-btn]").click();
1082 cy.goToPath(`/collections/${destinationCollection.uuid}`);
1084 cy.get("main").contains(destinationCollection.name).should("exist");
1085 cy.get("[data-cy=collection-files-panel]").and("contain", "bar");
1089 it("moves selected files into separate collections", () => {
1090 cy.createCollection(adminUser.token, {
1091 name: `Test Collection ${Math.floor(Math.random() * 999999)}`,
1092 owner_uuid: activeUser.user.uuid,
1093 preserve_version: true,
1094 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo 0:3:bar\n",
1095 }).as("sourceCollection");
1097 cy.getAll("@sourceCollection").then(function ([sourceCollection]) {
1098 // Visit collection, check basic information
1099 cy.loginAs(activeUser);
1100 cy.goToPath(`/collections/${sourceCollection.uuid}`);
1102 // Select both files
1103 cy.get("[data-cy=collection-files-panel]").within(() => {
1104 cy.get("input[type=checkbox]").first().click();
1105 cy.get("input[type=checkbox]").last().click();
1108 // Copy to separate collections
1109 cy.get("[data-cy=collection-files-panel-options-btn]").click();
1110 cy.get("[data-cy=context-menu]").contains("Move selected into separate collections").click();
1111 cy.get("[data-cy=form-dialog]").contains("Projects").click();
1112 cy.get("[data-cy=form-submit-btn]").click();
1114 // Verify created collections
1115 cy.waitForDom().get(".layout-pane-primary", { timeout: 12000 }).contains("Projects").click();
1116 cy.get("main").contains(`File moved from collection ${sourceCollection.name}/foo`).click();
1117 cy.get("[data-cy=collection-files-panel]").and("contain", "foo");
1118 cy.get(".layout-pane-primary").contains("Projects").click();
1119 cy.get("main").contains(`File moved from collection ${sourceCollection.name}/bar`).click();
1120 cy.get("[data-cy=collection-files-panel]").and("contain", "bar");
1124 it("creates new collection with properties on home project", function () {
1125 cy.loginAs(activeUser);
1126 cy.goToPath(`/projects/${activeUser.user.uuid}`);
1127 cy.get("[data-cy=breadcrumb-first]").should("contain", "Projects");
1128 cy.get("[data-cy=breadcrumb-last]").should("not.exist");
1129 // Create new collection
1130 cy.get("[data-cy=side-panel-button]").click();
1131 cy.get("[data-cy=side-panel-new-collection]").click();
1132 // Name between brackets tests bugfix #17582
1133 const collName = `[Test collection (${Math.floor(999999 * Math.random())})]`;
1135 // Select a storage class.
1136 cy.get("[data-cy=form-dialog]")
1137 .should("contain", "New collection")
1138 .and("contain", "Storage classes")
1139 .and("contain", "default")
1140 .and("contain", "foo")
1141 .and("contain", "bar")
1143 cy.get("[data-cy=parent-field]").within(() => {
1144 cy.get("input").should("have.value", "Home project");
1146 cy.get("[data-cy=name-field]").within(() => {
1147 cy.get("input").type(collName);
1149 cy.get("[data-cy=checkbox-foo]").click();
1153 // Key: Color (IDTAGCOLORS) - Value: Magenta (IDVALCOLORS3)
1154 cy.get("[data-cy=form-dialog]").should("not.contain", "Color: Magenta");
1155 cy.get("[data-cy=resource-properties-form]").within(() => {
1156 cy.get("[data-cy=property-field-key]").within(() => {
1157 cy.get("input").type("Color");
1159 cy.get("[data-cy=property-field-value]").within(() => {
1160 cy.get("input").type("Magenta");
1164 // Confirm proper vocabulary labels are displayed on the UI.
1165 cy.get("[data-cy=form-dialog]").should("contain", "Color: Magenta");
1167 // Value field should not complain about being required just after
1168 // adding a new property. See #19732
1169 cy.get("[data-cy=form-dialog]").should("not.contain", "This field is required");
1171 cy.get("[data-cy=form-submit-btn]").click();
1172 // Confirm that the user was taken to the newly created collection
1173 cy.get("[data-cy=form-dialog]").should("not.exist");
1174 cy.get("[data-cy=breadcrumb-first]").should("contain", "Projects");
1175 cy.get("[data-cy=breadcrumb-last]").should("contain", collName);
1176 cy.get("[data-cy=collection-info-panel]")
1177 .should("contain", "default")
1178 .and("contain", "foo")
1179 .and("contain", "Color: Magenta")
1180 .and("not.contain", "bar");
1181 // Confirm that the collection's properties has the real values.
1182 cy.doRequest("GET", "/arvados/v1/collections", null, {
1183 filters: `[["name", "=", "${collName}"]]`,
1188 expect(this.collections).to.have.lengthOf(1);
1189 expect(this.collections[0].properties).to.have.property("IDTAGCOLORS", "IDVALCOLORS3");
1193 it("shows responsible person for collection if available", () => {
1194 cy.createCollection(adminUser.token, {
1195 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
1196 owner_uuid: activeUser.user.uuid,
1197 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
1198 }).as("testCollection1");
1200 cy.createCollection(adminUser.token, {
1201 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
1202 owner_uuid: adminUser.user.uuid,
1203 manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
1205 .as("testCollection2")
1206 .then(function (testCollection2) {
1207 cy.shareWith(adminUser.token, activeUser.user.uuid, testCollection2.uuid, "can_write");
1210 cy.getAll("@testCollection1", "@testCollection2").then(function ([testCollection1, testCollection2]) {
1211 cy.loginAs(activeUser);
1213 cy.goToPath(`/collections/${testCollection1.uuid}`);
1214 cy.get("[data-cy=responsible-person-wrapper]").contains(activeUser.user.uuid);
1216 cy.goToPath(`/collections/${testCollection2.uuid}`);
1217 cy.get("[data-cy=responsible-person-wrapper]").contains(adminUser.user.uuid);
1221 describe("file upload", () => {
1223 cy.createCollection(adminUser.token, {
1224 name: `Test collection ${Math.floor(Math.random() * 999999)}`,
1225 owner_uuid: activeUser.user.uuid,
1226 manifest_text: "./subdir 37b51d194a7513e45b56f6524f2d51f2+3 0:3:foo\n. 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n",
1227 }).as("testCollection1");
1230 it("uploads a file and checks the collection UI to be fresh", () => {
1231 cy.getAll("@testCollection1").then(function ([testCollection1]) {
1232 cy.loginAs(activeUser);
1233 cy.goToPath(`/collections/${testCollection1.uuid}`);
1234 cy.get("[data-cy=upload-button]").click();
1235 cy.get("[data-cy=collection-files-panel]").contains("5mb_a.bin").should("not.exist");
1236 cy.get("[data-cy=collection-file-count]").should("contain", "2");
1237 cy.fixture("files/5mb.bin", "base64").then(content => {
1238 cy.get("[data-cy=drag-and-drop]").upload(content, "5mb_a.bin");
1239 cy.get("[data-cy=form-submit-btn]").click();
1240 cy.get("[data-cy=form-submit-btn]").should("not.exist");
1241 cy.get("[data-cy=collection-files-panel]").contains("5mb_a.bin").should("exist");
1242 cy.get("[data-cy=collection-file-count]").should("contain", "3");
1244 cy.get("[data-cy=collection-files-panel]").contains("subdir").click();
1245 cy.get("[data-cy=upload-button]").click();
1246 cy.fixture("files/5mb.bin", "base64").then(content => {
1247 cy.get("[data-cy=drag-and-drop]").upload(content, "5mb_b.bin");
1248 cy.get("[data-cy=form-submit-btn]").click();
1249 cy.waitForDom().get("[data-cy=form-submit-btn]").should("not.exist");
1250 // subdir gets unselected, I think this is a bug but
1251 // for the time being let's just make sure the test works.
1252 cy.get("[data-cy=collection-files-panel]").contains("subdir").click();
1253 cy.waitForDom().get("[data-cy=collection-files-right-panel]").contains("5mb_b.bin").should("exist");
1259 it("allows to cancel running upload", () => {
1260 cy.getAll("@testCollection1").then(function ([testCollection1]) {
1261 cy.loginAs(activeUser);
1263 cy.goToPath(`/collections/${testCollection1.uuid}`);
1265 cy.get("[data-cy=upload-button]").click();
1267 cy.fixture("files/5mb.bin", "base64").then(content => {
1268 cy.get("[data-cy=drag-and-drop]").upload(content, "5mb_a.bin");
1269 cy.get("[data-cy=drag-and-drop]").upload(content, "5mb_b.bin");
1271 cy.get("[data-cy=form-submit-btn]").click();
1273 cy.get("button").contains("Cancel").click();
1275 cy.get("[data-cy=form-submit-btn]").should("not.exist");
1280 it("allows to cancel single file from the running upload", () => {
1281 cy.getAll("@testCollection1").then(function ([testCollection1]) {
1282 cy.loginAs(activeUser);
1284 cy.goToPath(`/collections/${testCollection1.uuid}`);
1286 cy.get("[data-cy=upload-button]").click();
1288 cy.fixture("files/5mb.bin", "base64").then(content => {
1289 cy.get("[data-cy=drag-and-drop]").upload(content, "5mb_a.bin");
1290 cy.get("[data-cy=drag-and-drop]").upload(content, "5mb_b.bin");
1292 cy.get("[data-cy=form-submit-btn]").click();
1294 cy.get("button[aria-label=Remove]").eq(1).click();
1296 cy.get("[data-cy=form-submit-btn]").should("not.exist");
1298 cy.get("[data-cy=collection-files-panel]").contains("5mb_a.bin").should("exist");
1303 it("allows to cancel all files from the running upload", () => {
1304 cy.getAll("@testCollection1").then(function ([testCollection1]) {
1305 cy.loginAs(activeUser);
1307 cy.goToPath(`/collections/${testCollection1.uuid}`);
1309 // Confirm initial collection state.
1310 cy.get("[data-cy=collection-files-panel]").contains("bar").should("exist");
1311 cy.get("[data-cy=collection-files-panel]").contains("5mb_a.bin").should("not.exist");
1312 cy.get("[data-cy=collection-files-panel]").contains("5mb_b.bin").should("not.exist");
1314 cy.get("[data-cy=upload-button]").click();
1316 cy.fixture("files/5mb.bin", "base64").then(content => {
1317 cy.get("[data-cy=drag-and-drop]").upload(content, "5mb_a.bin");
1318 cy.get("[data-cy=drag-and-drop]").upload(content, "5mb_b.bin");
1320 cy.get("[data-cy=form-submit-btn]").click();
1322 cy.get("button[aria-label=Remove]").should("exist");
1323 cy.get("button[aria-label=Remove]").click({ multiple: true, force: true });
1325 cy.get("[data-cy=form-submit-btn]").should("not.exist");
1327 // Confirm final collection state.
1328 cy.get("[data-cy=collection-files-panel]").contains("bar").should("exist");
1329 // The following fails, but doesn't seem to happen
1330 // in the real world. Maybe there's a race between
1331 // the PUT request finishing and the 'Remove' button
1332 // dissapearing, because sometimes just one of the 2
1333 // files gets uploaded.
1334 // Maybe this will be needed to simulate a slow network:
1335 // https://docs.cypress.io/api/commands/intercept#Convenience-functions-1
1336 // cy.get('[data-cy=collection-files-panel]')
1337 // .contains('5mb_a.bin').should('not.exist');
1338 // cy.get('[data-cy=collection-files-panel]')
1339 // .contains('5mb_b.bin').should('not.exist');