From 1db9d688ae73951a0bb54c4cd70781275ed0662f Mon Sep 17 00:00:00 2001 From: Manuel Zedel Date: Tue, 17 Oct 2023 15:41:31 +0200 Subject: [PATCH] chore(e2e-tests): various improvements to test execution - moved saml data input to slower method to help firefox & webkit to pass - made release tag tests more robust against retries - allowed for more browser speed differences - aligned webkit terminal fixture with updated layout - multiple lint cleanups Signed-off-by: Manuel Zedel --- .../fixtures/terminalContent-webkit.png | Bin 4180 -> 6114 bytes tests/e2e_tests/integration/01-login.spec.ts | 2 +- .../integration/02-layoutAssertions.spec.ts | 10 ++--- tests/e2e_tests/integration/03-files.spec.ts | 39 ++++++++++++----- .../integration/04-deployments.spec.ts | 26 ++++++----- .../integration/05-deviceDetails.spec.ts | 41 +++++++++++------- .../integration/06-auditlogs.spec.ts | 2 +- tests/e2e_tests/integration/07-saml.spec.ts | 13 ++++-- tests/e2e_tests/integration/08-rbac.spec.ts | 2 +- tests/e2e_tests/utils/constants.ts | 5 ++- 10 files changed, 88 insertions(+), 52 deletions(-) diff --git a/tests/e2e_tests/fixtures/terminalContent-webkit.png b/tests/e2e_tests/fixtures/terminalContent-webkit.png index a5142ef0c9be09072faf01d06ec1beec0b144935..c42fad1b9c09909a8cfb8218cffc13fcfdd8a8f8 100644 GIT binary patch literal 6114 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 MaXejuy!y}o0Aw?CApigX literal 4180 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 J&{58h{|5>ULWTeU diff --git a/tests/e2e_tests/integration/01-login.spec.ts b/tests/e2e_tests/integration/01-login.spec.ts index 2d180b86d6..78b1b14a8c 100644 --- a/tests/e2e_tests/integration/01-login.spec.ts +++ b/tests/e2e_tests/integration/01-login.spec.ts @@ -109,7 +109,7 @@ test.describe('Login', () => { await isLoggedIn(page); let loginVisible = await page.getByRole('button', { name: /log in/i }).isVisible(); expect(loginVisible).toBeFalsy(); - await page.getByText('Help & support').click(); + await page.getByText(/Releases/i).click(); const cookies = await context.cookies(); await context.storageState({ path: 'storage.json' }); diff --git a/tests/e2e_tests/integration/02-layoutAssertions.spec.ts b/tests/e2e_tests/integration/02-layoutAssertions.spec.ts index 3a1b3380ce..d05ec274be 100644 --- a/tests/e2e_tests/integration/02-layoutAssertions.spec.ts +++ b/tests/e2e_tests/integration/02-layoutAssertions.spec.ts @@ -41,7 +41,7 @@ test.describe('Layout assertions', () => { await page.click(`.leftNav :text('Devices')`); let hasAcceptedDevice = false; try { - hasAcceptedDevice = await page.isVisible('.deviceListItem'); + hasAcceptedDevice = await page.isVisible(selectors.deviceListItem); } catch (e) { console.log(`no accepted device present so far`); } @@ -55,10 +55,10 @@ test.describe('Layout assertions', () => { } await page.locator(`input:near(:text("Status:"))`).first().click({ force: true }); await page.click(`css=.MuiPaper-root >> text=/Accepted/i`); - await page.waitForSelector(`css=.deviceListItem >> text=/original/`, { timeout: timeouts.sixtySeconds }); - const element = await page.textContent('.deviceListItem'); + await page.waitForSelector(`css=${selectors.deviceListItem} >> text=/original/`, { timeout: timeouts.sixtySeconds }); + const element = await page.textContent(selectors.deviceListItem); expect(element.includes('original')).toBeTruthy(); - await page.click(`.deviceListItem div:last-child`); + await page.click(`${selectors.deviceListItem} div:last-child`); await page.waitForSelector(`text=/Device information for/i`); expect(await page.isVisible('text=Authentication status')).toBeTruthy(); }); @@ -78,6 +78,6 @@ test.describe('Layout assertions', () => { await page.click(`.grouplist:has-text('All devices')`); await page.click(selectors.deviceListCheckbox); await page.click(`.grouplist:has-text('testgroup')`); - expect(await page.locator(`css=.deviceListItem >> text=/original/`)).toBeVisible(); + expect(await page.locator(`css=${selectors.deviceListItem} >> text=/original/`)).toBeVisible(); }); }); diff --git a/tests/e2e_tests/integration/03-files.spec.ts b/tests/e2e_tests/integration/03-files.spec.ts index 1a0b46442f..6f745695a0 100644 --- a/tests/e2e_tests/integration/03-files.spec.ts +++ b/tests/e2e_tests/integration/03-files.spec.ts @@ -43,6 +43,10 @@ test.describe('Files', () => { await page.click(`.leftNav :text('Releases')`); await page.getByText(/demo-artifact/i).click(); expect(await page.getByRole('heading', { name: /Release notes/i }).isVisible()).toBeTruthy(); + const hasNotes = await page.getByText('foo notes').isVisible(); + if (hasNotes) { + return; + } // layout based locators are not an option here, since the edit button is also visible on the nearby tags section // and the selector would get confused due to the proximity - so instead we loop over all the divs await page @@ -54,7 +58,7 @@ test.describe('Files', () => { await input.fill('foo notes'); await page.getByTestId('CheckIcon').click(); expect(input).not.toBeVisible(); - expect(await page.getByText('foo notes').isVisible()).toBeTruthy(); + expect(hasNotes).toBeTruthy(); }); test('allows release tags manipulation', async ({ baseUrl, loggedInPage: page }) => { @@ -75,8 +79,10 @@ test.describe('Files', () => { const input = await page.getByPlaceholder(/enter release tags/i); await input.fill('some,tags'); await page.getByTestId('CheckIcon').click(); + await page.waitForTimeout(timeouts.oneSecond); expect(input).not.toBeVisible(); await page.goto(`${baseUrl}ui/releases`); + await page.waitForSelector('text="some, tags"', { timeout: timeouts.oneSecond }); expect(await page.getByText('some, tags').isVisible()).toBeTruthy(); }); @@ -91,30 +97,41 @@ test.describe('Files', () => { await editButton.click(); const alreadyTagged = await page.getByRole('button', { name: 'some' }).isVisible(); if (alreadyTagged) { - await page.getByRole('button', { name: 'some' }).getByTestId('CancelIcon').click(); - await page.getByRole('button', { name: 'tags' }).getByTestId('CancelIcon').click(); + await Promise.all( + ['some', 'tags'].map(async name => { + const foundTag = await page.getByRole('button', { name }); + if (!(await foundTag.isVisible())) { + return Promise.resolve(); + } + return await foundTag.getByTestId('CancelIcon').click(); + }) + ); await page.getByTestId('CheckIcon').click(); - expect(await page.getByText('add release tags').isVisible()).toBeTruthy(); + // await page.waitForTimeout(timeouts.oneSecond); + expect(await page.getByPlaceholder(/add release tags/i).isVisible({ timeout: timeouts.oneSecond })).toBeTruthy(); await editButton.click(); } await page.getByPlaceholder(/enter release tags/i).fill(releaseTag); await page.getByTestId('CheckIcon').click(); await page.press('body', 'Escape'); - expect(await page.getByText(releaseTag).isVisible()).toBeTruthy(); + await page.waitForSelector('text=Upload', { timeout: timeouts.oneSecond }); + await page.press('body', 'Escape'); + expect(await page.getByText(releaseTag, { exact: false }).isVisible({ timeout: timeouts.oneSecond })).toBeTruthy(); }); test('allows release tags filtering', async ({ loggedInPage: page }) => { await page.click(`.leftNav :text('Releases')`); expect(await page.getByText(releaseTag.toLowerCase()).isVisible()).toBeTruthy(); await page.getByPlaceholder(/select tags/i).fill('foo,'); - await page.waitForTimeout(timeouts.oneSecond); - expect(await page.getByText(/There are no Releases/i).isVisible()).toBeTruthy(); + const releasesNote = await page.getByText(/There are no Releases*/i); + releasesNote.waitFor({ timeout: timeouts.default }); + await page.getByText(/mender-demo-artifact*/i).waitFor({ timeout: timeouts.default, state: 'detached' }); await page.getByText(/Clear filter/i).click(); - await page.waitForTimeout(timeouts.oneSecond); + await page.waitForSelector('text=/mender-demo-artifact*/i'); expect(await page.getByText(releaseTag.toLowerCase()).isVisible()).toBeTruthy(); await page.getByPlaceholder(/select tags/i).fill(`${releaseTag.toLowerCase()},`); - await page.waitForTimeout(timeouts.oneSecond); - expect(await page.getByText(/There are no Releases/i).isVisible()).not.toBeTruthy(); + await page.getByText(/mender-demo-artifact*/i).waitFor({ timeout: timeouts.default }); + expect(await releasesNote.isVisible()).toBeFalsy(); }); // test('allows uploading custom file creations', () => { @@ -156,7 +173,7 @@ test.describe('Files', () => { // TODO adjust test to better work with webkit, for now it should be good enough to assume file transfers work there too if the remote terminal works test.skip(!['enterprise', 'staging'].includes(environment) || ['webkit'].includes(browserName)); await page.click(`.leftNav :text('Devices')`); - await page.click(`.deviceListItem div:last-child`); + await page.click(`${selectors.deviceListItem} div:last-child`); await page.click(`text=/troubleshooting/i`); // the deviceconnect connection might not be established right away await page.waitForSelector('text=/Session status/i', { timeout: timeouts.tenSeconds }); diff --git a/tests/e2e_tests/integration/04-deployments.spec.ts b/tests/e2e_tests/integration/04-deployments.spec.ts index ddcb5ef62f..516b6aae66 100644 --- a/tests/e2e_tests/integration/04-deployments.spec.ts +++ b/tests/e2e_tests/integration/04-deployments.spec.ts @@ -35,14 +35,15 @@ test.describe('Deployments', () => { await page.click('.MuiSpeedDial-fab'); await page.click('[aria-label="deploy"]'); await page.waitForSelector(selectors.deviceGroupSelect, { timeout: timeouts.fiveSeconds }); - await page.focus(selectors.deviceGroupSelect); - await page.getByPlaceholder(/select a device group/i).fill('All'); + const deviceGroupSelect = await page.getByPlaceholder(/select a device group/i); + await deviceGroupSelect.focus(); + await deviceGroupSelect.fill('All'); await page.click(`#deployment-device-group-selection-listbox li:has-text('All devices')`); - const creationButton = await page.waitForSelector(selectors.deploymentCreation); + const creationButton = await page.getByRole('button', { name: /create deployment/i }); await creationButton.scrollIntoViewIfNeeded(); await creationButton.click(); await page.waitForSelector(selectors.deploymentListItem, { timeout: timeouts.tenSeconds }); - await page.click(`[role="tab"]:has-text('Finished')`); + await page.getByRole('tab', { name: /finished/i }).click(); await page.waitForSelector(selectors.deploymentListItemContent, { timeout: timeouts.sixtySeconds }); const datetime = await page.getAttribute(`${selectors.deploymentListItemContent} time`, 'datetime'); const time = dayjs(datetime); @@ -58,7 +59,7 @@ test.describe('Deployments', () => { await page.click('.MuiSpeedDial-fab'); await page.click('[aria-label="create-deployment"]'); await page.waitForSelector(selectors.releaseSelect, { timeout: timeouts.fiveSeconds }); - const releaseSelect = await page.locator(selectors.releaseSelect); + const releaseSelect = await page.getByPlaceholder(/select a release/i); await releaseSelect.focus(); await releaseSelect.fill('mender-demo'); await page.click(`#deployment-release-selection-listbox li`); @@ -68,11 +69,11 @@ test.describe('Deployments', () => { await releaseSelect.focus(); await releaseSelect.fill('mender-demo'); await page.click(`#deployment-release-selection-listbox li`); - const creationButton = await page.waitForSelector(selectors.deploymentCreation); + const creationButton = await page.getByRole('button', { name: /create deployment/i }); await creationButton.scrollIntoViewIfNeeded(); await creationButton.click(); - await page.waitForSelector(selectors.deploymentListItem, { timeout: timeouts.tenSeconds }); - await page.click(`[role="tab"]:has-text('Finished')`); + await expect(page.getByText(/Select a Release to deploy/i)).toHaveCount(0, { timeout: timeouts.tenSeconds }); + await page.getByRole('tab', { name: /finished/i }).click(); await page.waitForSelector(selectors.deploymentListItemContent, { timeout: timeouts.sixtySeconds }); const datetime = await page.getAttribute(`${selectors.deploymentListItemContent} time`, 'datetime'); const time = dayjs(datetime); @@ -86,21 +87,22 @@ test.describe('Deployments', () => { await page.click(`button:has-text('Create a deployment')`); await page.waitForSelector(selectors.releaseSelect, { timeout: timeouts.fiveSeconds }); - const releaseSelect = await page.locator(selectors.releaseSelect); + const releaseSelect = await page.getByPlaceholder(/select a release/i); await releaseSelect.focus(); await releaseSelect.fill('mender'); await page.click(`#deployment-release-selection-listbox li:has-text('mender-demo-artifact')`); await page.waitForSelector(selectors.deviceGroupSelect, { timeout: timeouts.fiveSeconds }); - const deviceGroupSelect = await page.locator(selectors.deviceGroupSelect); + const deviceGroupSelect = await page.getByPlaceholder(/select a device group/i); await deviceGroupSelect.focus(); await deviceGroupSelect.fill('test'); await page.click(`#deployment-device-group-selection-listbox li:has-text('testgroup')`); - const creationButton = await page.waitForSelector(selectors.deploymentCreation); + const creationButton = await page.getByRole('button', { name: /create deployment/i }); await creationButton.scrollIntoViewIfNeeded(); await creationButton.click(); + await expect(page.getByText(/Select a Release to deploy/i)).toHaveCount(0, { timeout: timeouts.tenSeconds }); await page.waitForSelector(selectors.deploymentListItem, { timeout: timeouts.tenSeconds }); - await page.click(`[role="tab"]:has-text('Finished')`); + await page.getByRole('tab', { name: /finished/i }).click(); await page.waitForSelector(selectors.deploymentListItemContent, { timeout: timeouts.sixtySeconds }); }); }); diff --git a/tests/e2e_tests/integration/05-deviceDetails.spec.ts b/tests/e2e_tests/integration/05-deviceDetails.spec.ts index e6b5b40324..205a2d9b80 100644 --- a/tests/e2e_tests/integration/05-deviceDetails.spec.ts +++ b/tests/e2e_tests/integration/05-deviceDetails.spec.ts @@ -29,7 +29,7 @@ test.describe('Device details', () => { test('has basic inventory', async ({ demoDeviceName, loggedInPage: page }) => { await page.click(`.leftNav :text('Devices')`); - await page.click(`.deviceListItem div:last-child`); + await page.click(`${selectors.deviceListItem} div:last-child`); await page.click(`text=/inventory/i`); expect(await page.isVisible(`css=.expandedDevice >> text=Linux`)).toBeTruthy(); expect(await page.isVisible(`css=.expandedDevice >> text=mac`)).toBeTruthy(); @@ -39,11 +39,11 @@ test.describe('Device details', () => { test('can be found', async ({ demoDeviceName, loggedInPage: page }) => { const searchField = await page.getByPlaceholder(/search devices/i); await searchField.fill(demoDeviceName); - await page.waitForSelector('.deviceListItem'); + await page.waitForSelector(selectors.deviceListItem); const slideOut = await page.locator('.MuiPaper-root'); expect(await slideOut.locator(`:text("${demoDeviceName}"):below(:text("clear search"))`).isVisible()).toBeTruthy(); expect(await slideOut.getByText('1-1 of 1').isVisible()).toBeTruthy(); - await page.click(`.deviceListItem`); + await page.click(selectors.deviceListItem); await page.waitForSelector('text=/device information/i'); expect(await page.getByText(/Authorization sets/i).isVisible()).toBeTruthy(); await page.click('[aria-label="close"]'); @@ -53,7 +53,8 @@ test.describe('Device details', () => { expect(await page.getByText(/device found/i).isVisible()).toBeTruthy(); }); - test('can be filtered', async ({ demoDeviceName, environment, loggedInPage: page }) => { + test('can be filtered', async ({ browserName, demoDeviceName, loggedInPage: page }) => { + test.setTimeout(2 * timeouts.fifteenSeconds); await page.click(`.leftNav :text('Devices')`); await page.getByRole('button', { name: /filters/i }).click(); await page.getByLabel(/attribute/i).fill(rootfs); @@ -61,23 +62,33 @@ test.describe('Device details', () => { await nameInput.fill(demoDeviceName); await page.waitForTimeout(timeouts.oneSecond); await nameInput.press('Enter'); - expect(await page.getByRole('button', { name: `${rootfs} = ${demoDeviceName}` }).isVisible()).toBeTruthy(); - await page.waitForSelector('.deviceListItem'); - expect(await page.getByText('1-1 of 1').isVisible()).toBeTruthy(); - await page.getByText(/clear filter/i).click(); - if (['enterprise', 'staging'].includes(environment)) { - await page.getByLabel(/attribute/i).fill(rootfs); - await page.getByText(/equals/i).click(); - await page.getByText(`doesn't exist`).click(); + if (browserName === 'webkit') { await page.waitForTimeout(timeouts.fiveSeconds); - expect(await page.getByRole('button', { name: `${rootfs} doesn't exist` }).isVisible()).toBeTruthy(); - expect(await page.getByText('No devices found').isVisible()).toBeTruthy(); } + expect(await page.getByRole('button', { name: `${rootfs} = ${demoDeviceName}` }).isVisible({ timeout: timeouts.default })).toBeTruthy(); + await page.waitForSelector(selectors.deviceListItem); + }); + + test('can be filtered into non-existence', async ({ environment, loggedInPage: page }) => { + test.skip(!['enterprise', 'staging'].includes(environment), 'not available in OS'); + test.setTimeout(2 * timeouts.fifteenSeconds); + await page.click(`.leftNav :text('Devices')`); + await page.getByRole('button', { name: /filters/i }).click(); + await page.getByLabel(/attribute/i).fill(rootfs); + await page.getByText(/equals/i).click(); + await page.getByText(`doesn't exist`).click(); + await page.waitForTimeout(timeouts.fiveSeconds); + expect(await page.getByRole('button', { name: `${rootfs} doesn't exist` }).isVisible()).toBeTruthy(); + expect(await page.getByText('No devices found').isVisible()).toBeTruthy(); + await page.getByText(/clear filter/i).click(); + await page.waitForSelector(selectors.deviceListItem); + const paginationVisible = await page.getByText('1-1').isVisible({ timeout: timeouts.default }); + expect(paginationVisible).toBeTruthy(); }); test('can open a terminal', async ({ browserName, loggedInPage: page }) => { await page.click(`.leftNav :text('Devices')`); - await page.click(`.deviceListItem div:last-child`); + await page.click(`${selectors.deviceListItem} div:last-child`); await page.click(`text=/troubleshooting/i`); // the deviceconnect connection might not be established right away await page.waitForSelector('text=/Session status/i', { timeout: timeouts.tenSeconds }); diff --git a/tests/e2e_tests/integration/06-auditlogs.spec.ts b/tests/e2e_tests/integration/06-auditlogs.spec.ts index 41a4023070..139fa29f8a 100644 --- a/tests/e2e_tests/integration/06-auditlogs.spec.ts +++ b/tests/e2e_tests/integration/06-auditlogs.spec.ts @@ -26,7 +26,7 @@ test.describe('Auditlogs', () => { test('will track remote terminal sessions', async ({ environment, loggedInPage: page }) => { test.skip(!['enterprise', 'staging'].includes(environment)); await page.click(`.leftNav :text('Devices')`); - await page.click(`.deviceListItem div:last-child`); + await page.click(`${selectors.deviceListItem} div:last-child`); await page.click(`text=/troubleshooting/i`); // the deviceconnect connection might not be established right away await page.waitForSelector('text=/Session status/i', { timeout: timeouts.tenSeconds }); diff --git a/tests/e2e_tests/integration/07-saml.spec.ts b/tests/e2e_tests/integration/07-saml.spec.ts index c5768cffe6..7c15169cda 100644 --- a/tests/e2e_tests/integration/07-saml.spec.ts +++ b/tests/e2e_tests/integration/07-saml.spec.ts @@ -59,7 +59,7 @@ test.describe('SAML Login via sso/id/login', () => { }); // Setups the SAML/SSO login with samltest.id Identity Provider - test('Set up SAML', async ({ context, environment, baseUrl, page }) => { + test('Set up SAML', async ({ browserName, context, environment, baseUrl, loggedInPage: page }) => { test.skip(environment !== 'staging'); // allow a lot of time to enter metadata + then some to handle uploading the config to the external service test.setTimeout(5 * timeouts.sixtySeconds + timeouts.fifteenSeconds); @@ -76,7 +76,12 @@ test.describe('SAML Login via sso/id/login', () => { await page.locator('text=input with the text editor').click(); const textfield = await page.locator('[aria-label="Editor content\\;Press Alt\\+F1 for Accessibility Options\\."]'); - await textfield.fill(metadata.replace(/(?:\r\n|\r|\n)/g, '')); + const cleanedMetaData = metadata.replace(/(?:\r\n|\r|\n)/g, ''); + if (browserName === 'firefox') { + await textfield.pressSequentially(cleanedMetaData); + } else { + await textfield.fill(cleanedMetaData); + } console.log('typing metadata done.'); // The screenshot saves the view of the typed metadata await page.screenshot({ 'path': 'saml-edit-saving.png' }); @@ -122,7 +127,7 @@ test.describe('SAML Login via sso/id/login', () => { }); // Creates a user with login that matches Identity privder (samltest.id) user email - test('Creates a user without a password', async ({ environment, browserName, baseUrl, page }) => { + test('Creates a user without a password', async ({ environment, browserName, baseUrl, loggedInPage: page }) => { test.skip(environment !== 'staging'); await page.goto(`${baseUrl}ui/settings/user-management`); const userExists = await page.isVisible(`text=${samlSettings.credentials[browserName].email}`); @@ -147,7 +152,7 @@ test.describe('SAML Login via sso/id/login', () => { // and verifies that login is successful. test('User can login via sso/login endpoint', async ({ environment, browserName, baseUrl, browser, loggedInPage }) => { test.skip(environment !== 'staging'); - test.setTimeout(2 * timeouts.fifteenSeconds); + test.setTimeout(3 * timeouts.fifteenSeconds); await loggedInPage.goto(`${baseUrl}ui/settings/organization-and-billing`); await loggedInPage.waitForSelector('text=View metadata in the text editor', { timeout: timeouts.tenSeconds }); diff --git a/tests/e2e_tests/integration/08-rbac.spec.ts b/tests/e2e_tests/integration/08-rbac.spec.ts index 634547c4fe..e22a5c9cbc 100644 --- a/tests/e2e_tests/integration/08-rbac.spec.ts +++ b/tests/e2e_tests/integration/08-rbac.spec.ts @@ -111,7 +111,7 @@ test.describe('RBAC functionality', () => { expect(await page.isVisible(`css=button >> text=Upload`)).toBeFalsy(); await page.click(`.leftNav :text('Devices')`); - await page.click(`.deviceListItem div:last-child`); + await page.click(`${selectors.deviceListItem} div:last-child`); // the created role does have permission to configure devices, so the section should be visible await page.click(`text=/configuration/i`); await page.waitForSelector('text=/Device configuration/i', { timeout: timeouts.tenSeconds }); diff --git a/tests/e2e_tests/utils/constants.ts b/tests/e2e_tests/utils/constants.ts index 6930e5bda3..9f85f9e0b6 100644 --- a/tests/e2e_tests/utils/constants.ts +++ b/tests/e2e_tests/utils/constants.ts @@ -11,12 +11,13 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +const deviceListItem = '.deviceListItem'; export const selectors = { - deploymentCreation: 'text=/Create deployment/i', deploymentListItem: '.deployment-item', deploymentListItemContent: '.deployment-item:not(.deployment-header-item)', deviceGroupSelect: '#deployment-device-group-selection', - deviceListCheckbox: '.deviceListItem input', + deviceListCheckbox: `${deviceListItem} input`, + deviceListItem, email: '[name=email]', loggedInText: /License information/i, password: '[name=password]',