test(e2e): Playwright tests for restructured edit page
This commit is contained in:
parent
fb1e51361f
commit
6964931286
299
tests/e2e/service-edit-agenda.spec.ts
Normal file
299
tests/e2e/service-edit-agenda.spec.ts
Normal file
|
|
@ -0,0 +1,299 @@
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
async function navigateToEditPage(page) {
|
||||||
|
await page.goto('/services');
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
|
const editButton = page.getByTestId('service-list-edit-button').first();
|
||||||
|
const hasEditableService = await editButton.isVisible().catch(() => false);
|
||||||
|
|
||||||
|
if (!hasEditableService) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await editButton.click();
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
test('edit seite zeigt ablauf sektion statt accordion bloecke', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(page).toHaveURL(/.*services\/\d+\/edit/);
|
||||||
|
|
||||||
|
const agendaSection = page.getByTestId('agenda-section');
|
||||||
|
const emptyState = page.getByText('Keine Ablauf-Elemente vorhanden');
|
||||||
|
const hasAgenda = await agendaSection.isVisible().catch(() => false);
|
||||||
|
const hasEmptyState = await emptyState.isVisible().catch(() => false);
|
||||||
|
|
||||||
|
expect(hasAgenda || hasEmptyState).toBe(true);
|
||||||
|
|
||||||
|
const blockToggles = page.getByTestId('service-edit-block-toggle');
|
||||||
|
const toggleCount = await blockToggles.count();
|
||||||
|
expect(toggleCount).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('informations block ist oben sichtbar', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const informationBlock = page.getByTestId('information-block');
|
||||||
|
await expect(informationBlock).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ablauf ueberschrift ist sichtbar', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(page.getByText('Ablauf')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('agenda items zeigen korrekte elemente oder empty state', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const agendaSection = page.getByTestId('agenda-section');
|
||||||
|
const emptyState = page.getByText('Keine Ablauf-Elemente vorhanden');
|
||||||
|
|
||||||
|
const hasAgenda = await agendaSection.isVisible().catch(() => false);
|
||||||
|
const hasEmptyState = await emptyState.isVisible().catch(() => false);
|
||||||
|
|
||||||
|
if (hasEmptyState) {
|
||||||
|
await expect(page.getByText('Bitte synchronisiere die Daten zuerst')).toBeVisible();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(hasAgenda).toBe(true);
|
||||||
|
|
||||||
|
const agendaRows = page.getByTestId('agenda-item-row');
|
||||||
|
const songItems = page.getByTestId('song-agenda-item');
|
||||||
|
const headerItems = page.getByTestId('agenda-header-item');
|
||||||
|
|
||||||
|
const rowCount = await agendaRows.count();
|
||||||
|
const songCount = await songItems.count();
|
||||||
|
const headerCount = await headerItems.count();
|
||||||
|
|
||||||
|
expect(rowCount + songCount + headerCount).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('header items zeigen titel als ueberschrift', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerItems = page.getByTestId('agenda-header-item');
|
||||||
|
const headerCount = await headerItems.count();
|
||||||
|
|
||||||
|
if (headerCount === 0) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstHeader = headerItems.first();
|
||||||
|
await expect(firstHeader).toBeVisible();
|
||||||
|
const headerText = await firstHeader.textContent();
|
||||||
|
expect(headerText?.trim().length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('song agenda items zeigen songtitel', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const songItems = page.getByTestId('song-agenda-item');
|
||||||
|
const songCount = await songItems.count();
|
||||||
|
|
||||||
|
if (songCount === 0) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstSong = songItems.first();
|
||||||
|
await expect(firstSong).toBeVisible();
|
||||||
|
|
||||||
|
const songTitle = firstSong.getByTestId('song-agenda-title');
|
||||||
|
await expect(songTitle).toBeVisible();
|
||||||
|
const titleText = await songTitle.textContent();
|
||||||
|
expect(titleText?.trim().length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('song agenda items zeigen arrangement pill wenn zugeordnet', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const arrangementPills = page.getByTestId('arrangement-pill');
|
||||||
|
const pillCount = await arrangementPills.count();
|
||||||
|
|
||||||
|
if (pillCount === 0) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstPill = arrangementPills.first();
|
||||||
|
await expect(firstPill).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('song agenda item zeigt arrangement bearbeiten button', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const editArrangementBtn = page.getByTestId('song-edit-arrangement');
|
||||||
|
const hasBtn = await editArrangementBtn.first().isVisible().catch(() => false);
|
||||||
|
|
||||||
|
if (!hasBtn) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(editArrangementBtn.first()).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('generische agenda items zeigen titel', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const agendaRows = page.getByTestId('agenda-item-row');
|
||||||
|
const rowCount = await agendaRows.count();
|
||||||
|
|
||||||
|
if (rowCount === 0) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstRow = agendaRows.first();
|
||||||
|
await expect(firstRow).toBeVisible();
|
||||||
|
|
||||||
|
const itemTitle = firstRow.getByTestId('agenda-item-title');
|
||||||
|
await expect(itemTitle).toBeVisible();
|
||||||
|
expect(await itemTitle.textContent()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('nicht zugeordnete songs zeigen erstellung anfragen button', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestBtn = page.getByTestId('song-request-creation');
|
||||||
|
const hasUnmatched = await requestBtn.first().isVisible().catch(() => false);
|
||||||
|
|
||||||
|
if (!hasUnmatched) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(requestBtn.first()).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('song suche und manuelle zuordnung sichtbar bei nicht zugeordnetem song', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchInput = page.getByTestId('song-search-input');
|
||||||
|
const hasSearch = await searchInput.first().isVisible().catch(() => false);
|
||||||
|
|
||||||
|
if (!hasSearch) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(searchInput.first()).toBeVisible();
|
||||||
|
|
||||||
|
const assignBtn = page.getByTestId('song-assign-button');
|
||||||
|
await expect(assignBtn.first()).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('uebersetzungs checkbox sichtbar bei songs mit uebersetzung', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const translationCheckbox = page.getByTestId('song-translation-checkbox');
|
||||||
|
const hasTranslation = await translationCheckbox.first().isVisible().catch(() => false);
|
||||||
|
|
||||||
|
if (!hasTranslation) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState = await translationCheckbox.first().isChecked();
|
||||||
|
|
||||||
|
await translationCheckbox.first().click();
|
||||||
|
await page.waitForTimeout(300);
|
||||||
|
|
||||||
|
const toggledState = await translationCheckbox.first().isChecked();
|
||||||
|
expect(toggledState).not.toBe(initialState);
|
||||||
|
|
||||||
|
await translationCheckbox.first().click();
|
||||||
|
await page.waitForTimeout(300);
|
||||||
|
|
||||||
|
const restoredState = await translationCheckbox.first().isChecked();
|
||||||
|
expect(restoredState).toBe(initialState);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sticky action bar ist sichtbar', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionBar = page.getByTestId('service-edit-action-bar');
|
||||||
|
await expect(actionBar).toBeVisible();
|
||||||
|
|
||||||
|
const inProgress = page.getByText('In Bearbeitung');
|
||||||
|
const finalized = page.getByText('Abgeschlossen');
|
||||||
|
|
||||||
|
const hasInProgress = await inProgress.isVisible().catch(() => false);
|
||||||
|
const hasFinalized = await finalized.isVisible().catch(() => false);
|
||||||
|
|
||||||
|
expect(hasInProgress || hasFinalized).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('abschliessen button in action bar sichtbar', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalizeBtn = page.getByTestId('service-edit-finalize-button');
|
||||||
|
const hasFinalizeBtn = await finalizeBtn.isVisible().catch(() => false);
|
||||||
|
|
||||||
|
if (!hasFinalizeBtn) {
|
||||||
|
const reopenBtn = page.getByTestId('service-edit-reopen-button');
|
||||||
|
await expect(reopenBtn).toBeVisible();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(finalizeBtn).toBeVisible();
|
||||||
|
await expect(finalizeBtn).toContainText('Abschließen');
|
||||||
|
|
||||||
|
const finalizeDownloadBtn = page.getByTestId('service-edit-finalize-download-button');
|
||||||
|
await expect(finalizeDownloadBtn).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('zurueck button navigiert zur service liste', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const backBtn = page.getByTestId('service-edit-back-icon-button');
|
||||||
|
await expect(backBtn).toBeVisible();
|
||||||
|
|
||||||
|
await backBtn.click();
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
|
await expect(page).toHaveURL(/.*services$/);
|
||||||
|
});
|
||||||
|
|
@ -25,12 +25,10 @@ test('navigate to first editable service edit page', async ({ page }) => {
|
||||||
await expect(informationBlock).toBeVisible();
|
await expect(informationBlock).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test 2: Information block accordion is visible and can be expanded/collapsed
|
test('information block is always visible without accordion toggle', async ({ page }) => {
|
||||||
test('information block accordion is visible and can be expanded/collapsed', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
await page.goto('/services');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
// Find first unfinalized service
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
const editButton = page.getByTestId('service-list-edit-button').first();
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
const hasEditableService = await editButton.isVisible().catch(() => false);
|
||||||
|
|
||||||
|
|
@ -41,38 +39,12 @@ test('information block accordion is visible and can be expanded/collapsed', asy
|
||||||
await editButton.click();
|
await editButton.click();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
// Find the Information block toggle button
|
|
||||||
const blockToggles = page.getByTestId('service-edit-block-toggle');
|
|
||||||
const informationToggle = blockToggles.filter({ has: page.locator('text=Information') }).first();
|
|
||||||
|
|
||||||
const toggleExists = await informationToggle.isVisible().catch(() => false);
|
|
||||||
if (!toggleExists) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify toggle button is visible
|
|
||||||
await expect(informationToggle).toBeVisible();
|
|
||||||
|
|
||||||
// Get the Information block content container
|
|
||||||
const informationBlock = page.getByTestId('information-block');
|
const informationBlock = page.getByTestId('information-block');
|
||||||
|
|
||||||
// Verify block is initially visible (expanded by default)
|
|
||||||
await expect(informationBlock).toBeVisible();
|
await expect(informationBlock).toBeVisible();
|
||||||
|
|
||||||
// Click toggle to collapse
|
const blockToggles = page.getByTestId('service-edit-block-toggle');
|
||||||
await informationToggle.click();
|
const toggleCount = await blockToggles.count();
|
||||||
await page.waitForTimeout(300); // Wait for transition
|
expect(toggleCount).toBe(0);
|
||||||
|
|
||||||
// Verify block is hidden
|
|
||||||
const isHidden = await informationBlock.isHidden().catch(() => true);
|
|
||||||
expect(isHidden).toBe(true);
|
|
||||||
|
|
||||||
// Click toggle again to expand
|
|
||||||
await informationToggle.click();
|
|
||||||
await page.waitForTimeout(300); // Wait for transition
|
|
||||||
|
|
||||||
// Verify block is visible again
|
|
||||||
await expect(informationBlock).toBeVisible();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test 3: Upload area is visible with drag-and-drop zone and click-to-upload
|
// Test 3: Upload area is visible with drag-and-drop zone and click-to-upload
|
||||||
|
|
|
||||||
|
|
@ -1,204 +1,62 @@
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
// Test 1: Navigate to first editable (non-finalized) service edit page
|
async function navigateToEditPage(page) {
|
||||||
test('navigate to first editable service edit page', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
await page.goto('/services');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
// Find first unfinalized service (one with edit button)
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
const editButton = page.getByTestId('service-list-edit-button').first();
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
const hasEditableService = await editButton.isVisible().catch(() => false);
|
||||||
|
|
||||||
if (!hasEditableService) {
|
if (!hasEditableService) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await editButton.click();
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
test('navigate to first editable service edit page', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
test.skip();
|
test.skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click edit button
|
|
||||||
await editButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Verify we're on the edit page
|
|
||||||
await expect(page).toHaveURL(/.*services\/\d+\/edit/);
|
await expect(page).toHaveURL(/.*services\/\d+\/edit/);
|
||||||
|
|
||||||
// Verify Moderation block is visible
|
const agendaSection = page.getByTestId('agenda-section');
|
||||||
const moderationBlock = page.getByTestId('moderation-block');
|
const emptyState = page.getByText('Keine Ablauf-Elemente vorhanden');
|
||||||
await expect(moderationBlock).toBeVisible();
|
const hasAgenda = await agendaSection.isVisible().catch(() => false);
|
||||||
|
const hasEmptyState = await emptyState.isVisible().catch(() => false);
|
||||||
|
|
||||||
|
expect(hasAgenda || hasEmptyState).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test 2: Moderation block accordion is visible and can be expanded/collapsed
|
test.skip('moderation block accordion — replaced by agenda view', async () => {});
|
||||||
test('moderation block accordion is visible and can be expanded/collapsed', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Find first unfinalized service
|
test('agenda items with slides show slide uploader', async ({ page }) => {
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
const navigated = await navigateToEditPage(page);
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
if (!navigated) {
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
test.skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
await editButton.click();
|
const agendaRows = page.getByTestId('agenda-item-row');
|
||||||
await page.waitForLoadState('networkidle');
|
const rowCount = await agendaRows.count();
|
||||||
|
|
||||||
// Find the Moderation block toggle button
|
if (rowCount === 0) {
|
||||||
const blockToggles = page.getByTestId('service-edit-block-toggle');
|
|
||||||
const moderationToggle = blockToggles.filter({ has: page.locator('text=Moderation') }).first();
|
|
||||||
|
|
||||||
const toggleExists = await moderationToggle.isVisible().catch(() => false);
|
|
||||||
if (!toggleExists) {
|
|
||||||
test.skip();
|
test.skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify toggle button is visible
|
const addSlidesBtn = page.getByTestId('agenda-item-add-slides').first();
|
||||||
await expect(moderationToggle).toBeVisible();
|
const hasSlidesBtn = await addSlidesBtn.isVisible().catch(() => false);
|
||||||
|
|
||||||
// Get the Moderation block content container
|
if (!hasSlidesBtn) {
|
||||||
const moderationBlock = page.getByTestId('moderation-block');
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
// Verify block is initially visible (expanded by default)
|
await expect(addSlidesBtn).toBeVisible();
|
||||||
await expect(moderationBlock).toBeVisible();
|
|
||||||
|
|
||||||
// Click toggle to collapse
|
|
||||||
await moderationToggle.click();
|
|
||||||
await page.waitForTimeout(300); // Wait for transition
|
|
||||||
|
|
||||||
// Verify block is hidden
|
|
||||||
const isHidden = await moderationBlock.isHidden().catch(() => true);
|
|
||||||
expect(isHidden).toBe(true);
|
|
||||||
|
|
||||||
// Click toggle again to expand
|
|
||||||
await moderationToggle.click();
|
|
||||||
await page.waitForTimeout(300); // Wait for transition
|
|
||||||
|
|
||||||
// Verify block is visible again
|
|
||||||
await expect(moderationBlock).toBeVisible();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test 3: Upload area is visible with drag-and-drop zone (NO datepicker)
|
test.skip('existing moderation slides display as thumbnails — replaced by agenda item slides', async () => {});
|
||||||
test('upload area is visible with drag-and-drop zone', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Find first unfinalized service
|
test.skip('delete button on moderation slide triggers confirmation — replaced by agenda item slides', async () => {});
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
await editButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Verify Moderation block uploader is visible
|
|
||||||
const uploader = page.getByTestId('moderation-block-uploader');
|
|
||||||
await expect(uploader).toBeVisible();
|
|
||||||
|
|
||||||
// Verify dropzone is visible
|
|
||||||
const dropzone = page.getByTestId('slide-uploader-dropzone');
|
|
||||||
await expect(dropzone).toBeVisible();
|
|
||||||
|
|
||||||
// Verify dropzone contains expected text
|
|
||||||
await expect(dropzone).toContainText('Dateien hier ablegen');
|
|
||||||
await expect(dropzone).toContainText('oder klicken zum Auswählen');
|
|
||||||
|
|
||||||
// Verify NO expire date input (unlike Information block)
|
|
||||||
const expireInput = page.getByTestId('slide-uploader-expire-input');
|
|
||||||
const expireInputExists = await expireInput.isVisible().catch(() => false);
|
|
||||||
expect(expireInputExists).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test 4: Existing moderation slides display as thumbnails
|
|
||||||
test('existing moderation slides display as thumbnails', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Find first unfinalized service
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
await editButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Verify slide grid is visible
|
|
||||||
const slideGrid = page.getByTestId('moderation-block-grid');
|
|
||||||
await expect(slideGrid).toBeVisible();
|
|
||||||
|
|
||||||
// Check if slides exist
|
|
||||||
const slideThumbnails = page.locator('[data-testid="slide-grid-delete-button"]');
|
|
||||||
const slideCount = await slideThumbnails.count();
|
|
||||||
|
|
||||||
if (slideCount === 0) {
|
|
||||||
// No slides exist - verify empty state message
|
|
||||||
const emptyState = slideGrid.locator('text=Noch keine Folien vorhanden');
|
|
||||||
await expect(emptyState).toBeVisible();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slides exist - verify first thumbnail is visible
|
|
||||||
const firstThumbnail = page.locator('[data-testid="slide-grid-delete-button"]').first();
|
|
||||||
await expect(firstThumbnail).toBeVisible();
|
|
||||||
|
|
||||||
// Verify delete button is visible on hover
|
|
||||||
const deleteButton = firstThumbnail;
|
|
||||||
await expect(deleteButton).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test 5: Delete button on moderation slide thumbnail triggers confirmation
|
|
||||||
test('delete button on moderation slide thumbnail triggers confirmation', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Find first unfinalized service
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
await editButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Check if slides exist
|
|
||||||
const slideThumbnails = page.locator('[data-testid="slide-grid-delete-button"]');
|
|
||||||
const slideCount = await slideThumbnails.count();
|
|
||||||
|
|
||||||
if (slideCount === 0) {
|
|
||||||
// No slides exist - skip this test
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get first delete button
|
|
||||||
const firstDeleteButton = page.getByTestId('slide-grid-delete-button').first();
|
|
||||||
await expect(firstDeleteButton).toBeVisible();
|
|
||||||
|
|
||||||
// Click delete button
|
|
||||||
await firstDeleteButton.click();
|
|
||||||
await page.waitForTimeout(200);
|
|
||||||
|
|
||||||
// Verify confirmation dialog appears
|
|
||||||
const confirmDialog = page.locator('text=Folie löschen?');
|
|
||||||
await expect(confirmDialog).toBeVisible();
|
|
||||||
|
|
||||||
// Verify dialog contains expected text
|
|
||||||
await expect(page.locator('text=Möchtest du die Folie')).toBeVisible();
|
|
||||||
await expect(page.locator('text=wirklich löschen?')).toBeVisible();
|
|
||||||
|
|
||||||
// Verify cancel button is visible
|
|
||||||
const cancelButton = page.locator('button:has-text("Abbrechen")').first();
|
|
||||||
await expect(cancelButton).toBeVisible();
|
|
||||||
|
|
||||||
// Click cancel to close dialog without deleting
|
|
||||||
await cancelButton.click();
|
|
||||||
await page.waitForTimeout(200);
|
|
||||||
|
|
||||||
// Verify dialog is closed
|
|
||||||
const dialogClosed = await confirmDialog.isHidden().catch(() => true);
|
|
||||||
expect(dialogClosed).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -1,204 +1,41 @@
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
// Test 1: Navigate to first editable (non-finalized) service edit page
|
async function navigateToEditPage(page) {
|
||||||
test('navigate to first editable service edit page', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
await page.goto('/services');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
// Find first unfinalized service (one with edit button)
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
const editButton = page.getByTestId('service-list-edit-button').first();
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
const hasEditableService = await editButton.isVisible().catch(() => false);
|
||||||
|
|
||||||
if (!hasEditableService) {
|
if (!hasEditableService) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await editButton.click();
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
test('navigate to first editable service edit page', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
test.skip();
|
test.skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click edit button
|
|
||||||
await editButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Verify we're on the edit page
|
|
||||||
await expect(page).toHaveURL(/.*services\/\d+\/edit/);
|
await expect(page).toHaveURL(/.*services\/\d+\/edit/);
|
||||||
|
|
||||||
// Verify Sermon block is visible
|
const agendaSection = page.getByTestId('agenda-section');
|
||||||
const sermonBlock = page.getByTestId('sermon-block');
|
const emptyState = page.getByText('Keine Ablauf-Elemente vorhanden');
|
||||||
await expect(sermonBlock).toBeVisible();
|
const hasAgenda = await agendaSection.isVisible().catch(() => false);
|
||||||
|
const hasEmptyState = await emptyState.isVisible().catch(() => false);
|
||||||
|
|
||||||
|
expect(hasAgenda || hasEmptyState).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test 2: Sermon block accordion is visible and can be expanded/collapsed
|
test.skip('sermon block accordion — replaced by agenda view', async () => {});
|
||||||
test('sermon block accordion is visible and can be expanded/collapsed', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Find first unfinalized service
|
test.skip('sermon upload area — replaced by agenda item slide uploader', async () => {});
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
test.skip('existing sermon slides as thumbnails — replaced by agenda item slides', async () => {});
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
await editButton.click();
|
test.skip('delete button on sermon slide — replaced by agenda item slides', async () => {});
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Find the Sermon block toggle button (3rd block)
|
|
||||||
const blockToggles = page.getByTestId('service-edit-block-toggle');
|
|
||||||
const sermonToggle = blockToggles.filter({ has: page.locator('text=Predigt') }).first();
|
|
||||||
|
|
||||||
const toggleExists = await sermonToggle.isVisible().catch(() => false);
|
|
||||||
if (!toggleExists) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify toggle button is visible
|
|
||||||
await expect(sermonToggle).toBeVisible();
|
|
||||||
|
|
||||||
// Get the Sermon block content container
|
|
||||||
const sermonBlock = page.getByTestId('sermon-block');
|
|
||||||
|
|
||||||
// Verify block is initially visible (expanded by default)
|
|
||||||
await expect(sermonBlock).toBeVisible();
|
|
||||||
|
|
||||||
// Click toggle to collapse
|
|
||||||
await sermonToggle.click();
|
|
||||||
await page.waitForTimeout(300); // Wait for transition
|
|
||||||
|
|
||||||
// Verify block is hidden
|
|
||||||
const isHidden = await sermonBlock.isHidden().catch(() => true);
|
|
||||||
expect(isHidden).toBe(true);
|
|
||||||
|
|
||||||
// Click toggle again to expand
|
|
||||||
await sermonToggle.click();
|
|
||||||
await page.waitForTimeout(300); // Wait for transition
|
|
||||||
|
|
||||||
// Verify block is visible again
|
|
||||||
await expect(sermonBlock).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test 3: Upload area is visible with drag-and-drop zone (NO datepicker)
|
|
||||||
test('upload area is visible with drag-and-drop zone', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Find first unfinalized service
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
await editButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Verify Sermon block uploader is visible
|
|
||||||
const uploader = page.getByTestId('sermon-block-uploader');
|
|
||||||
await expect(uploader).toBeVisible();
|
|
||||||
|
|
||||||
// Verify dropzone is visible
|
|
||||||
const dropzone = page.getByTestId('slide-uploader-dropzone');
|
|
||||||
await expect(dropzone).toBeVisible();
|
|
||||||
|
|
||||||
// Verify dropzone contains expected text
|
|
||||||
await expect(dropzone).toContainText('Dateien hier ablegen');
|
|
||||||
await expect(dropzone).toContainText('oder klicken zum Auswählen');
|
|
||||||
|
|
||||||
// Verify NO expire date input (unlike Information block)
|
|
||||||
const expireInput = page.getByTestId('slide-uploader-expire-input');
|
|
||||||
const expireInputExists = await expireInput.isVisible().catch(() => false);
|
|
||||||
expect(expireInputExists).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test 4: Existing sermon slides display as thumbnails
|
|
||||||
test('existing sermon slides display as thumbnails', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Find first unfinalized service
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
await editButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Verify slide grid is visible
|
|
||||||
const slideGrid = page.getByTestId('sermon-block-grid');
|
|
||||||
await expect(slideGrid).toBeVisible();
|
|
||||||
|
|
||||||
// Check if slides exist
|
|
||||||
const slideThumbnails = page.locator('[data-testid="slide-grid-delete-button"]');
|
|
||||||
const slideCount = await slideThumbnails.count();
|
|
||||||
|
|
||||||
if (slideCount === 0) {
|
|
||||||
// No slides exist - verify empty state message
|
|
||||||
const emptyState = slideGrid.locator('text=Noch keine Folien vorhanden');
|
|
||||||
await expect(emptyState).toBeVisible();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slides exist - verify first thumbnail is visible
|
|
||||||
const firstThumbnail = page.locator('[data-testid="slide-grid-delete-button"]').first();
|
|
||||||
await expect(firstThumbnail).toBeVisible();
|
|
||||||
|
|
||||||
// Verify delete button is visible on hover
|
|
||||||
const deleteButton = firstThumbnail;
|
|
||||||
await expect(deleteButton).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test 5: Delete button on sermon slide thumbnail triggers confirmation
|
|
||||||
test('delete button on sermon slide thumbnail triggers confirmation', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Find first unfinalized service
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
await editButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Check if slides exist
|
|
||||||
const slideThumbnails = page.locator('[data-testid="slide-grid-delete-button"]');
|
|
||||||
const slideCount = await slideThumbnails.count();
|
|
||||||
|
|
||||||
if (slideCount === 0) {
|
|
||||||
// No slides exist - skip this test
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get first delete button
|
|
||||||
const firstDeleteButton = page.getByTestId('slide-grid-delete-button').first();
|
|
||||||
await expect(firstDeleteButton).toBeVisible();
|
|
||||||
|
|
||||||
// Click delete button
|
|
||||||
await firstDeleteButton.click();
|
|
||||||
await page.waitForTimeout(200);
|
|
||||||
|
|
||||||
// Verify confirmation dialog appears
|
|
||||||
const confirmDialog = page.locator('text=Folie löschen?');
|
|
||||||
await expect(confirmDialog).toBeVisible();
|
|
||||||
|
|
||||||
// Verify dialog contains expected text
|
|
||||||
await expect(page.locator('text=Möchtest du die Folie')).toBeVisible();
|
|
||||||
await expect(page.locator('text=wirklich löschen?')).toBeVisible();
|
|
||||||
|
|
||||||
// Verify cancel button is visible
|
|
||||||
const cancelButton = page.locator('button:has-text("Abbrechen")').first();
|
|
||||||
await expect(cancelButton).toBeVisible();
|
|
||||||
|
|
||||||
// Click cancel to close dialog without deleting
|
|
||||||
await cancelButton.click();
|
|
||||||
await page.waitForTimeout(200);
|
|
||||||
|
|
||||||
// Verify dialog is closed
|
|
||||||
const dialogClosed = await confirmDialog.isHidden().catch(() => true);
|
|
||||||
expect(dialogClosed).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
// Test 1: Songs block accordion can be expanded and collapsed
|
async function navigateToEditPage(page) {
|
||||||
test('songs block accordion can be expanded and collapsed', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
await page.goto('/services');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
|
|
@ -9,375 +8,184 @@ test('songs block accordion can be expanded and collapsed', async ({ page }) =>
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
const hasEditableService = await editButton.isVisible().catch(() => false);
|
||||||
|
|
||||||
if (!hasEditableService) {
|
if (!hasEditableService) {
|
||||||
test.skip();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await editButton.click();
|
await editButton.click();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Find the Songs block toggle button (4th block)
|
test.skip('songs block accordion — replaced by agenda view', async () => {});
|
||||||
const blockToggles = page.getByTestId('service-edit-block-toggle');
|
|
||||||
const songsToggle = blockToggles.filter({ has: page.locator('text=Songs') }).first();
|
|
||||||
|
|
||||||
const toggleExists = await songsToggle.isVisible().catch(() => false);
|
test('song items visible in agenda or empty state', async ({ page }) => {
|
||||||
if (!toggleExists) {
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
test.skip();
|
test.skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
await expect(songsToggle).toBeVisible();
|
const songItems = page.getByTestId('song-agenda-item');
|
||||||
|
const songCount = await songItems.count();
|
||||||
// Get the Songs block content container
|
|
||||||
const songsBlock = page.getByTestId('songs-block');
|
|
||||||
|
|
||||||
// Verify block is initially visible (expanded by default)
|
|
||||||
await expect(songsBlock).toBeVisible();
|
|
||||||
|
|
||||||
// Click toggle to collapse
|
|
||||||
await songsToggle.click();
|
|
||||||
await page.waitForTimeout(300); // Wait for transition
|
|
||||||
|
|
||||||
// Verify block is hidden
|
|
||||||
const isHidden = await songsBlock.isHidden().catch(() => true);
|
|
||||||
expect(isHidden).toBe(true);
|
|
||||||
|
|
||||||
// Click toggle again to expand
|
|
||||||
await songsToggle.click();
|
|
||||||
await page.waitForTimeout(300); // Wait for transition
|
|
||||||
|
|
||||||
// Verify block is visible again
|
|
||||||
await expect(songsBlock).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test 2: Song list shows songs in correct order or empty state
|
|
||||||
test('song list shows songs or empty state', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
await editButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const songsBlock = page.getByTestId('songs-block');
|
|
||||||
await expect(songsBlock).toBeVisible();
|
|
||||||
|
|
||||||
// Check for song cards
|
|
||||||
const songCards = page.getByTestId('songs-block-song-card');
|
|
||||||
const songCount = await songCards.count();
|
|
||||||
|
|
||||||
if (songCount === 0) {
|
if (songCount === 0) {
|
||||||
// No songs - verify empty state message
|
const emptyState = page.getByText('Keine Ablauf-Elemente vorhanden');
|
||||||
await expect(songsBlock).toContainText('Fuer diesen Service sind aktuell keine Songs vorhanden.');
|
const hasEmptyState = await emptyState.isVisible().catch(() => false);
|
||||||
|
expect(hasEmptyState).toBe(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Songs exist - verify first card is rendered with order label
|
const firstSongItem = songItems.first();
|
||||||
const firstSongCard = songCards.first();
|
await expect(firstSongItem).toBeVisible();
|
||||||
await expect(firstSongCard).toBeVisible();
|
|
||||||
await expect(firstSongCard.locator('text=/Song \\d+/')).toBeVisible();
|
const songTitle = firstSongItem.getByTestId('song-agenda-title');
|
||||||
|
await expect(songTitle).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test 3: Each song row shows name, CCLI ID, and status badge
|
test('song agenda item shows title and ccli info', async ({ page }) => {
|
||||||
test('song row shows name, CCLI ID, and status badge', async ({ page }) => {
|
const navigated = await navigateToEditPage(page);
|
||||||
await page.goto('/services');
|
if (!navigated) {
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
test.skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
await editButton.click();
|
const songItems = page.getByTestId('song-agenda-item');
|
||||||
await page.waitForLoadState('networkidle');
|
const songCount = await songItems.count();
|
||||||
|
|
||||||
const songCards = page.getByTestId('songs-block-song-card');
|
|
||||||
const songCount = await songCards.count();
|
|
||||||
|
|
||||||
if (songCount === 0) {
|
if (songCount === 0) {
|
||||||
test.skip();
|
test.skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstSongCard = songCards.first();
|
const firstSongItem = songItems.first();
|
||||||
|
const songTitle = firstSongItem.getByTestId('song-agenda-title');
|
||||||
// Verify CCLI label exists
|
await expect(songTitle).toBeVisible();
|
||||||
await expect(firstSongCard.locator('text=/CCLI:/')).toBeVisible();
|
|
||||||
|
|
||||||
// Verify translation indicator exists
|
|
||||||
await expect(firstSongCard.locator('text=/Hat Uebersetzung:/')).toBeVisible();
|
|
||||||
|
|
||||||
// Verify status badge exists (either "Zugeordnet" or "Nicht zugeordnet")
|
|
||||||
const statusBadge = firstSongCard.locator('text=/zugeordnet/i').first();
|
|
||||||
await expect(statusBadge).toBeVisible();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test 4: Unmatched songs show "Erstellung anfragen" button and manual assign select
|
test('unmatched songs show request creation button in agenda', async ({ page }) => {
|
||||||
test('unmatched songs show request creation button and manual assign', async ({ page }) => {
|
const navigated = await navigateToEditPage(page);
|
||||||
await page.goto('/services');
|
if (!navigated) {
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
test.skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
await editButton.click();
|
const requestButton = page.getByTestId('song-request-creation').first();
|
||||||
await page.waitForLoadState('networkidle');
|
const hasUnmatched = await requestButton.isVisible().catch(() => false);
|
||||||
|
|
||||||
const songCards = page.getByTestId('songs-block-song-card');
|
|
||||||
const songCount = await songCards.count();
|
|
||||||
|
|
||||||
if (songCount === 0) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for unmatched song card (has "Nicht zugeordnet" badge)
|
|
||||||
const unmatchedCard = songCards.filter({ has: page.locator('text=Nicht zugeordnet') }).first();
|
|
||||||
const hasUnmatched = await unmatchedCard.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasUnmatched) {
|
if (!hasUnmatched) {
|
||||||
test.skip();
|
test.skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify "Erstellung anfragen" button
|
|
||||||
const requestButton = page.getByTestId('songs-block-request-button').first();
|
|
||||||
await expect(requestButton).toBeVisible();
|
await expect(requestButton).toBeVisible();
|
||||||
await expect(requestButton).toContainText('Erstellung anfragen');
|
|
||||||
|
|
||||||
// Verify search input
|
const searchInput = page.getByTestId('song-search-input').first();
|
||||||
const searchInput = page.getByTestId('songs-block-search-input').first();
|
|
||||||
await expect(searchInput).toBeVisible();
|
await expect(searchInput).toBeVisible();
|
||||||
|
|
||||||
// Verify song select dropdown
|
const assignButton = page.getByTestId('song-assign-button').first();
|
||||||
const songSelect = page.getByTestId('songs-block-song-select').first();
|
|
||||||
await expect(songSelect).toBeVisible();
|
|
||||||
|
|
||||||
// Verify "Zuordnen" (assign) button
|
|
||||||
const assignButton = page.getByTestId('songs-block-assign-button').first();
|
|
||||||
await expect(assignButton).toBeVisible();
|
await expect(assignButton).toBeVisible();
|
||||||
await expect(assignButton).toContainText('Zuordnen');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test 5: Matched songs show arrangement dropdown with options
|
test('matched songs show arrangement pill in agenda', async ({ page }) => {
|
||||||
test('matched songs show arrangement dropdown with options', async ({ page }) => {
|
const navigated = await navigateToEditPage(page);
|
||||||
await page.goto('/services');
|
if (!navigated) {
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
test.skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
await editButton.click();
|
const arrangementPill = page.getByTestId('arrangement-pill').first();
|
||||||
await page.waitForLoadState('networkidle');
|
const hasPill = await arrangementPill.isVisible().catch(() => false);
|
||||||
|
|
||||||
// Check for arrangement configurator (only present for matched songs)
|
if (!hasPill) {
|
||||||
const arrangementConfigurator = page.getByTestId('arrangement-configurator').first();
|
test.skip();
|
||||||
const hasMatched = await arrangementConfigurator.isVisible().catch(() => false);
|
}
|
||||||
|
|
||||||
if (!hasMatched) {
|
await expect(arrangementPill).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('arrangement edit button opens arrangement dialog', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const editArrangementBtn = page.getByTestId('song-edit-arrangement').first();
|
||||||
|
const hasBtn = await editArrangementBtn.isVisible().catch(() => false);
|
||||||
|
|
||||||
|
if (!hasBtn) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
await editArrangementBtn.click();
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
const arrangementDialog = page.getByTestId('arrangement-dialog');
|
||||||
|
await expect(arrangementDialog).toBeVisible();
|
||||||
|
|
||||||
|
const closeBtn = page.getByTestId('arrangement-dialog-close-btn');
|
||||||
|
await closeBtn.click();
|
||||||
|
await page.waitForTimeout(300);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('arrangement dialog has select, add, clone buttons', async ({ page }) => {
|
||||||
|
const navigated = await navigateToEditPage(page);
|
||||||
|
if (!navigated) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
const editArrangementBtn = page.getByTestId('song-edit-arrangement').first();
|
||||||
|
const hasBtn = await editArrangementBtn.isVisible().catch(() => false);
|
||||||
|
|
||||||
|
if (!hasBtn) {
|
||||||
|
test.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
await editArrangementBtn.click();
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
const arrangementDialog = page.getByTestId('arrangement-dialog');
|
||||||
|
const dialogVisible = await arrangementDialog.isVisible().catch(() => false);
|
||||||
|
|
||||||
|
if (!dialogVisible) {
|
||||||
test.skip();
|
test.skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify arrangement select dropdown exists
|
|
||||||
const arrangementSelect = page.getByTestId('arrangement-select').first();
|
const arrangementSelect = page.getByTestId('arrangement-select').first();
|
||||||
await expect(arrangementSelect).toBeVisible();
|
await expect(arrangementSelect).toBeVisible();
|
||||||
|
|
||||||
// Verify add button
|
const addButton = page.getByTestId('arrangement-new-btn').first();
|
||||||
const addButton = page.getByTestId('arrangement-add-button').first();
|
|
||||||
await expect(addButton).toBeVisible();
|
await expect(addButton).toBeVisible();
|
||||||
await expect(addButton).toContainText('Hinzufügen');
|
|
||||||
|
|
||||||
// Verify clone button
|
const cloneButton = page.getByTestId('arrangement-clone-btn').first();
|
||||||
const cloneButton = page.getByTestId('arrangement-clone-button').first();
|
|
||||||
await expect(cloneButton).toBeVisible();
|
await expect(cloneButton).toBeVisible();
|
||||||
await expect(cloneButton).toContainText('Klonen');
|
|
||||||
|
const closeBtn = page.getByTestId('arrangement-dialog-close-btn');
|
||||||
|
await closeBtn.click();
|
||||||
|
await page.waitForTimeout(300);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test 6: Arrangement "Hinzufügen" (Add) button opens name prompt
|
test.skip('preview button is present for matched songs — replaced by agenda view', async () => {});
|
||||||
test('arrangement add button opens name prompt', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
test.skip('download button is present for matched songs — replaced by agenda view', async () => {});
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
await editButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const addButton = page.getByTestId('arrangement-add-button').first();
|
|
||||||
const hasMatched = await addButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasMatched) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up dialog handler before clicking (window.prompt is synchronous)
|
|
||||||
let promptShown = false;
|
|
||||||
page.once('dialog', async (dialog) => {
|
|
||||||
promptShown = true;
|
|
||||||
expect(dialog.type()).toBe('prompt');
|
|
||||||
expect(dialog.message()).toContain('Name des neuen Arrangements');
|
|
||||||
await dialog.dismiss(); // Cancel without creating
|
|
||||||
});
|
|
||||||
|
|
||||||
await addButton.click();
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
|
|
||||||
expect(promptShown).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test 7: Arrangement "Klonen" (Clone) button opens name prompt
|
|
||||||
test('arrangement clone button opens name prompt', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
await editButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const cloneButton = page.getByTestId('arrangement-clone-button').first();
|
|
||||||
const hasMatched = await cloneButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasMatched) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify arrangement select has options (clone requires a selected arrangement)
|
|
||||||
const arrangementSelect = page.getByTestId('arrangement-select').first();
|
|
||||||
const optionCount = await arrangementSelect.locator('option').count();
|
|
||||||
|
|
||||||
if (optionCount === 0) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up dialog handler before clicking
|
|
||||||
let promptShown = false;
|
|
||||||
page.once('dialog', async (dialog) => {
|
|
||||||
promptShown = true;
|
|
||||||
expect(dialog.type()).toBe('prompt');
|
|
||||||
expect(dialog.message()).toContain('Name des neuen Arrangements');
|
|
||||||
await dialog.dismiss(); // Cancel without creating
|
|
||||||
});
|
|
||||||
|
|
||||||
await cloneButton.click();
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
|
|
||||||
expect(promptShown).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test 8: Preview button opens song preview (placeholder toast for now)
|
|
||||||
test('preview button is present for matched songs', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
await editButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const previewButton = page.getByTestId('songs-block-preview-button').first();
|
|
||||||
const hasMatched = await previewButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasMatched) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify preview button exists with correct text
|
|
||||||
await expect(previewButton).toBeVisible();
|
|
||||||
await expect(previewButton).toContainText('Vorschau');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test 9: Download (PDF) button is present for songs with selected arrangement
|
|
||||||
test('download button is present for matched songs', async ({ page }) => {
|
|
||||||
await page.goto('/services');
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
await editButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const downloadButton = page.getByTestId('songs-block-download-button').first();
|
|
||||||
const hasMatched = await downloadButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasMatched) {
|
|
||||||
test.skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify download button exists with correct text
|
|
||||||
await expect(downloadButton).toBeVisible();
|
|
||||||
await expect(downloadButton).toContainText('PDF herunterladen');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test 10: Translation checkbox toggles (if song has translation)
|
|
||||||
test('translation checkbox toggles if song has translation', async ({ page }) => {
|
test('translation checkbox toggles if song has translation', async ({ page }) => {
|
||||||
await page.goto('/services');
|
const navigated = await navigateToEditPage(page);
|
||||||
await page.waitForLoadState('networkidle');
|
if (!navigated) {
|
||||||
|
|
||||||
const editButton = page.getByTestId('service-list-edit-button').first();
|
|
||||||
const hasEditableService = await editButton.isVisible().catch(() => false);
|
|
||||||
|
|
||||||
if (!hasEditableService) {
|
|
||||||
test.skip();
|
test.skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
await editButton.click();
|
const translationCheckbox = page.getByTestId('song-translation-checkbox').first();
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const translationCheckbox = page.getByTestId('songs-block-translation-checkbox').first();
|
|
||||||
const hasTranslation = await translationCheckbox.isVisible().catch(() => false);
|
const hasTranslation = await translationCheckbox.isVisible().catch(() => false);
|
||||||
|
|
||||||
if (!hasTranslation) {
|
if (!hasTranslation) {
|
||||||
test.skip();
|
test.skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current checked state
|
|
||||||
const initialState = await translationCheckbox.isChecked();
|
const initialState = await translationCheckbox.isChecked();
|
||||||
|
|
||||||
// Toggle the checkbox
|
|
||||||
await translationCheckbox.click();
|
await translationCheckbox.click();
|
||||||
await page.waitForTimeout(300);
|
await page.waitForTimeout(300);
|
||||||
|
|
||||||
// Verify state changed
|
|
||||||
const toggledState = await translationCheckbox.isChecked();
|
const toggledState = await translationCheckbox.isChecked();
|
||||||
expect(toggledState).not.toBe(initialState);
|
expect(toggledState).not.toBe(initialState);
|
||||||
|
|
||||||
// Toggle back to restore original state
|
|
||||||
await translationCheckbox.click();
|
await translationCheckbox.click();
|
||||||
await page.waitForTimeout(300);
|
await page.waitForTimeout(300);
|
||||||
|
|
||||||
// Verify restored to original state
|
|
||||||
const restoredState = await translationCheckbox.isChecked();
|
const restoredState = await translationCheckbox.isChecked();
|
||||||
expect(restoredState).toBe(initialState);
|
expect(restoredState).toBe(initialState);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue