feat: map shapes, image persistence, grab tool, layer rename/delete, i18n, page metadata

This commit is contained in:
AlacrisDevs
2026-02-08 23:11:09 +02:00
parent 75a2aefadb
commit f2384bceb8
125 changed files with 22605 additions and 3902 deletions

View File

@@ -8,13 +8,13 @@ test.describe('File Management', () => {
await navigateTo(page, `/${TEST_ORG_SLUG}/documents`);
});
test('should load files page with header and + New button', async ({ page }) => {
test('should load files page with header and New button', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Files' })).toBeVisible();
await expect(page.getByRole('button', { name: '+ New' })).toBeVisible();
await expect(page.getByRole('button', { name: 'New' })).toBeVisible();
});
test('should open Create New modal with type selectors', async ({ page }) => {
await page.getByRole('button', { name: '+ New' }).click();
await page.getByRole('button', { name: 'New' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
await expect(modal.getByText('Create New')).toBeVisible();
@@ -30,7 +30,7 @@ test.describe('File Management', () => {
test('should create a folder and see it in the file list', async ({ page }) => {
const folderName = `Test Folder ${Date.now()}`;
await page.getByRole('button', { name: '+ New' }).click();
await page.getByRole('button', { name: 'New' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
// Select Folder type
@@ -46,7 +46,7 @@ test.describe('File Management', () => {
test('should create a document and navigate to editor', async ({ page }) => {
const docName = `Test Doc ${Date.now()}`;
await page.getByRole('button', { name: '+ New' }).click();
await page.getByRole('button', { name: 'New' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
// Document type is default
@@ -59,7 +59,7 @@ test.describe('File Management', () => {
test('should create a kanban board from files and navigate to it', async ({ page }) => {
const boardName = `Test Board ${Date.now()}`;
await page.getByRole('button', { name: '+ New' }).click();
await page.getByRole('button', { name: 'New' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
// Select Kanban type
@@ -79,7 +79,7 @@ test.describe('File Management', () => {
test('should navigate into a folder and see breadcrumbs', async ({ page }) => {
// First create a folder
const folderName = `Nav Folder ${Date.now()}`;
await page.getByRole('button', { name: '+ New' }).click();
await page.getByRole('button', { name: 'New' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
await modal.getByText('Folder').click();
@@ -101,7 +101,7 @@ test.describe('File Management', () => {
test('should rename a file via right-click context menu', async ({ page }) => {
// Create a document first
const originalName = `Rename Me ${Date.now()}`;
await page.getByRole('button', { name: '+ New' }).click();
await page.getByRole('button', { name: 'New' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
await modal.getByText('Folder').click();
@@ -112,7 +112,7 @@ test.describe('File Management', () => {
// Right-click to open context menu
await page.getByText(originalName).first().click({ button: 'right' });
// Context menu should appear find the Rename button inside the fixed z-50 context menu
// Context menu should appear - find the Rename button inside the fixed z-50 context menu
const contextMenuPanel = page.locator('.fixed.z-50.bg-night');
await expect(contextMenuPanel).toBeVisible({ timeout: 3000 });
const renameBtn = contextMenuPanel.locator('button', { hasText: 'Rename' });
@@ -160,12 +160,12 @@ test.describe('Kanban Board Page', () => {
test('should load kanban page with header', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Kanban' })).toBeVisible();
await expect(page.getByRole('button', { name: '+ New' })).toBeVisible();
await expect(page.getByRole('button', { name: 'New' })).toBeVisible();
});
test('should open Create Board modal and create a board', async ({ page }) => {
const boardName = `PW Board ${Date.now()}`;
await page.getByRole('button', { name: '+ New' }).click();
await page.getByRole('button', { name: 'New' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
await expect(modal.getByText('Create Board')).toBeVisible();
@@ -181,7 +181,7 @@ test.describe('Kanban Board Page', () => {
test('should show board selector when multiple boards exist', async ({ page }) => {
// Create two boards
for (const name of [`Board A ${Date.now()}`, `Board B ${Date.now()}`]) {
await page.getByRole('button', { name: '+ New' }).click();
await page.getByRole('button', { name: 'New' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
await modal.getByPlaceholder('e.g. Sprint 1').fill(name);
@@ -254,20 +254,20 @@ test.describe('Settings Page - Members', () => {
await expect(page.getByText('Pending Invites')).toBeVisible({ timeout: 5000 });
await expect(page.getByText(testInviteEmail).first()).toBeVisible({ timeout: 5000 });
// Clean up: cancel the invite find the row containing the email, then its Cancel button
// Clean up: cancel the invite - find the row containing the email, then its close button
const inviteRow = page.locator('.bg-light\\/5').filter({ hasText: testInviteEmail });
await inviteRow.getByRole('button', { name: 'Cancel' }).click();
await inviteRow.getByTitle('Cancel invite').click();
await expect(page.getByText(testInviteEmail)).not.toBeVisible({ timeout: 5000 });
});
test('should show pending invites section when invites exist', async ({ page }) => {
await page.getByRole('button', { name: 'Members' }).click();
await expect(page.getByText('Team Members').first()).toBeVisible({ timeout: 5000 });
await expect(page.getByRole('heading', { name: /Team Members/ })).toBeVisible({ timeout: 5000 });
// Check if Pending Invites section exists (from previous test runs or this session)
const hasPending = await page.getByText('Pending Invites').isVisible().catch(() => false);
if (hasPending) {
// Each invite should have Copy Link and Cancel buttons
await expect(page.getByRole('button', { name: 'Copy Link' }).first()).toBeVisible();
// Each invite should have copy and cancel icon buttons
await expect(page.getByTitle('Cancel invite').first()).toBeVisible();
}
});
});
@@ -320,7 +320,7 @@ test.describe('Settings Page - Roles', () => {
// Role should appear in the list
await expect(page.getByText(roleName)).toBeVisible({ timeout: 5000 });
// Clean up: delete the role set up dialog handler BEFORE clicking
// Clean up: delete the role - set up dialog handler BEFORE clicking
page.on('dialog', dialog => dialog.accept());
// Find the specific role card that contains the role name and click its Delete button
const deleteBtn = page.getByRole('button', { name: 'Delete' }).last();
@@ -348,7 +348,7 @@ test.describe('Settings Page - Tags', () => {
await page.getByRole('button', { name: 'Create Tag' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
await modal.getByPlaceholder('e.g., Bug').fill(tagName);
await modal.getByLabel('Tag name').fill(tagName);
await modal.getByRole('button', { name: 'Create' }).click();
await expect(modal).not.toBeVisible({ timeout: 5000 });
await expect(page.getByText(tagName)).toBeVisible({ timeout: 5000 });
@@ -356,7 +356,7 @@ test.describe('Settings Page - Tags', () => {
// Clean up: delete the tag
page.on('dialog', dialog => dialog.accept());
const tagCard = page.locator('div').filter({ hasText: tagName }).last();
const deleteBtn = tagCard.getByRole('button').filter({ hasText: /delete/i }).first();
const deleteBtn = tagCard.locator('button').filter({ hasText: /delete/i }).first();
if (await deleteBtn.isVisible()) {
await deleteBtn.click();
await expect(page.getByText(tagName)).not.toBeVisible({ timeout: 5000 });
@@ -386,8 +386,8 @@ test.describe('Calendar - Event CRUD', () => {
await navigateTo(page, `/${TEST_ORG_SLUG}/calendar`);
});
test('should open create event modal via + New button', async ({ page }) => {
await page.getByRole('button', { name: '+ New' }).click();
test('should open create event modal via New button', async ({ page }) => {
await page.getByRole('button', { name: 'New' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
await expect(modal.getByText('Title')).toBeVisible();
@@ -399,7 +399,7 @@ test.describe('Calendar - Event CRUD', () => {
test('should create an event and see it on the calendar', async ({ page }) => {
test.setTimeout(60000);
const eventTitle = `PW Event ${Date.now()}`;
await page.getByRole('button', { name: '+ New' }).click();
await page.getByRole('button', { name: 'New' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
@@ -416,7 +416,7 @@ test.describe('Calendar - Event CRUD', () => {
test.setTimeout(60000);
// Create an event first
const eventTitle = `PW Detail ${Date.now()}`;
await page.getByRole('button', { name: '+ New' }).click();
await page.getByRole('button', { name: 'New' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
await modal.getByLabel('Title').fill(eventTitle);
@@ -433,22 +433,26 @@ test.describe('Calendar - Event CRUD', () => {
test('should delete an event', async ({ page }) => {
test.setTimeout(60000);
const eventTitle = `PW Delete ${Date.now()}`;
await page.getByRole('button', { name: '+ New' }).click();
await page.getByRole('button', { name: 'New' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
await modal.getByLabel('Title').fill(eventTitle);
await modal.getByRole('button', { name: 'Create' }).click();
await expect(modal).not.toBeVisible({ timeout: 5000 });
await expect(page.getByText(eventTitle).first()).toBeVisible({ timeout: 5000 });
// Click event to open detail modal, then delete
// Switch to Day view so the event is always visible (month view hides overflow behind +N)
await page.waitForTimeout(500);
await page.locator('button', { hasText: 'Day' }).filter({ hasText: /^Day$/ }).click();
await expect(page.getByText(eventTitle).first()).toBeVisible({ timeout: 10000 });
// Click event on calendar to open detail modal, then delete
await page.getByText(eventTitle).first().click();
const detailModal = page.getByRole('dialog');
await expect(detailModal).toBeVisible({ timeout: 3000 });
await expect(detailModal).toBeVisible({ timeout: 5000 });
page.on('dialog', dialog => dialog.accept());
const deleteBtn = detailModal.getByRole('button', { name: 'Delete' });
if (await deleteBtn.isVisible()) {
if (await deleteBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await deleteBtn.click();
await expect(detailModal).not.toBeVisible({ timeout: 5000 });
}
@@ -467,7 +471,7 @@ test.describe('Kanban - Card CRUD', () => {
const boardName = `PW Card Board ${Date.now()}`;
// Create board
await page.getByRole('button', { name: '+ New' }).click();
await page.getByRole('button', { name: 'New' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
await modal.getByPlaceholder('e.g. Sprint 1').fill(boardName);
@@ -475,26 +479,19 @@ test.describe('Kanban - Card CRUD', () => {
await expect(modal).not.toBeVisible({ timeout: 5000 });
await expect(page.getByText('To Do')).toBeVisible({ timeout: 5000 });
// Add a card click the + button in the first column
const addCardBtn = page.locator('button[title="Add card"]').first();
if (await addCardBtn.isVisible()) {
await addCardBtn.click();
} else {
// Fallback: look for any add button in the column area
const plusBtn = page.locator('button').filter({ hasText: '+' }).first();
await plusBtn.click();
}
// Add a card - click the "Add card" button in the first column
const addCardBtn = page.getByRole('button', { name: 'Add card' }).first();
await expect(addCardBtn).toBeVisible({ timeout: 3000 });
await addCardBtn.click();
// Card creation modal or inline input should appear
// Card creation modal should appear
const cardModal = page.getByRole('dialog');
const hasModal = await cardModal.isVisible().catch(() => false);
if (hasModal) {
const cardTitle = `PW Card ${Date.now()}`;
await cardModal.getByLabel('Title').fill(cardTitle);
await cardModal.getByRole('button', { name: 'Create' }).click();
await expect(cardModal).not.toBeVisible({ timeout: 5000 });
await expect(page.getByText(cardTitle).first()).toBeVisible({ timeout: 5000 });
}
await expect(cardModal).toBeVisible({ timeout: 3000 });
const cardTitle = `PW Card ${Date.now()}`;
await cardModal.getByLabel('Title').fill(cardTitle);
await cardModal.getByRole('button', { name: 'Add Card' }).click();
await expect(cardModal).not.toBeVisible({ timeout: 5000 });
await expect(page.getByText(cardTitle).first()).toBeVisible({ timeout: 5000 });
});
test('should open card detail modal on card click', async ({ page }) => {
@@ -502,7 +499,7 @@ test.describe('Kanban - Card CRUD', () => {
const boardName = `PW Detail Board ${Date.now()}`;
// Create board
await page.getByRole('button', { name: '+ New' }).click();
await page.getByRole('button', { name: 'New' }).click();
const modal = page.getByRole('dialog');
await expect(modal).toBeVisible({ timeout: 3000 });
await modal.getByPlaceholder('e.g. Sprint 1').fill(boardName);
@@ -510,25 +507,23 @@ test.describe('Kanban - Card CRUD', () => {
await expect(modal).not.toBeVisible({ timeout: 5000 });
await expect(page.getByText('To Do')).toBeVisible({ timeout: 5000 });
// Add a card via the + button
const addCardBtn = page.locator('button[title="Add card"]').first();
if (await addCardBtn.isVisible()) {
await addCardBtn.click();
const cardModal = page.getByRole('dialog');
const hasModal = await cardModal.isVisible().catch(() => false);
if (hasModal) {
const cardTitle = `PW Click Card ${Date.now()}`;
await cardModal.getByLabel('Title').fill(cardTitle);
await cardModal.getByRole('button', { name: 'Create' }).click();
await expect(cardModal).not.toBeVisible({ timeout: 5000 });
// Add a card via the "Add card" button
const addCardBtn = page.getByRole('button', { name: 'Add card' }).first();
await expect(addCardBtn).toBeVisible({ timeout: 3000 });
await addCardBtn.click();
// Click the card to open detail modal
await page.getByText(cardTitle).first().click();
const detailModal = page.getByRole('dialog');
await expect(detailModal).toBeVisible({ timeout: 5000 });
await expect(detailModal.getByText(cardTitle)).toBeVisible();
}
}
const cardModal = page.getByRole('dialog');
await expect(cardModal).toBeVisible({ timeout: 3000 });
const cardTitle = `PW Click Card ${Date.now()}`;
await cardModal.getByLabel('Title').fill(cardTitle);
await cardModal.getByRole('button', { name: 'Add Card' }).click();
await expect(cardModal).not.toBeVisible({ timeout: 5000 });
// Click the card to open detail modal
await page.getByText(cardTitle).first().click();
const detailModal = page.getByRole('dialog');
await expect(detailModal).toBeVisible({ timeout: 5000 });
await expect(detailModal.getByLabel('Title')).toHaveValue(cardTitle);
});
});