diff --git a/docker-compose.tests.macos.yaml b/docker-compose.tests.macos.yaml new file mode 100755 index 00000000..bebbcbea --- /dev/null +++ b/docker-compose.tests.macos.yaml @@ -0,0 +1,116 @@ +version: "3.8" + +services: + webapp-backoffice: + image: node:20 + platform: linux/arm64 + working_dir: /app + volumes: + - ./webapp-backoffice:/app + environment: + POSTGRESQL_ADDON_URI: postgres://user:password@db:5432/jdma + NODEMAILER_HOST: mailhog + NODEMAILER_PORT: 1025 + NODEMAILER_USER: null + NODEMAILER_PASSWORD: null + NODEMAILER_FROM: "Équipe JDMA contact.jdma@design.numerique.gouv.fr" + NODEMAILER_BASEURL: http://localhost:3000 + NEXTAUTH_SECRET: secretauth + NEXTAUTH_URL: http://localhost:3000 + JWT_SECRET: mysecret + JWT_SIGNING_KEY: mykey + JWT_ENCRYPTION_KEY: myencryptionkey + JWT_ENCRYPTION_ALGORITHM: HS512 + JWT_EXPIRATION_TIME: 2592000 + NEXT_PUBLIC_BO_APP_URL: http://localhost:3000 + ES_ADDON_URI: https://localhost:9200 + ES_ADDON_USER: "elastic" + ES_ADDON_PASSWORD: "ES_ADDON_PASSWORD" + command: sh -c "npm rebuild bcrypt --build-from-source && yarn install && yarn build && npx prisma generate && npx prisma migrate reset --force && yarn start" + depends_on: + db: + condition: service_healthy + ports: + - "3000:3000" + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:3000/ || exit 1"] + interval: 10s + retries: 3 + start_period: 10s + timeout: 10s + networks: + - jdma-network + + webapp-form: + image: node:20 + platform: linux/arm64 + working_dir: /app + volumes: + - ./webapp-form:/app + environment: + POSTGRESQL_ADDON_URI: postgres://user:password@db:5432/jdma + NODEMAILER_HOST: mailhog + NODEMAILER_PORT: 1025 + command: sh -c "npm rebuild bcrypt --build-from-source && yarn install && yarn build && npx prisma generate && yarn startB" + depends_on: + db: + condition: service_healthy + ports: + - "3001:3001" + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:3001/ || exit 1"] + interval: 10s + retries: 3 + start_period: 10s + timeout: 10s + networks: + - jdma-network + + db: + image: postgres:16-alpine + environment: + POSTGRES_USER: user + POSTGRES_PASSWORD: password + POSTGRES_DB: jdma + volumes: + - pg_data:/var/lib/postgresql/data + ports: + - "5555:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U user -d jdma"] + interval: 10s + retries: 3 + start_period: 10s + timeout: 10s + networks: + - jdma-network + + mailhog: + image: mailhog/mailhog:latest + ports: + - "8025:8025" # Interface web de MailHog + - "1025:1025" # SMTP + networks: + - jdma-network + + # Service pour reset et reseed la base de données + db-reset: + image: node:20 + working_dir: /app + volumes: + - ./webapp-backoffice:/app + environment: + POSTGRESQL_ADDON_URI: postgres://user:password@db:5432/jdma + command: sh -c "npx prisma migrate reset --force --skip-generate --skip-seed && npx prisma db seed" + depends_on: + db: + condition: service_healthy + networks: + - jdma-network + +volumes: + pg_data: + +networks: + jdma-network: + name: jdma-network diff --git a/webapp-backoffice/cypress/e2e/jdma/bo/users.cy.js b/webapp-backoffice/cypress/e2e/jdma/bo/users.cy.js new file mode 100755 index 00000000..1317496c --- /dev/null +++ b/webapp-backoffice/cypress/e2e/jdma/bo/users.cy.js @@ -0,0 +1,174 @@ +const app_url = Cypress.env('app_base_url'); +const app_form_url = Cypress.env('app_form_base_url'); +const adminEmail = Cypress.env('admin_user_mail'); +const adminPassword = Cypress.env('admin_user_password'); + +const selectors = { + loginForm: { + email: 'input[name="email"]', + password: 'input[type="password"]', + continueButton: '[class*="LoginForm-button"]' + }, + dashboard: { + products: '/administration/dashboard/products', + entities: '/administration/dashboard/entities', + users: '/administration/dashboard/users', + nameTestOrga: 'e2e-jdma-entity-test', + nameTestService: 'e2e-jdma-service-test' + }, + modal: { + product: 'dialog#product-modal', + entity: 'dialog#entity-modal', + button: 'dialog#button-modal' + }, + sideMenu: { + menu: 'nav.fr-sidemenu', + menuItem: 'li.fr-sidemenu__item' + }, + modalFooter: '.fr-modal__footer', + productTitle: '[class*="productTitle"]', + productForm: '#product-form' +}; + +describe('jdma-users', () => { + beforeEach(() => { + cy.visit(`${app_url}/login`); + loginAsAdmin(); + cy.url().should('eq', `${app_url}${selectors.dashboard.products}`); + }); + + it('should create a service and attach an organization', () => { + createProduct(); + }); + + it('should navigate to created product access page', () => { + navigateToCreatedProduct(); + }); + + it('should display service administrators', () => { + navigateToCreatedProduct(); + cy.get('div.fr-card div.fr-grid-row div.fr-col span').should( + 'contain', + 'admin@example.com' + ); + }); + + it('should display organization administrators', () => { + navigateToCreatedProduct(); + cy.get('div.fr-card').should('contain', 'admin@example.com'); + }); + + it('should invite an administrator', () => { + navigateToCreatedProduct(); + cy.get('button').contains('Inviter des administrateurs').click(); + + cy.get('input[class*="fr-input"]').type('user3@example.com'); + + cy.get('button').contains('Inviter').click(); + }); + + it('should display the invited user', () => { + navigateToCreatedProduct(); + cy.get('div.fr-card div.fr-grid-row div.fr-col span').should( + 'contain', + 'user3@example.com' + ); + }); + + it('should navigate to the user page, verify if the user is admin and then remove the access', () => { + navigateToCreatedProduct(); + cy.get('nav.fr-nav ul.fr-nav__list li.fr-nav__item a') + .filter(':contains("Utilisateurs")') + .first() + .click(); + cy.url().should('include', '/administration/dashboard/users'); + cy.get('div.fr-card div.fr-grid-row div.fr-col a') + .filter(':contains("user 3")') + .first() + .click(); + cy.wait(1000); + cy.url().should('include', '/administration/dashboard/user/'); + cy.get(selectors.sideMenu.menu) + .should('be.visible') + .within(() => { + cy.get(selectors.sideMenu.menuItem).contains('Accès').click(); + }); + //target the organization card under the correct organization and then remove the access from the service previously created + cy.get('h5') + .filter(':contains("e2e-jdma-entity-test")') + .should('exist') + .get('div.fr-card') + .contains('e2e-jdma-service-test-users') + .first() + .get('button') + .contains("Retirer l'accès") + .click(); + }); + + it('should have removed the user', () => { + navigateToCreatedProduct(); + cy.get('div.fr-card div span', { timeout: 10000 }).should( + 'not.contain', + 'user3@example.com' + ); + }); +}); + +function navigateToCreatedProduct() { + cy.visit(`${app_url}${selectors.dashboard.products}`); + cy.url().should('include', selectors.dashboard.products); + cy.get(selectors.productTitle) + .filter(':contains("e2e-jdma-service-test-users")') + .should('have.length', 1) + .should('contain', 'e2e-jdma-service-test-users') + .closest('a') + .first() + .click(); + cy.url().should('include', '/administration/dashboard/product/'); + cy.get(selectors.sideMenu.menu) + .should('be.visible') + .within(() => { + cy.get(selectors.sideMenu.menuItem).contains("Gérer l'accès").click(); + }); +} + +function loginAsAdmin() { + login(adminEmail, adminPassword); +} + +function addUrls(urls) { + urls.forEach((url, index) => { + if (index > 0) cy.contains('button', 'Ajouter un URL').click(); + cy.get(`input[name="urls.${index}.value"]`).type(url); + }); +} + +function login(email, password) { + cy.get(selectors.loginForm.email).type(email); + cy.get(selectors.loginForm.continueButton).contains('Continuer').click(); + cy.get(selectors.loginForm.password).type(password); + cy.get(selectors.loginForm.continueButton).contains('Confirmer').click(); +} + +function createProduct() { + cy.contains('button', 'Ajouter un nouveau service').click(); + cy.get(selectors.productForm, { timeout: 10000 }) + .should('be.visible') + .within(() => { + cy.get('input[name="title"]').type('e2e-jdma-service-test-users'); + selectEntity(); + addUrls(['http://testurl1.com/', 'http://testurl2.com/']); + }); + + cy.get(selectors.modalFooter) + .contains('button', 'Ajouter ce service') + .click(); +} + +function selectEntity() { + cy.get('input#entity-select-autocomplete').click(); + cy.get('div[role="presentation"]') + .should('be.visible') + .find('[id="entity-select-autocomplete-option-0"]') + .click(); +} diff --git a/webapp-backoffice/cypress/e2e/jdma/launcher.cy.js b/webapp-backoffice/cypress/e2e/jdma/launcher.cy.js index 7e5e1fc3..f13fb2a2 100755 --- a/webapp-backoffice/cypress/e2e/jdma/launcher.cy.js +++ b/webapp-backoffice/cypress/e2e/jdma/launcher.cy.js @@ -3,4 +3,5 @@ import './bo/admin.cy.js'; import './bo/register.cy.js'; import './form/review.cy.js'; import './bo/reviewCheck.cy.js'; -import './bo/account.cy.js' +import './bo/account.cy.js'; +import './bo/users.cy.js';