167 lines
7.7 KiB
TypeScript
167 lines
7.7 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
|
|
/**
|
|
* E2E tests for Product Category CRUD operations.
|
|
*
|
|
* Prerequisites:
|
|
* - Frontend running on http://localhost:3001 (npm run dev)
|
|
* - Backend running on http://localhost:8081 (docker compose up)
|
|
* - Auth setup must run first (profile selected)
|
|
*/
|
|
|
|
const UNIQUE = Date.now()
|
|
const CATEGORY_NAME = `E2E Catégorie Produit ${UNIQUE}`
|
|
const CATEGORY_NOTES = `Notes de test automatisé ${UNIQUE}`
|
|
const CATEGORY_NAME_UPDATED = `${CATEGORY_NAME} modifié`
|
|
const CATEGORY_NOTES_UPDATED = `${CATEGORY_NOTES} — mis à jour`
|
|
|
|
test.describe('Product Category CRUD', () => {
|
|
test.describe.configure({ mode: 'serial' })
|
|
|
|
// ──────────────────────────────────────────────
|
|
// CREATE
|
|
// ──────────────────────────────────────────────
|
|
|
|
test('should display the product category list page', async ({ page }) => {
|
|
await page.goto('/product-category')
|
|
await expect(page.getByRole('heading', { name: 'Catégories de produit' })).toBeVisible({ timeout: 10_000 })
|
|
await expect(page.getByText('Catégories enregistrées')).toBeVisible()
|
|
})
|
|
|
|
test('should navigate to the create form', async ({ page }) => {
|
|
await page.goto('/product-category')
|
|
// The toolbar button text is "Créer" (with a plus icon)
|
|
await page.getByRole('button', { name: /créer/i }).click()
|
|
await expect(page).toHaveURL('/product-category/new')
|
|
await expect(page.getByRole('heading', { name: 'Nouvelle catégorie de produit' })).toBeVisible()
|
|
})
|
|
|
|
test('should show validation error for short name', async ({ page }) => {
|
|
await page.goto('/product-category/new')
|
|
await page.locator('#model-type-name').fill('A')
|
|
// The form submit button in ModelTypeForm is also "Créer"
|
|
await page.locator('button[type="submit"]').click()
|
|
await expect(page.getByText('Le nom doit contenir au moins 2 caractères')).toBeVisible()
|
|
})
|
|
|
|
test('should create a new product category', async ({ page }) => {
|
|
await page.goto('/product-category/new')
|
|
await page.locator('#model-type-name').fill(CATEGORY_NAME)
|
|
await page.locator('#model-type-notes').fill(CATEGORY_NOTES)
|
|
|
|
// Verify category is locked to PRODUCT
|
|
const categorySelect = page.locator('#model-type-category')
|
|
await expect(categorySelect).toBeDisabled()
|
|
await expect(categorySelect).toHaveValue('PRODUCT')
|
|
|
|
await page.locator('button[type="submit"]').click()
|
|
|
|
// Should redirect to list and show success toast
|
|
await expect(page).toHaveURL('/product-category', { timeout: 10_000 })
|
|
await expect(page.getByText('Catégorie de produit créée avec succès')).toBeVisible()
|
|
})
|
|
|
|
// ──────────────────────────────────────────────
|
|
// READ
|
|
// ──────────────────────────────────────────────
|
|
|
|
test('should display the created category in the list', async ({ page }) => {
|
|
await page.goto('/product-category')
|
|
// Target the table cell specifically (desktop view also renders a mobile card)
|
|
await expect(page.getByRole('cell', { name: CATEGORY_NAME })).toBeVisible({ timeout: 10_000 })
|
|
})
|
|
|
|
test('should find the category via search', async ({ page }) => {
|
|
await page.goto('/product-category')
|
|
|
|
// Type in search input (placeholder: "Rechercher par nom…")
|
|
const searchInput = page.getByPlaceholder('Rechercher par nom…')
|
|
await searchInput.fill(UNIQUE.toString())
|
|
// Wait for debounce (300ms) + API response
|
|
await page.waitForTimeout(500)
|
|
|
|
await expect(page.getByRole('cell', { name: CATEGORY_NAME })).toBeVisible({ timeout: 5_000 })
|
|
})
|
|
|
|
// ──────────────────────────────────────────────
|
|
// UPDATE
|
|
// ──────────────────────────────────────────────
|
|
|
|
test('should navigate to the edit page', async ({ page }) => {
|
|
await page.goto('/product-category')
|
|
await expect(page.getByRole('cell', { name: CATEGORY_NAME })).toBeVisible({ timeout: 10_000 })
|
|
|
|
// Find the row with our category and click "Éditer"
|
|
const row = page.getByRole('row').filter({ hasText: CATEGORY_NAME })
|
|
await row.getByRole('button', { name: 'Éditer' }).click()
|
|
|
|
await expect(page.getByRole('heading', { name: /modifier/i })).toBeVisible({ timeout: 10_000 })
|
|
})
|
|
|
|
test('should edit the category name and notes', async ({ page }) => {
|
|
await page.goto('/product-category')
|
|
await expect(page.getByRole('cell', { name: CATEGORY_NAME })).toBeVisible({ timeout: 10_000 })
|
|
|
|
const row = page.getByRole('row').filter({ hasText: CATEGORY_NAME })
|
|
await row.getByRole('button', { name: 'Éditer' }).click()
|
|
await expect(page.getByRole('heading', { name: /modifier/i })).toBeVisible({ timeout: 10_000 })
|
|
|
|
// Update name
|
|
const nameInput = page.locator('#model-type-name')
|
|
await nameInput.clear()
|
|
await nameInput.fill(CATEGORY_NAME_UPDATED)
|
|
|
|
// Update notes
|
|
const notesTextarea = page.locator('#model-type-notes')
|
|
await notesTextarea.clear()
|
|
await notesTextarea.fill(CATEGORY_NOTES_UPDATED)
|
|
|
|
await page.locator('button[type="submit"]').click()
|
|
|
|
// Should redirect and show success
|
|
await expect(page).toHaveURL('/product-category', { timeout: 10_000 })
|
|
await expect(page.getByText('Catégorie de produit mise à jour avec succès')).toBeVisible()
|
|
})
|
|
|
|
test('should display updated category in the list', async ({ page }) => {
|
|
await page.goto('/product-category')
|
|
await expect(page.getByRole('cell', { name: CATEGORY_NAME_UPDATED })).toBeVisible({ timeout: 10_000 })
|
|
})
|
|
|
|
// ──────────────────────────────────────────────
|
|
// DELETE
|
|
// ──────────────────────────────────────────────
|
|
|
|
test('should cancel deletion when clicking Annuler', async ({ page }) => {
|
|
await page.goto('/product-category')
|
|
await expect(page.getByRole('cell', { name: CATEGORY_NAME_UPDATED })).toBeVisible({ timeout: 10_000 })
|
|
|
|
const row = page.getByRole('row').filter({ hasText: CATEGORY_NAME_UPDATED })
|
|
await row.getByRole('button', { name: 'Supprimer' }).click()
|
|
|
|
// Confirmation modal should appear
|
|
await expect(page.getByText('Supprimer ce type ?')).toBeVisible()
|
|
await page.getByRole('button', { name: 'Annuler' }).click()
|
|
|
|
// Category should still be present
|
|
await expect(page.getByRole('cell', { name: CATEGORY_NAME_UPDATED })).toBeVisible()
|
|
})
|
|
|
|
test('should delete the category', async ({ page }) => {
|
|
await page.goto('/product-category')
|
|
await expect(page.getByRole('cell', { name: CATEGORY_NAME_UPDATED })).toBeVisible({ timeout: 10_000 })
|
|
|
|
const row = page.getByRole('row').filter({ hasText: CATEGORY_NAME_UPDATED })
|
|
await row.getByRole('button', { name: 'Supprimer' }).click()
|
|
|
|
// Confirm deletion in modal
|
|
await expect(page.getByText('Supprimer ce type ?')).toBeVisible()
|
|
// Click the confirm "Supprimer" button inside the modal (btn-error style)
|
|
await page.locator('button.btn-error').filter({ hasText: 'Supprimer' }).click()
|
|
|
|
// Should show success toast and category should disappear
|
|
await expect(page.getByText(/supprimé avec succès/i)).toBeVisible({ timeout: 10_000 })
|
|
await expect(page.getByRole('cell', { name: CATEGORY_NAME_UPDATED })).not.toBeVisible()
|
|
})
|
|
})
|