diff --git a/packages/manager/cypress/e2e/core/account/service-transfer.spec.ts b/packages/manager/cypress/e2e/core/account/service-transfer.spec.ts index 633604849e8..67f2e29a43e 100644 --- a/packages/manager/cypress/e2e/core/account/service-transfer.spec.ts +++ b/packages/manager/cypress/e2e/core/account/service-transfer.spec.ts @@ -164,7 +164,7 @@ describe('Account service transfers', () => { cy.wait(['@getTransfers', '@getTransfers', '@getTransfers']); // Confirm that pending transfers are displayed in "Pending Service Transfers" panel. - cy.defer(getProfile()).then((profile: Profile) => { + cy.defer(getProfile(), 'getting profile').then((profile: Profile) => { const dateFormatOptions = { timezone: profile.timezone }; cy.get('[data-qa-panel="Pending Service Transfers"]') .should('be.visible') @@ -248,116 +248,118 @@ describe('Account service transfers', () => { return linode; }; - cy.defer(setupLinode()).then((linode: Linode) => { - interceptInitiateEntityTransfer().as('initiateTransfer'); + cy.defer(setupLinode(), 'creating and booting Linode').then( + (linode: Linode) => { + interceptInitiateEntityTransfer().as('initiateTransfer'); - // Navigate to Service Transfer landing page, initiate transfer. - cy.visitWithLogin(serviceTransferLandingUrl); - ui.button - .findByTitle('Make a Service Transfer') - .should('be.visible') - .should('be.enabled') - .click(); - - cy.findByText('Make a Service Transfer').should('be.visible'); - cy.url().should('endWith', serviceTransferCreateUrl); - initiateLinodeTransfer(linode.label); - - cy.wait('@initiateTransfer').then((response) => { - const token = response?.response?.body.token; - if (!token) { - throw new Error( - 'Failed to retrieve generated transfer token from API response.' - ); - } - - ui.dialog - .findByTitle('Service Transfer Token') - .should('be.visible') - .within(() => { - // Confirm that user is advised to transfer token using a secure means, - // and that they are informed that the transfer may take up to an hour. - cy.findByText('secure delivery method', { exact: false }).should( - 'be.visible' - ); - cy.findByText('may take up to an hour', { exact: false }).should( - 'be.visible' - ); - - cy.findByDisplayValue(token).should('be.visible'); - - // Close dialog. - cy.findByLabelText('Close').should('be.visible').click(); - }); - - // Confirm token is listed on landing page and that correct information - // is shown in modal when token is clicked. - cy.findByText(token) - .should('be.visible') - .closest('tr') - .within(() => { - cy.findByText(token).should('be.visible').click(); - }); - - ui.dialog - .findByTitle('Service Transfer Details') - .should('be.visible') - .within(() => { - cy.findByText(token).should('be.visible'); - cy.findByText(linode.id).should('be.visible'); - cy.get('[data-qa-close-drawer]').should('be.visible').click(); - }); - - // Attempt to receive the an invalid token. - redeemToken(randomUuid()); - assertReceiptError('Not found'); - - // Attempt to receive previously generated token. - redeemToken(token); - assertReceiptError( - 'You cannot initiate a transfer to another user on your account.' - ); - - // Attempt to generate a new token for the same Linode. + // Navigate to Service Transfer landing page, initiate transfer. + cy.visitWithLogin(serviceTransferLandingUrl); ui.button .findByTitle('Make a Service Transfer') .should('be.visible') .should('be.enabled') .click(); + cy.findByText('Make a Service Transfer').should('be.visible'); + cy.url().should('endWith', serviceTransferCreateUrl); initiateLinodeTransfer(linode.label); - const errorMessage = `Cannot transfer Linode(s) with ID(s) ${linode.id}: Already pending another transfer request.`; - cy.findByText(errorMessage).should('be.visible'); - // Navigate back to landing page and cancel transfer. - cy.contains('a', 'Service Transfers').should('be.visible').click(); - cy.url().should('endWith', serviceTransferLandingUrl); - - cy.findByText(token) - .should('be.visible') - .closest('tr') - .within(() => { - ui.button - .findByTitle('Cancel') - .should('be.visible') - .should('be.enabled') - .click(); - }); - - ui.dialog - .findByTitle('Cancel this Service Transfer?') - .should('be.visible') - .within(() => { - ui.buttonGroup - .findButtonByTitle('Cancel Service Transfer') - .should('be.visible') - .should('be.enabled') - .click(); - }); + cy.wait('@initiateTransfer').then((response) => { + const token = response?.response?.body.token; + if (!token) { + throw new Error( + 'Failed to retrieve generated transfer token from API response.' + ); + } + + ui.dialog + .findByTitle('Service Transfer Token') + .should('be.visible') + .within(() => { + // Confirm that user is advised to transfer token using a secure means, + // and that they are informed that the transfer may take up to an hour. + cy.findByText('secure delivery method', { exact: false }).should( + 'be.visible' + ); + cy.findByText('may take up to an hour', { exact: false }).should( + 'be.visible' + ); + + cy.findByDisplayValue(token).should('be.visible'); + + // Close dialog. + cy.findByLabelText('Close').should('be.visible').click(); + }); + + // Confirm token is listed on landing page and that correct information + // is shown in modal when token is clicked. + cy.findByText(token) + .should('be.visible') + .closest('tr') + .within(() => { + cy.findByText(token).should('be.visible').click(); + }); + + ui.dialog + .findByTitle('Service Transfer Details') + .should('be.visible') + .within(() => { + cy.findByText(token).should('be.visible'); + cy.findByText(linode.id).should('be.visible'); + cy.get('[data-qa-close-drawer]').should('be.visible').click(); + }); + + // Attempt to receive the an invalid token. + redeemToken(randomUuid()); + assertReceiptError('Not found'); + + // Attempt to receive previously generated token. + redeemToken(token); + assertReceiptError( + 'You cannot initiate a transfer to another user on your account.' + ); - ui.toast.assertMessage('Service transfer canceled successfully.'); - }); - }); + // Attempt to generate a new token for the same Linode. + ui.button + .findByTitle('Make a Service Transfer') + .should('be.visible') + .should('be.enabled') + .click(); + + initiateLinodeTransfer(linode.label); + const errorMessage = `Cannot transfer Linode(s) with ID(s) ${linode.id}: Already pending another transfer request.`; + cy.findByText(errorMessage).should('be.visible'); + + // Navigate back to landing page and cancel transfer. + cy.contains('a', 'Service Transfers').should('be.visible').click(); + cy.url().should('endWith', serviceTransferLandingUrl); + + cy.findByText(token) + .should('be.visible') + .closest('tr') + .within(() => { + ui.button + .findByTitle('Cancel') + .should('be.visible') + .should('be.enabled') + .click(); + }); + + ui.dialog + .findByTitle('Cancel this Service Transfer?') + .should('be.visible') + .within(() => { + ui.buttonGroup + .findButtonByTitle('Cancel Service Transfer') + .should('be.visible') + .should('be.enabled') + .click(); + }); + + ui.toast.assertMessage('Service transfer canceled successfully.'); + }); + } + ); }); /* diff --git a/packages/manager/cypress/e2e/core/account/smoke-billing-activity.spec.ts b/packages/manager/cypress/e2e/core/account/smoke-billing-activity.spec.ts index 691972e8b84..2ae25e42833 100644 --- a/packages/manager/cypress/e2e/core/account/smoke-billing-activity.spec.ts +++ b/packages/manager/cypress/e2e/core/account/smoke-billing-activity.spec.ts @@ -88,7 +88,7 @@ let cachedGetProfile = {}; // Fetch and cache real profile object. beforeEach(() => { - cy.defer(getProfile()).then((profile: Profile) => { + cy.defer(getProfile(), 'getting profile').then((profile: Profile) => { cachedGetProfile = profile; }); }); diff --git a/packages/manager/cypress/e2e/core/domains/smoke-create-domain-records.spec.ts b/packages/manager/cypress/e2e/core/domains/smoke-create-domain-records.spec.ts index d158cfccdf5..fa9cd136b9a 100644 --- a/packages/manager/cypress/e2e/core/domains/smoke-create-domain-records.spec.ts +++ b/packages/manager/cypress/e2e/core/domains/smoke-create-domain-records.spec.ts @@ -91,7 +91,7 @@ const createRecords = () => [ authenticate(); describe('Creates Domains record with Form', () => { beforeEach(() => { - cy.wrap(deleteAllTestDomains()); + cy.defer(deleteAllTestDomains(), 'cleaning up test domains'); }); createRecords().forEach((rec) => { diff --git a/packages/manager/cypress/e2e/core/domains/smoke-delete-domain.spec.ts b/packages/manager/cypress/e2e/core/domains/smoke-delete-domain.spec.ts index 711667beb25..65907f075c4 100644 --- a/packages/manager/cypress/e2e/core/domains/smoke-delete-domain.spec.ts +++ b/packages/manager/cypress/e2e/core/domains/smoke-delete-domain.spec.ts @@ -20,67 +20,69 @@ describe('Delete a Domain', () => { group: 'test-group', }); - cy.defer(createDomain(domainRequest)).then((domain: Domain) => { - cy.visitWithLogin('/domains'); + cy.defer(createDomain(domainRequest), 'creating domain').then( + (domain: Domain) => { + cy.visitWithLogin('/domains'); - // Confirm that domain is listed and initiate deletion. - cy.findByText(domain.domain) - .should('be.visible') - .closest('tr') - .within(() => { - ui.actionMenu - .findByTitle(`Action menu for Domain ${domain}`) - .should('be.visible') - .click(); - }); - ui.actionMenuItem.findByTitle('Delete').should('be.visible').click(); + // Confirm that domain is listed and initiate deletion. + cy.findByText(domain.domain) + .should('be.visible') + .closest('tr') + .within(() => { + ui.actionMenu + .findByTitle(`Action menu for Domain ${domain}`) + .should('be.visible') + .click(); + }); + ui.actionMenuItem.findByTitle('Delete').should('be.visible').click(); - // Cancel deletion when prompted to confirm. - ui.dialog - .findByTitle(`Delete Domain ${domain.domain}?`) - .should('be.visible') - .within(() => { - ui.buttonGroup - .findButtonByTitle('Cancel') - .should('be.visible') - .should('be.enabled') - .click(); - }); + // Cancel deletion when prompted to confirm. + ui.dialog + .findByTitle(`Delete Domain ${domain.domain}?`) + .should('be.visible') + .within(() => { + ui.buttonGroup + .findButtonByTitle('Cancel') + .should('be.visible') + .should('be.enabled') + .click(); + }); - // Confirm that domain is still listed and initiate deletion again. - cy.findByText(domain.domain) - .should('be.visible') - .closest('tr') - .within(() => { - ui.actionMenu - .findByTitle(`Action menu for Domain ${domain}`) - .should('be.visible') - .click(); - }); - ui.actionMenuItem.findByTitle('Delete').should('be.visible').click(); + // Confirm that domain is still listed and initiate deletion again. + cy.findByText(domain.domain) + .should('be.visible') + .closest('tr') + .within(() => { + ui.actionMenu + .findByTitle(`Action menu for Domain ${domain}`) + .should('be.visible') + .click(); + }); + ui.actionMenuItem.findByTitle('Delete').should('be.visible').click(); - // Confirm deletion. - ui.dialog - .findByTitle(`Delete Domain ${domain.domain}?`) - .should('be.visible') - .within(() => { - // The button should be disabled before confirming the correct domain - ui.buttonGroup - .findButtonByTitle('Delete Domain') - .should('be.visible') - .should('be.disabled'); + // Confirm deletion. + ui.dialog + .findByTitle(`Delete Domain ${domain.domain}?`) + .should('be.visible') + .within(() => { + // The button should be disabled before confirming the correct domain + ui.buttonGroup + .findButtonByTitle('Delete Domain') + .should('be.visible') + .should('be.disabled'); - containsClick('Domain Name').type(domain.domain); - ui.buttonGroup - .findButtonByTitle('Delete Domain') - .should('be.visible') - .should('be.enabled') - .click(); - }); + containsClick('Domain Name').type(domain.domain); + ui.buttonGroup + .findButtonByTitle('Delete Domain') + .should('be.visible') + .should('be.enabled') + .click(); + }); - // Confirm that domain is deleted. - cy.visitWithLogin('/domains'); - cy.findByText(domain.domain).should('not.exist'); - }); + // Confirm that domain is deleted. + cy.visitWithLogin('/domains'); + cy.findByText(domain.domain).should('not.exist'); + } + ); }); }); diff --git a/packages/manager/cypress/e2e/core/firewalls/create-firewall.spec.ts b/packages/manager/cypress/e2e/core/firewalls/create-firewall.spec.ts index 886c1050f3c..2543fd8760d 100644 --- a/packages/manager/cypress/e2e/core/firewalls/create-firewall.spec.ts +++ b/packages/manager/cypress/e2e/core/firewalls/create-firewall.spec.ts @@ -70,7 +70,7 @@ describe('create firewall', () => { label: randomLabel(), }; - cy.defer(createLinode(linodeRequest)).then((linode) => { + cy.defer(createLinode(linodeRequest), 'creating Linode').then((linode) => { interceptCreateFirewall().as('createFirewall'); cy.visitWithLogin('/firewalls/create'); diff --git a/packages/manager/cypress/e2e/core/firewalls/delete-firewall.spec.ts b/packages/manager/cypress/e2e/core/firewalls/delete-firewall.spec.ts index 63ddd08d48b..0e0379e3acd 100644 --- a/packages/manager/cypress/e2e/core/firewalls/delete-firewall.spec.ts +++ b/packages/manager/cypress/e2e/core/firewalls/delete-firewall.spec.ts @@ -21,54 +21,56 @@ describe('delete firewall', () => { region: chooseRegion().id, }); - cy.defer(createFirewall(firewallRequest)).then((firewall: Firewall) => { - cy.visitWithLogin('/firewalls'); + cy.defer(createFirewall(firewallRequest), 'creating firewalls').then( + (firewall: Firewall) => { + cy.visitWithLogin('/firewalls'); - // Confirm that firewall is listed and initiate deletion. - cy.findByText(firewall.label) - .should('be.visible') - .closest('tr') - .within(() => { - fbtVisible('Delete'); - fbtClick('Delete'); - }); + // Confirm that firewall is listed and initiate deletion. + cy.findByText(firewall.label) + .should('be.visible') + .closest('tr') + .within(() => { + fbtVisible('Delete'); + fbtClick('Delete'); + }); - // Cancel deletion when prompted to confirm. - ui.dialog - .findByTitle(`Delete Firewall ${firewall.label}?`) - .should('be.visible') - .within(() => { - ui.buttonGroup - .findButtonByTitle('Cancel') - .should('be.visible') - .should('be.enabled') - .click(); - }); + // Cancel deletion when prompted to confirm. + ui.dialog + .findByTitle(`Delete Firewall ${firewall.label}?`) + .should('be.visible') + .within(() => { + ui.buttonGroup + .findButtonByTitle('Cancel') + .should('be.visible') + .should('be.enabled') + .click(); + }); - // Confirm that firewall is still listed and initiate deletion again. - cy.findByText(firewall.label) - .should('be.visible') - .closest('tr') - .within(() => { - fbtVisible('Delete'); - fbtClick('Delete'); - }); + // Confirm that firewall is still listed and initiate deletion again. + cy.findByText(firewall.label) + .should('be.visible') + .closest('tr') + .within(() => { + fbtVisible('Delete'); + fbtClick('Delete'); + }); - // Confirm deletion. - ui.dialog - .findByTitle(`Delete Firewall ${firewall.label}?`) - .should('be.visible') - .within(() => { - ui.buttonGroup - .findButtonByTitle('Delete Firewall') - .should('be.visible') - .should('be.enabled') - .click(); - }); + // Confirm deletion. + ui.dialog + .findByTitle(`Delete Firewall ${firewall.label}?`) + .should('be.visible') + .within(() => { + ui.buttonGroup + .findButtonByTitle('Delete Firewall') + .should('be.visible') + .should('be.enabled') + .click(); + }); - // Confirm that firewall is deleted. - cy.visitWithLogin('/firewalls'); - cy.findByText(firewall.label).should('not.exist'); - }); + // Confirm that firewall is deleted. + cy.visitWithLogin('/firewalls'); + cy.findByText(firewall.label).should('not.exist'); + } + ); }); }); diff --git a/packages/manager/cypress/e2e/core/firewalls/update-firewall.spec.ts b/packages/manager/cypress/e2e/core/firewalls/update-firewall.spec.ts index 73fe702a565..8621879ae49 100644 --- a/packages/manager/cypress/e2e/core/firewalls/update-firewall.spec.ts +++ b/packages/manager/cypress/e2e/core/firewalls/update-firewall.spec.ts @@ -188,107 +188,108 @@ describe('update firewall', () => { }), }); - cy.defer(createLinodeAndFirewall(linodeRequest, firewallRequest)).then( - ([linode, firewall]) => { - cy.visitWithLogin('/firewalls'); - - // Confirm that firewall is listed on landing page with expected configuration. - cy.findByText(firewall.label) - .closest('tr') - .within(() => { - cy.findByText(firewall.label).should('be.visible'); - cy.findByText('Enabled').should('be.visible'); - cy.findByText('No rules').should('be.visible'); - cy.findByText('None assigned').should('be.visible'); - }); - - // Go to the firewalls edit page - cy.findByText(firewall.label).click(); - - // In Rules tab, add inbound rules - addFirewallRules(inboundRule, 'inbound'); - - // Confirm that the inbound rules are listed on edit page with expected configuration - cy.get('[data-rbd-droppable-context-id="0"]') - .should('be.visible') - .within(() => { - cy.findByText(inboundRule.label).should('be.visible'); - cy.findByText(inboundRule.protocol).should('be.visible'); - cy.findByText(inboundRule.ports).should('be.visible'); - cy.findByText(inboundRule.action).should('be.visible'); - }); - - // Add outbound rules - addFirewallRules(outboundRule, 'outbound'); - - // Confirm that the outbound rules are listed on edit page with expected configuration - cy.get('[data-rbd-droppable-context-id="1"]') - .should('be.visible') - .within(() => { - cy.findByText(outboundRule.label).should('be.visible'); - cy.findByText(outboundRule.protocol).should('be.visible'); - cy.findByText(outboundRule.ports).should('be.visible'); - cy.findByText(outboundRule.action).should('be.visible'); - }); - - // Save configuration - interceptUpdateFirewallRules(firewall.id).as('updateFirewallRules'); - ui.button - .findByTitle('Save Changes') - .should('be.visible') - .should('be.enabled') - .click(); - - // Go back to landing page and check rules are added to the firewall - cy.wait('@updateFirewallRules'); - cy.visitWithLogin('/firewalls'); - cy.findByText(firewall.label) - .closest('tr') - .within(() => { - cy.findByText('1 Inbound / 1 Outbound').should('be.visible'); - }); - - // Go to the firewalls edit page - cy.findByText(firewall.label).click(); - - // Remove inbound rules - removeFirewallRules(inboundRule.label); - - // Remove outbound rules - removeFirewallRules(outboundRule.label); - - // Save configuration - interceptUpdateFirewallRules(firewall.id).as('updateFirewallRules'); - ui.button - .findByTitle('Save Changes') - .should('be.visible') - .should('be.enabled') - .click(); - - // Go back to landing page and check rules are removed to the firewall - cy.wait('@updateFirewallRules'); - cy.visitWithLogin('/firewalls'); - cy.findByText(firewall.label) - .closest('tr') - .within(() => { - cy.findByText('No rules').should('be.visible'); - }); - - // Go to the firewalls edit page - cy.findByText(firewall.label).click(); - - // Confirm that the firewall can be assigned to the linode - interceptUpdateFirewallLinodes(firewall.id).as('updateFirewallLinodes'); - addLinodesToFirewall(firewall, linode); - cy.wait('@updateFirewallLinodes'); - cy.visitWithLogin('/firewalls'); - cy.findByText(firewall.label) - .closest('tr') - .within(() => { - cy.findByText(linode.label).should('be.visible'); - }); - } - ); + cy.defer( + createLinodeAndFirewall(linodeRequest, firewallRequest), + 'creating Linode and firewall' + ).then(([linode, firewall]) => { + cy.visitWithLogin('/firewalls'); + + // Confirm that firewall is listed on landing page with expected configuration. + cy.findByText(firewall.label) + .closest('tr') + .within(() => { + cy.findByText(firewall.label).should('be.visible'); + cy.findByText('Enabled').should('be.visible'); + cy.findByText('No rules').should('be.visible'); + cy.findByText('None assigned').should('be.visible'); + }); + + // Go to the firewalls edit page + cy.findByText(firewall.label).click(); + + // In Rules tab, add inbound rules + addFirewallRules(inboundRule, 'inbound'); + + // Confirm that the inbound rules are listed on edit page with expected configuration + cy.get('[data-rbd-droppable-context-id="0"]') + .should('be.visible') + .within(() => { + cy.findByText(inboundRule.label).should('be.visible'); + cy.findByText(inboundRule.protocol).should('be.visible'); + cy.findByText(inboundRule.ports).should('be.visible'); + cy.findByText(inboundRule.action).should('be.visible'); + }); + + // Add outbound rules + addFirewallRules(outboundRule, 'outbound'); + + // Confirm that the outbound rules are listed on edit page with expected configuration + cy.get('[data-rbd-droppable-context-id="1"]') + .should('be.visible') + .within(() => { + cy.findByText(outboundRule.label).should('be.visible'); + cy.findByText(outboundRule.protocol).should('be.visible'); + cy.findByText(outboundRule.ports).should('be.visible'); + cy.findByText(outboundRule.action).should('be.visible'); + }); + + // Save configuration + interceptUpdateFirewallRules(firewall.id).as('updateFirewallRules'); + ui.button + .findByTitle('Save Changes') + .should('be.visible') + .should('be.enabled') + .click(); + + // Go back to landing page and check rules are added to the firewall + cy.wait('@updateFirewallRules'); + cy.visitWithLogin('/firewalls'); + cy.findByText(firewall.label) + .closest('tr') + .within(() => { + cy.findByText('1 Inbound / 1 Outbound').should('be.visible'); + }); + + // Go to the firewalls edit page + cy.findByText(firewall.label).click(); + + // Remove inbound rules + removeFirewallRules(inboundRule.label); + + // Remove outbound rules + removeFirewallRules(outboundRule.label); + + // Save configuration + interceptUpdateFirewallRules(firewall.id).as('updateFirewallRules'); + ui.button + .findByTitle('Save Changes') + .should('be.visible') + .should('be.enabled') + .click(); + + // Go back to landing page and check rules are removed to the firewall + cy.wait('@updateFirewallRules'); + cy.visitWithLogin('/firewalls'); + cy.findByText(firewall.label) + .closest('tr') + .within(() => { + cy.findByText('No rules').should('be.visible'); + }); + + // Go to the firewalls edit page + cy.findByText(firewall.label).click(); + + // Confirm that the firewall can be assigned to the linode + interceptUpdateFirewallLinodes(firewall.id).as('updateFirewallLinodes'); + addLinodesToFirewall(firewall, linode); + cy.wait('@updateFirewallLinodes'); + cy.visitWithLogin('/firewalls'); + cy.findByText(firewall.label) + .closest('tr') + .within(() => { + cy.findByText(linode.label).should('be.visible'); + }); + }); }); /* @@ -312,76 +313,77 @@ describe('update firewall', () => { }, }); - cy.defer(createLinodeAndFirewall(linodeRequest, firewallRequest)).then( - ([_linode, firewall]) => { - cy.visitWithLogin('/firewalls'); - - // Confirm that firewall is listed on landing page with expected configuration. - cy.findByText(firewall.label) - .closest('tr') - .within(() => { - cy.findByText(firewall.label).should('be.visible'); - cy.findByText('Enabled').should('be.visible'); - cy.findByText('No rules').should('be.visible'); - cy.findByText('None assigned').should('be.visible'); - }); - - // Click 'Disable' button and confirm action. - cy.findByText(firewall.label) - .should('be.visible') - .closest('tr') - .within(() => { - fbtVisible('Disable'); - fbtClick('Disable'); - }); - - ui.dialog - .findByTitle(`Disable Firewall ${firewall.label}?`) - .should('be.visible') - .within(() => { - ui.buttonGroup - .findButtonByTitle('Disable Firewall') - .should('be.visible') - .should('be.enabled') - .click(); - }); - - // Confirm status is updated on landing page. - cy.findByText(firewall.label) - .closest('tr') - .within(() => { - cy.findByText('Disabled').should('be.visible'); - }); - - cy.visitWithLogin('/firewalls'); - - // Click 'Enable' button and confirm action. - cy.findByText(firewall.label) - .should('be.visible') - .closest('tr') - .within(() => { - fbtVisible('Enable'); - fbtClick('Enable'); - }); - - ui.dialog - .findByTitle(`Enable Firewall ${firewall.label}?`) - .should('be.visible') - .within(() => { - ui.buttonGroup - .findButtonByTitle('Enable Firewall') - .should('be.visible') - .should('be.enabled') - .click(); - }); - - // Confirm status is updated on landing page. - cy.findByText(firewall.label) - .closest('tr') - .within(() => { - cy.findByText('Enabled').should('be.visible'); - }); - } - ); + cy.defer( + createLinodeAndFirewall(linodeRequest, firewallRequest), + 'creating Linode and firewall' + ).then(([_linode, firewall]) => { + cy.visitWithLogin('/firewalls'); + + // Confirm that firewall is listed on landing page with expected configuration. + cy.findByText(firewall.label) + .closest('tr') + .within(() => { + cy.findByText(firewall.label).should('be.visible'); + cy.findByText('Enabled').should('be.visible'); + cy.findByText('No rules').should('be.visible'); + cy.findByText('None assigned').should('be.visible'); + }); + + // Click 'Disable' button and confirm action. + cy.findByText(firewall.label) + .should('be.visible') + .closest('tr') + .within(() => { + fbtVisible('Disable'); + fbtClick('Disable'); + }); + + ui.dialog + .findByTitle(`Disable Firewall ${firewall.label}?`) + .should('be.visible') + .within(() => { + ui.buttonGroup + .findButtonByTitle('Disable Firewall') + .should('be.visible') + .should('be.enabled') + .click(); + }); + + // Confirm status is updated on landing page. + cy.findByText(firewall.label) + .closest('tr') + .within(() => { + cy.findByText('Disabled').should('be.visible'); + }); + + cy.visitWithLogin('/firewalls'); + + // Click 'Enable' button and confirm action. + cy.findByText(firewall.label) + .should('be.visible') + .closest('tr') + .within(() => { + fbtVisible('Enable'); + fbtClick('Enable'); + }); + + ui.dialog + .findByTitle(`Enable Firewall ${firewall.label}?`) + .should('be.visible') + .within(() => { + ui.buttonGroup + .findButtonByTitle('Enable Firewall') + .should('be.visible') + .should('be.enabled') + .click(); + }); + + // Confirm status is updated on landing page. + cy.findByText(firewall.label) + .closest('tr') + .within(() => { + cy.findByText('Enabled').should('be.visible'); + }); + }); }); }); diff --git a/packages/manager/cypress/e2e/core/linodes/rescue-linode.spec.ts b/packages/manager/cypress/e2e/core/linodes/rescue-linode.spec.ts index efe6e6b1a9b..e0057493dd4 100644 --- a/packages/manager/cypress/e2e/core/linodes/rescue-linode.spec.ts +++ b/packages/manager/cypress/e2e/core/linodes/rescue-linode.spec.ts @@ -49,7 +49,7 @@ describe('Rescue Linodes', () => { * - Confirms that toast appears confirming successful reboot into rescue mode. */ it('Can reboot a Linode into rescue mode', () => { - cy.wrap, Linode>(createAndBootLinode()).then( + cy.defer(createAndBootLinode(), 'creating and booting Linode').then( (linode: Linode) => { // mock 200 response interceptGetLinodeDetails(linode.id).as('getLinode'); @@ -89,7 +89,7 @@ describe('Rescue Linodes', () => { region: chooseRegion().id, }); - cy.wrap, Linode>(createLinode(linodeRequest)).then( + cy.defer(createLinode(linodeRequest), 'creating Linode').then( (linode: Linode) => { interceptGetLinodeDetails(linode.id).as('getLinode'); interceptRebootLinodeIntoRescueMode(linode.id).as( diff --git a/packages/manager/cypress/e2e/core/objectStorage/access-key.e2e.spec.ts b/packages/manager/cypress/e2e/core/objectStorage/access-key.e2e.spec.ts index 28c01b53cac..f06ab20a86c 100644 --- a/packages/manager/cypress/e2e/core/objectStorage/access-key.e2e.spec.ts +++ b/packages/manager/cypress/e2e/core/objectStorage/access-key.e2e.spec.ts @@ -108,7 +108,10 @@ describe('object storage access key end-to-end tests', () => { }); // Create a bucket before creating access key. - cy.defer(createBucket(bucketRequest)).then(() => { + cy.defer( + createBucket(bucketRequest), + 'creating Object Storage bucket' + ).then(() => { const keyLabel = randomLabel(); interceptGetAccessKeys().as('getKeys'); diff --git a/packages/manager/cypress/e2e/core/objectStorage/object-storage.e2e.spec.ts b/packages/manager/cypress/e2e/core/objectStorage/object-storage.e2e.spec.ts index ee8a61d3be7..d7a1355bcb5 100644 --- a/packages/manager/cypress/e2e/core/objectStorage/object-storage.e2e.spec.ts +++ b/packages/manager/cypress/e2e/core/objectStorage/object-storage.e2e.spec.ts @@ -180,7 +180,10 @@ describe('object storage end-to-end tests', () => { { path: 'object-storage-files/2.jpg', name: '2.jpg' }, ]; - cy.defer(setUpBucket(bucketLabel, bucketCluster)).then(() => { + cy.defer( + setUpBucket(bucketLabel, bucketCluster), + 'creating Object Storage bucket' + ).then(() => { interceptUploadBucketObjectS3( bucketLabel, bucketCluster, diff --git a/packages/manager/cypress/e2e/core/stackscripts/create-stackscripts.spec.ts b/packages/manager/cypress/e2e/core/stackscripts/create-stackscripts.spec.ts index 64e844f3aa5..20eb08704e0 100644 --- a/packages/manager/cypress/e2e/core/stackscripts/create-stackscripts.spec.ts +++ b/packages/manager/cypress/e2e/core/stackscripts/create-stackscripts.spec.ts @@ -285,7 +285,10 @@ describe('stackscripts', () => { interceptCreateLinode().as('createLinode'); cy.visitWithLogin('/stackscripts/create'); - cy.defer(createLinodeAndImage()).then((privateImage) => { + cy.defer(createLinodeAndImage(), { + label: 'creating Linode and Image', + timeout: 180000, + }).then((privateImage) => { cy.fixture(stackscriptBasicPath).then((stackscriptBasic) => { fillOutStackscriptForm( stackscriptLabel, diff --git a/packages/manager/cypress/e2e/core/volumes/attach-volume.spec.ts b/packages/manager/cypress/e2e/core/volumes/attach-volume.spec.ts index 363562739c9..ad85d761c9d 100644 --- a/packages/manager/cypress/e2e/core/volumes/attach-volume.spec.ts +++ b/packages/manager/cypress/e2e/core/volumes/attach-volume.spec.ts @@ -66,66 +66,14 @@ describe('volume attach and detach flows', () => { createLinode(linodeRequest), ]); - cy.defer(entityPromise).then(([volume, linode]: [Volume, Linode]) => { - interceptAttachVolume(volume.id).as('attachVolume'); - cy.visitWithLogin('/volumes', { - localStorageOverrides: pageSizeOverride, - }); - - // Confirm that volume is listed, initiate attachment. - cy.findByText(volume.label) - .should('be.visible') - .closest('tr') - .within(() => { - cy.findByLabelText(`Action menu for Volume ${volume.label}`).click(); - }); - - cy.get('[data-qa-action-menu-item="Attach"]:visible') - .should('be.visible') - .click(); - - // Input Linode label and submit. - cy.get(`[data-qa-drawer-title="Attach Volume ${volume.label}"]`) - .closest('[data-qa-drawer="true"]') - .should('be.visible') - .within(() => { - cy.findByText('Select a Linode') - .click() - .type(`${linode.label}{enter}`); - cy.findByTestId('input-loading').should('not.exist'); - cy.get('[data-qa-buttons="true"]').within(() => { - cy.findByText('Attach').should('be.visible').click(); - }); - }); - - // Confirm that volume has been attached to Linode. - cy.wait('@attachVolume').its('response.statusCode').should('eq', 200); - ui.toast.assertMessage(`Volume ${volume.label} successfully attached.`); - cy.findByText(volume.label) - .should('be.visible') - .closest('tr') - .within(() => { - cy.findByText(linode.label).should('be.visible'); - }); - }); - }); - - // TODO Unskip once volume detach issue is resolved. - /* - * - Clicks "Detach" action menu item for volume. - * - Confirms that volume detach toast appears and that Linode is no longer listed as attached for Volume. - */ - it.skip('detaches a volume from a Linode', () => { - cy.defer(createLinodeAndAttachVolume()).then( - ([linode, volume]: [Linode, Volume]) => { - interceptDetachVolume(volume.id).as('detachVolume'); - - // @TODO Wait for Linode to finish provisioning before initiating detach. + cy.defer(entityPromise, 'creating Volume and Linode').then( + ([volume, linode]: [Volume, Linode]) => { + interceptAttachVolume(volume.id).as('attachVolume'); cy.visitWithLogin('/volumes', { localStorageOverrides: pageSizeOverride, }); - // Confirm that volume is listed, initiate detachment. + // Confirm that volume is listed, initiate attachment. cy.findByText(volume.label) .should('be.visible') .closest('tr') @@ -135,27 +83,32 @@ describe('volume attach and detach flows', () => { ).click(); }); - cy.get('[data-qa-action-menu-item="Detach"]:visible') + cy.get('[data-qa-action-menu-item="Attach"]:visible') .should('be.visible') .click(); - cy.findByText(`Detach Volume ${volume.label}?`) + // Input Linode label and submit. + cy.get(`[data-qa-drawer-title="Attach Volume ${volume.label}"]`) + .closest('[data-qa-drawer="true"]') .should('be.visible') - .closest('[role="dialog"]') .within(() => { - cy.findByText('Detach Volume').should('be.visible').click(); + cy.findByText('Select a Linode') + .click() + .type(`${linode.label}{enter}`); + cy.findByTestId('input-loading').should('not.exist'); + cy.get('[data-qa-buttons="true"]').within(() => { + cy.findByText('Attach').should('be.visible').click(); + }); }); - // Confirm that volume has been detached. - cy.wait('@detachVolume').its('response.statusCode').should('eq', 200); - // @TODO Improve toast check. - cy.findByText(`Volume ${volume.label} successfully detached.`); + // Confirm that volume has been attached to Linode. + cy.wait('@attachVolume').its('response.statusCode').should('eq', 200); + ui.toast.assertMessage(`Volume ${volume.label} successfully attached.`); cy.findByText(volume.label) .should('be.visible') .closest('tr') .within(() => { - cy.findByText('Unattached').should('be.visible'); - cy.findByText(linode.label).should('not.exist'); + cy.findByText(linode.label).should('be.visible'); }); } ); @@ -163,56 +116,107 @@ describe('volume attach and detach flows', () => { // TODO Unskip once volume detach issue is resolved. /* - * - Clicks "Detach" action menu item for volume on Linode details page. - * - Confirms that volume is no longer listed on Linode details page. - * - Confirms that Linode is no longer listed as attached to Volume on Volumes landing page. + * - Clicks "Detach" action menu item for volume. + * - Confirms that volume detach toast appears and that Linode is no longer listed as attached for Volume. */ - it.skip('detaches a volume from a Linode via Linode details page', () => { - cy.defer(createLinodeAndAttachVolume()).then( - ([linode, volume]: [Linode, Volume]) => { - // Wait for Linode to finish provisioning and booting. - cy.visitWithLogin(`/linodes/${linode.id}/storage`); - cy.get('[data-qa-linode-status="true"]').within(() => { - cy.findByText('RUNNING').should('be.visible'); - }); + it.skip('detaches a volume from a Linode', () => { + cy.defer( + createLinodeAndAttachVolume(), + 'creating attached Volume and Linode' + ).then(([linode, volume]: [Linode, Volume]) => { + interceptDetachVolume(volume.id).as('detachVolume'); - // Confirm that Volume is listed on Linode details page and initiate detachment. - cy.findByLabelText('List of volume').within(() => { - cy.findByText(volume.label) - .closest('tr') - .within(() => { - cy.findByLabelText(`Action menu for Volume ${volume.label}`) - .should('be.visible') - .click(); - }); + // @TODO Wait for Linode to finish provisioning before initiating detach. + cy.visitWithLogin('/volumes', { + localStorageOverrides: pageSizeOverride, + }); + + // Confirm that volume is listed, initiate detachment. + cy.findByText(volume.label) + .should('be.visible') + .closest('tr') + .within(() => { + cy.findByLabelText(`Action menu for Volume ${volume.label}`).click(); }); - cy.get('[data-qa-action-menu-item="Detach"]:visible') - .should('be.visible') - .click(); + cy.get('[data-qa-action-menu-item="Detach"]:visible') + .should('be.visible') + .click(); - cy.findByText(`Detach Volume ${volume.label}?`) - .should('be.visible') - .closest('[role="dialog"]') - .within(() => { - cy.findByText('Detach Volume').should('be.visible').click(); - }); + cy.findByText(`Detach Volume ${volume.label}?`) + .should('be.visible') + .closest('[role="dialog"]') + .within(() => { + cy.findByText('Detach Volume').should('be.visible').click(); + }); - // Confirm that Volume is no longer listed on Linode details page. - cy.findByLabelText('List of volume').within(() => { - cy.findByText(volume.label).should('not.exist'); + // Confirm that volume has been detached. + cy.wait('@detachVolume').its('response.statusCode').should('eq', 200); + // @TODO Improve toast check. + cy.findByText(`Volume ${volume.label} successfully detached.`); + cy.findByText(volume.label) + .should('be.visible') + .closest('tr') + .within(() => { + cy.findByText('Unattached').should('be.visible'); + cy.findByText(linode.label).should('not.exist'); }); + }); + }); - // Confirm that Volume is no longer shown as attached on Volumes landing page. - cy.visitWithLogin('/volumes'); + // TODO Unskip once volume detach issue is resolved. + /* + * - Clicks "Detach" action menu item for volume on Linode details page. + * - Confirms that volume is no longer listed on Linode details page. + * - Confirms that Linode is no longer listed as attached to Volume on Volumes landing page. + */ + it.skip('detaches a volume from a Linode via Linode details page', () => { + cy.defer( + createLinodeAndAttachVolume(), + 'creating attached Volume and Linode' + ).then(([linode, volume]: [Linode, Volume]) => { + // Wait for Linode to finish provisioning and booting. + cy.visitWithLogin(`/linodes/${linode.id}/storage`); + cy.get('[data-qa-linode-status="true"]').within(() => { + cy.findByText('RUNNING').should('be.visible'); + }); + + // Confirm that Volume is listed on Linode details page and initiate detachment. + cy.findByLabelText('List of volume').within(() => { cy.findByText(volume.label) - .should('be.visible') .closest('tr') .within(() => { - cy.findByText('Unattached').should('be.visible'); - cy.findByText(linode.label).should('not.to.exist'); + cy.findByLabelText(`Action menu for Volume ${volume.label}`) + .should('be.visible') + .click(); }); - } - ); + }); + + cy.get('[data-qa-action-menu-item="Detach"]:visible') + .should('be.visible') + .click(); + + cy.findByText(`Detach Volume ${volume.label}?`) + .should('be.visible') + .closest('[role="dialog"]') + .within(() => { + cy.findByText('Detach Volume').should('be.visible').click(); + }); + + // Confirm that Volume is no longer listed on Linode details page. + cy.findByLabelText('List of volume').within(() => { + cy.findByText(volume.label).should('not.exist'); + }); + + // Confirm that Volume is no longer shown as attached on Volumes landing page. + cy.visitWithLogin('/volumes'); + cy.findByText(volume.label) + .should('be.visible') + .closest('tr') + .within(() => { + cy.findByText('Unattached').should('be.visible'); + cy.findByText(linode.label).should('not.to.exist'); + }); + }); }); }); diff --git a/packages/manager/cypress/e2e/core/volumes/clone-volume.spec.ts b/packages/manager/cypress/e2e/core/volumes/clone-volume.spec.ts index 353f32af047..7e75ac352dd 100644 --- a/packages/manager/cypress/e2e/core/volumes/clone-volume.spec.ts +++ b/packages/manager/cypress/e2e/core/volumes/clone-volume.spec.ts @@ -27,42 +27,46 @@ describe('volume clone flow', () => { const cloneVolumeLabel = randomLabel(); - cy.defer(createVolume(volumeRequest)).then((volume: Volume) => { - interceptCloneVolume(volume.id).as('cloneVolume'); - cy.visitWithLogin('/volumes', { - localStorageOverrides: pageSizeOverride, - }); - - // Confirm that volume is listed, initiate clone. - cy.findByText(volume.label) - .should('be.visible') - .closest('tr') - .within(() => { - cy.findByLabelText(`Action menu for Volume ${volume.label}`).click(); + cy.defer(createVolume(volumeRequest), 'creating volume').then( + (volume: Volume) => { + interceptCloneVolume(volume.id).as('cloneVolume'); + cy.visitWithLogin('/volumes', { + localStorageOverrides: pageSizeOverride, }); - cy.get('[data-qa-action-menu-item="Clone"]:visible') - .should('be.visible') - .click(); + // Confirm that volume is listed, initiate clone. + cy.findByText(volume.label) + .should('be.visible') + .closest('tr') + .within(() => { + cy.findByLabelText( + `Action menu for Volume ${volume.label}` + ).click(); + }); + + cy.get('[data-qa-action-menu-item="Clone"]:visible') + .should('be.visible') + .click(); - // Input new volume label and submit. - cy.get('[data-qa-drawer-title="Clone Volume"]') - .closest('[data-qa-drawer="true"]') - .should('be.visible') - .within(() => { - cy.findByText('Label').click().type(cloneVolumeLabel); - cy.get('[data-qa-buttons="true"]').within(() => { - cy.findByText('Clone Volume').should('be.visible').click(); + // Input new volume label and submit. + cy.get('[data-qa-drawer-title="Clone Volume"]') + .closest('[data-qa-drawer="true"]') + .should('be.visible') + .within(() => { + cy.findByText('Label').click().type(cloneVolumeLabel); + cy.get('[data-qa-buttons="true"]').within(() => { + cy.findByText('Clone Volume').should('be.visible').click(); + }); }); - }); - // Confirm that volume has been cloned. - cy.wait('@cloneVolume').its('response.statusCode').should('eq', 200); - cy.findByText(cloneVolumeLabel) - .closest('tr') - .within(() => { - cy.findByText(`${volume.size} GB`).should('be.visible'); - }); - }); + // Confirm that volume has been cloned. + cy.wait('@cloneVolume').its('response.statusCode').should('eq', 200); + cy.findByText(cloneVolumeLabel) + .closest('tr') + .within(() => { + cy.findByText(`${volume.size} GB`).should('be.visible'); + }); + } + ); }); }); diff --git a/packages/manager/cypress/e2e/core/volumes/create-volume.spec.ts b/packages/manager/cypress/e2e/core/volumes/create-volume.spec.ts index 0210e85a3b0..84642d4d459 100644 --- a/packages/manager/cypress/e2e/core/volumes/create-volume.spec.ts +++ b/packages/manager/cypress/e2e/core/volumes/create-volume.spec.ts @@ -79,7 +79,7 @@ describe('volume create flow', () => { regionLabel: region.label, }; - cy.defer(createLinode(linodeRequest)).then((linode) => { + cy.defer(createLinode(linodeRequest), 'creating Linode').then((linode) => { interceptCreateVolume().as('createVolume'); cy.visitWithLogin('/volumes/create', { localStorageOverrides: pageSizeOverride, @@ -131,51 +131,53 @@ describe('volume create flow', () => { region: chooseRegion().id, }); - cy.defer(createLinode(linodeRequest)).then((linode: Linode) => { - const volume = { - label: randomLabel(), - size: `${randomNumber(10, 250)}`, - }; + cy.defer(createLinode(linodeRequest), 'creating Linode').then( + (linode: Linode) => { + const volume = { + label: randomLabel(), + size: `${randomNumber(10, 250)}`, + }; - cy.visitWithLogin(`/linodes/${linode.id}/storage`, { - localStorageOverrides: pageSizeOverride, - }); + cy.visitWithLogin(`/linodes/${linode.id}/storage`, { + localStorageOverrides: pageSizeOverride, + }); - // Click "Create Volume" button, fill out and submit volume create drawer form. - fbtClick('Create Volume'); - cy.get('[data-qa-drawer="true"]').within(() => { - fbtVisible(`Create Volume for ${linode.label}`); - containsClick('Create and Attach Volume'); - containsClick('Label').type(volume.label); - containsClick('Size').type(`{selectall}{backspace}${volume.size}`); + // Click "Create Volume" button, fill out and submit volume create drawer form. fbtClick('Create Volume'); - }); - - // Confirm volume configuration drawer opens, then close it. - cy.get('[data-qa-drawer="true"]').within(() => { - fbtVisible('Volume scheduled for creation.'); - getClick('[data-qa-close-drawer="true"]'); - }); + cy.get('[data-qa-drawer="true"]').within(() => { + fbtVisible(`Create Volume for ${linode.label}`); + containsClick('Create and Attach Volume'); + containsClick('Label').type(volume.label); + containsClick('Size').type(`{selectall}{backspace}${volume.size}`); + fbtClick('Create Volume'); + }); - // Confirm that volume is listed on Linode 'Storage' details page. - cy.findByText(volume.label) - .closest('tr') - .within(() => { - fbtVisible(volume.label); - fbtVisible(`${volume.size} GB`); + // Confirm volume configuration drawer opens, then close it. + cy.get('[data-qa-drawer="true"]').within(() => { + fbtVisible('Volume scheduled for creation.'); + getClick('[data-qa-close-drawer="true"]'); }); - // Confirm that volume is listed on landing page with expected configuration. - cy.visitWithLogin('/volumes', { - localStorageOverrides: pageSizeOverride, - }); - cy.findByText(volume.label) - .closest('tr') - .within(() => { - cy.findByText(volume.label).should('be.visible'); - cy.findByText(`${volume.size} GB`).should('be.visible'); - cy.findByText(linode.label).should('be.visible'); + // Confirm that volume is listed on Linode 'Storage' details page. + cy.findByText(volume.label) + .closest('tr') + .within(() => { + fbtVisible(volume.label); + fbtVisible(`${volume.size} GB`); + }); + + // Confirm that volume is listed on landing page with expected configuration. + cy.visitWithLogin('/volumes', { + localStorageOverrides: pageSizeOverride, }); - }); + cy.findByText(volume.label) + .closest('tr') + .within(() => { + cy.findByText(volume.label).should('be.visible'); + cy.findByText(`${volume.size} GB`).should('be.visible'); + cy.findByText(linode.label).should('be.visible'); + }); + } + ); }); }); diff --git a/packages/manager/cypress/e2e/core/volumes/delete-volume.spec.ts b/packages/manager/cypress/e2e/core/volumes/delete-volume.spec.ts index e3cc39ff061..e927e95639f 100644 --- a/packages/manager/cypress/e2e/core/volumes/delete-volume.spec.ts +++ b/packages/manager/cypress/e2e/core/volumes/delete-volume.spec.ts @@ -29,59 +29,61 @@ describe('volume delete flow', () => { region: chooseRegion().id, }); - cy.defer(createVolume(volumeRequest)).then((volume: Volume) => { - interceptDeleteVolume(volume.id).as('deleteVolume'); - cy.visitWithLogin('/volumes', { - localStorageOverrides: pageSizeOverride, - }); + cy.defer(createVolume(volumeRequest), 'creating volume').then( + (volume: Volume) => { + interceptDeleteVolume(volume.id).as('deleteVolume'); + cy.visitWithLogin('/volumes', { + localStorageOverrides: pageSizeOverride, + }); - // Confirm that volume is listed and initiate deletion. - cy.findByText(volume.label).should('be.visible'); - cy.findByLabelText(`Action menu for Volume ${volume.label}`).click(); - cy.get('[data-qa-action-menu-item="Delete"]:visible') - .should('be.visible') - .click(); + // Confirm that volume is listed and initiate deletion. + cy.findByText(volume.label).should('be.visible'); + cy.findByLabelText(`Action menu for Volume ${volume.label}`).click(); + cy.get('[data-qa-action-menu-item="Delete"]:visible') + .should('be.visible') + .click(); - // Cancel deletion when prompted to confirm. - ui.dialog - .findByTitle(`Delete Volume ${volume.label}?`) - .should('be.visible') - .within(() => { - ui.buttonGroup - .findButtonByTitle('Cancel') - .should('be.visible') - .should('be.enabled') - .click(); - }); + // Cancel deletion when prompted to confirm. + ui.dialog + .findByTitle(`Delete Volume ${volume.label}?`) + .should('be.visible') + .within(() => { + ui.buttonGroup + .findButtonByTitle('Cancel') + .should('be.visible') + .should('be.enabled') + .click(); + }); - // Confirm that volume is still listed and initiate deletion again. - cy.findByText(volume.label).should('be.visible'); - cy.findByLabelText(`Action menu for Volume ${volume.label}`).click(); - cy.get('[data-qa-action-menu-item="Delete"]:visible') - .should('be.visible') - .click(); + // Confirm that volume is still listed and initiate deletion again. + cy.findByText(volume.label).should('be.visible'); + cy.findByLabelText(`Action menu for Volume ${volume.label}`).click(); + cy.get('[data-qa-action-menu-item="Delete"]:visible') + .should('be.visible') + .click(); - // Confirm deletion. - ui.dialog - .findByTitle(`Delete Volume ${volume.label}?`) - .should('be.visible') - .within(() => { - cy.findByLabelText('Volume Label') - .should('be.visible') - .click() - .type(volume.label); + // Confirm deletion. + ui.dialog + .findByTitle(`Delete Volume ${volume.label}?`) + .should('be.visible') + .within(() => { + cy.findByLabelText('Volume Label') + .should('be.visible') + .click() + .type(volume.label); - ui.buttonGroup - .findButtonByTitle('Delete') - .should('be.visible') - .should('be.enabled') - .click(); - }); + ui.buttonGroup + .findButtonByTitle('Delete') + .should('be.visible') + .should('be.enabled') + .click(); + }); - // Confirm that volume is deleted. - cy.wait('@deleteVolume').its('response.statusCode').should('eq', 200); - cy.findByText(volume.label).should('not.exist'); - ui.toast.assertMessage('Volume successfully deleted.'); - }); + // Confirm that volume is deleted. + cy.wait('@deleteVolume').its('response.statusCode').should('eq', 200); + cy.findByText(volume.label).should('not.exist'); + ui.toast.assertMessage('Volume successfully deleted.'); + } + ); }); }); diff --git a/packages/manager/cypress/e2e/core/volumes/resize-volume.spec.ts b/packages/manager/cypress/e2e/core/volumes/resize-volume.spec.ts index 17282438743..69f0686b9f6 100644 --- a/packages/manager/cypress/e2e/core/volumes/resize-volume.spec.ts +++ b/packages/manager/cypress/e2e/core/volumes/resize-volume.spec.ts @@ -30,51 +30,55 @@ describe('volume resize flow', () => { size: oldSize, }); - cy.defer(createVolume(volumeRequest)).then((volume: Volume) => { - interceptResizeVolume(volume.id).as('resizeVolume'); - cy.visitWithLogin('/volumes', { - localStorageOverrides: pageSizeOverride, - }); - - // Confirm that volume is listed with expected size, initiate resize. - cy.findByText(volume.label) - .should('be.visible') - .closest('tr') - .within(() => { - cy.findByText(`${oldSize} GB`).should('be.visible'); - cy.findByLabelText(`Action menu for Volume ${volume.label}`).click(); + cy.defer(createVolume(volumeRequest), 'creating Volume').then( + (volume: Volume) => { + interceptResizeVolume(volume.id).as('resizeVolume'); + cy.visitWithLogin('/volumes', { + localStorageOverrides: pageSizeOverride, }); - cy.get('[data-qa-action-menu-item="Resize"]:visible') - .should('be.visible') - .click(); + // Confirm that volume is listed with expected size, initiate resize. + cy.findByText(volume.label) + .should('be.visible') + .closest('tr') + .within(() => { + cy.findByText(`${oldSize} GB`).should('be.visible'); + cy.findByLabelText( + `Action menu for Volume ${volume.label}` + ).click(); + }); + + cy.get('[data-qa-action-menu-item="Resize"]:visible') + .should('be.visible') + .click(); - // Input new volume size and submit. - cy.get('[data-qa-drawer="true"]') - .should('be.visible') - .within(() => { - cy.findByText('Size') - .click() - .type(`{selectall}{backspace}${newSize}`); - cy.get('[data-qa-buttons="true"]').within(() => { - cy.findByText('Resize Volume').should('be.visible').click(); + // Input new volume size and submit. + cy.get('[data-qa-drawer="true"]') + .should('be.visible') + .within(() => { + cy.findByText('Size') + .click() + .type(`{selectall}{backspace}${newSize}`); + cy.get('[data-qa-buttons="true"]').within(() => { + cy.findByText('Resize Volume').should('be.visible').click(); + }); }); - }); - // Confirm that volume is resized. - cy.wait('@resizeVolume').its('response.statusCode').should('eq', 200); - cy.findByText('Volume scheduled to be resized.') - .should('be.visible') - .closest('[data-qa-drawer="true"]') - .within(() => { - cy.findByText('Close').click(); - }); + // Confirm that volume is resized. + cy.wait('@resizeVolume').its('response.statusCode').should('eq', 200); + cy.findByText('Volume scheduled to be resized.') + .should('be.visible') + .closest('[data-qa-drawer="true"]') + .within(() => { + cy.findByText('Close').click(); + }); - cy.findByText(volume.label) - .closest('tr') - .within(() => { - cy.findByText(`${newSize} GB`).should('be.visible'); - }); - }); + cy.findByText(volume.label) + .closest('tr') + .within(() => { + cy.findByText(`${newSize} GB`).should('be.visible'); + }); + } + ); }); }); diff --git a/packages/manager/cypress/e2e/core/volumes/update-volume.spec.ts b/packages/manager/cypress/e2e/core/volumes/update-volume.spec.ts index b1838fc6b85..20a67a4c1a4 100644 --- a/packages/manager/cypress/e2e/core/volumes/update-volume.spec.ts +++ b/packages/manager/cypress/e2e/core/volumes/update-volume.spec.ts @@ -19,57 +19,59 @@ describe('volume update flow', () => { const newLabel = randomLabel(); const newTags = [randomLabel(5), randomLabel(5), randomLabel(5)]; - cy.defer(createVolume(volumeRequest)).then((volume: Volume) => { - cy.visitWithLogin('/volumes', { - // Temporarily force volume table to show up to 100 results per page. - // This is a workaround while we wait to get stuck volumes removed. - // @TODO Remove local storage override when stuck volumes are removed from test accounts. - localStorageOverrides: { - PAGE_SIZE: 100, - }, - }); - - // Confirm that volume is listed on landing page, click "Edit" to open drawer. - cy.findByText(volume.label) - .should('be.visible') - .closest('tr') - .within(() => { - cy.findByText('Edit').click(); + cy.defer(createVolume(volumeRequest), 'creating volume').then( + (volume: Volume) => { + cy.visitWithLogin('/volumes', { + // Temporarily force volume table to show up to 100 results per page. + // This is a workaround while we wait to get stuck volumes removed. + // @TODO Remove local storage override when stuck volumes are removed from test accounts. + localStorageOverrides: { + PAGE_SIZE: 100, + }, }); - // Enter new label and add tags, click "Save Changes". - cy.get('[data-qa-drawer="true"]').within(() => { - cy.findByText('Edit Volume').should('be.visible'); - cy.findByDisplayValue(volume.label) + // Confirm that volume is listed on landing page, click "Edit" to open drawer. + cy.findByText(volume.label) .should('be.visible') - .click() - .type(`{selectall}{backspace}${newLabel}{enter}`); + .closest('tr') + .within(() => { + cy.findByText('Edit').click(); + }); - cy.findByText('Type to choose or create a tag.') - .should('be.visible') - .click() - .type(`${newTags.join('{enter}')}{enter}`); + // Enter new label and add tags, click "Save Changes". + cy.get('[data-qa-drawer="true"]').within(() => { + cy.findByText('Edit Volume').should('be.visible'); + cy.findByDisplayValue(volume.label) + .should('be.visible') + .click() + .type(`{selectall}{backspace}${newLabel}{enter}`); - cy.findByText('Save Changes').should('be.visible').click(); - }); + cy.findByText('Type to choose or create a tag.') + .should('be.visible') + .click() + .type(`${newTags.join('{enter}')}{enter}`); - // Confirm new label is applied, click "Edit" to re-open drawer. - cy.findByText(newLabel) - .should('be.visible') - .closest('tr') - .within(() => { - cy.findByText('Edit').click(); + cy.findByText('Save Changes').should('be.visible').click(); }); - // Confirm new label and tags are shown. - cy.get('[data-qa-drawer="true"]').within(() => { - cy.findByText('Edit Volume').should('be.visible'); - cy.findByDisplayValue(newLabel).should('be.visible'); + // Confirm new label is applied, click "Edit" to re-open drawer. + cy.findByText(newLabel) + .should('be.visible') + .closest('tr') + .within(() => { + cy.findByText('Edit').click(); + }); - newTags.forEach((newTag) => { - cy.findByText(newTag).should('be.visible'); + // Confirm new label and tags are shown. + cy.get('[data-qa-drawer="true"]').within(() => { + cy.findByText('Edit Volume').should('be.visible'); + cy.findByDisplayValue(newLabel).should('be.visible'); + + newTags.forEach((newTag) => { + cy.findByText(newTag).should('be.visible'); + }); }); - }); - }); + } + ); }); }); diff --git a/packages/manager/cypress/support/commands.ts b/packages/manager/cypress/support/commands.ts index b461f101deb..ae485e701ad 100644 --- a/packages/manager/cypress/support/commands.ts +++ b/packages/manager/cypress/support/commands.ts @@ -28,22 +28,78 @@ import 'cypress-axe'; import './login'; /** - * Returns a Cypress Promise that can be used in place of the given Promise. + * Describes an object which can contain a label. + */ +export interface Labelable { + label: string; +} + +/** + * Yields a Cypress Promise that can be used in place of the given Promise. * - * @param {Promise} promise - Promise with result to await. + * @param promise - Promise with result to await. + * @param options - Defer options. * - * @returns {any} Promise result. + * @returns Promise result. */ -Cypress.Commands.add('defer', (promise: Promise) => { - return new Cypress.Promise((resolve, reject) => { - promise - .then((...data) => { - resolve(...data); - }) - .catch((...data) => { - reject(...data); - }); - }); -}); +Cypress.Commands.add( + 'defer', + { prevSubject: false }, + ( + promise: Promise, + labelOrOptions?: + | string + | Partial + ) => { + // Gets the label that will used as the description for Cypress's log. + const commandLabel = (() => { + if (typeof labelOrOptions === 'string') { + return labelOrOptions; + } + return labelOrOptions?.label ?? 'waiting for promise'; + })(); + + // Gets the options object that will be passed to `cy.wrap`. + const wrapOptions = (() => { + if (typeof labelOrOptions !== 'string') { + return { + ...(labelOrOptions ?? {}), + log: false, + }; + } + return { log: false }; + })(); + + const timeout = (() => { + if (typeof labelOrOptions !== 'string') { + return labelOrOptions?.timeout; + } + return undefined; + })(); + + const commandLog = Cypress.log({ + name: 'defer', + message: commandLabel, + autoEnd: false, + end: false, + timeout, + }); + + // Wraps the given promise in order to update Cypress's log on completion. + const wrapPromise = async (): Promise => { + let result: T; + try { + result = await promise; + } catch (e) { + commandLog.end(); + throw e; + } + commandLog.end(); + return result; + }; + + return cy.wrap, T>(wrapPromise(), wrapOptions); + } +); import '@testing-library/cypress/add-commands'; diff --git a/packages/manager/cypress/support/e2e.js b/packages/manager/cypress/support/e2e.ts similarity index 89% rename from packages/manager/cypress/support/e2e.js rename to packages/manager/cypress/support/e2e.ts index 71d26093445..00b98c90eec 100644 --- a/packages/manager/cypress/support/e2e.js +++ b/packages/manager/cypress/support/e2e.ts @@ -13,7 +13,6 @@ // https://on.cypress.io/configuration // *********************************************************** -// Import commands.js using ES2015 syntax: import chaiString from 'chai-string'; import { authenticate } from './api/authentication'; import { deleteAllTestData } from './ui/common'; @@ -30,7 +29,10 @@ import './request-tracking'; // Runs before each test file. before(() => { - cy.wrap(deleteAllTestData(), { + cy.defer(deleteAllTestData(), { + // Describe action in Cypress output. + label: 'Cleaning up test resources', + // Make sure there's enough time to accommodate retries when necessary. timeout: 120000, }); diff --git a/packages/manager/cypress/support/index.d.ts b/packages/manager/cypress/support/index.d.ts index c2f66873753..cb07a7be8d3 100644 --- a/packages/manager/cypress/support/index.d.ts +++ b/packages/manager/cypress/support/index.d.ts @@ -1,5 +1,6 @@ import type { LinodeVisitOptions } from './login.ts'; import type { CommonRequestMockOptions } from './intercepts/common'; +import { Labelable } from './commands'; declare global { namespace Cypress { @@ -15,11 +16,16 @@ declare global { ): Chainable; /** - * Custom command to get a Cypress Promise that can be used in place of the given Promise. + * Yields a Cypress Promise from the given native Promise. * * @example cy.defer(new Promise('value')).then((val) => {...}) */ - defer(promise: Promise): Chainable<>; + defer( + promise: Promise, + labelOrOptions?: + | string + | Partial + ): Chainable<>; //mockCommonRequests(options?: CommonRequestMockOptions | undefined): Chainable<>; diff --git a/packages/manager/cypress/support/util/backoff.ts b/packages/manager/cypress/support/util/backoff.ts index bc24f428322..f7a6ab0ba77 100644 --- a/packages/manager/cypress/support/util/backoff.ts +++ b/packages/manager/cypress/support/util/backoff.ts @@ -46,6 +46,7 @@ export const attemptWithBackoff = async ( promiseCallback: () => Promise ): Promise => { const { initialDelay, maxAttempts } = backoffMethod.options; + const attemptErrors: unknown[] = []; if (initialDelay) { await timeout(initialDelay); @@ -59,6 +60,7 @@ export const attemptWithBackoff = async ( const result = await promiseCallback(); return result; } catch (e) { + attemptErrors.push(e); if (nextAttempt <= maxAttempts) { const backoffTime = backoffMethod.calculateBackoff(nextAttempt); await timeout(backoffTime); @@ -66,7 +68,13 @@ export const attemptWithBackoff = async ( } } - throw new Error(`Failed to resolve promise after ${maxAttempts} attempt(s)`); + const errorMessage = attemptErrors.reduce( + (acc: string, cur: unknown, index: number) => { + return `${acc}\n\nAttempt #${index + 1}:\n${cur}`; + }, + `Failed to resolve promise after ${maxAttempts} attempt(s):` + ); + throw new Error(errorMessage); }; /**