From 7ac57a35e42296d970380b7d4a3c023abbb08202 Mon Sep 17 00:00:00 2001 From: Julien Bouquillon Date: Tue, 2 Apr 2024 08:59:02 +0200 Subject: [PATCH] feat: playwright --- .github/workflows/e2e.yml | 22 - .github/workflows/playwright.yml | 32 ++ .gitignore | 6 +- .node-version | 2 +- .talismanrc | 2 +- Dockerfile | 4 +- README.md | 19 + cypress.json | 5 - cypress/fixtures/example.json | 5 - cypress/integration/accessibilite.spec.ts | 7 - cypress/integration/cgu.spec.ts | 7 - cypress/integration/healthz.spec.ts | 6 - cypress/integration/home.spec.ts | 7 - cypress/integration/mentions-legales.spec.ts | 7 - .../politique-confidentialite.spec.ts | 11 - cypress/integration/stats.spec.ts | 6 - cypress/plugins/index.ts | 22 - cypress/support/commands.ts | 25 - cypress/support/index.ts | 20 - cypress/tsconfig.json | 10 - package.json | 18 +- playwright.config.ts | 78 +++ sentry.edge.config.ts | 19 + src/pages/politique-confidentialite.mdx | 2 +- src/pages/stats.tsx | 2 +- tests/accessibilite.spec.ts | 29 + tests/cgu.spec.ts | 32 ++ tests/healthz.spec.ts | 12 + tests/home.spec.ts | 17 + tests/mentions-legales.spec.ts | 25 + tests/politique-confidentialite.spec.ts | 28 + tests/stats.spec.ts | 20 + tsconfig.json | 6 +- yarn.lock | 503 +++--------------- 34 files changed, 418 insertions(+), 598 deletions(-) delete mode 100644 .github/workflows/e2e.yml create mode 100644 .github/workflows/playwright.yml delete mode 100644 cypress.json delete mode 100644 cypress/fixtures/example.json delete mode 100644 cypress/integration/accessibilite.spec.ts delete mode 100644 cypress/integration/cgu.spec.ts delete mode 100644 cypress/integration/healthz.spec.ts delete mode 100644 cypress/integration/home.spec.ts delete mode 100644 cypress/integration/mentions-legales.spec.ts delete mode 100644 cypress/integration/politique-confidentialite.spec.ts delete mode 100644 cypress/integration/stats.spec.ts delete mode 100644 cypress/plugins/index.ts delete mode 100644 cypress/support/commands.ts delete mode 100644 cypress/support/index.ts delete mode 100644 cypress/tsconfig.json create mode 100644 playwright.config.ts create mode 100644 sentry.edge.config.ts create mode 100644 tests/accessibilite.spec.ts create mode 100644 tests/cgu.spec.ts create mode 100644 tests/healthz.spec.ts create mode 100644 tests/home.spec.ts create mode 100644 tests/mentions-legales.spec.ts create mode 100644 tests/politique-confidentialite.spec.ts create mode 100644 tests/stats.spec.ts diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml deleted file mode 100644 index 251f59fa..00000000 --- a/.github/workflows/e2e.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Testing e2e - -on: pull_request - -concurrency: - cancel-in-progress: true - group: e2e-${{ github.ref }} - -jobs: - e2e: - name: Testing e2e - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Cypress run - uses: cypress-io/github-action@v5 - with: - build: yarn build - start: npx serve@latest out - component: false - install-command: yarn --immutable diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 00000000..a1f60d73 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,32 @@ +name: Playwright Tests +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] +jobs: + test: + timeout-minutes: 20 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 18 + - name: Install dependencies + run: npm install -g yarn && yarn + - name: Install Playwright Browsers + run: yarn playwright install --with-deps + - name: Build app + run: yarn build + env: + NEXT_PUBLIC_BASE_PATH: "" + NODE_ENV: production + - name: Run Playwright tests + run: yarn playwright test + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 8ebf21a0..22fd7d6e 100644 --- a/.gitignore +++ b/.gitignore @@ -56,4 +56,8 @@ robots.txt !.yarn/versions # storybook -/.out \ No newline at end of file +/.out +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/.node-version b/.node-version index 2edeafb0..25bf17fc 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -20 \ No newline at end of file +18 \ No newline at end of file diff --git a/.talismanrc b/.talismanrc index 08f0b6aa..3a4e9cb1 100644 --- a/.talismanrc +++ b/.talismanrc @@ -20,7 +20,7 @@ fileignoreconfig: - filename: .yarn/** checksum: any - filename: Dockerfile - checksum: 9605a14ae835a4263de3badf041bfbde2823f2d9f2342e09c369ec9c7ae8615f + checksum: 31aa8689fb079f89949f8c4e5a917b040176f8a5b8492ba0a5252ad5e1bba049 - filename: README.md checksum: 7c55475ff28dbfa46c9c5715918add6a0202eafddca3cf9d35ebcdd443d76ffd - filename: pages/mui.tsx diff --git a/Dockerfile b/Dockerfile index ce85f946..56c14011 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ -ARG NODE_VERSION=20-alpine3.18@sha256:5ff63217ec2757b29a4414e0f787bfc13c1f9cb6f053e46ff05c1a51bbd2e8e6 +ARG NODE_VERSION=20-alpine3.19@sha256:ef3f47741e161900ddd07addcaca7e76534a9205e4cd73b2ed091ba339004a75 # Install dependencies only when needed FROM node:$NODE_VERSION AS builder -RUN apk add --no-cache libc6-compat=1.2.4-r2 +RUN apk add --no-cache libc6-compat WORKDIR /app COPY yarn.lock package.json ./ diff --git a/README.md b/README.md index 041e1c68..3091bd7b 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,21 @@ yarn # to install dependencies yarn dev # to run in dev mode ``` +Point your browser to [http://127.0.0.1:3000/template](http://127.0.0.1:3000/template) and start playing. + +### Tests + +``` +# run JEST unit tests +yarn test + +# build, serve and launch playwright interactive end-to-end tests +yarn e2e --ui + +# run storybook +yarn storybook +``` + ### Gestion des environnements Les variables issues des docker build-args, sont à utiliser dans `next.config.js`, pour les autres, il faut les définir dans les différents [`.env.*`](https://nextjs.org/docs/basic-features/environment-variables#environment-variable-load-order). @@ -41,3 +56,7 @@ Le fichier `.env.development` est utilisé pour l'environnement de développemen | [codegouvfr/docsify-dsfr-template](https://github.com/codegouvfr/docsify-dsfr-template) | Template DSFR pour [docsify](https://docsify.js.org/#/) | | [sneko/dsfr-connect](https://github.com/sneko/dsfr-connect) | Themes DSFR pour bootstrap, vuetify, mui, infima, emails... | | [socialgouv/template](https://github.com/socialgouv/template) | Version initiale de ce template | + +``` + +``` diff --git a/cypress.json b/cypress.json deleted file mode 100644 index 968e628c..00000000 --- a/cypress.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "baseUrl": "http://localhost:3000", - "defaultCommandTimeout": 10000, - "requestTimeout": 10000 -} diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json deleted file mode 100644 index 02e42543..00000000 --- a/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} diff --git a/cypress/integration/accessibilite.spec.ts b/cypress/integration/accessibilite.spec.ts deleted file mode 100644 index c54a4d91..00000000 --- a/cypress/integration/accessibilite.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -describe("Accessibilité page", () => { - it("should render the page", () => { - cy.visit("http://localhost:3000/accessibilite"); - cy.get("h1").should("contain", "Déclaration d’accessibilité"); - cy.get("h2").should("contain", "Amélioration et contact"); - }); -}); diff --git a/cypress/integration/cgu.spec.ts b/cypress/integration/cgu.spec.ts deleted file mode 100644 index 7f95d4fc..00000000 --- a/cypress/integration/cgu.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -describe("CGU page", () => { - it("should render the page", () => { - cy.visit("http://localhost:3000/cgu"); - cy.get("h1").should("contain", "Conditions générales d'utilisation"); - cy.get("h2").should("contain", "Absence de garantie"); - }); -}); diff --git a/cypress/integration/healthz.spec.ts b/cypress/integration/healthz.spec.ts deleted file mode 100644 index 3a23a7b4..00000000 --- a/cypress/integration/healthz.spec.ts +++ /dev/null @@ -1,6 +0,0 @@ -describe("Healthz page", () => { - it("should render the page", () => { - cy.visit("http://localhost:3000/healthz"); - cy.get("h1").should("contain", "App is up and running"); - }); -}); diff --git a/cypress/integration/home.spec.ts b/cypress/integration/home.spec.ts deleted file mode 100644 index f96fae72..00000000 --- a/cypress/integration/home.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -describe("Home page", () => { - it("should render the main page", () => { - cy.visit("http://localhost:3000/"); - cy.title().should("equal", "Template | beta.gouv.fr"); - cy.get("h1").should("contain", "Template"); - }); -}); diff --git a/cypress/integration/mentions-legales.spec.ts b/cypress/integration/mentions-legales.spec.ts deleted file mode 100644 index b822ecf8..00000000 --- a/cypress/integration/mentions-legales.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -describe("Mentions légales page", () => { - it("should render the page", () => { - cy.visit("http://localhost:3000/mentions-legales"); - cy.get("h1").should("contain", "Mentions légales"); - cy.get("h2").should("contain", "Hébergement du site"); - }); -}); diff --git a/cypress/integration/politique-confidentialite.spec.ts b/cypress/integration/politique-confidentialite.spec.ts deleted file mode 100644 index 3264017e..00000000 --- a/cypress/integration/politique-confidentialite.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -describe("Politique de confidentialité page", () => { - it("should render the page", () => { - cy.visit("http://localhost:3000/politique-confidentialite"); - cy.get("h1").should("contain", "Politique de confidentialité"); - cy.get("h2").should( - "contain", - "Traitement des données à caractère personnel" - ); - cy.get("h2").should("contain", "Cookies"); - }); -}); diff --git a/cypress/integration/stats.spec.ts b/cypress/integration/stats.spec.ts deleted file mode 100644 index 2333ae92..00000000 --- a/cypress/integration/stats.spec.ts +++ /dev/null @@ -1,6 +0,0 @@ -describe("Stats page", () => { - it("should render the page", () => { - cy.visit("http://localhost:3000/stats"); - cy.get("h1").should("contain", "Statistiques d'utilisation"); - }); -}); diff --git a/cypress/plugins/index.ts b/cypress/plugins/index.ts deleted file mode 100644 index 6684b018..00000000 --- a/cypress/plugins/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -/// -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -/** - * @type {Cypress.PluginConfig} - */ -// eslint-disable-next-line no-unused-vars -export default (on: any, config: any) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config -}; diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts deleted file mode 100644 index 119ab03f..00000000 --- a/cypress/support/commands.ts +++ /dev/null @@ -1,25 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add('login', (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) diff --git a/cypress/support/index.ts b/cypress/support/index.ts deleted file mode 100644 index d076cec9..00000000 --- a/cypress/support/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import "./commands"; - -// Alternatively you can use CommonJS syntax: -// require('./commands') diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json deleted file mode 100644 index 5642ab1b..00000000 --- a/cypress/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "noEmit": true, - "isolatedModules": false, - "types": ["cypress"] - }, - "include": ["../node_modules/cypress", "./**/*.ts"], - "exclude": ["node_modules"] -} diff --git a/package.json b/package.json index f6a5483d..a605dcef 100644 --- a/package.json +++ b/package.json @@ -18,13 +18,9 @@ "lint-staged": "lint-staged", "export": "next export", "build:export": "npm run build && npm run export", - "e2e": "cypress open", - "e2e:headless": "cypress run", + "e2e": "NEXT_PUBLIC_BASE_PATH='' NODE_ENV=production yarn build && yarn playwright test", "test": "jest", "test:watch": "jest --watch", - "test:e2e": "start-server-and-test dev http://localhost:3000 e2e", - "test:e2e:headless": "start-server-and-test dev http://localhost:3000 e2e:headless", - "storybook:start": "storybook dev --docs -p 6006", "storybook:build": "yarn build-storybook -c .storybook -o .out", "type-check": "tsc --noEmit", "type-check:watch": "npm run type-check -- --watch", @@ -36,7 +32,7 @@ "@codegouvfr/react-dsfr": "^1.9.5", "@emotion/react": "^11.11.4", "@emotion/server": "^11.11.0", - "@emotion/styled": "^11.11.0", + "@emotion/styled": "^11.11.5", "@gouvfr/dsfr-chart": "^1.0.0", "@mdx-js/loader": "^3.0.1", "@mdx-js/react": "^3.0.1", @@ -47,6 +43,7 @@ "@next/mdx": "^14.1.4", "@sentry/nextjs": "^7.109.0", "@socialgouv/matomo-next": "^1.8.1", + "dayjs": "^1.11.10", "is-ci": "^3.0.1", "next": "14.1.4", "react": "18.2.0", @@ -56,6 +53,7 @@ }, "devDependencies": { "@babel/core": "^7.24.3", + "@playwright/test": "^1.42.1", "@storybook/addon-actions": "^7.6.17", "@storybook/addon-docs": "^7.6.17", "@storybook/addon-essentials": "^7.6.17", @@ -71,10 +69,9 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^12.1.2", "@types/mdx": "^2.0.12", - "@types/node": "20.11.30", + "@types/node": "^18", "@types/react": "18.2.73", "@types/react-dom": "18.2.23", - "cypress": "^9.4.1", "eslint": "8.57.0", "eslint-config-next": "14.1.4", "eslint-plugin-jsx-a11y": "^6.8.0", @@ -87,7 +84,6 @@ "start-server-and-test": "^2.0.3", "storybook": "^7.6.17", "storybook-dark-mode": "^3.0.3", - "typescript": "4.9.5" - }, - "packageManager": "yarn@4.0.2" + "typescript": "5.4.3" + } } diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000..27e93333 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,78 @@ +import { defineConfig, devices } from "@playwright/test"; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: "./tests", + /* Run tests in files in parallel */ + fullyParallel: true, + /* 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 ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* 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. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: "http://127.0.0.1:3000", + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: "on-first-retry", + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: "chromium", + use: { ...devices["Desktop Chrome"] }, + }, + + // { + // name: "firefox", + // use: { ...devices["Desktop Firefox"] }, + // }, + + // { + // name: "webkit", + // use: { ...devices["Desktop Safari"] }, + // }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: "npx serve@latest out", + url: "http://127.0.0.1:3000", + reuseExistingServer: !process.env.CI, + timeout: 180 * 1000, + }, +}); diff --git a/sentry.edge.config.ts b/sentry.edge.config.ts new file mode 100644 index 00000000..590522e6 --- /dev/null +++ b/sentry.edge.config.ts @@ -0,0 +1,19 @@ +// This file configures the initialization of Sentry on the browser. +// The config you add here will be used whenever a page is visited. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +import * as Sentry from "@sentry/nextjs"; + +const SENTRY_DSN = process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN; +const SENTRY_ENV = process.env.SENTRY_ENV || process.env.NEXT_PUBLIC_SENTRY_ENV; + +Sentry.init({ + dsn: SENTRY_DSN ?? "", + environment: SENTRY_ENV ?? "development", + // Adjust this value in production, or use tracesSampler for greater control + tracesSampleRate: 0.1, + // ... + // Note: if you want to override the automatic release value, do not set a + // `release` value here - use the environment variable `SENTRY_RELEASE`, so + // that it will also get attached to your source maps +}); diff --git a/src/pages/politique-confidentialite.mdx b/src/pages/politique-confidentialite.mdx index b6732615..4e5ca348 100644 --- a/src/pages/politique-confidentialite.mdx +++ b/src/pages/politique-confidentialite.mdx @@ -14,7 +14,7 @@ import Alert from "@codegouvfr/react-dsfr/Alert"; # Politique de confidentialité -## Traitement des données à caractère personnelle +## Traitement des données à caractère personnel Template ne vous demande ni ne stocke d’information nominative. diff --git a/src/pages/stats.tsx b/src/pages/stats.tsx index d4c7e185..f49b3af4 100644 --- a/src/pages/stats.tsx +++ b/src/pages/stats.tsx @@ -28,7 +28,7 @@ const Stats: NextPage = () => { <>