test(e2e): add data-testid attributes for Playwright e2e (Patch 008 R3.1.e)

Minimal testid additions to 4 renderer files so Playwright can target
stable selectors: table-renderer, cell-{rowId}-{fieldName}, kanban-board,
kanban-column-{label}, kanban-card-{rowId}, calendar-renderer,
inline-editor-input, inline-editor-readonly.

Also adds Dockerfile.e2e for the client build used in docker-compose.e2e.yml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Corentin JOGUET 2026-05-08 00:37:39 +02:00
parent ea00386877
commit ba8d8678a0
6 changed files with 116 additions and 6 deletions

View file

@ -632,3 +632,54 @@ En attendant, le rendu est identique fonctionnellement (HTML table + colonnes de
- Decider du sort de l'EE branding ("Powered by Docmost" sur les pages partagees publiques)
- Crowdin / i18n : ajouter une cle `appName` au lieu du hardcode et router via `getAppName()`
- Strategie : renommer le package npm `docmost` -> `docadenice` quand on aura un build pipeline custom complet (impacte trop d'imports actuellement)
---
## Patch 008 — R3.1.e : data-testid pour Playwright e2e
**Commit** : (local-only, branche `acadenice/main`)
**Date** : 2026-05-08
**Scope** : ajouts `data-testid` minimaux dans les renderers — aucune logique modifiee
### Fichiers modifies
#### `apps/client/src/features/acadenice/database-view/renderers/table-renderer.tsx`
- `<table>` : ajout `data-testid="table-renderer"` — permet a Playwright de cibler le renderer sans dependre de la structure CSS.
- `<td>` : ajout `data-testid={`cell-${row.id}-${field.name}`}` — permet de cibler une cellule specifique par row ID et nom de champ.
#### `apps/client/src/features/acadenice/database-view/components/inline-editor.tsx`
- Branche `!canWrite` : ajout `data-testid="inline-editor-readonly"` sur le `<span>` — permet aux tests RBAC de verifier que l'editeur est bien en lecture seule.
- Branche `default` (`TextInput`) : ajout `data-testid="inline-editor-input"` — permet de cibler l'input dans les tests d'edition inline.
#### `apps/client/src/features/acadenice/database-view/renderers/kanban-renderer.tsx`
- `KanbanCard` wrapper div : ajout `data-testid={`kanban-card-${row.id}`}` — ciblage par row ID pour les tests de drag.
- `KanbanColumn` wrapper div : ajout `data-testid={`kanban-column-${column.label}`}` — ciblage par label de colonne.
- Board div (DndContext child) : ajout `data-testid="kanban-board"` — detection de la presence du kanban dans la page.
#### `apps/client/src/features/acadenice/database-view/renderers/calendar-renderer.tsx`
- Root div du composant : ajout `data-testid="calendar-renderer"` — detection de la presence du calendrier.
#### `apps/client/Dockerfile.e2e` (nouveau)
- Cree pour docker-compose.e2e.yml : build Vite du client + serve via `serve@14` sur le port 5173.
- Utilise uniquement pour les e2e — pas de changement sur le Dockerfile de production.
### Raison des choix
- `data-testid` sur les elements conteneurs plutot que sur les elements internes — plus stable face aux refactors de structure interne.
- Pas de `data-testid` sur les elements Mantine (Button, Select, etc.) : ces composants ont leurs propres selectors d'accessibilite (role, aria-label) que Playwright prefere nativement.
- Aucun `data-testid` ajoute dans les hooks, services, ou extension Tiptap — non necessaire pour les assertions UI e2e.
### Tests impactes
7 scenarios e2e dans `e2e/tests/` utilisent ces testids :
- `database-view-insert.spec.ts` : `table-renderer`
- `database-view-edit-inline.spec.ts` : `cell-{rowId}-{fieldName}`, `inline-editor-input`
- `database-view-realtime-sse.spec.ts` : `table-renderer`
- `database-view-rbac-denied.spec.ts` : `inline-editor-readonly`
- `database-view-kanban-drag.spec.ts` : `kanban-board`, `kanban-column-{label}`, `kanban-card-{rowId}`
- `database-view-calendar-reschedule.spec.ts` : `calendar-renderer`

View file

@ -0,0 +1,44 @@
# Dockerfile.e2e — DocAdenice client for e2e stack.
#
# Builds the Vite app and serves it via a lightweight static server.
# Used only in docker-compose.e2e.yml — not for production.
FROM node:22-alpine AS build
WORKDIR /app
# Copy workspace root (monorepo — client may depend on shared packages).
COPY package*.json ./
COPY apps/client/package*.json ./apps/client/
# Install deps.
RUN npm ci --workspace=apps/client 2>/dev/null || \
(cd apps/client && npm ci)
COPY . .
# Build with e2e environment placeholders.
# VITE_ vars must be set at build time (Vite inlines them).
# In CI they are overridden via docker-compose env: section.
ARG VITE_APP_URL=http://localhost:3001
ARG VITE_BRIDGE_URL=http://localhost:4001
ENV VITE_APP_URL=${VITE_APP_URL}
ENV VITE_BRIDGE_URL=${VITE_BRIDGE_URL}
RUN cd apps/client && npm run build
# --- Serve stage ---
FROM node:22-alpine AS serve
WORKDIR /app
# Use serve package to host the built assets.
RUN npm install -g serve@14
COPY --from=build /app/apps/client/dist ./dist
# serve on port 5173 to match Vite dev server default.
EXPOSE 5173
CMD ["serve", "-s", "dist", "-l", "5173"]

View file

@ -53,7 +53,12 @@ export function InlineEditor({
if (!canWrite) {
return (
<Tooltip label={t("database_view.edit.permission_denied")} withArrow>
<span className={styles.readOnly}>{formatDisplayValue(initialValue)}</span>
<span
className={styles.readOnly}
data-testid="inline-editor-readonly"
>
{formatDisplayValue(initialValue)}
</span>
</Tooltip>
);
}
@ -171,6 +176,7 @@ export function InlineEditor({
onKeyDown={handleKeyDown}
className={styles.input}
size="xs"
data-testid="inline-editor-input"
/>
);
}

View file

@ -198,7 +198,7 @@ export function CalendarRenderer({ tableId, viewId, bridgeUrl }: CalendarRendere
];
return (
<div>
<div data-testid="calendar-renderer">
<div className={styles.toolbar}>
<SegmentedControl
size="xs"

View file

@ -161,7 +161,12 @@ function KanbanCard({ row, primaryField, canWrite, onRename }: KanbanCardProps)
};
return (
<div ref={setNodeRef} style={style} className={styles.cardWrapper}>
<div
ref={setNodeRef}
style={style}
className={styles.cardWrapper}
data-testid={`kanban-card-${row.id}`}
>
<Card
shadow="xs"
padding="xs"
@ -207,7 +212,10 @@ function KanbanColumn({ column, primaryField, canWrite, onCardRename }: KanbanCo
const { t } = useTranslation();
return (
<div className={styles.column}>
<div
className={styles.column}
data-testid={`kanban-column-${column.label}`}
>
<div className={styles.columnHeader}>
<Text size="sm" fw={600} className={styles.columnTitle}>
{column.label}
@ -391,7 +399,7 @@ export function KanbanRenderer({ tableId, viewId, bridgeUrl }: KanbanRendererPro
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
>
<div className={styles.board}>
<div className={styles.board} data-testid="kanban-board">
{columns.map((col) => (
<KanbanColumn
key={col.id}

View file

@ -117,6 +117,7 @@ function TableBody({
<td
key={field.id}
className={styles.td}
data-testid={`cell-${row.id}-${field.name}`}
onDoubleClick={() => {
if (!isEditing) {
onCellDoubleClick(row.id, field.id);
@ -207,7 +208,7 @@ export function TableRenderer({ tableId, viewId, bridgeUrl }: TableRendererProps
return (
<div>
<div className={styles.wrapper}>
<table className={styles.table}>
<table className={styles.table} data-testid="table-renderer">
<thead>
<tr>
{fields.map((field) => (