diff --git a/README.md b/README.md index 90b33b6f84..0c0e86d86f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Spruce is the React UI for MongoDB's continuous integration software. 1. Clone the Spruce Github repository 2. Ensure you have Node.js v16+ and MongoDB Command Line Database Tools v100.8.0+ installed -3. Ask a colleague for theiar .cmdrc.json file and follow the instructions +3. Ask a colleague for their .cmdrc.json file and follow the instructions [here](#environment-variables) 4. Run `yarn` 5. Start a local evergreen server by doing the following: diff --git a/cypress/integration/preferences/public_key_management.ts b/cypress/integration/preferences/public_key_management.ts index eef90ca89b..45848547f4 100644 --- a/cypress/integration/preferences/public_key_management.ts +++ b/cypress/integration/preferences/public_key_management.ts @@ -1,10 +1,9 @@ -describe("Public Key Management Page", { testIsolation: false }, () => { +describe("Public Key Management Page", () => { const route = "/preferences/publickeys"; - + beforeEach(() => { + cy.visit(route); + }); describe("Public keys list", () => { - before(() => { - cy.visit(route); - }); it("Displays the user's public keys", () => { cy.dataCy("table-key-name").each(($el, index) => cy.wrap($el).contains([keyName1, keyName2][index]) @@ -18,6 +17,8 @@ describe("Public Key Management Page", { testIsolation: false }, () => { cy.dataCy("table-key-name").first().contains(keyName2); }); it('Displays "No keys saved. Add a new key to populate the list." when no keys are available', () => { + cy.dataCy("delete-btn").first().click(); + cy.contains("button", "Yes").click(); cy.dataCy("delete-btn").first().click(); cy.contains("button", "Yes").click(); cy.contains("No keys saved. Add a new key to populate the list."); @@ -25,11 +26,10 @@ describe("Public Key Management Page", { testIsolation: false }, () => { }); describe("Add New Key Modal", () => { - before(() => { - cy.visit(route); + beforeEach(() => { + cy.dataCy("add-key-button").click(); }); it("Displays errors when the modal opens", () => { - cy.dataCy("add-key-button").click(); cy.dataCy("error-message").each(($el, index) => cy.wrap($el).contains([err1, err2][index]) ); @@ -42,15 +42,16 @@ describe("Public Key Management Page", { testIsolation: false }, () => { }); it("Should include the public in the public key list after adding", () => { + cy.dataCy("key-name-input").clear(); + cy.dataCy("key-name-input").type(keyName3); cy.dataCy("key-value-input").clear(); cy.dataCy("key-value-input").type(pubKey, { delay: 0 }); cy.contains("button", "Save").click(); - cy.dataCy("table-key-name").first().contains(keyName3); + cy.dataCy("table-key-name").eq(1).contains(keyName3); }); it("Should show an error if the key name already exists", () => { - cy.dataCy("add-key-button").click(); - cy.dataCy("key-name-input").type(keyName3, { delay: 0 }); + cy.dataCy("key-name-input").type(keyName2, { delay: 0 }); cy.dataCy("error-message").first().contains(err3); }); @@ -60,40 +61,40 @@ describe("Public Key Management Page", { testIsolation: false }, () => { }); describe("Edit Key Modal", () => { - before(() => { + beforeEach(() => { cy.visit(route); + cy.dataCy("edit-btn").first().click(); }); it("Should not have any errors when the modal opens", () => { - cy.dataCy("edit-btn").first().click(); cy.dataCy("error-message").should("have.length", 0); }); it("After submitting, the key name and key value are updated", () => { cy.dataCy("key-name-input").clear(); cy.dataCy("key-name-input").type(keyName4); - cy.dataCy("key-value-input").clear(); + cy.dataCy("key-value-input").clear(); cy.dataCy("key-value-input").type(pubKey2, { delay: 0 }); cy.contains("button", "Save").click(); cy.dataCy("key-edit-modal").should("not.exist"); - cy.dataCy("table-key-name").first().contains(keyName4); - cy.dataCy("edit-btn").first().click(); + cy.dataCy("table-key-name").eq(1).contains(keyName4); + cy.dataCy("edit-btn").eq(1).click(); cy.dataCy("key-name-input").should("have.value", keyName4); cy.dataCy("key-value-input").should("have.value", pubKey2); cy.dataCy("key-value-input").clear(); cy.dataCy("key-value-input").type(pubKey3, { delay: 0 }); cy.contains("button", "Save").click(); cy.dataCy("key-edit-modal").should("not.exist"); - cy.dataCy("table-key-name").first().contains(keyName4); - cy.dataCy("edit-btn").first().click(); + cy.dataCy("table-key-name").eq(1).contains(keyName4); + cy.dataCy("edit-btn").eq(1).click(); cy.dataCy("key-name-input").should("have.value", keyName4); cy.dataCy("key-value-input").should("have.value", pubKey3); cy.dataCy("key-value-input").clear(); cy.dataCy("key-value-input").type(pubKey4, { delay: 0 }); cy.contains("button", "Save").click(); cy.dataCy("key-edit-modal").should("not.exist"); - cy.dataCy("table-key-name").first().contains(keyName4); - cy.dataCy("edit-btn").first().click(); + cy.dataCy("table-key-name").eq(1).contains(keyName4); + cy.dataCy("edit-btn").eq(1).click(); cy.dataCy("key-name-input").should("have.value", keyName4); cy.dataCy("key-value-input").should("have.value", pubKey4); }); diff --git a/cypress/integration/projectSettings/admin_actions.ts b/cypress/integration/projectSettings/admin_actions.ts index 3686908075..229cdb049b 100644 --- a/cypress/integration/projectSettings/admin_actions.ts +++ b/cypress/integration/projectSettings/admin_actions.ts @@ -19,11 +19,10 @@ describe("Duplicating a project", () => { }); }); -describe("Creating a new project", () => { - const destination = getGeneralRoute(project); - - it("Successfully creates a new project", () => { - cy.visit(destination); +describe("Creating a new project and deleting it", () => { + it("Successfully creates a new project and then deletes it", () => { + // Create project + cy.visit(getGeneralRoute(project)); cy.dataCy("new-project-button").click(); cy.dataCy("new-project-menu").should("be.visible"); cy.dataCy("create-project-button").click(); @@ -38,14 +37,9 @@ describe("Creating a new project", () => { cy.validateToast("success"); cy.url().should("include", "my-new-project"); - }); -}); - -describe("Deleting a project", () => { - const destination = getGeneralRoute("my-new-project"); - it("Successfully deletes a project", () => { - cy.visit(destination); + // Delete project + cy.visit(getGeneralRoute("my-new-project")); cy.dataCy("attach-repo-button").click(); cy.dataCy("attach-repo-modal") .find("button") diff --git a/cypress/integration/projectSettings/containers.ts b/cypress/integration/projectSettings/containers.ts index 9debaebd05..decf6265d4 100644 --- a/cypress/integration/projectSettings/containers.ts +++ b/cypress/integration/projectSettings/containers.ts @@ -14,7 +14,8 @@ describe("Containers", () => { it("shouldn't be able to save anything if no changes were made", () => { saveButtonEnabled(false); }); - it("should be able to add a container configuration and save it", () => { + it("should be able to add and save container configuration and then delete it", () => { + // Add configuration cy.dataCy("add-button").should("be.visible"); cy.dataCy("add-button").trigger("mouseover").click(); cy.dataCy("container-size-row").should("exist"); @@ -31,8 +32,8 @@ describe("Containers", () => { cy.dataCy("save-settings-button").scrollIntoView(); clickSave(); cy.validateToast("success", "Successfully updated project"); - }); - it("should be able to delete a container configuration", () => { + + // Delete configuration cy.dataCy("container-size-row").should("exist"); cy.dataCy("delete-item-button").should("be.visible"); cy.dataCy("delete-item-button").should("not.be.disabled"); diff --git a/cypress/integration/projectSettings/plugins.ts b/cypress/integration/projectSettings/plugins.ts index ece6df511c..ba5ae344a8 100644 --- a/cypress/integration/projectSettings/plugins.ts +++ b/cypress/integration/projectSettings/plugins.ts @@ -3,10 +3,9 @@ import { clickSave } from "../../utils"; describe("Plugins", () => { const patchPage = "version/5ecedafb562343215a7ff297"; - beforeEach(() => { + it("Should set an external link to render on patch metadata panel and then unset it to revert the changes", () => { + // Set the external link cy.visit(getPluginsRoute(projectUseRepoEnabled)); - }); - it("Should set an external link to render on patch metadata panel", () => { cy.dataCy("requesters-input").click(); cy.getInputByLabel("Commits").check({ force: true }); cy.getInputByLabel("Patches").check({ force: true }); @@ -24,8 +23,9 @@ describe("Plugins", () => { "href", "https://example.com/5ecedafb562343215a7ff297" ); - }); - it("Unsetting the external link should remove it from the patch metadata panel", () => { + + // Unset the external link + cy.visit(getPluginsRoute(projectUseRepoEnabled)); cy.dataCy("requesters-input").click(); cy.getInputByLabel("Commits").uncheck({ force: true }); cy.getInputByLabel("Patches").uncheck({ force: true }); diff --git a/cypress/integration/spawn/volume.ts b/cypress/integration/spawn/volume.ts index 72fc3a52e4..20515086c8 100644 --- a/cypress/integration/spawn/volume.ts +++ b/cypress/integration/spawn/volume.ts @@ -89,7 +89,7 @@ describe("Navigating to Spawn Volume page", { testIsolation: false }, () => { "1de2728dd9de82efc02dc21f6ca046eaa559462414d28e0b6bba6436436ac873" ).should("not.exist"); cy.dataCy("mounted-badge").contains("8 Mounted"); - cy.dataCy("free-badge").contains("3 Free"); + cy.dataCy("free-badge").contains("4 Free"); }); it("Clicking on unmount should result in a success toast appearing.", () => { diff --git a/cypress/integration/task/task_actions.ts b/cypress/integration/task/task_actions.ts index f6558fd043..9f89a6f3d2 100644 --- a/cypress/integration/task/task_actions.ts +++ b/cypress/integration/task/task_actions.ts @@ -1,4 +1,4 @@ -describe("Task Action Buttons", { testIsolation: false }, () => { +describe("Task Action Buttons", () => { describe("Based on the state of the task, some buttons should be disabled and others should be clickable. Clicking on buttons produces banners messaging if the action succeeded or failed.", () => { it("Schedule button should be disabled on a completed task", () => { cy.visit(tasks[1]); @@ -12,6 +12,7 @@ describe("Task Action Buttons", { testIsolation: false }, () => { }); it("Clicking Unschedule button should unschedule a task and display a success toast", () => { + cy.visit(tasks[5]); cy.dataCy("ellipsis-btn").click(); cy.dataCy("card-dropdown").should("be.visible"); cy.dataCy("unschedule-task").click(); @@ -19,13 +20,14 @@ describe("Task Action Buttons", { testIsolation: false }, () => { }); it("Abort button should be disabled on completed tasks", () => { + cy.visit(tasks[3]); cy.dataCy("ellipsis-btn").click(); cy.dataCy("card-dropdown").should("be.visible"); cy.dataCy("abort-task").should("have.attr", "disabled"); }); it("Clicking on set priority, entering a priority value and submitting should result in a success toast.", () => { - cy.visit(tasks[3]); + cy.visit(tasks[5]); cy.dataCy("ellipsis-btn").click(); cy.dataCy("card-dropdown").should("be.visible"); cy.dataCy("prioritize-task").click(); @@ -42,7 +44,7 @@ describe("Task Action Buttons", { testIsolation: false }, () => { }); it("Should correctly disable/enable the task when clicked", () => { - cy.visit(tasks[1]); + cy.visit(tasks[5]); cy.dataCy("ellipsis-btn").click(); cy.dataCy("card-dropdown").should("be.visible"); cy.dataCy("disable-enable").click(); @@ -67,10 +69,10 @@ describe("Task Action Buttons", { testIsolation: false }, () => { const prioritySuccessBannerText = "Priority for task updated to 99"; const restartSuccessBannerText = "Task scheduled to restart"; const unscheduleSuccessBannerText = "Task marked as unscheduled"; - const tasks = { 1: "/task/evergreen_ubuntu1604_test_model_patch_5e823e1f28baeaa22ae00823d83e03082cd148ab_5e4ff3abe3c3317e352062e4_20_02_21_15_13_48", 2: "/task/evergreen_lint_lint_service_patch_5e823e1f28baeaa22ae00823d83e03082cd148ab_5e4ff3abe3c3317e352062e4_20_02_21_15_13_48", 3: "/task/evergreen_ubuntu1604_dist_patch_33016573166a36bd5f46b4111151899d5c4e95b1_5ecedafb562343215a7ff297_20_05_27_21_39_46", 4: "/task/mci_ubuntu1604_display_asdf_patch_a1d2c8f70bf5c543de8b9641ac1ec08def1ddb26_5f74d99ab2373627c047c5e5_20_09_30_19_16_47/execution-tasks?execution=0&sorts=STATUS%3AASC", + 5: "/task/evergreen_test_model_patch_5e823e1f28baeaa22ae00823d83e03082cd148ab_5e4ff3abe3c3317e352062e4_20_02_21_15_13_48", }; diff --git a/cypress/integration/task/task_route.ts b/cypress/integration/task/task_route.ts index 9bff02eff1..4121e310b7 100644 --- a/cypress/integration/task/task_route.ts +++ b/cypress/integration/task/task_route.ts @@ -13,7 +13,7 @@ describe("Task Page Route", () => { it("should display an appropriate status badge when visiting task pages", () => { cy.visit(`/task/${tasks[1]}`); - cy.dataCy("task-status-badge").contains("Aborted"); + cy.dataCy("task-status-badge").contains("Dispatched"); cy.visit(`/task/${tasks[2]}`); cy.dataCy("task-status-badge").contains("Running"); cy.visit(`/task/${tasks[3]}`); diff --git a/cypress/integration/version/patch_with_aborted_task.ts b/cypress/integration/version/patch_with_aborted_task.ts index 092693d384..b0c14e99e9 100644 --- a/cypress/integration/version/patch_with_aborted_task.ts +++ b/cypress/integration/version/patch_with_aborted_task.ts @@ -1,6 +1,9 @@ describe("Patch with aborted task", () => { it("Shows status `aborted` in task table for tasks that were aborted", () => { - cy.visit("/version/5ee1efb3d1fe073e194e8b5c"); + cy.visit( + "task/mongodb_mongo_main_enterprise_rhel_62_64_bit_dbtest_patch_0af9c85d7e2ba60f592f2d7a9a35217e254e59fb_5ee1efb3d1fe073e194e8b5c_20_06_11_08_48_06" + ); + cy.dataCy("bc-message").click(); cy.get( "[data-row-key=mongodb_mongo_main_enterprise_rhel_62_64_bit_dbtest_patch_0af9c85d7e2ba60f592f2d7a9a35217e254e59fb_5ee1efb3d1fe073e194e8b5c_20_06_11_08_48_06]" ).within(() => { diff --git a/cypress/integration/version/restart_modal.ts b/cypress/integration/version/restart_modal.ts index 7a578182f4..694bb7dad4 100644 --- a/cypress/integration/version/restart_modal.ts +++ b/cypress/integration/version/restart_modal.ts @@ -1,25 +1,20 @@ -describe( - "Restarting a patch with Downstream Tasks", - { testIsolation: false }, - () => { - it("Clicking on the Select Downstream Tasks should show the downstream projects", () => { - const versionWithDownstream = `/version/5f74d99ab2373627c047c5e5`; - cy.visit(versionWithDownstream); - cy.dataCy("restart-version").click(); - cy.dataCy("select-downstream").first().click(); - cy.dataCy("select-downstream").first().contains("evergreen").click(); - }); - } -); +describe("Restarting a patch with Downstream Tasks", () => { + it("Clicking on the Select Downstream Tasks should show the downstream projects", () => { + const versionWithDownstream = `/version/5f74d99ab2373627c047c5e5`; + cy.visit(versionWithDownstream); + cy.dataCy("restart-version").click(); + cy.dataCy("select-downstream").first().click(); + cy.dataCy("select-downstream").first().contains("evergreen").click(); + }); +}); -describe("Restarting a patch", { testIsolation: false }, () => { - it("Clicking on the Restart button opens a patch restart modal", () => { +describe("Restarting a patch", () => { + beforeEach(() => { cy.visit(path); cy.dataCy("version-restart-modal").should("not.exist"); cy.dataCy("restart-version").click(); cy.dataCy("version-restart-modal").should("be.visible"); }); - it("Clicking on a variant should toggle an accordion drop down of tasks", () => { cy.dataCy("variant-accordion").first().click(); cy.dataCy("task-status-checkbox").should("exist"); @@ -36,7 +31,8 @@ describe("Restarting a patch", { testIsolation: false }, () => { cy.dataCy("task-status-badge").should("contain.text", "1 of 1 Selected"); }); - it("Selecting on the task status filter should toggle the tasks that have matching statuses to it", () => { + // TODO: Drop skip in https://jira.mongodb.org/browse/EVG-20762 + it.skip("Selecting on the task status filter should toggle the tasks that have matching statuses to it", () => { cy.dataCy("task-status-filter").click(); cy.getInputByLabel("All").check({ force: true }); cy.dataCy("task-status-filter").click(); @@ -52,7 +48,8 @@ describe("Restarting a patch", { testIsolation: false }, () => { cy.dataCy("task-status-filter").click(); }); - it("Selecting on the base status filter should toggle the tasks that have matching statuses to it", () => { + // TODO: Drop skip in https://jira.mongodb.org/browse/EVG-20762 + it.skip("Selecting on the base status filter should toggle the tasks that have matching statuses to it", () => { cy.dataCy("version-restart-modal").within(() => { cy.dataCy("base-task-status-filter").click(); cy.getInputByLabel("Succeeded").check({ force: true }); @@ -73,9 +70,7 @@ describe("Restarting a patch", { testIsolation: false }, () => { it("Restarting a task should close the modal and display a success message if it occurs successfully.", () => { cy.dataCy("version-restart-modal").within(() => { - cy.dataCy("task-status-filter").click(); - cy.getInputByLabel("Unscheduled").check({ force: true }); - cy.dataCy("task-status-filter").click(); + cy.dataCy("task-status-checkbox").first().click({ force: true }); cy.contains("button", "Restart").click(); }); cy.dataCy("version-restart-modal").should("not.exist"); @@ -83,7 +78,7 @@ describe("Restarting a patch", { testIsolation: false }, () => { }); }); -describe("Restarting mainline commits", { testIsolation: false }, () => { +describe("Restarting mainline commits", () => { it("should be able to restart scheduled mainline commit tasks", () => { cy.visit("/version/spruce_ab494436448fbb1d244833046ea6f6af1544e86d"); cy.dataCy("restart-version").should( diff --git a/cypress/integration/version/routes.ts b/cypress/integration/version/routes.ts index 313f20a73d..20c8dcb87d 100644 --- a/cypress/integration/version/routes.ts +++ b/cypress/integration/version/routes.ts @@ -74,7 +74,7 @@ describe("Version route", () => { .trigger("mouseover") .within(($el) => { // @ts-expect-error - expect($el.text()).to.contain("1Undispatched"); + expect($el.text()).to.contain("1Succeeded"); }); }); }); @@ -95,14 +95,15 @@ describe("Version route", () => { .and("equal", "true"); cy.location("search").should( "include", - "sorts=STATUS%3AASC%3BBASE_STATUS%3ADESC&statuses=undispatched-umbrella,unscheduled,aborted,blocked&variant=%5Eubuntu1604%24" + "sorts=STATUS%3AASC%3BBASE_STATUS%3ADESC&statuses=success&variant=%5Eubuntu1604%24" ); + // TODO: Drop skip in https://jira.mongodb.org/browse/EVG-20762 // Check that filter values have updated. - cy.toggleTableFilter(2); - cy.getInputByLabel("Unscheduled") - .should("have.attr", "aria-checked") - .and("equal", "true"); + // cy.toggleTableFilter(2); + // cy.getInputByLabel("success") + // .should("have.attr", "aria-checked") + // .and("equal", "true"); cy.toggleTableFilter(4); cy.dataCy("variant-input-wrapper") @@ -128,7 +129,7 @@ describe("Version route", () => { }); cy.location("search").should( "include", - "sorts=STATUS%3AASC%3BBASE_STATUS%3ADESC&statuses=undispatched-umbrella,unscheduled,aborted,blocked&variant=%5Eubuntu1604%24" + "sorts=STATUS%3AASC%3BBASE_STATUS%3ADESC&statuses=success&variant=%5Eubuntu1604%24" ); }); }); diff --git a/cypress/integration/version/task_duration.ts b/cypress/integration/version/task_duration.ts index 99f8c92c1c..a0402f5aca 100644 --- a/cypress/integration/version/task_duration.ts +++ b/cypress/integration/version/task_duration.ts @@ -20,7 +20,8 @@ describe("Task Duration Tab", () => { cy.location("search").should("include", `page=0`); }); - it("updates URL appropriately when status filter is applied", () => { + // TODO: Drop skip in https://jira.mongodb.org/browse/EVG-20762 + it.skip("updates URL appropriately when status filter is applied", () => { cy.visit(TASK_DURATION_ROUTE); // Apply status filter. @@ -74,7 +75,7 @@ describe("Task Duration Tab", () => { cy.location("search").should("include", "duration=DESC"); cy.get(`[aria-label="sort"]`).click(); cy.location("search").should("include", "duration=ASC"); - const shortestTask = "generate-lint"; + const shortestTask = "test-auth"; cy.contains(shortestTask).should("be.visible"); cy.dataCy("task-duration-table-row") .first() diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 83fb2affbd..2f1bb93432 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -75,10 +75,9 @@ Cypress.Commands.add("logout", () => { /* toggleTableFilter */ Cypress.Commands.add("toggleTableFilter", (colNum: number) => { - cy.get(`.ant-table-thead > tr > :nth-child(${colNum})`) - .find("[role=button]") - .first() - .click(); + const selector = `.ant-table-thead > tr > :nth-child(${colNum})`; + cy.get(selector).should("be.visible"); + cy.get(selector).find("[role=button]").first().click(); cy.get(":not(.ant-dropdown-hidden) > .ant-table-filter-dropdown").should( "be.visible" );