diff --git a/playwright/README.md b/playwright/README.md index 683df6371..48e6728a8 100644 --- a/playwright/README.md +++ b/playwright/README.md @@ -53,6 +53,9 @@ $ export CONSOLE_ADMIN_EMAIL= $ export CONSOLE_TEST_USERNAME= $ export CONSOLE_TEST_PASSWORD= $ export CONSOLE_TEST_EMAIL= +$ export CONSOLE_SECOND_TEST_USERNAME= +$ export CONSOLE_SECOND_TEST_PASSWORD= +$ export CONSOLE_SECOND_TEST_EMAIL= $ export CONSOLE_GLOBAL_TIMEOUT='900000' $ export CONSOLE_EXPECT_TIMEOUT='900000' $ export CONSOLE_DESTINATION_CLUSTER= diff --git a/playwright/config/console.ts b/playwright/config/console.ts index 1e7ffe8f7..cef81bca4 100644 --- a/playwright/config/console.ts +++ b/playwright/config/console.ts @@ -48,6 +48,16 @@ export const tcolinUser: Credentials = { email: 'thibault.colin@test.com', } +// User for integration test +export const secondTestUser: Credentials = { + id: 'test', + username: process.env.CONSOLE_SECOND_TEST_USERNAME.trim(), + password: process.env.CONSOLE_SECOND_TEST_PASSWORD.trim(), + firstName: 'test', + lastName: 'test', + email: process.env.CONSOLE_SECOND_TEST_EMAIL.trim(), +} + export async function signInCloudPiNative({ page, credentials, diff --git a/playwright/integration-tests/user-flow.spec.ts b/playwright/integration-tests/user-flow.spec.ts index f2cd408e6..b9720dd15 100644 --- a/playwright/integration-tests/user-flow.spec.ts +++ b/playwright/integration-tests/user-flow.spec.ts @@ -1,5 +1,5 @@ import { expect, test } from '@playwright/test' -import { adminUser, testUser, clientURL, signInCloudPiNative } from '../config/console' +import { adminUser, secondTestUser, testUser, clientURL, signInCloudPiNative } from '../config/console' import { addProject, @@ -8,13 +8,13 @@ import { } from '../e2e-tests/utils' const projectsToDelete: string[] = [] +const projectName = 'socleprojecttest' +const repositoryName = 'socle-project-test' +const destinationCluster = process.env.CONSOLE_DESTINATION_CLUSTER || 'cpin-app-hp' +projectsToDelete.push(projectName) -test.describe('Integration tests user flow', { tag: '@integ' }, () => { +test.describe('Integration tests user flow: project creation', { tag: '@integ' }, () => { test.describe.configure({ mode: 'serial' }) - const projectName = 'socleprojecttest' - const repositoryName = 'socle-project-test' - const destinationCluster = process.env.CONSOLE_DESTINATION_CLUSTER || 'cpin-app-hp' - projectsToDelete.push(projectName) test('Preliminary checks', { tag: '@replayable' }, async ({ page }) => { await page.goto(clientURL) @@ -68,6 +68,10 @@ test.describe('Integration tests user flow', { tag: '@integ' }, () => { await page.getByTestId('updateRepoBtn').click() await expect(page.getByRole('heading', { name: 'Opération en cours...' })).toBeVisible() }) +}) + +test.describe('Integration tests user flow: first checks', { tag: '@integ' }, () => { + test.describe.configure({ mode: 'parallel' }) test('Check Vault kv', { tag: '@replayable' }, async ({ page }) => { await page.goto(clientURL) @@ -90,6 +94,37 @@ test.describe('Integration tests user flow', { tag: '@integ' }, () => { await expect(page1.getByRole('link', { name: 'forge-dso' })).not.toBeVisible() }) + test('Project permissions', async ({ page }) => { + await page.goto(clientURL) + await signInCloudPiNative({ page, credentials: testUser }) + await page.getByTestId('menuMyProjects').click() + await page.getByRole('link', { name: projectName }).click() + // Add user to project + await page.getByTestId('test-tab-team').click() + await page.getByTestId('addUserSuggestionInput').locator('input').fill(secondTestUser.email) + await page.getByTestId('addUserBtn').click() + await expect(page.getByRole('heading', { name: 'Reprovisionnement nécessaire' })).toBeVisible() + // Create read-only role + await page.getByTestId('test-tab-roles').click() + await page.getByTestId('addRoleBtn').click() + await page.getByTestId('roleNameInput').fill('readOnly') + await page.getByText('Afficher les secrets Permet d').click() + await page.getByText('Voir les environnements').click() + await page.getByText('Voir les dépôts Permet de').click() + await page.getByTestId('saveBtn').click() + await expect(page.getByText('Rôle mis à jour')).toBeVisible() + await expect(page.getByRole('heading', { name: 'Reprovisionnement nécessaire' })).toBeVisible() + // Add user to read-only role + await page.getByTestId('test-members').click() + await page.getByLabel('Rôles', { exact: true }).getByText(secondTestUser.email).click() + await expect(page.getByText('Rôle mis à jour')).toBeVisible() + await expect(page.getByRole('heading', { name: 'Reprovisionnement nécessaire' })).toBeVisible() + // Replay project hooks + await page.getByTestId('replayHooksBtn').click() + await expect(page.getByRole('heading', { name: 'Opération en cours...' })).toBeVisible() + await expect(page.getByText('Le projet a été reprovisionn')).toBeVisible() + }) + test('Pipelines run', { tag: '@replayable' }, async ({ page }) => { await page.goto(clientURL) await signInCloudPiNative({ page, credentials: testUser }) @@ -119,6 +154,10 @@ test.describe('Integration tests user flow', { tag: '@integ' }, () => { page1.getByRole('link', { name: 'Status: Passed test-sonar' }), ).toBeVisible() }) +}) + +test.describe('Integration tests user flow: after pipelines checks', { tag: '@integ' }, () => { + test.describe.configure({ mode: 'parallel' }) test('Prepare ArgoCD deployment', async ({ page }) => { await page.goto(clientURL) @@ -177,6 +216,10 @@ test.describe('Integration tests user flow', { tag: '@integ' }, () => { // Check trivy scan result, hopefully will stay at C await expect(page1.getByRole('button', { name: 'C', exact: true })).toBeVisible() }) +}) + +test.describe('Integration tests user flow: deployment and metrics', { tag: '@integ' }, () => { + test.describe.configure({ mode: 'serial' }) test('ArgoCD deployment', { tag: '@replayable' }, async ({ page }) => { await page.goto(clientURL) @@ -200,10 +243,67 @@ test.describe('Integration tests user flow', { tag: '@integ' }, () => { await expect(page2.locator('html')).toContainText('Application is running') }) - test('Cleanup user test data', async ({ page }) => { + test('Check Grafana', { tag: '@replayable' }, async ({ page }) => { + await page.goto(clientURL) + await signInCloudPiNative({ page, credentials: testUser }) + await page.getByTestId('menuMyProjects').click() + await page.getByRole('link', { name: projectName }).click() + await page.getByTestId('test-tab-services').click() + const page1Promise = page.waitForEvent('popup') + await page.getByRole('link', { name: 'Grafana' }).click() + const page1 = await page1Promise + await page1.getByRole('link', { name: 'Sign in with grafana-projects' }).click() + await expect(page1.getByRole('link', { name: 'Grafana', exact: true })).toBeVisible() + await page1.getByTestId('data-testid Toggle menu').click() + await page1.getByRole('button', { name: 'Expand section Dashboards' }).click() + await page1.getByRole('link', { name: 'Dashboards', exact: true }).click() + await page1.getByRole('link', { name: 'dso-grafana' }).click() + // Check if we can see some metrics + await page1.getByRole('link', { name: 'Kubernetes / Views /' }).click() + await expect(page1.getByText('0.100')).toBeVisible() // Cpu request + await expect(page1.getByText('0.500')).toBeVisible() // Cpu limit + await expect(page1.getByText('256')).toBeVisible() // Memory request + await expect(page1.getByText('512')).toBeVisible() // Memory limit + // Check if we can see some logs + await page1.getByTestId('data-testid dso-grafana breadcrumb').click() + await page1.getByRole('link', { name: 'Loki Kubernetes Logs' }).click() + await expect(page1.getByTestId('data-testid Panel status error').first()).not.toBeVisible() + await expect(page1.locator('.rc-drawer-mask')).not.toBeVisible() + await page1.getByTestId('data-testid TimePicker Open Button').click() + await page1.getByText('Last 1 hour').click() + await page1.locator('.css-13x53bc-Icon-topVerticalAlign').first().click() + await expect(page1.getByRole('cell', { name: 'app_kubernetes_io_name' }).nth(1)).toBeVisible() + await expect(page1.getByText('demo-java-helm').nth(1)).toBeVisible() + }) +}) + +test.describe('Integration tests user flow: Cleanup', { tag: '@integ' }, () => { + test.describe.configure({ mode: 'serial' }) + + test('Remove permissions and user', async ({ page }) => { + await page.goto(clientURL) + await signInCloudPiNative({ page, credentials: testUser }) + await page.getByTestId('menuMyProjects').click() + await page.getByRole('link', { name: projectName }).click() + // Remove role membership + await page.getByTestId('test-tab-roles').click() + await page.getByRole('button', { name: 'readOnly' }).click() + await page.getByTestId('test-members').click() + await page.getByLabel('Rôles', { exact: true }).getByText(secondTestUser.email).click() + await expect(page.getByText('Rôle mis à jour')).toBeVisible() + await expect(page.getByRole('heading', { name: 'Reprovisionnement nécessaire' })).toBeVisible() + // Remove role + await page.getByTestId('test-general').click() + await page.getByTestId('deleteBtn').click() + await page.getByText('Rôle supprimé').click() + // Remove project membership + await page.getByTestId('test-tab-team').click() + await page.getByTitle(`Retirer ${secondTestUser.email} du`).click() + }) + + test('Remove stage', async ({ page }) => { await page.goto(clientURL) await signInCloudPiNative({ page, credentials: testUser }) - // ArgoCD deployment will be deleted when stage is deleted await page.getByTestId('menuMyProjects').click() await page.getByRole('link', { name: projectName }).click() await page.getByRole('cell', { name: 'integ' }).click() @@ -214,6 +314,7 @@ test.describe('Integration tests user flow', { tag: '@integ' }, () => { await expect( page.getByRole('cell', { name: 'Aucun environnement existant' }), ).toBeVisible() + // ArgoCD deployment will be deleted when stage is deleted await page.getByTestId('test-tab-services').click() const page1Promise = page.waitForEvent('popup') await page.getByRole('link', { name: 'ArgoCD DSO' }).click() @@ -222,7 +323,11 @@ test.describe('Integration tests user flow', { tag: '@integ' }, () => { await expect( page1.locator('span').filter({ hasText: `${projectName}-integ-socle-` }), ).not.toBeVisible() - // Remove repository from project + }) + + test('Remove repository from project', async ({ page }) => { + await page.goto(clientURL) + await signInCloudPiNative({ page, credentials: testUser }) await page.getByTestId('test-tab-resources').click() await page.getByRole('cell', { name: repositoryName }).click() await page.getByTestId('showDeleteRepoBtn').click() diff --git a/playwright/playwright.config.integration.ts b/playwright/playwright.config.integration.ts index 0a788d8a1..c4ac98a4e 100644 --- a/playwright/playwright.config.integration.ts +++ b/playwright/playwright.config.integration.ts @@ -13,13 +13,11 @@ dotenv.config({ */ export default defineConfig({ testDir: './integration-tests', - /* Run tests in files in parallel */ - fullyParallel: false, /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, /* Retry on CI only */ retries: process.env.CI ? 3 : 1, - workers: 1, + workers: 3, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: 'html', /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */