diff --git a/apps/client/.env-example b/apps/client/.env-example index 4431c04dbb..2f23f06f97 100644 --- a/apps/client/.env-example +++ b/apps/client/.env-example @@ -11,7 +11,7 @@ SERVER_PORT=4000 # Domaine (host:port) du serveur Keycloak pour l'authentification KEYCLOAK_DOMAIN=localhost:8090 # Nom du royaume Keycloak utilisé par la console -KEYCLOAK_REALM=cloud-pi-native +KEYCLOAK_REALM=dso # Protocole de communication avec Keycloak (http | https) KEYCLOAK_PROTOCOL=http # Identifiant du client Keycloak côté frontend diff --git a/apps/client/.env.docker-example b/apps/client/.env.docker-example index 6e8c5c0765..1f05a462c6 100644 --- a/apps/client/.env.docker-example +++ b/apps/client/.env.docker-example @@ -8,7 +8,7 @@ SERVER_PORT=8080 # Domaine (host:port) du serveur Keycloak pour l'authentification KEYCLOAK_DOMAIN=localhost:8090 # Nom du royaume Keycloak utilisé par la console -KEYCLOAK_REALM=cloud-pi-native +KEYCLOAK_REALM=dso # Protocole de communication avec Keycloak (http | https) KEYCLOAK_PROTOCOL=http # Identifiant du client Keycloak côté frontend diff --git a/apps/server-nestjs/.env-example b/apps/server-nestjs/.env-example index d34d6f1497..0c706ecb89 100644 --- a/apps/server-nestjs/.env-example +++ b/apps/server-nestjs/.env-example @@ -10,7 +10,7 @@ SESSION_SECRET=a-very-strong-secret-with-more-than-32-char # Domaine (host:port) du serveur Keycloak pour l'authentification KEYCLOAK_DOMAIN=localhost:8090 # Nom du royaume Keycloak utilisé par la console -KEYCLOAK_REALM=cloud-pi-native +KEYCLOAK_REALM=dso # Protocole de communication avec Keycloak (http | https) KEYCLOAK_PROTOCOL=http # Identifiant du client Keycloak côté backend diff --git a/apps/server-nestjs/.env.docker-example b/apps/server-nestjs/.env.docker-example index 45ea0aae63..54609cef2f 100644 --- a/apps/server-nestjs/.env.docker-example +++ b/apps/server-nestjs/.env.docker-example @@ -11,7 +11,7 @@ SESSION_SECRET=a-very-strong-secret-with-more-than-32-char # Domaine du Keycloak (nom du service Docker Compose + port interne) KEYCLOAK_DOMAIN=keycloak:8080 # Nom du royaume Keycloak utilisé par la console -KEYCLOAK_REALM=cloud-pi-native +KEYCLOAK_REALM=dso # Protocole de communication avec Keycloak (http en Docker local) KEYCLOAK_PROTOCOL=http # Identifiant du client Keycloak côté backend diff --git a/apps/server-nestjs/documentation/mise-en-place-nginx-etrangleur/PLAN.md b/apps/server-nestjs/documentation/mise-en-place-nginx-etrangleur/PLAN.md index ceb69034c8..b7ba2c4258 100644 --- a/apps/server-nestjs/documentation/mise-en-place-nginx-etrangleur/PLAN.md +++ b/apps/server-nestjs/documentation/mise-en-place-nginx-etrangleur/PLAN.md @@ -162,7 +162,7 @@ Idem dev (avec Watch) + volumes kubeconfig. ### Tâche 3.5 : `docker/docker-compose.local.yml` ✅ -`nginx-strangler` ajouté avec `host.docker.internal`, port `8082:8080`. +`nginx-strangler` ajouté avec `host.docker.internal`, port `8082:8080`, `extra_hosts: host-gateway`. ### Tâche 3.6 : `apps/client/nginx/default.docker.conf` ✅ diff --git a/apps/server/.env-example b/apps/server/.env-example index 558f88f1e8..e26aaeea95 100644 --- a/apps/server/.env-example +++ b/apps/server/.env-example @@ -11,7 +11,7 @@ SESSION_SECRET=a-very-strong-secret-with-more-than-32-char # Domaine (host:port) du serveur Keycloak pour l'authentification KEYCLOAK_DOMAIN=localhost:8090 # Nom du royaume Keycloak utilisé par la console -KEYCLOAK_REALM=cloud-pi-native +KEYCLOAK_REALM=dso # Protocole de communication avec Keycloak (http | https) KEYCLOAK_PROTOCOL=http # Identifiant du client Keycloak côté backend diff --git a/apps/server/.env.docker-example b/apps/server/.env.docker-example index 57566b0396..571be2ff45 100644 --- a/apps/server/.env.docker-example +++ b/apps/server/.env.docker-example @@ -13,7 +13,7 @@ SESSION_SECRET=a-very-strong-secret-with-more-than-32-char # Domaine du Keycloak (nom du service Docker Compose + port interne) KEYCLOAK_DOMAIN=keycloak:8080 # Nom du royaume Keycloak utilisé par la console -KEYCLOAK_REALM=cloud-pi-native +KEYCLOAK_REALM=dso # Protocole de communication avec Keycloak (http en Docker local) KEYCLOAK_PROTOCOL=http # Identifiant du client Keycloak côté backend diff --git a/docker/docker-compose.local.yml b/docker/docker-compose.local.yml index 5022ee16ed..80473f1823 100644 --- a/docker/docker-compose.local.yml +++ b/docker/docker-compose.local.yml @@ -106,6 +106,8 @@ services: dockerfile: apps/nginx-strangler/Dockerfile image: dso-console/nginx-strangler:local container_name: dso-console_nginx-strangler + extra_hosts: + - host.docker.internal:${HOST_GATEWAY_IP:-host-gateway} ports: - 4000:8080 environment: diff --git a/keycloak/realms/realm-dev.json b/keycloak/realms/realm-dev.json index 7becdd4cab..f7e932d8c0 100644 --- a/keycloak/realms/realm-dev.json +++ b/keycloak/realms/realm-dev.json @@ -1,5 +1,5 @@ { - "realm": "cloud-pi-native", + "realm": "dso", "enabled": true, "accessTokenLifespan": 300, "accessCodeLifespan": 60, @@ -10,9 +10,10 @@ "registrationAllowed": false, "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", - "requiredCredentials": [ - "password" - ], + "requiredCredentials": ["password"], + "attributes": { + "frontendUrl": "http://localhost:8090" + }, "browserSecurityHeaders": { "contentSecurityPolicyReportOnly": "", "xContentTypeOptions": "nosniff", @@ -48,14 +49,9 @@ "value": "test" } ], - "realmRoles": [ - "user", - "offline_access" - ], + "realmRoles": ["user", "offline_access"], "clientRoles": { - "account": [ - "manage-account" - ] + "account": ["manage-account"] } }, { @@ -72,18 +68,11 @@ "value": "test" } ], - "realmRoles": [ - "user", - "offline_access" - ], + "realmRoles": ["user", "offline_access"], "clientRoles": { - "account": [ - "manage-account" - ] + "account": ["manage-account"] }, - "groups": [ - "admin" - ] + "groups": ["admin"] }, { "id": "cb8e5b4b-7b7b-40f5-935f-594f48ae6567", @@ -99,14 +88,9 @@ "value": "test" } ], - "realmRoles": [ - "user", - "offline_access" - ], + "realmRoles": ["user", "offline_access"], "clientRoles": { - "account": [ - "manage-account" - ] + "account": ["manage-account"] } }, { @@ -123,14 +107,9 @@ "value": "test" } ], - "realmRoles": [ - "user", - "offline_access" - ], + "realmRoles": ["user", "offline_access"], "clientRoles": { - "account": [ - "manage-account" - ] + "account": ["manage-account"] } }, { @@ -147,14 +126,9 @@ "value": "test" } ], - "realmRoles": [ - "user", - "offline_access" - ], + "realmRoles": ["user", "offline_access"], "clientRoles": { - "account": [ - "manage-account" - ] + "account": ["manage-account"] } }, { @@ -171,14 +145,9 @@ "value": "test" } ], - "realmRoles": [ - "user", - "offline_access" - ], + "realmRoles": ["user", "offline_access"], "clientRoles": { - "account": [ - "manage-account" - ] + "account": ["manage-account"] } }, { @@ -195,21 +164,12 @@ "value": "admin" } ], - "realmRoles": [ - "user", - "admin" - ], + "realmRoles": ["user", "admin"], "clientRoles": { - "realm-management": [ - "realm-admin" - ], - "account": [ - "manage-account" - ] + "realm-management": ["realm-admin"], + "account": ["manage-account"] }, - "groups": [ - "admin" - ] + "groups": ["admin"] } ], "roles": { @@ -237,12 +197,8 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": [ - "*" - ], - "webOrigins": [ - "*" - ], + "redirectUris": ["*"], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -280,10 +236,7 @@ "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": true, "nodeReRegistrationTimeout": -1, - "defaultClientScopes": [ - "generic", - "basic" - ], + "defaultClientScopes": ["generic", "basic"], "optionalClientScopes": [ "address", "phone", @@ -348,9 +301,7 @@ "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": true, "nodeReRegistrationTimeout": -1, - "defaultClientScopes": [ - "generic" - ], + "defaultClientScopes": ["generic"], "optionalClientScopes": [ "address", "phone", @@ -386,17 +337,19 @@ "gui.order": "", "consent.screen.text": "" }, - "protocolMappers": [{ - "id": "910359ad-0368-44dd-b10c-d1cae6c6dd17", - "name": "sub", - "protocol": "openid-connect", - "protocolMapper": "oidc-sub-mapper", - "consentRequired": false, - "config": { - "introspection.token.claim": "true", - "access.token.claim": "true" + "protocolMappers": [ + { + "id": "910359ad-0368-44dd-b10c-d1cae6c6dd17", + "name": "sub", + "protocol": "openid-connect", + "protocolMapper": "oidc-sub-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } } - }] + ] }, { "id": "1611458d-38ab-4a9f-859e-1812d251c1ac", @@ -619,9 +572,6 @@ "adminTheme": "DSFR", "emailTheme": "DSFR", "internationalizationEnabled": true, - "supportedLocales": [ - "en", - "fr" - ], + "supportedLocales": ["en", "fr"], "defaultLocale": "fr" } diff --git a/playwright/e2e-tests/clusters.spec.ts b/playwright/e2e-tests/clusters.spec.ts index 6011036673..e7917af0d2 100644 --- a/playwright/e2e-tests/clusters.spec.ts +++ b/playwright/e2e-tests/clusters.spec.ts @@ -125,7 +125,6 @@ test.describe('Clusters page', () => { }) // Validate await page.getByTestId(`clusterLink-${clusterName}`).click() - await expect(page.getByTestId('cpin-loader')).toHaveCount(0) await expect(page.getByTestId('infosInput')).toHaveValue(informations) }) @@ -138,7 +137,6 @@ test.describe('Clusters page', () => { await signInCloudPiNative({ page, credentials: adminUser }) await page.getByTestId('menuAdministrationBtn').click() await page.getByTestId('menuAdministrationClusters').click() - await expect(page.getByTestId('cpin-loader')).toHaveCount(0) await page.getByTestId('addClusterLink').click() await page.getByTestId('labelInput').fill(clusterName) await page.getByTestId('infosInput').fill(informations) diff --git a/playwright/e2e-tests/repos.spec.ts b/playwright/e2e-tests/repos.spec.ts index 7711d1dd84..41362ea716 100644 --- a/playwright/e2e-tests/repos.spec.ts +++ b/playwright/e2e-tests/repos.spec.ts @@ -20,7 +20,7 @@ test.describe('Repositories', () => { // good hint that something is very very wrong) test( 'Should handle repository form validation', - { tag: ['@e2e', '@need-rework'] }, + { tag: ['@e2e', '@repos', '@need-rework'] }, async ({ page }) => { // Arrange await page.goto(clientURL) @@ -46,6 +46,7 @@ test.describe('Repositories', () => { ) await page.getByTestId('addRepoLink').click() + await page.locator('dialog').waitFor({ state: 'visible' }) await expect(page.locator('h2')).toContainText( 'Ajouter un dépôt au projet', ) @@ -53,6 +54,10 @@ test.describe('Repositories', () => { await setCheckbox( page.getByTestId('standaloneRepoSwitch').locator('input'), ) + // The double-fill is desired, because the first one often fails ¯\_(ツ)_/¯ + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) await page .getByTestId('internalRepoNameInput') .fill(repo.internalRepoName) @@ -69,6 +74,10 @@ test.describe('Repositories', () => { .locator('//following-sibling::*[1]'), ).toContainText(invalidInternalRepoErrorMessage) await expect(page.getByTestId('addRepoBtn')).toBeDisabled() + // The double-fill is desired, because the first one often fails ¯\_(ツ)_/¯ + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) await page .getByTestId('internalRepoNameInput') .fill(repo.internalRepoName) @@ -133,7 +142,7 @@ test.describe('Repositories', () => { test( 'Should add a standalone public repo', - { tag: '@e2e' }, + { tag: ['@e2e', '@repos'] }, async ({ page }) => { // Arrange await page.goto(clientURL) @@ -159,6 +168,10 @@ test.describe('Repositories', () => { await expect(page.locator('h2')).toContainText( 'Ajouter un dépôt au projet', ) + // The double-fill is desired, because the first one often fails ¯\_(ツ)_/¯ + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) await page .getByTestId('internalRepoNameInput') .fill(repo.internalRepoName) @@ -175,7 +188,7 @@ test.describe('Repositories', () => { test( 'Should add an external public repo', - { tag: '@e2e' }, + { tag: ['@e2e', '@repos'] }, async ({ page }) => { // Arrange await page.goto(clientURL) @@ -205,6 +218,10 @@ test.describe('Repositories', () => { await expect(page.locator('h2')).toContainText( 'Ajouter un dépôt au projet', ) + // The double-fill is desired, because the first one often fails ¯\_(ツ)_/¯ + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) await page .getByTestId('internalRepoNameInput') .fill(repo.internalRepoName) @@ -218,7 +235,7 @@ test.describe('Repositories', () => { test( 'Should add an external private repo', - { tag: '@e2e' }, + { tag: ['@e2e', '@repos'] }, async ({ page }) => { // Arrange await page.goto(clientURL) @@ -248,6 +265,10 @@ test.describe('Repositories', () => { await expect(page.locator('h2')).toContainText( 'Ajouter un dépôt au projet', ) + // The double-fill is desired, because the first one often fails ¯\_(ツ)_/¯ + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) await page .getByTestId('internalRepoNameInput') .fill(repo.internalRepoName) @@ -263,7 +284,7 @@ test.describe('Repositories', () => { test( 'Should add an external public infra repo', - { tag: '@e2e' }, + { tag: ['@e2e', '@repos'] }, async ({ page }) => { // Arrange await page.goto(clientURL) @@ -293,6 +314,10 @@ test.describe('Repositories', () => { await expect(page.locator('h2')).toContainText( 'Ajouter un dépôt au projet', ) + // The double-fill is desired, because the first one often fails ¯\_(ツ)_/¯ + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) await page .getByTestId('internalRepoNameInput') .fill(repo.internalRepoName) @@ -307,7 +332,7 @@ test.describe('Repositories', () => { test( 'Should add an external private infra repo', - { tag: '@e2e' }, + { tag: ['@e2e', '@repos'] }, async ({ page }) => { // Arrange await page.goto(clientURL) @@ -337,6 +362,10 @@ test.describe('Repositories', () => { await expect(page.locator('h2')).toContainText( 'Ajouter un dépôt au projet', ) + // The double-fill is desired, because the first one often fails ¯\_(ツ)_/¯ + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) await page .getByTestId('internalRepoNameInput') .fill(repo.internalRepoName) @@ -354,189 +383,228 @@ test.describe('Repositories', () => { }, ) - test('Should update a repo', { tag: '@e2e' }, async ({ page }) => { - // Arrange - await page.goto(clientURL) - await signInCloudPiNative({ page, credentials: testUser }) - const { name: projectName, slug: projectSlug } = await createProject({ - page, - }) - const repo = { - externalRepoUrl: 'https://github.com/externalUser03/repo03.git', - externalToken: 'private-token', - externalUserName: 'this-is+tobi', - internalRepoName: 'repo03', - isInfra: true, - isPrivate: false, - } - - // Act - Create repository - await page.getByTestId('menuMyProjects').click() - await page.getByRole('link', { name: projectName }).click() - await expect(page.locator('h1')).toContainText(projectName) - await expect(page.getByTestId('project-slug')).toHaveText( - projectSlugTextRegexp(projectSlug), - ) - await page.getByTestId('addRepoLink').click() - await expect(page.locator('h2')).toContainText( - 'Ajouter un dépôt au projet', - ) - await page.getByTestId('internalRepoNameInput').fill(repo.internalRepoName) - await page.getByTestId('externalRepoUrlInput').fill(repo.externalRepoUrl) - await setCheckbox(page.getByTestId('input-checkbox-infraRepoCbx')) - await page.getByTestId('addRepoBtn').click() - await expect( - page.getByTestId(`repoTr-${repo.internalRepoName}`), - ).toBeVisible() - - // Assert - Update repository - await page.getByTestId(`repoTr-repo03`).click() - await expect(page.getByTestId('internalRepoNameInput')).toBeDisabled() - await page - .getByTestId('externalRepoUrlInput') - .fill('https://github.com/externalUser04/new-repo.git') - await setCheckbox(page.getByTestId('input-checkbox-privateRepoCbx')) - await page.getByTestId('externalUserNameInput').fill('newUser') - await page.getByTestId('externalTokenInput').fill('newToken') - await expect(page.getByTestId('input-checkbox-infraRepoCbx')).toBeEnabled() - await page.getByTestId('updateRepoBtn').click() - await expect(page.getByTestId(`repoTr-repo03`)).toBeVisible() - await page.reload() - await page.getByTestId(`repoTr-repo03`).click() - await expect(page.getByTestId('externalRepoUrlInput')).toHaveValue( - 'https://github.com/externalUser04/new-repo.git', - ) - await expect( - page.getByTestId('input-checkbox-privateRepoCbx'), - ).toBeChecked() - await expect(page.getByTestId('externalUserNameInput')).toHaveValue( - 'newUser', - ) - await expect(page.getByTestId('externalTokenInput')).toHaveValue(fakeToken) - }) - - test('Should synchronise a repo', { tag: '@e2e' }, async ({ page }) => { - // Arrange - await page.goto(clientURL) - await signInCloudPiNative({ page, credentials: testUser }) - const { name: projectName, slug: projectSlug } = await createProject({ - page, - }) - const repo = { - externalRepoUrl: 'https://github.com/externalUser03/repo03.git', - externalToken: 'private-token', - externalUserName: 'this-is+tobi', - internalRepoName: 'repo03', - isInfra: true, - isPrivate: false, - } - - // Act - Create repository - await page.getByTestId('menuMyProjects').click() - await page.getByRole('link', { name: projectName }).click() - await expect(page.locator('h1')).toContainText(projectName) - await expect(page.getByTestId('project-slug')).toHaveText( - projectSlugTextRegexp(projectSlug), - ) - await page.getByTestId('addRepoLink').click() - await expect(page.locator('h2')).toContainText( - 'Ajouter un dépôt au projet', - ) - await page.getByTestId('internalRepoNameInput').fill(repo.internalRepoName) - await page.getByTestId('externalRepoUrlInput').fill(repo.externalRepoUrl) - await setCheckbox(page.getByTestId('input-checkbox-infraRepoCbx')) - await page.getByTestId('addRepoBtn').click() - await expect( - page.getByTestId(`repoTr-${repo.internalRepoName}`), - ).toBeVisible() - - // Assert - Update repository - await page.getByTestId(`repoTr-repo03`).click() - await expect(page.getByTestId('branchNameInput')).toHaveValue('main') - await expect(page.getByTestId('branchNameInput')).toBeEnabled() - await expect(page.getByTestId('syncRepoBtn')).toBeEnabled() - await page - .getByTestId('toggleSyncAllBranches') - .locator('input') - .check({ force: true }) - await expect(page.getByTestId('branchNameInput')).not.toBeVisible() - await expect(page.getByTestId('syncRepoBtn')).toBeEnabled() - await page - .getByTestId('toggleSyncAllBranches') - .locator('input') - .uncheck({ force: true }) - await expect(page.getByTestId('branchNameInput')).toHaveValue('main') - await expect(page.getByTestId('branchNameInput')).toBeEnabled() - await expect(page.getByTestId('syncRepoBtn')).toBeEnabled() - await page.getByTestId('syncRepoBtn').click() - await expect(page.getByTestId('snackbar')).toContainText( - 'Travail de synchronisation lancé pour le dépôt repo03', - ) - await page.getByTestId('branchNameInput').clear() - await expect(page.getByTestId('syncRepoBtn')).toBeDisabled() - await page.getByTestId('branchNameInput').fill('develop') - await expect(page.getByTestId('syncRepoBtn')).toBeEnabled() - await page.getByTestId('syncRepoBtn').click() - await expect(page.getByTestId('snackbar')).toContainText( - 'Travail de synchronisation lancé pour le dépôt repo03', - ) - }) - - test('Should delete a repo', { tag: '@e2e' }, async ({ page }) => { - // Arrange - await page.goto(clientURL) - await signInCloudPiNative({ page, credentials: testUser }) - const { name: projectName, slug: projectSlug } = await createProject({ - page, - }) - const repo = { - externalRepoUrl: 'https://github.com/externalUser03/repo03.git', - externalToken: 'private-token', - externalUserName: 'this-is+tobi', - internalRepoName: 'repo03', - isInfra: true, - isPrivate: false, - } - - // Act - Create repository - await page.getByTestId('menuMyProjects').click() - await page.getByRole('link', { name: projectName }).click() - await expect(page.locator('h1')).toContainText(projectName) - await expect(page.getByTestId('project-slug')).toHaveText( - projectSlugTextRegexp(projectSlug), - ) - await page.getByTestId('addRepoLink').click() - await expect(page.locator('h2')).toContainText( - 'Ajouter un dépôt au projet', - ) - await page.getByTestId('internalRepoNameInput').fill(repo.internalRepoName) - await page.getByTestId('externalRepoUrlInput').fill(repo.externalRepoUrl) - await setCheckbox(page.getByTestId('input-checkbox-infraRepoCbx')) - await page.getByTestId('addRepoBtn').click() - await expect( - page.getByTestId(`repoTr-${repo.internalRepoName}`), - ).toBeVisible() - - // Assert - Delete repository - // - await page.getByTestId(`repoTr-${repo.internalRepoName}`).click() - await expect(page.getByTestId('repo-form')).toBeVisible() - await expect(page.getByTestId('deleteRepoInput')).not.toBeVisible() - await expect(page.getByTestId('deleteRepoZone')).toBeVisible() - await page.getByTestId('showDeleteRepoBtn').click() - await expect(page.getByTestId('deleteRepoBtn')).toBeDisabled() - await page.getByTestId('deleteRepoInput').fill(deleteValidationInput) - await page.getByTestId('deleteRepoBtn').click() - await expect(page.getByTestId('repo-form')).not.toBeVisible() - await page.reload() - await expect( - page.getByTestId(`repoTr-${repo.internalRepoName}`), - ).not.toBeVisible() - }) + test( + 'Should update a repo', + { tag: ['@e2e', '@repos'] }, + async ({ page }) => { + // Arrange + await page.goto(clientURL) + await signInCloudPiNative({ page, credentials: testUser }) + const { name: projectName, slug: projectSlug } = await createProject({ + page, + }) + const repo = { + externalRepoUrl: 'https://github.com/externalUser03/repo03.git', + externalToken: 'private-token', + externalUserName: 'this-is+tobi', + internalRepoName: 'repo03', + isInfra: true, + isPrivate: false, + } + + // Act - Create repository + await page.getByTestId('menuMyProjects').click() + await page.getByRole('link', { name: projectName }).click() + await expect(page.locator('h1')).toContainText(projectName) + await expect(page.getByTestId('project-slug')).toHaveText( + projectSlugTextRegexp(projectSlug), + ) + await page.getByTestId('addRepoLink').click() + await expect(page.locator('h2')).toContainText( + 'Ajouter un dépôt au projet', + ) + // The double-fill is desired, because the first one often fails ¯\_(ツ)_/¯ + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) + await page.getByTestId('externalRepoUrlInput').fill(repo.externalRepoUrl) + await setCheckbox(page.getByTestId('input-checkbox-infraRepoCbx')) + await page.getByTestId('addRepoBtn').click() + await expect( + page.getByTestId(`repoTr-${repo.internalRepoName}`), + ).toBeVisible() + + // Assert - Update repository + await page.getByTestId(`repoTr-repo03`).click() + await expect(page.getByTestId('internalRepoNameInput')).toBeDisabled() + await page + .getByTestId('externalRepoUrlInput') + .fill('https://github.com/externalUser04/new-repo.git') + await setCheckbox(page.getByTestId('input-checkbox-privateRepoCbx')) + await page.getByTestId('externalUserNameInput').fill('newUser') + await page.getByTestId('externalTokenInput').fill('newToken') + await expect( + page.getByTestId('input-checkbox-infraRepoCbx'), + ).toBeEnabled() + await page.getByTestId('updateRepoBtn').click() + await expect(page.getByTestId(`repoTr-repo03`)).toBeVisible() + await page.reload() + await page.getByTestId(`repoTr-repo03`).click() + await expect(page.getByTestId('externalRepoUrlInput')).toHaveValue( + 'https://github.com/externalUser04/new-repo.git', + ) + await expect( + page.getByTestId('input-checkbox-privateRepoCbx'), + ).toBeChecked() + await expect(page.getByTestId('externalUserNameInput')).toHaveValue( + 'newUser', + ) + await expect(page.getByTestId('externalTokenInput')).toHaveValue( + fakeToken, + ) + }, + ) + + test( + 'Should synchronise a repo', + { tag: ['@e2e', '@repos'] }, + async ({ page }) => { + // Arrange + await page.goto(clientURL) + await signInCloudPiNative({ page, credentials: testUser }) + const { name: projectName, slug: projectSlug } = await createProject({ + page, + }) + const repo = { + externalRepoUrl: 'https://github.com/externalUser03/repo03.git', + externalToken: 'private-token', + externalUserName: 'this-is+tobi', + internalRepoName: 'repo03', + isInfra: true, + isPrivate: false, + } + + // Act - Create repository + await page.getByTestId('menuMyProjects').click() + await page.getByRole('link', { name: projectName }).click() + await expect(page.locator('h1')).toContainText(projectName) + await expect(page.getByTestId('project-slug')).toHaveText( + projectSlugTextRegexp(projectSlug), + ) + await page.getByTestId('addRepoLink').click() + await expect(page.locator('h2')).toContainText( + 'Ajouter un dépôt au projet', + ) + // The double-fill is desired, because the first one often fails ¯\_(ツ)_/¯ + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) + await page.getByTestId('externalRepoUrlInput').fill(repo.externalRepoUrl) + await setCheckbox(page.getByTestId('input-checkbox-infraRepoCbx')) + await page.getByTestId('addRepoBtn').click() + await expect( + page.getByTestId(`repoTr-${repo.internalRepoName}`), + ).toBeVisible() + + // Assert - Update repository + await page.getByTestId(`repoTr-repo03`).click() + await expect(page.getByTestId('branchNameInput')).toHaveValue('main') + await expect(page.getByTestId('branchNameInput')).toBeEnabled() + await expect(page.getByTestId('syncRepoBtn')).toBeEnabled() + await page + .getByTestId('toggleSyncAllBranches') + .locator('input') + .check({ force: true }) + await expect(page.getByTestId('branchNameInput')).not.toBeVisible() + await expect(page.getByTestId('syncRepoBtn')).toBeEnabled() + await page + .getByTestId('toggleSyncAllBranches') + .locator('input') + .uncheck({ force: true }) + await expect(page.getByTestId('branchNameInput')).toHaveValue('main') + await expect(page.getByTestId('branchNameInput')).toBeEnabled() + await expect(page.getByTestId('syncRepoBtn')).toBeEnabled() + await page.getByTestId('syncRepoBtn').click() + await expect( + page.getByText( + 'Travail de synchronisation lancé pour le dépôt repo03Fermer le message', + ), + ).toBeVisible() + + await page.getByTestId('branchNameInput').clear() + await expect(page.getByTestId('syncRepoBtn')).toBeDisabled() + await page.getByTestId('branchNameInput').fill('develop') + await expect(page.getByTestId('syncRepoBtn')).toBeEnabled() + await page.getByTestId('syncRepoBtn').click() + await expect( + page.getByText( + 'Travail de synchronisation lancé pour le dépôt repo03Fermer le message', + ), + ).toBeVisible() + }, + ) + + test( + 'Should delete a repo', + { tag: ['@e2e', '@repos'] }, + async ({ page }) => { + // Arrange + await page.goto(clientURL) + await signInCloudPiNative({ page, credentials: testUser }) + const { name: projectName, slug: projectSlug } = await createProject({ + page, + }) + const repo = { + externalRepoUrl: 'https://github.com/externalUser03/repo03.git', + externalToken: 'private-token', + externalUserName: 'this-is+tobi', + internalRepoName: 'repo03', + isInfra: true, + isPrivate: false, + } + + // Act - Create repository + await page.getByTestId('menuMyProjects').click() + await page.getByRole('link', { name: projectName }).click() + await expect(page.locator('h1')).toContainText(projectName) + await expect(page.getByTestId('project-slug')).toHaveText( + projectSlugTextRegexp(projectSlug), + ) + await page.getByTestId('addRepoLink').click() + await expect(page.locator('h2')).toContainText( + 'Ajouter un dépôt au projet', + ) + // The double-fill is desired, because the first one often fails ¯\_(ツ)_/¯ + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) + await page.getByTestId('externalRepoUrlInput').fill(repo.externalRepoUrl) + await setCheckbox(page.getByTestId('input-checkbox-infraRepoCbx')) + await page.getByTestId('addRepoBtn').click() + await expect( + page.getByTestId(`repoTr-${repo.internalRepoName}`), + ).toBeVisible() + + // Assert - Delete repository + // + await page.getByTestId(`repoTr-${repo.internalRepoName}`).click() + await expect(page.getByTestId('repo-form')).toBeVisible() + await expect(page.getByTestId('deleteRepoInput')).not.toBeVisible() + await expect(page.getByTestId('deleteRepoZone')).toBeVisible() + await page.getByTestId('showDeleteRepoBtn').click() + await expect(page.getByTestId('deleteRepoBtn')).toBeDisabled() + await page.getByTestId('deleteRepoInput').fill(deleteValidationInput) + await page.getByTestId('deleteRepoBtn').click() + await expect(page.getByTestId('repo-form')).not.toBeVisible() + await page.reload() + await expect( + page.getByTestId(`repoTr-${repo.internalRepoName}`), + ).not.toBeVisible() + }, + ) test( 'Should not be able to delete a repository if not owner', - { tag: '@e2e' }, + { tag: ['@e2e', '@repos'] }, async ({ page }) => { // Arrange await page.goto(clientURL) @@ -568,6 +636,10 @@ test.describe('Repositories', () => { await expect(page.locator('h2')).toContainText( 'Ajouter un dépôt au projet', ) + // The double-fill is desired, because the first one often fails ¯\_(ツ)_/¯ + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) await page .getByTestId('internalRepoNameInput') .fill(repo.internalRepoName) @@ -598,7 +670,7 @@ test.describe('Repositories', () => { test( 'Should not be able to delete a repository if project locked', - { tag: '@e2e' }, + { tag: ['@e2e', '@repos'] }, async ({ page }) => { // Arrange await page.goto(clientURL) @@ -635,6 +707,10 @@ test.describe('Repositories', () => { await expect(page.locator('h2')).toContainText( 'Ajouter un dépôt au projet', ) + // The double-fill is desired, because the first one often fails ¯\_(ツ)_/¯ + await page + .getByTestId('internalRepoNameInput') + .fill(repo.internalRepoName) await page .getByTestId('internalRepoNameInput') .fill(repo.internalRepoName) diff --git a/playwright/e2e-tests/service-chains.spec.ts b/playwright/e2e-tests/service-chains.spec.ts index 1deb0d078a..e1dc6def9d 100644 --- a/playwright/e2e-tests/service-chains.spec.ts +++ b/playwright/e2e-tests/service-chains.spec.ts @@ -3,55 +3,66 @@ import { adminUser, clientURL, signInCloudPiNative } from '../config/console' test.describe('Service Chains page', () => { test.describe('Given an Admin-level user', () => { - // @TODO These tests assume that there is at least one Service Chain present - // in the mocked up data. Ensure that this is true at all times ! - test('should list service chains', { tag: '@e2e' }, async ({ page }) => { - await page.goto(clientURL) - await signInCloudPiNative({ page, credentials: adminUser }) - await page.getByTestId('menuAdministrationBtn').click() - await page.getByTestId('menuAdministrationServiceChains').click() - // We take the first service chain available - await expect( - page + test( + 'should list service chains', + { tag: ['@e2e', '@service-chains'] }, + async ({ page }) => { + await page.goto(clientURL) + await signInCloudPiNative({ page, credentials: adminUser }) + await page.getByTestId('menuAdministrationBtn').click() + await page.getByTestId('menuAdministrationServiceChains').click() + // We take the first service chain available + await expect( + page + .getByTestId('tableAdministrationServiceChains') + .locator('tr') + .nth(1), + ).toBeVisible() + }, + ) + + test( + 'should show a service chain details', + { tag: ['@e2e', '@service-chains'] }, + async ({ page }) => { + await page.goto(clientURL) + await signInCloudPiNative({ page, credentials: adminUser }) + await page.getByTestId('menuAdministrationBtn').click() + await page.getByTestId('menuAdministrationServiceChains').click() + // We take the first service chain available + await page .getByTestId('tableAdministrationServiceChains') .locator('tr') - .nth(1), - ).toBeVisible() - }) + .nth(1) + .click() + await expect( + page.getByRole('heading', { name: 'Chaîne de services' }), + ).toBeVisible() + }, + ) - // TODO: I gave up on this test, it's stuck on a loading thing - test.skip('should show a service chain details', { tag: '@e2e' }, async ({ page }) => { - await page.goto(clientURL) - await signInCloudPiNative({ page, credentials: adminUser }) - await page.getByTestId('menuAdministrationBtn').click() - await page.getByTestId('menuAdministrationServiceChains').click() - // We take the first service chain available - await page - .getByTestId('tableAdministrationServiceChains') - .locator('tr') - .nth(1) - .click() - await expect( - page.getByRole('heading', { name: 'Chaîne de services' }), - ).toBeVisible() - }) + test( + 'should show a service chain flows', + { tag: ['@e2e', '@service-chains', '@service-chain-flows'] }, + async ({ page }) => { + await page.goto(clientURL) + await signInCloudPiNative({ page, credentials: adminUser }) + await page.getByTestId('menuAdministrationBtn').click() + await expect(page.locator('.fr-collapsing')).toBeVisible() + await expect(page.locator('.fr-collapsing')).not.toBeVisible() - // TODO: I gave up on this test, it's stuck on a loading thing - test.skip('should show a service chain flows', { tag: '@e2e' }, async ({ page }) => { - await page.goto(clientURL) - await signInCloudPiNative({ page, credentials: adminUser }) - await page.getByTestId('menuAdministrationBtn').click() - await page.getByTestId('menuAdministrationServiceChains').click() - await expect(page.getByTestId('cpin-loader')).toHaveCount(0) - // We take the first service chain available - await page - .getByTestId('tableAdministrationServiceChains') - .locator('tr') - .nth(1) - .click() - await expect( - page.getByTestId('service-chain-flows'), - ).toBeVisible() - }) + await page.getByTestId('menuAdministrationServiceChains').click() + await expect( + page.getByTitle('Voir les détails de la chaîne de service').first(), + ).toBeVisible() + // We take the first service chain available + await page + .getByTestId('tableAdministrationServiceChains') + .locator('tr') + .nth(1) + .click() + await expect(page.getByTestId('service-chain-flows')).toBeVisible() + }, + ) }) }) diff --git a/playwright/helpers/cluster.ts b/playwright/helpers/cluster.ts index 1c2cdefe16..2239dd8904 100644 --- a/playwright/helpers/cluster.ts +++ b/playwright/helpers/cluster.ts @@ -111,7 +111,6 @@ export async function createCluster({ if (informations) await fillClusterInformations(page, informations) await page.getByTestId('addClusterBtn').click() - await expect(page.getByTestId('cpin-loader')).toHaveCount(0) await searchCluster(page, clusterName) return clusterName } diff --git a/playwright/helpers/navigation.ts b/playwright/helpers/navigation.ts index 2e5cb9a095..3d4dc22a7d 100644 --- a/playwright/helpers/navigation.ts +++ b/playwright/helpers/navigation.ts @@ -1,9 +1,7 @@ import type { Page } from '@playwright/test' -import { expect } from '@playwright/test' export async function openMyProjects({ page }: { page: Page }) { await page.getByTestId('menuMyProjects').click() - await expect(page.getByTestId('cpin-loader')).toHaveCount(0) } async function ensureAdminMenuVisible(page: Page, menuTestId: string) { @@ -15,17 +13,14 @@ async function ensureAdminMenuVisible(page: Page, menuTestId: string) { export async function openClustersAdministration({ page }: { page: Page }) { await ensureAdminMenuVisible(page, 'menuAdministrationClusters') await page.getByTestId('menuAdministrationClusters').click() - await expect(page.getByTestId('cpin-loader')).toHaveCount(0) } export async function openStagesAdministration({ page }: { page: Page }) { await ensureAdminMenuVisible(page, 'menuAdministrationStages') await page.getByTestId('menuAdministrationStages').click() - await expect(page.getByTestId('cpin-loader')).toHaveCount(0) } export async function openZonesAdministration({ page }: { page: Page }) { await ensureAdminMenuVisible(page, 'menuAdministrationZones') await page.getByTestId('menuAdministrationZones').click() - await expect(page.getByTestId('cpin-loader')).toHaveCount(0) }