From 7d45a9437c0cadb469189a3f9de4212fad5733fc Mon Sep 17 00:00:00 2001 From: cliu-akamai <126020611+cliu-akamai@users.noreply.github.com> Date: Wed, 7 Aug 2024 16:10:52 -0400 Subject: [PATCH] test: [M3-8136] - Add Cypress integration test for closing support tickets (#10697) * M3-8136: Add Cypress integration test for closing support tickets * Added changeset: Add Cypress integration test for closing support tickets * Fixed comments --- .../pr-10697-tests-1721417045353.md | 5 + .../close-support-ticket.spec.ts | 156 ++++++++++++++++++ .../open-support-ticket.spec.ts | 2 +- .../support/constants/help-and-support.ts | 14 ++ .../cypress/support/intercepts/support.ts | 19 ++- 5 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 packages/manager/.changeset/pr-10697-tests-1721417045353.md create mode 100644 packages/manager/cypress/e2e/core/helpAndSupport/close-support-ticket.spec.ts create mode 100644 packages/manager/cypress/support/constants/help-and-support.ts diff --git a/packages/manager/.changeset/pr-10697-tests-1721417045353.md b/packages/manager/.changeset/pr-10697-tests-1721417045353.md new file mode 100644 index 00000000000..f0192adb2df --- /dev/null +++ b/packages/manager/.changeset/pr-10697-tests-1721417045353.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tests +--- + +Add Cypress integration test for closing support tickets ([#10697](https://github.com/linode/manager/pull/10697)) diff --git a/packages/manager/cypress/e2e/core/helpAndSupport/close-support-ticket.spec.ts b/packages/manager/cypress/e2e/core/helpAndSupport/close-support-ticket.spec.ts new file mode 100644 index 00000000000..f9eb2a840cb --- /dev/null +++ b/packages/manager/cypress/e2e/core/helpAndSupport/close-support-ticket.spec.ts @@ -0,0 +1,156 @@ +import 'cypress-file-upload'; +import { + mockAppendFeatureFlags, + mockGetFeatureFlagClientstream, +} from 'support/intercepts/feature-flags'; +import { makeFeatureFlagData } from 'support/util/feature-flags'; +import { ui } from 'support/ui'; +import { + randomItem, + randomLabel, + randomNumber, + randomPhrase, +} from 'support/util/random'; +import { supportTicketFactory } from 'src/factories'; +import { + mockGetSupportTicket, + mockGetSupportTickets, + mockGetSupportTicketReplies, + mockCloseSupportTicket, +} from 'support/intercepts/support'; +import { SEVERITY_LABEL_MAP } from 'src/features/Support/SupportTickets/constants'; +import { + closableMessage, + closeButtonText, +} from 'support/constants/help-and-support'; + +describe('close support tickets', () => { + /* + * - Opens a Help & Support ticket with mocked ticket data. + * - Confirms that there is no "close ticket" button showing up for the default support ticket. + */ + it('cannot close a default support ticket by customers', () => { + const mockTicket = supportTicketFactory.build({ + id: randomNumber(), + summary: randomLabel(), + description: randomPhrase(), + severity: randomItem([1, 2, 3]), + status: 'new', + }); + + // Get severity label for numeric severity level. + // Bail out if we're unable to get a valid label -- this indicates a mismatch between the test and source. + const severityLabel = SEVERITY_LABEL_MAP.get(mockTicket.severity!); + if (!severityLabel) { + throw new Error( + `Unable to retrieve label for severity level '${mockTicket.severity}'. Is this a valid support severity level?` + ); + } + + mockAppendFeatureFlags({ + supportTicketSeverity: makeFeatureFlagData(true), + }); + mockGetFeatureFlagClientstream(); + mockGetSupportTickets([mockTicket]); + mockGetSupportTicket(mockTicket).as('getSupportTicket'); + mockGetSupportTicketReplies(mockTicket.id, []).as('getReplies'); + + cy.visitWithLogin('/support/tickets'); + + // Confirm that tickets are listed as expected. + cy.findByText(mockTicket.summary).should('be.visible').click(); + + cy.wait(['@getSupportTicket', '@getReplies']); + + cy.url().should('endWith', `/tickets/${mockTicket.id}`); + cy.findByText( + mockTicket.status.substring(0, 1).toUpperCase() + + mockTicket.status.substring(1) + ).should('be.visible'); + cy.findByText(`#${mockTicket.id}: ${mockTicket.summary}`).should( + 'be.visible' + ); + cy.findByText(mockTicket.description).should('be.visible'); + cy.findByText(severityLabel).should('be.visible'); + + // Confirm that the support ticket is not closable by default. + cy.findByText(closableMessage, { exact: false }).should('not.exist'); + }); + + /* + * - Opens a Help & Support ticket with mocked ticket data. + * - Confirms that the closable support ticket can be closed by customers successfully. + */ + it('can close a closable support ticket', () => { + const mockTicket = supportTicketFactory.build({ + id: randomNumber(), + summary: randomLabel(), + description: randomPhrase(), + severity: randomItem([1, 2, 3]), + status: 'new', + closable: true, + }); + + const mockClosedTicket = supportTicketFactory.build({ + ...mockTicket, + status: 'closed', + closed: 'close by customers', + }); + + // Get severity label for numeric severity level. + // Bail out if we're unable to get a valid label -- this indicates a mismatch between the test and source. + const severityLabel = SEVERITY_LABEL_MAP.get(mockTicket.severity!); + if (!severityLabel) { + throw new Error( + `Unable to retrieve label for severity level '${mockTicket.severity}'. Is this a valid support severity level?` + ); + } + + mockAppendFeatureFlags({ + supportTicketSeverity: makeFeatureFlagData(true), + }); + mockGetFeatureFlagClientstream(); + mockGetSupportTickets([mockTicket]); + mockGetSupportTicket(mockTicket).as('getSupportTicket'); + mockGetSupportTicketReplies(mockTicket.id, []).as('getReplies'); + mockCloseSupportTicket(mockTicket.id).as('closeSupportTicket'); + + cy.visitWithLogin('/support/tickets'); + + // Confirm that tickets are listed as expected. + cy.findByText(mockTicket.summary).should('be.visible').click(); + + cy.wait(['@getSupportTicket', '@getReplies']); + + // Confirm that the closable message shows up. + cy.findByText(closableMessage, { exact: false }).should('be.visible'); + + // Confirm that the "close the ticket" button can be clicked. + ui.button.findByTitle(closeButtonText).should('be.visible').click(); + ui.dialog + .findByTitle('Confirm Ticket Close') + .should('be.visible') + .within(() => { + cy.findByText('Are you sure you want to close this ticket?').should( + 'be.visible' + ); + ui.button + .findByTitle('Confirm') + .should('be.visible') + .should('be.enabled') + .click(); + + cy.wait('@closeSupportTicket'); + }); + + mockGetSupportTickets([mockClosedTicket]); + mockGetSupportTicket(mockClosedTicket).as('getClosedSupportTicket'); + cy.visit('/support/tickets'); + + // Confirm that the ticket is closed. + cy.findByText(mockClosedTicket.summary).should('be.visible').click(); + cy.wait('@getClosedSupportTicket'); + cy.get('[aria-label="Ticket status is closed"]').should('be.visible'); + cy.findByText('Closed'); + }); +}); diff --git a/packages/manager/cypress/e2e/core/helpAndSupport/open-support-ticket.spec.ts b/packages/manager/cypress/e2e/core/helpAndSupport/open-support-ticket.spec.ts index 1161505d52b..07636b6f0ee 100644 --- a/packages/manager/cypress/e2e/core/helpAndSupport/open-support-ticket.spec.ts +++ b/packages/manager/cypress/e2e/core/helpAndSupport/open-support-ticket.spec.ts @@ -56,7 +56,7 @@ import { mockGetClusters } from 'support/intercepts/lke'; import { linodeCreatePage } from 'support/ui/pages'; import { chooseRegion } from 'support/util/regions'; -describe('help & support', () => { +describe('open support tickets', () => { after(() => { cleanUp(['linodes']); }); diff --git a/packages/manager/cypress/support/constants/help-and-support.ts b/packages/manager/cypress/support/constants/help-and-support.ts new file mode 100644 index 00000000000..7a781477065 --- /dev/null +++ b/packages/manager/cypress/support/constants/help-and-support.ts @@ -0,0 +1,14 @@ +/** + * Close button text that is displayed in the detail of closable support ticket. + */ +export const closeButtonText = 'close this ticket'; + +/** + * Closable message that is displayed in the detail of closable support ticket. + */ +export const closableMessage = 'If everything is resolved, you can'; + +/** + * Dialog title that is in close ticket dialog. + */ +export const closeTicketDialogTitle = 'Confirm Ticket Close'; diff --git a/packages/manager/cypress/support/intercepts/support.ts b/packages/manager/cypress/support/intercepts/support.ts index d4a65e94b71..4b55900a614 100644 --- a/packages/manager/cypress/support/intercepts/support.ts +++ b/packages/manager/cypress/support/intercepts/support.ts @@ -58,7 +58,24 @@ export const mockGetSupportTicket = ( }; /** - * Intercepts request to fetch support tickets and mocks response. + * Interepts request to close a support ticket and mocks response. + * + * @param ticketId - Numeric ID of support ticket for which to mock replies. + * + * @returns Cypress chainable. + */ +export const mockCloseSupportTicket = ( + ticketId: number +): Cypress.Chainable => { + return cy.intercept( + 'POST', + apiMatcher(`support/tickets/${ticketId}/close`), + makeResponse({}) + ); +}; + +/** + * Intercepts request to fetch open support tickets and mocks response. * * @param tickets - Array of support ticket objects with which to mock response. *