J'ai toujours aimé regarder mes tests unitaires s'exécuter (et réussir). Ils sont rapides et réussir les tests me donne l’assurance que mes pièces individuelles se comportent comme elles sont censées le faire. À l’inverse, j’ai souvent eu du mal à prioriser les tests de bout en bout pour le navigateur, car leur écriture et leur exécution étaient extrêmement lentes.
Heureusement, les outils de test de bout en bout dans le navigateur se sont améliorés et plus rapides au fil des années. Et avec une configuration de navigateur sans tête, je peux exécuter mes tests de navigateur dans le cadre de mon CI.
Récemment, je suis tombé sur cet article de blog Heroku parlant de l'automatisation des tests dans le navigateur avec Chrome sans tête dans Heroku CI. Heroku dispose d'un buildpack qui installe Chrome sans tête, que vous pouvez appeler pour vos tests dans le pipeline CI.
L'exemple de configuration tiré de l'article de blog était une application React testée avec Puppeteer et Jest. C'est un bon début … mais que se passe-t-il si j'utilise Playwright au lieu de Puppeteer ? Est-ce possible ?
J'ai décidé d'enquêter. Il s'avère que — oui, vous pouvez aussi le faire avec Playwright ! J'ai donc capturé les étapes dont vous auriez besoin pour exécuter les tests Playwright sur le navigateur Chrome sans tête utilisé dans Heroku CI. Dans cet article, je vais vous guider à travers les étapes de configuration.
Les tests de bout en bout capturent la manière dont les utilisateurs interagissent réellement avec votre application dans un navigateur, validant ainsi des flux de travail complets. Playwright rend ce processus assez transparent grâce aux tests dans Chrome, Firefox et Safari. Bien sûr, exécuter une série complète de tests de navigateur dans CI est assez lourd, c'est pourquoi le mode sans tête est utile.
Le buildpack Chrome for Testing de Heroku installe Chrome sur une application Heroku, afin que vous puissiez exécuter vos tests Playwright dans Heroku CI avec une configuration vraiment légère.
Puisque je venais d'essayer cela, j'ai créé le dépôt GitHub qui était initialement référencé dans le billet de blog Heroku. L'application était une simple application React avec un lien, une saisie de texte et un bouton de soumission. Il y a eu trois tests :
Vérifiez que le lien fonctionne et redirige vers le bon emplacement.
Vérifiez que la saisie de texte affiche correctement la saisie de l'utilisateur.
Vérifiez que la soumission du formulaire met à jour le texte affiché sur la page.
Assez simple. Maintenant, j'avais juste besoin de changer le code pour utiliser Playwright au lieu de Puppeteer et Jest. Oh, et je voulais aussi utiliser pnpm au lieu de npm. Voici un lien vers mon dépôt GitHub forké.
Passons en revue les étapes que j'ai suivies pour modifier le code. J'ai commencé avec mon dépôt forké, identique au dépôt heroku-examples.
Je voulais utiliser pnpm au lieu de npm. (Préférence personnelle.) Alors, voici ce que j’ai fait en premier :
~/project$ corepack enable pnpm ~/project$ corepack use pnpm@latest Installing pnpm@9.12.3 in the project… … Progress: resolved 1444, reused 1441, downloaded 2, added 1444, done … Done in 14.4s ~/project$ rm package-lock.json ~/project$ pnpm install # just to show everything's good Lockfile is up to date, resolution step is skipped Already up to date Done in 1.3s
Ensuite, j'ai supprimé Puppeteer et Jest, et j'ai ajouté Playwright.
~/project$ pnpm remove \ babel-jest jest jest-puppeteer @testing-library/jest-dom ~/project$ $ pnpm create playwright Getting started with writing end-to-end tests with Playwright: Initializing project in '.' ✔ Do you want to use TypeScript or JavaScript? · JavaScript ✔ Where to put your end-to-end tests? · tests ✔ Add a GitHub Actions workflow? (y/N) · false ✔ Install Playwright browsers (can be done manually via 'pnpm exec playwright install')? (Y/n) · false ✔ Install Playwright operating system dependencies (requires sudo / root - can be done manually via 'sudo pnpm exec playwright install-deps')? (y/N) · false Installing Playwright Test (pnpm add --save-dev @playwright/test)… … Installing Types (pnpm add --save-dev @types/node)… … Done in 2.7s Writing playwright.config.js. Writing tests/example.spec.js. Writing tests-examples/demo-todo-app.spec.js. Writing package.json.
J'ai également supprimé la section de configuration Jest de package.json.
Vous pouvez exécuter vos tests Playwright dans Chrome, Firefox et Safari. Comme je me concentrais sur Chrome, j'ai supprimé les autres navigateurs de la section projets du fichier playwright.config.js généré :
/* Configure projects for major browsers */ projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, // { // name: 'firefox', // use: { ...devices['Desktop Firefox'] }, // }, // // { // name: 'webkit', // use: { ...devices['Desktop Safari'] }, // }, ], …
Le code original avait un fichier de test Puppeteer à l'adresse src/tests/puppeteer.test.js. J'ai déplacé ce fichier vers tests/playwright.spec.js. Ensuite, j'ai mis à jour le test pour utiliser les conventions de Playwright, qui se sont mappées assez clairement. Le nouveau fichier de test ressemblait à ceci :
const ROOT_URL = 'http://localhost:8080'; const { test, expect } = require('@playwright/test'); const inputSelector = 'input[name="name"]'; const submitButtonSelector = 'button[type="submit"]'; const greetingSelector = 'h5#greeting'; const name = 'John Doe'; test.beforeEach(async ({ page }) => { await page.goto(ROOT_URL); }); test.describe('Playwright link', () => { test('should navigate to Playwright documentation page', async ({ page }) => { await page.click('a[href="https://playwright.dev/"]'); await expect(page.title()).resolves.toMatch('| Playwright'); }); }); test.describe('Text input', () => { test('should display the entered text in the text input', async ({ page }) => { await page.fill(inputSelector, name); // Verify the input value const inputValue = await page.inputValue(inputSelector); expect(inputValue).toBe(name); }); }); test.describe('Form submission', () => { test('should display the "Hello, X" message after form submission', async ({ page }) => { const expectedGreeting = `Hello, ${name}.`; await page.fill(inputSelector, name); await page.click(submitButtonSelector); await page.waitForSelector(greetingSelector); const greetingText = await page.textContent(greetingSelector); expect(greetingText).toBe(expectedGreeting); }); });
Pour tester mon application React, je devais d'abord la lancer (sur http://localhost:8080) dans un processus distinct, puis je pourrais exécuter mes tests. Ce serait le cas si j'utilisais Puppeteer ou Playwright. Avec Puppeteer, l'exemple Heroku utilisait le package start-server-and-test. Cependant, vous pouvez configurer Playwright pour lancer l'application avant d'exécuter des tests. C'est plutôt pratique !
J'ai supprimé start-server-and-test de mon projet.
~/project$ pnpm remove start-server-and-test
Dans playwright.config.js, j'ai décommenté la section webServer en bas, en la modifiant pour ressembler à ceci :
/* Run your local dev server before starting the tests */ webServer: { command: 'pnpm start', url: 'http://127.0.0.1:8080', reuseExistingServer: !process.env.CI, },
Ensuite, j'ai supprimé le script test:ci du fichier package.json d'origine. Au lieu de cela, mon script de test ressemblait à ceci :
"scripts": { … "test": "playwright test --project=chromium --reporter list" },
Playwright installe les derniers binaires du navigateur à utiliser pour ses tests. Donc, sur ma machine locale, j'avais besoin de Playwright pour installer sa version de Chromium.
~/project$ pnpm playwright install chromium Downloading Chromium 130.0.6723.31 (playwright build v1140) from https://playwright.azureedge.net/builds/chromium/1140/chromium-linux.zip 164.5 MiB [====================] 100%
Remarque : Le buildpack Chrome for Testing sur Heroku installe le navigateur que nous utiliserons pour les tests. Nous configurerons notre CI pour que Playwright utilise ce navigateur au lieu de consacrer du temps et des ressources à installer le sien.
Avec ça, j’étais prêt. Il était temps de tester mes tests localement.
~/project$ pnpm test > playwright test --project=chromium --reporter list Running 3 tests using 3 workers ✓ 1 [chromium] > playwright.spec.js:21:3 > Text input > should display the entered text in the text input (911ms) ✘ 2 [chromium] > playwright.spec.js:14:3 > Playwright link > should navigate to Playwright documentation page (5.2s) ✓ 3 [chromium] > playwright.spec.js:31:3 > Form submission > should display the "Hello, X" message after form submission (959ms) ... - waiting for locator('a[href="https://playwright.dev/"]') 13 | test.describe('Playwright link', () => { 14 | test('should navigate to Playwright documentation page', async ({ page }) => { > 15 | await page.click('a[href="https://playwright.dev/"]'); | ^ 16 | await expect(page.title()).resolves.toMatch('| Playwright'); 17 | }); 18 | });
Oh ! C'est exact. J'ai modifié mon test pour m'attendre à ce que le lien dans l'application me dirige vers la documentation de Playwright au lieu de celle de Puppeteer. J'avais besoin de mettre à jour src/App.js à la ligne 19 :
<Link href="https://playwright.dev/" rel="noopener"> Playwright Documentation </Link>
Maintenant, il était temps de refaire les tests…
~/project$ pnpm test > playwright test --project=chromium --reporter list Running 3 tests using 3 workers ✓ 1 [chromium] > playwright.spec.js:21:3 > Text input > should display the entered text in the text input (1.1s) ✓ 2 [chromium] > playwright.spec.js:14:3 > Playwright link > should navigate to Playwright documentation page (1.1s) ✓ 3 [chromium] > playwright.spec.js:31:3 > Form submission > should display the "Hello, X" message after form submission (1.1s) 3 passed (5.7s)
Les tests ont réussi ! Ensuite, il était temps de nous amener sur Heroku CI.
J'ai suivi les instructions du billet de blog Heroku pour configurer mon application dans un pipeline Heroku CI.
Dans Heroku, j'ai créé un nouveau pipeline et je l'ai connecté à mon dépôt GitHub forké.
Ensuite, j'ai ajouté mon application à la mise en scène.
Ensuite, je suis allé dans l'onglet Tests et j'ai cliqué sur Activer Heroku CI.
Enfin, j'ai modifié le fichier app.json pour supprimer le script de test qui était configuré pour appeler npm test:ci. J'avais déjà supprimé le script test:ci de mon fichier package.json. Le script de test dans package.json était désormais celui à utiliser, et Heroku CI le rechercherait par défaut.
Mon fichier app.json, qui veillait à utiliser le buildpack Chrome for Testing, ressemblait à ceci :
~/project$ corepack enable pnpm ~/project$ corepack use pnpm@latest Installing pnpm@9.12.3 in the project… … Progress: resolved 1444, reused 1441, downloaded 2, added 1444, done … Done in 14.4s ~/project$ rm package-lock.json ~/project$ pnpm install # just to show everything's good Lockfile is up to date, resolution step is skipped Already up to date Done in 1.3s
J'ai poussé mon code vers GitHub, ce qui a déclenché un test dans Heroku CI.
Le test a échoué, mais je n’étais pas inquiet. Je savais qu'il y aurait une configuration Playwright à faire.
En fouillant dans le journal de test, j'ai trouvé ceci :
~/project$ pnpm remove \ babel-jest jest jest-puppeteer @testing-library/jest-dom ~/project$ $ pnpm create playwright Getting started with writing end-to-end tests with Playwright: Initializing project in '.' ✔ Do you want to use TypeScript or JavaScript? · JavaScript ✔ Where to put your end-to-end tests? · tests ✔ Add a GitHub Actions workflow? (y/N) · false ✔ Install Playwright browsers (can be done manually via 'pnpm exec playwright install')? (Y/n) · false ✔ Install Playwright operating system dependencies (requires sudo / root - can be done manually via 'sudo pnpm exec playwright install-deps')? (y/N) · false Installing Playwright Test (pnpm add --save-dev @playwright/test)… … Installing Types (pnpm add --save-dev @types/node)… … Done in 2.7s Writing playwright.config.js. Writing tests/example.spec.js. Writing tests-examples/demo-todo-app.spec.js. Writing package.json.
Playwright recherchait l'instance du navigateur Chrome. Je pourrais l'installer avec la commande playwright install chromium dans le cadre de ma configuration de test CI. Mais cela irait à l’encontre de l’objectif même du buildpack Chrome for Testing. Chrome était déjà installé ; J'avais juste besoin de le souligner correctement.
En regardant en arrière dans mon journal de configuration de test pour Heroku, j'ai trouvé ces lignes :
/* Configure projects for major browsers */ projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, // { // name: 'firefox', // use: { ...devices['Desktop Firefox'] }, // }, // // { // name: 'webkit', // use: { ...devices['Desktop Safari'] }, // }, ], …
Donc, le navigateur que je voulais utiliser était /app/.chrome-for-testing/chrome-linux64/chrome. J'aurais juste besoin que Playwright le cherche là-bas.
Remarque : Si vous n'êtes pas intéressé par les détails essentiels ici, vous pouvez ignorer cette section et simplement copier le app.json complet plus bas. Cela devrait vous donner ce dont vous avez besoin pour être opérationnel avec Playwright sur Heroku CI.
Dans la documentation de Playwright, j'ai découvert que vous pouvez définir une variable d'environnement qui indique à Playwright si vous avez utilisé un emplacement personnalisé pour toutes ses installations de navigateur. Cette variable d'environnement est PLAYWRIGHT_BROWSERS_PATH. J'ai décidé de commencer par là.
Dans app.json, j'ai défini une variable d'environnement comme celle-ci :
const ROOT_URL = 'http://localhost:8080'; const { test, expect } = require('@playwright/test'); const inputSelector = 'input[name="name"]'; const submitButtonSelector = 'button[type="submit"]'; const greetingSelector = 'h5#greeting'; const name = 'John Doe'; test.beforeEach(async ({ page }) => { await page.goto(ROOT_URL); }); test.describe('Playwright link', () => { test('should navigate to Playwright documentation page', async ({ page }) => { await page.click('a[href="https://playwright.dev/"]'); await expect(page.title()).resolves.toMatch('| Playwright'); }); }); test.describe('Text input', () => { test('should display the entered text in the text input', async ({ page }) => { await page.fill(inputSelector, name); // Verify the input value const inputValue = await page.inputValue(inputSelector); expect(inputValue).toBe(name); }); }); test.describe('Form submission', () => { test('should display the "Hello, X" message after form submission', async ({ page }) => { const expectedGreeting = `Hello, ${name}.`; await page.fill(inputSelector, name); await page.click(submitButtonSelector); await page.waitForSelector(greetingSelector); const greetingText = await page.textContent(greetingSelector); expect(greetingText).toBe(expectedGreeting); }); });
J'ai poussé mon code vers GitHub pour voir ce qui se passerait avec mes tests en CI.
Comme prévu, cela a encore échoué. Cependant, l'erreur de journal a montré ceci :
~/project$ corepack enable pnpm ~/project$ corepack use pnpm@latest Installing pnpm@9.12.3 in the project… … Progress: resolved 1444, reused 1441, downloaded 2, added 1444, done … Done in 14.4s ~/project$ rm package-lock.json ~/project$ pnpm install # just to show everything's good Lockfile is up to date, resolution step is skipped Already up to date Done in 1.3s
Cela m'a rapproché. J'ai décidé de faire ceci :
~/project$ pnpm remove \ babel-jest jest jest-puppeteer @testing-library/jest-dom ~/project$ $ pnpm create playwright Getting started with writing end-to-end tests with Playwright: Initializing project in '.' ✔ Do you want to use TypeScript or JavaScript? · JavaScript ✔ Where to put your end-to-end tests? · tests ✔ Add a GitHub Actions workflow? (y/N) · false ✔ Install Playwright browsers (can be done manually via 'pnpm exec playwright install')? (Y/n) · false ✔ Install Playwright operating system dependencies (requires sudo / root - can be done manually via 'sudo pnpm exec playwright install-deps')? (y/N) · false Installing Playwright Test (pnpm add --save-dev @playwright/test)… … Installing Types (pnpm add --save-dev @types/node)… … Done in 2.7s Writing playwright.config.js. Writing tests/example.spec.js. Writing tests-examples/demo-todo-app.spec.js. Writing package.json.
/* Configure projects for major browsers */ projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, // { // name: 'firefox', // use: { ...devices['Desktop Firefox'] }, // }, // // { // name: 'webkit', // use: { ...devices['Desktop Safari'] }, // }, ], …
Cependant, je me demandais si cela serait à l'épreuve du temps. À terme, Playwright utiliserait une nouvelle version de Chromium, et elle ne regarderait plus dans un dossier Chrome-1140. Comment pourrais-je savoir à quoi ressemblerait Playwright ?
C'est à ce moment-là que j'ai découvert que vous pouvez effectuer un essai d'installation du navigateur.
const ROOT_URL = 'http://localhost:8080'; const { test, expect } = require('@playwright/test'); const inputSelector = 'input[name="name"]'; const submitButtonSelector = 'button[type="submit"]'; const greetingSelector = 'h5#greeting'; const name = 'John Doe'; test.beforeEach(async ({ page }) => { await page.goto(ROOT_URL); }); test.describe('Playwright link', () => { test('should navigate to Playwright documentation page', async ({ page }) => { await page.click('a[href="https://playwright.dev/"]'); await expect(page.title()).resolves.toMatch('| Playwright'); }); }); test.describe('Text input', () => { test('should display the entered text in the text input', async ({ page }) => { await page.fill(inputSelector, name); // Verify the input value const inputValue = await page.inputValue(inputSelector); expect(inputValue).toBe(name); }); }); test.describe('Form submission', () => { test('should display the "Hello, X" message after form submission', async ({ page }) => { const expectedGreeting = `Hello, ${name}.`; await page.fill(inputSelector, name); await page.click(submitButtonSelector); await page.waitForSelector(greetingSelector); const greetingText = await page.textContent(greetingSelector); expect(greetingText).toBe(expectedGreeting); }); });
Cette ligne « Emplacement d’installation » était cruciale. Et, si nous définissons PLAYWRIGHT_BROWSERS_PATH, voici ce que nous verrions :
~/project$ pnpm remove start-server-and-test
C’est ce que je veux. Avec un peu de magie maladroite, j'ai fait ceci :
/* Run your local dev server before starting the tests */ webServer: { command: 'pnpm start', url: 'http://127.0.0.1:8080', reuseExistingServer: !process.env.CI, },
Avec tout cela compris, j'avais simplement besoin d'ajouter un script de configuration de test à app.json. Parce que PLAYWRIGHT_BROWSERS_PATH est déjà défini dans env, mon script serait un peu plus simple. C'était mon dernier fichier app.json :
"scripts": { … "test": "playwright test --project=chromium --reporter list" },
Je vais brièvement expliquer ce que fait test-setup :
Comptabilisation de PLAYWRIGHT_BROWSERS_PATH, utilise playwright install -- dry-run avec awk pour déterminer le dossier racine dans lequel Playwright recherchera le navigateur Chrome. Définit ceci comme valeur de la variable CHROMIUM_PATH.
Crée un nouveau dossier (et tous les dossiers parents nécessaires) dans CHROMIUM_PATH/chrome-linux, qui est le dossier réel dans lequel Playwright recherchera le binaire Chrome.
Crée un lien symbolique dans ce dossier, pour que Chrome pointe vers l'installation Heroku buildpack de Chrome (/app/.chrome-for-testing/chrome-linux64/chrome).
Avec mon fichier app.json mis à jour, Playwright devrait pouvoir utiliser l'installation de Chrome à partir du buildpack. Il était temps de refaire les tests.
Succès !
Le script de configuration du test s'est exécuté comme prévu.
Le dramaturge a pu accéder au binaire Chrome et exécuter les tests, qui ont réussi.
Les tests de bout en bout de mes applications Web deviennent moins fastidieux, j'y donne donc de plus en plus la priorité. Ces derniers jours, cela signifie également utiliser davantage Playwright. C’est flexible et rapide. Et maintenant que j'ai fait le travail (pour moi et pour vous !) pour le rendre opérationnel avec le buildpack Chrome for Testing dans Heroku CI, je peux commencer à créer mes suites de tests d'automatisation de navigateur une fois encore une fois.
Le code de cette procédure pas à pas est disponible dans mon référentiel GitHub.
Bon codage !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!