diff --git a/scripts/docs/fr/150.engineering/fr-050.playwright.md b/scripts/docs/fr/150.engineering/fr-050.playwright.md index e69de29bb2..e73ae6c803 100644 --- a/scripts/docs/fr/150.engineering/fr-050.playwright.md +++ b/scripts/docs/fr/150.engineering/fr-050.playwright.md @@ -0,0 +1,438 @@ +*** + +titre : "Tests dramaturge E2E" +description: "Aperçu des tests e2e basés sur les dramaturges" +balises : \['Ingénierie'] +------------------------- + +## Comment exécuter des tests + +Tous les tests résident dans`tests/playwright`dossier. + +Assurez-vous d'installer les dépendances (dans le dossier playwright) : + +```bash +pnpm --filter=playwright install +pnpm exec playwright install --with-deps chromium +``` + +### Exécuter le serveur de test + +Démarrez le serveur de test backend (dans`packages/nocodb`dossier): + +```bash +pnpm run watch:run:playwright +``` + +Démarrez le serveur de test frontend (dans`packages/nc-gui`dossier): + +```bash +NUXT_PAGE_TRANSITION_DISABLE=true pnpm run dev +``` + +### Exécution de tous les tests + +Pour sélectionner le type de base de données, renommez`.env.example`à`.env`Et mettre`E2E_DEV_DB_TYPE`à`sqlite`(défaut),`mysql`ou`pg`. + +mode sans tête (sans ouvrir le navigateur) : + +```bash +pnpm run test +``` + +avec navigateur : + +```bash +pnpm run test:debug +``` + +Pour configurer MySQL (sakila) : + +```bash +docker-compose -f ./tests/playwright/scripts/docker-compose-mysql-playwright.yml up -d +``` + +Pour configurer postgres (sakila): + +```bash +docker-compose -f ./tests/playwright/scripts/docker-compose-playwright-pg.yml +``` + +### Exécution de tests individuels + +Ajouter`.only`au test que vous souhaitez exécuter : + +```js +test.only('should login', async ({ page }) => { + // ... +}) +``` + +```bash +pnpm run test +``` + +## Concepts + +### Tests indépendants + +* Tous les tests sont indépendants les uns des autres. +* Chaque test commence par un nouveau projet avec une nouvelle base de données sakila (l'option de ne pas utiliser sakila db est également disponible). +* Chaque test crée un nouvel utilisateur (e-mail comme`user@nocodb.com`) et se connecte avec cet utilisateur au tableau de bord. + +Mises en garde : + +* Certaines choses sont partagées avec les utilisateurs, les plugins, etc. Soyez donc prudent lorsque vous écrivez des tests à ce sujet. Un correctif pour cela est en préparation. +* Lors du test, nous préfixons l'e-mail et le projet avec l'identifiant du test, qui sera supprimé une fois le test terminé. + +### Que tester + +* Vérification de l'interface utilisateur. Cela inclut la vérification de l'état de l'élément de l'interface utilisateur, c'est-à-dire si l'élément est visible, si l'élément a un texte particulier, etc. +* Le test doit vérifier tous les flux d’utilisateurs. Un test a un délai d'expiration par défaut de 60 secondes. Si un test dure plus de 60 secondes, c’est le signe que le test doit être divisé en tests plus petits. +* Le test doit également vérifier tous les effets secondaires de la fonctionnalité (c'est-à-dire lors de l'ajout d'un nouveau type de champ, il doit également vérifier la suppression du champ), ainsi que les cas d'erreur. +* Le nom du test doit être descriptif. Il devrait être facile de comprendre ce que fait le test en lisant simplement le nom du test. + +### Dramaturge + +* Playwright est une bibliothèque nodejs pour automatiser Chrome, Firefox et Webkit. +* Pour chaque test, un nouveau contexte de navigateur est créé. Cela signifie que chaque test s'exécute dans une nouvelle fenêtre de navigation privée. +* Pour une affirmation, utilisez toujours`expect`depuis`@playwright/test`bibliothèque. Cette bibliothèque fournit de nombreuses assertions utiles, qui intègrent également une logique de nouvelle tentative. + +## Objets de page + +* Les objets de page sont utilisés pour résumer les composants/la page. Cela rend les tests plus lisibles et maintenables. +* Tous les objets de la page sont dans`tests/playwright/pages`dossier. +* Tout le code lié au test doit être dans des objets de page. +* Les méthodes doivent être aussi fines que possible et il est préférable d'avoir plusieurs méthodes plutôt qu'une grande méthode, ce qui améliore la réutilisabilité. + +Les méthodes d'un objet page peuvent être classées en 2 catégories : + +* Actions : effectue des actions d'interface utilisateur telles que cliquer, taper, sélectionner, etc. Est également responsable d'attendre que l'élément soit prêt et que l'action soit effectuée. Cela incluait l'attente de la fin des appels d'API. +* Assertions: Asserts the state of the UI element, i.e if the element is visible, if the element has a particular text etc. Use `expect`depuis`@playwright/test`et sinon, utilisez`expect.poll`attendre que l'affirmation passe. + +## Écrire un test + +Écrivons un test pour tester la fonctionnalité du filtre. + +Pour simplifier, nous aurons`DashboardPage`implémenté, qui aura toutes les méthodes liées à la page du tableau de bord ainsi que ses composants enfants comme Grid, etc. + +### Créer une suite de tests + +Créer un nouveau fichier`filter.spec.ts`dans`tests/playwright/tests`dossier et utilisation`setup`méthode pour créer un nouveau projet et un nouveau utilisateur. + +```js +import { test, expect } from '@playwright/test'; +import setup, { NcContext } from '../setup'; + +test.describe('Filter', () => { + let context: NcContext; + + test.beforeEach(async ({ page }) => { + context = await setup({ page }); + }) + + test('should filter', async ({ page }) => { + // ... + }); +}); +``` + +### Créer un objet de page + +Étant donné que le filtre est limité, du point de vue de l'interface utilisateur, à un`Toolbar`, nous ajouterons un objet de page de filtre à`ToolbarPage`objet de page. + +```js +export class ToolbarPage extends BasePage { + readonly parent: GridPage | GalleryPage | FormPage | KanbanPage; + readonly filter: ToolbarFilterPage; + + constructor(parent: GridPage | GalleryPage | FormPage | KanbanPage) { + super(parent.rootPage); + this.parent = parent; + this.filter = new ToolbarFilterPage(this); + } +} +``` + +Nous allons créer`ToolbarFilterPage`objet page, qui aura toutes les méthodes liées au filtre. + +```js +export class ToolbarFilterPage extends BasePage { + readonly toolbar: ToolbarPage; + + constructor(toolbar: ToolbarPage) { + super(toolbar.rootPage); + this.toolbar = toolbar; + } +} +``` + +Ici`BasePage`est une classe abstraite, utilisée pour appliquer la structure à tous les objets de page. Ainsi tous les objets de page*devrait*hériter`BasePage`. + +* Helper methods like `waitForResponse`et`getClipboardText`(cela peut être accessible sur n'importe quel objet de page, avec`this.waitForResponse`) +* Fournit une structure pour les objets de page, impose à tous les objets de page d'avoir`rootPage`propriété, qui est l’objet de page créé dans la configuration du test. +* Oblige toutes les pages à avoir un`get`méthode qui renverra le localisateur du conteneur principal de cette page, nous pouvons donc avoir une sélection dom ciblée, c'est-à-dire + +```js +// This will only select the button inside the container of the concerned page +await this.get().querySelector('button').count(); +``` + +### Ecrire une méthode d'action + +C'est une méthode qui réinitialisera/effacera tous les filtres. Puisqu'il s'agit d'une méthode d'action, elle attendra également le`delete`filtrer l'API à retourner. Ignorer cet appel d'API entraînera des problèmes dans le test, sur toute la ligne. + +```js +async resetFilter() { + await this.waitForResponse({ + uiAction: async () => await this.get().locator('.nc-filter-item-remove-btn').click(), + httpMethodsToMatch: ['DELETE'], + requestUrlPathToMatch: '/api/v1/db/meta/filters/', + }); +} +``` + +### Writing an assertion/verification method + +Here we use `expect`depuis`@playwright/test`bibliothèque, qui intègre une logique de nouvelle tentative. + +```js +import { expect } from '@playwright/test'; + +async verifyFilter({ title }: { title: string }) { + await expect( + this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('input[type="checkbox"]') + ).toBeChecked(); +} +``` + +## Conseils pour éviter les desquamations +*** + +titre : "Tests dramaturge E2E" +description: "Aperçu des tests e2e basés sur les dramaturges" +balises : \['Ingénierie'] +------------------------- + +## Comment exécuter des tests + +Tous les tests résident dans`tests/playwright`dossier. + +Assurez-vous d'installer les dépendances (dans le dossier playwright) : + +```bash +pnpm --filter=playwright install +pnpm exec playwright install --with-deps chromium +``` + +### Exécuter le serveur de test + +Démarrez le serveur de test backend (dans`packages/nocodb`dossier): + +```bash +pnpm run watch:run:playwright +``` + +Démarrez le serveur de test frontend (dans`packages/nc-gui`dossier): + +```bash +NUXT_PAGE_TRANSITION_DISABLE=true pnpm run dev +``` + +### Exécution de tous les tests + +Pour sélectionner le type de base de données, renommez`.env.example`à`.env`Et mettre`E2E_DEV_DB_TYPE`à`sqlite`(défaut),`mysql`ou`pg`. + +mode sans tête (sans ouvrir le navigateur) : + +```bash +pnpm run test +``` + +avec navigateur : + +```bash +pnpm run test:debug +``` + +Pour configurer MySQL (sakila) : + +```bash +docker-compose -f ./tests/playwright/scripts/docker-compose-mysql-playwright.yml up -d +``` + +Pour configurer postgres (sakila): + +```bash +docker-compose -f ./tests/playwright/scripts/docker-compose-playwright-pg.yml +``` + +### Exécution de tests individuels + +Ajouter`.only`au test que vous souhaitez exécuter : + +```js +test.only('should login', async ({ page }) => { + // ... +}) +``` + +```bash +pnpm run test +``` + +## Concepts + +### Tests indépendants + +* Tous les tests sont indépendants les uns des autres. +* Chaque test commence par un nouveau projet avec une nouvelle base de données sakila (l'option de ne pas utiliser sakila db est également disponible). +* Chaque test crée un nouvel utilisateur (e-mail comme`user@nocodb.com`) et se connecte avec cet utilisateur au tableau de bord. + +Mises en garde : + +* Certaines choses sont partagées avec les utilisateurs, les plugins, etc. Soyez donc prudent lorsque vous écrivez des tests à ce sujet. Un correctif pour cela est en préparation. +* Lors du test, nous préfixons l'e-mail et le projet avec l'identifiant du test, qui sera supprimé une fois le test terminé. + +### Que tester + +* Vérification de l'interface utilisateur. Cela inclut la vérification de l'état de l'élément de l'interface utilisateur, c'est-à-dire si l'élément est visible, si l'élément a un texte particulier, etc. +* Le test doit vérifier tous les flux d’utilisateurs. Un test a un délai d'expiration par défaut de 60 secondes. Si un test dure plus de 60 secondes, c’est le signe que le test doit être divisé en tests plus petits. +* Le test doit également vérifier tous les effets secondaires de la fonctionnalité (c'est-à-dire lors de l'ajout d'un nouveau type de champ, il doit également vérifier la suppression du champ), ainsi que les cas d'erreur. +* Le nom du test doit être descriptif. Il devrait être facile de comprendre ce que fait le test en lisant simplement le nom du test. + +### Dramaturge + +* Playwright est une bibliothèque nodejs pour automatiser Chrome, Firefox et Webkit. +* Pour chaque test, un nouveau contexte de navigateur est créé. Cela signifie que chaque test s'exécute dans une nouvelle fenêtre de navigation privée. +* Pour une affirmation, utilisez toujours`expect`depuis`@playwright/test`bibliothèque. Cette bibliothèque fournit de nombreuses assertions utiles, qui intègrent également une logique de nouvelle tentative. + +## Objets de page + +* Les objets de page sont utilisés pour résumer les composants/la page. Cela rend les tests plus lisibles et maintenables. +* Tous les objets de la page sont dans`tests/playwright/pages`dossier. +* Tout le code lié au test doit être dans des objets de page. +* Les méthodes doivent être aussi fines que possible et il est préférable d'avoir plusieurs méthodes plutôt qu'une grande méthode, ce qui améliore la réutilisabilité. + +Les méthodes d'un objet page peuvent être classées en 2 catégories : + +* Actions : effectue des actions d'interface utilisateur telles que cliquer, taper, sélectionner, etc. Est également responsable d'attendre que l'élément soit prêt et que l'action soit effectuée. Cela incluait l'attente de la fin des appels d'API. +* Assertions: Asserts the state of the UI element, i.e if the element is visible, if the element has a particular text etc. Use `expect`depuis`@playwright/test`et sinon, utilisez`expect.poll`attendre que l'affirmation passe. + +## Écrire un test + +Écrivons un test pour tester la fonctionnalité du filtre. + +Pour simplifier, nous aurons`DashboardPage`implémenté, qui aura toutes les méthodes liées à la page du tableau de bord ainsi que ses composants enfants comme Grid, etc. + +### Créer une suite de tests + +Créer un nouveau fichier`filter.spec.ts`dans`tests/playwright/tests`dossier et utilisation`setup`méthode pour créer un nouveau projet et un nouveau utilisateur. + +```js +import { test, expect } from '@playwright/test'; +import setup, { NcContext } from '../setup'; + +test.describe('Filter', () => { + let context: NcContext; + + test.beforeEach(async ({ page }) => { + context = await setup({ page }); + }) + + test('should filter', async ({ page }) => { + // ... + }); +}); +``` + +### Créer un objet de page + +Étant donné que le filtre est limité, du point de vue de l'interface utilisateur, à un`Toolbar`, nous ajouterons un objet de page de filtre à`ToolbarPage`objet de page. + +```js +export class ToolbarPage extends BasePage { + readonly parent: GridPage | GalleryPage | FormPage | KanbanPage; + readonly filter: ToolbarFilterPage; + + constructor(parent: GridPage | GalleryPage | FormPage | KanbanPage) { + super(parent.rootPage); + this.parent = parent; + this.filter = new ToolbarFilterPage(this); + } +} +``` + +Nous allons créer`ToolbarFilterPage`objet page, qui aura toutes les méthodes liées au filtre. + +```js +export class ToolbarFilterPage extends BasePage { + readonly toolbar: ToolbarPage; + + constructor(toolbar: ToolbarPage) { + super(toolbar.rootPage); + this.toolbar = toolbar; + } +} +``` + +Ici`BasePage`est une classe abstraite, utilisée pour appliquer la structure à tous les objets de page. Ainsi tous les objets de page*devrait*hériter`BasePage`. + +* Helper methods like `waitForResponse`et`getClipboardText`(cela peut être accessible sur n'importe quel objet de page, avec`this.waitForResponse`) +* Fournit une structure pour les objets de page, impose à tous les objets de page d'avoir`rootPage`propriété, qui est l’objet de page créé dans la configuration du test. +* Oblige toutes les pages à avoir un`get`méthode qui renverra le localisateur du conteneur principal de cette page, nous pouvons donc avoir une sélection dom ciblée, c'est-à-dire + +```js +// This will only select the button inside the container of the concerned page +await this.get().querySelector('button').count(); +``` + +### Ecrire une méthode d'action + +C'est une méthode qui réinitialisera/effacera tous les filtres. Puisqu'il s'agit d'une méthode d'action, elle attendra également le`delete`filtrer l'API à retourner. Ignorer cet appel d'API entraînera des problèmes dans le test, sur toute la ligne. + +```js +async resetFilter() { + await this.waitForResponse({ + uiAction: async () => await this.get().locator('.nc-filter-item-remove-btn').click(), + httpMethodsToMatch: ['DELETE'], + requestUrlPathToMatch: '/api/v1/db/meta/filters/', + }); +} +``` + +### Writing an assertion/verification method + +Here we use `expect`depuis`@playwright/test`bibliothèque, qui intègre une logique de nouvelle tentative. + +```js +import { expect } from '@playwright/test'; + +async verifyFilter({ title }: { title: string }) { + await expect( + this.get().locator(`[data-testid="nc-fields-menu-${title}"]`).locator('input[type="checkbox"]') + ).toBeChecked(); +} +``` + +## Conseils pour éviter les desquamations + +* Si une action de l'interface utilisateur provoque un appel d'API ou un changement d'état de l'interface utilisateur, attendez que cet appel d'API se termine ou que l'état de l'interface utilisateur change. +* Ce qu'il faut attendre peut être spécifique à la situation, mais en général, il est préférable d'attendre que l'état final soit atteint, c'est-à-dire dans le cas de la création d'un filtre, alors qu'il semble qu'attendre que l'API du filtre se termine soit suffisant, mais après son retournez, les enregistrements de la table sont rechargés et l'état de l'interface utilisateur change, il est donc préférable d'attendre que les enregistrements de la table soient rechargés. + +## Accéder au rapport du dramaturge dans le CI + +* Ouvrir`Summary`dans le workflow CI dans les actions github. +* Faites défiler jusqu'à`Artifacts`section. +* Accédez aux rapports comportant le suffixe du type de base de données et du numéro de partition (correspondant au nom du flux de travail CI). c'est à dire`playwright-report-mysql-2`est pour`playwright-mysql-2`flux de travail. +* Téléchargez-le et exécutez`pnpm install -D @playwright/test && npx playwright show-report ./`dans le dossier téléchargé. + +* Si une action de l'interface utilisateur provoque un appel d'API ou un changement d'état de l'interface utilisateur, attendez que cet appel d'API se termine ou que l'état de l'interface utilisateur change. +* Ce qu'il faut attendre peut être spécifique à la situation, mais en général, il est préférable d'attendre que l'état final soit atteint, c'est-à-dire dans le cas de la création d'un filtre, alors qu'il semble qu'attendre que l'API du filtre se termine soit suffisant, mais après son retournez, les enregistrements de la table sont rechargés et l'état de l'interface utilisateur change, il est donc préférable d'attendre que les enregistrements de la table soient rechargés. + +## Accéder au rapport du dramaturge dans le CI + +* Ouvrir`Summary`dans le workflow CI dans les actions github. +* Faites défiler jusqu'à`Artifacts`section. +* Accédez aux rapports comportant le suffixe du type de base de données et du numéro de partition (correspondant au nom du flux de travail CI). c'est à dire`playwright-report-mysql-2`est pour`playwright-mysql-2`flux de travail. +* Téléchargez-le et exécutez`pnpm install -D @playwright/test && npx playwright show-report ./`dans le dossier téléchargé.