fix: improve project-switcher data-testid for uniqueness and special chars

The data-testid generation was using only the sanitized project name which
could produce collisions and didn't handle special characters properly.

Changes:
- Combine stable project.id with sanitized name: project-switcher-{id}-{name}
- Expand sanitization to remove non-alphanumeric chars (except hyphens)
- Collapse multiple hyphens and trim leading/trailing hyphens
- Update E2E tests to use ends-with selector for matching

This ensures test IDs are deterministic, unique, and safe for CSS selectors.
This commit is contained in:
Stefan de Vogelaere
2026-01-18 14:29:04 +01:00
parent 327aef89a2
commit ef2dcbacd4
4 changed files with 24 additions and 4 deletions

View File

@@ -37,10 +37,28 @@ export function ProjectSwitcherItem({
const IconComponent = getIconComponent();
const hasCustomIcon = !!project.customIconPath;
// Create a sanitized project name for test ID:
// - Convert to lowercase
// - Replace spaces with hyphens
// - Remove all non-alphanumeric characters except hyphens
// - Collapse multiple hyphens into single hyphen
// - Trim leading/trailing hyphens
const sanitizedName = project.name
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/[^a-z0-9-]/g, '')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '');
// Combine project.id with sanitized name for uniqueness and readability
// Format: project-switcher-{id}-{sanitizedName}
const testId = `project-switcher-${project.id}-${sanitizedName}`;
return (
<button
onClick={onClick}
onContextMenu={onContextMenu}
data-testid={testId}
className={cn(
'group w-full aspect-square rounded-xl flex items-center justify-center relative overflow-hidden',
'transition-all duration-200 ease-out',
@@ -60,7 +78,6 @@ export function ProjectSwitcherItem({
'hover:scale-105 active:scale-95'
)}
title={project.name}
data-testid={`project-switcher-${project.id}`}
>
{hasCustomIcon ? (
<img

View File

@@ -131,7 +131,8 @@ test.describe('Feature Manual Review Flow', () => {
}
// Verify we're on the correct project (project switcher button shows project name)
await expect(page.getByTestId(`project-switcher-project-${projectName}`)).toBeVisible({
// Use ends-with selector since data-testid format is: project-switcher-{id}-{sanitizedName}
await expect(page.locator(`[data-testid$="-${projectName}"]`)).toBeVisible({
timeout: 10000,
});

View File

@@ -78,7 +78,8 @@ test.describe('Project Creation', () => {
// Wait for project to be set as current and visible on the page
// The project name appears in the project switcher button
await expect(page.getByTestId(`project-switcher-project-${projectName}`)).toBeVisible({
// Use ends-with selector since data-testid format is: project-switcher-{id}-{sanitizedName}
await expect(page.locator(`[data-testid$="-${projectName}"]`)).toBeVisible({
timeout: 15000,
});

View File

@@ -157,8 +157,9 @@ test.describe('Open Project', () => {
// Wait for a project to be set as current and visible on the page
// The project name appears in the project switcher button
// Use ends-with selector since data-testid format is: project-switcher-{id}-{sanitizedName}
if (targetProjectName) {
await expect(page.getByTestId(`project-switcher-project-${targetProjectName}`)).toBeVisible({
await expect(page.locator(`[data-testid$="-${targetProjectName}"]`)).toBeVisible({
timeout: 15000,
});
}