From e893f1649ade82941bdc82435a0864dbb6261ee9 Mon Sep 17 00:00:00 2001 From: jdamore-linode <97627410+jdamore-linode@users.noreply.github.com> Date: Tue, 27 Aug 2024 09:10:22 -0400 Subject: [PATCH] test: [M3-8478] - Fix Cypress StackScript Linode deploy test flake (#10826) * Add account availability intercept util * Scroll desired autocomplete item into view * Improve region selection in StackScript Linode deploy test, improve StackScript input speed * Added changeset: Resolve StackScript Linode deploy test flake --- .../pr-10826-tests-1724431261722.md | 5 ++ .../stackscripts/create-stackscripts.spec.ts | 52 ++++++++++++------- .../cypress/support/intercepts/account.ts | 9 ++++ .../cypress/support/ui/autocomplete.ts | 16 ++++-- 4 files changed, 59 insertions(+), 23 deletions(-) create mode 100644 packages/manager/.changeset/pr-10826-tests-1724431261722.md diff --git a/packages/manager/.changeset/pr-10826-tests-1724431261722.md b/packages/manager/.changeset/pr-10826-tests-1724431261722.md new file mode 100644 index 00000000000..44f90061d7f --- /dev/null +++ b/packages/manager/.changeset/pr-10826-tests-1724431261722.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tests +--- + +Resolve StackScript Linode deploy test flake ([#10826](https://github.com/linode/manager/pull/10826)) 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 d3ab097469e..cb14c8e9265 100644 --- a/packages/manager/cypress/e2e/core/stackscripts/create-stackscripts.spec.ts +++ b/packages/manager/cypress/e2e/core/stackscripts/create-stackscripts.spec.ts @@ -5,6 +5,7 @@ import { pollLinodeDiskSize, } from 'support/util/polling'; import { randomLabel, randomString, randomPhrase } from 'support/util/random'; +import { interceptGetAccountAvailability } from 'support/intercepts/account'; import { interceptCreateStackScript, interceptGetStackScripts, @@ -13,7 +14,7 @@ import { interceptCreateLinode } from 'support/intercepts/linodes'; import { ui } from 'support/ui'; import { createLinodeRequestFactory } from 'src/factories'; import { createImage, getLinodeDisks, resizeLinodeDisk } from '@linode/api-v4'; -import { chooseRegion } from 'support/util/regions'; +import { chooseRegion, getRegionByLabel } from 'support/util/regions'; import { SimpleBackoffMethod } from 'support/util/backoff'; import { cleanUp } from 'support/util/cleanup'; import { createTestLinode } from 'support/util/linodes'; @@ -35,6 +36,20 @@ const stackScriptErrorNoShebang = const stackScriptErrorUdfAlphanumeric = 'UDF names can only contain alphanumeric and underscore characters.'; +/** + * Sets the StackScript field's value programmatically rather than via simulated typing. + * + * Cypress's typing operation is slow for long strings, so we can save several + * seconds by setting the value directly, then simulating a couple keystrokes. + * + * @param script - Script contents to input. + */ +const inputStackScript = (script: string) => { + cy.get('[data-qa-textfield-label="Script"]').should('be.visible').click(); + + cy.focused().invoke('val', script).type(' {backspace}'); +}; + /** * Fills out the StackScript creation form. * @@ -69,11 +84,8 @@ const fillOutStackscriptForm = ( cy.findByText(`${targetImage}`).should('be.visible').click(); - // Insert a script with invalid UDF data. - cy.get('[data-qa-textfield-label="Script"]') - .should('be.visible') - .click() - .type(script); + // Insert a script. + inputStackScript(script); }; /** @@ -87,9 +99,14 @@ const fillOutStackscriptForm = ( */ const fillOutLinodeForm = (label: string, regionName: string) => { const password = randomString(32); + const region = getRegionByLabel(regionName); ui.regionSelect.find().click(); - ui.regionSelect.findItemByRegionLabel(regionName).click(); + ui.regionSelect + .findItemByRegionLabel(regionName) + .should('be.visible') + .click(); + ui.regionSelect.find().should('have.value', `${region.label} (${region.id})`); cy.findByText('Linode Label') .should('be.visible') @@ -176,6 +193,7 @@ describe('Create stackscripts', () => { interceptCreateStackScript().as('createStackScript'); interceptGetStackScripts().as('getStackScripts'); interceptCreateLinode().as('createLinode'); + interceptGetAccountAvailability().as('getAvailability'); cy.visitWithLogin('/stackscripts/create'); @@ -199,11 +217,7 @@ describe('Create stackscripts', () => { cy.findByText(stackScriptErrorNoShebang).should('be.visible'); cy.fixture(stackscriptUdfInvalidPath).then((stackScriptUdfInvalid) => { - cy.get('[data-qa-textfield-label="Script"]') - .should('be.visible') - .click() - .type('{selectall}{backspace}') - .type(stackScriptUdfInvalid); + inputStackScript(stackScriptUdfInvalid); }); ui.buttonGroup @@ -217,11 +231,7 @@ describe('Create stackscripts', () => { // Insert a script with valid UDF data and submit StackScript create form. cy.fixture(stackscriptUdfPath).then((stackScriptUdf) => { - cy.get('[data-qa-textfield-label="Script"]') - .should('be.visible') - .click() - .type('{selectall}{backspace}') - .type(stackScriptUdf); + inputStackScript(stackScriptUdf); }); ui.buttonGroup @@ -252,6 +262,9 @@ describe('Create stackscripts', () => { .should('be.enabled') .click(); + // Wait for availability to be retrieved before interacting with form. + cy.wait('@getAvailability'); + // Fill out Linode creation form, confirm UDF fields behave as expected. fillOutLinodeForm(linodeLabel, linodeRegion.label); @@ -276,7 +289,10 @@ describe('Create stackscripts', () => { // Confirm that Linode has been created and is provisioning. cy.findByText(linodeLabel).should('be.visible'); - cy.findByText('PROVISIONING').should('be.visible'); + + // In rare cases, the Linode can provision quicker than this assertion happens, + // so we want to account for cases where it's already booting or even running. + cy.findByText(/(PROVISIONING|BOOTING|RUNNING)/).should('be.visible'); }); /* diff --git a/packages/manager/cypress/support/intercepts/account.ts b/packages/manager/cypress/support/intercepts/account.ts index 54472bb1fe1..6e81f7b0455 100644 --- a/packages/manager/cypress/support/intercepts/account.ts +++ b/packages/manager/cypress/support/intercepts/account.ts @@ -678,3 +678,12 @@ export const mockGetMaintenance = ( } }); }; + +/** + * Intercepts GET request to fetch account region availability. + * + * @returns Cypress chainable. + */ +export const interceptGetAccountAvailability = (): Cypress.Chainable => { + return cy.intercept('GET', apiMatcher('account/availability*')); +}; diff --git a/packages/manager/cypress/support/ui/autocomplete.ts b/packages/manager/cypress/support/ui/autocomplete.ts index e87e51f1f6f..9545c72b528 100644 --- a/packages/manager/cypress/support/ui/autocomplete.ts +++ b/packages/manager/cypress/support/ui/autocomplete.ts @@ -32,11 +32,17 @@ export const autocompletePopper = { title: string, options?: SelectorMatcherOptions ): Cypress.Chainable => { - return cy - .document() - .its('body') - .find('[data-qa-autocomplete-popper]') - .findByText(title, options); + return ( + cy + .document() + .its('body') + .find('[data-qa-autocomplete-popper]') + .findByText(title, options) + // Scroll to the desired item before yielding. + // Apply a negative top offset to account for cases where the desired + // item may be obscured by the drop-down sticky category heading. + .scrollIntoView({ offset: { left: 0, top: -45 } }) + ); }, };