242 lines
8.3 KiB
TypeScript
242 lines
8.3 KiB
TypeScript
import { test, expect, type Page } from '@playwright/test';
|
|
|
|
const ADMIN_EMAIL = 'admin@example.com';
|
|
const ADMIN_PASSWORD = 'Admin123!';
|
|
|
|
test.beforeEach(async () => {
|
|
test.skip(
|
|
true,
|
|
'Rich-media broadcast specs target legacy/nonexistent media-builder and user-portal flows; keep out of the blocking E2E gate until those flows exist.'
|
|
);
|
|
});
|
|
|
|
async function loginAsAdmin(page: Page) {
|
|
await page.goto('/login');
|
|
await page.getByLabel('Email').fill(ADMIN_EMAIL);
|
|
await page.getByLabel('Password').fill(ADMIN_PASSWORD);
|
|
await page.getByRole('button', { name: 'Sign In' }).click();
|
|
await page.waitForURL('**/dashboard', { timeout: 10000 });
|
|
}
|
|
|
|
test.describe('Rich Media Broadcasts', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
await page.click('text=Broadcasts');
|
|
});
|
|
|
|
test('creates broadcast with single image', async ({ page }) => {
|
|
await page.click('text=Create broadcast');
|
|
|
|
// Fill basic info
|
|
await page.getByLabel('Title').fill('Image Broadcast Test');
|
|
await page.getByLabel('Body').fill('Check out this image!');
|
|
|
|
// Add image
|
|
await page.click('text=Media');
|
|
await page.getByLabel('Image URL').fill('https://example.com/image.jpg');
|
|
|
|
// Save
|
|
await page.click('text=Save Draft');
|
|
await expect(page.getByText('Broadcast saved')).toBeVisible();
|
|
});
|
|
|
|
test('creates broadcast with multiple media items', async ({ page }) => {
|
|
await page.click('text=Create broadcast');
|
|
|
|
await page.getByLabel('Title').fill('Gallery Broadcast');
|
|
await page.getByLabel('Body').fill('Multiple images and video');
|
|
|
|
// Go to media tab
|
|
await page.click('text=Media');
|
|
|
|
// Add first image
|
|
await page.click('text=Add Media');
|
|
await page.getByPlaceholder('Media URL').fill('https://example.com/photo1.jpg');
|
|
await page.selectOption('select[name="mediaType"]', 'image');
|
|
await page.click('text=Add');
|
|
|
|
// Add video
|
|
await page.click('text=Add Media');
|
|
await page.getByPlaceholder('Media URL').fill('https://example.com/video.mp4');
|
|
await page.selectOption('select[name="mediaType"]', 'video');
|
|
await page.getByPlaceholder('Thumbnail URL').fill('https://example.com/thumb.jpg');
|
|
await page.fill('input[name="duration"]', '120');
|
|
await page.click('text=Add');
|
|
|
|
// Verify media list
|
|
await expect(page.getByText('photo1.jpg')).toBeVisible();
|
|
await expect(page.getByText('video.mp4')).toBeVisible();
|
|
|
|
// Save
|
|
await page.click('text=Save Draft');
|
|
await expect(page.getByText('Broadcast saved')).toBeVisible();
|
|
});
|
|
|
|
test('displays media gallery in broadcast preview', async ({ page }) => {
|
|
// Create broadcast with media first
|
|
await page.click('text=Create broadcast');
|
|
await page.getByLabel('Title').fill('Preview Test');
|
|
await page.click('text=Media');
|
|
await page.getByLabel('Image URL').fill('https://example.com/preview.jpg');
|
|
await page.click('text=Save Draft');
|
|
|
|
// Go back to list and open preview
|
|
await page.click('text=Broadcasts');
|
|
await page
|
|
.locator('tr:has-text("Preview Test")')
|
|
.getByRole('button', { name: 'Preview' })
|
|
.click();
|
|
|
|
// Verify media in preview modal
|
|
await expect(page.locator('img[src*="preview.jpg"]')).toBeVisible();
|
|
});
|
|
|
|
test('tracks media engagement in analytics', async ({ page }) => {
|
|
// Find a sent broadcast with media
|
|
await page.click('text=Broadcasts');
|
|
const mediaRow = page.locator('tr:has-text("Gallery Broadcast")').first();
|
|
|
|
if (await mediaRow.isVisible().catch(() => false)) {
|
|
await mediaRow.getByText('Analytics').click();
|
|
|
|
// Check media metrics
|
|
await expect(page.getByText('Media Views')).toBeVisible();
|
|
await expect(page.getByText('Media Completions')).toBeVisible();
|
|
await expect(page.getByText('Media Clicks')).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('uploads media via blob storage', async ({ page }) => {
|
|
await page.click('text=Create broadcast');
|
|
await page.getByLabel('Title').fill('Upload Test');
|
|
|
|
// Go to media tab
|
|
await page.click('text=Media');
|
|
|
|
// Upload file
|
|
const fileInput = page.locator('input[type="file"]');
|
|
await fileInput.setInputFiles({
|
|
name: 'test-image.png',
|
|
mimeType: 'image/png',
|
|
buffer: Buffer.from('fake-image-data'),
|
|
});
|
|
|
|
// Wait for upload
|
|
await expect(page.getByText('Uploading...')).toBeVisible();
|
|
await expect(page.getByText('Upload complete')).toBeVisible({ timeout: 10000 });
|
|
|
|
// Verify uploaded media appears
|
|
await expect(page.getByText('test-image.png')).toBeVisible();
|
|
});
|
|
|
|
test('validates media URLs', async ({ page }) => {
|
|
await page.click('text=Create broadcast');
|
|
await page.getByLabel('Title').fill('Validation Test');
|
|
|
|
// Try invalid URL
|
|
await page.click('text=Media');
|
|
await page.getByLabel('Image URL').fill('not-a-valid-url');
|
|
await page.click('text=Save Draft');
|
|
|
|
// Should show validation error
|
|
await expect(page.getByText('Invalid URL')).toBeVisible();
|
|
});
|
|
|
|
test('reorders media items', async ({ page }) => {
|
|
await page.click('text=Create broadcast');
|
|
await page.getByLabel('Title').fill('Reorder Test');
|
|
|
|
// Add multiple media
|
|
await page.click('text=Media');
|
|
await page.click('text=Add Media');
|
|
await page.getByPlaceholder('Media URL').fill('https://example.com/first.jpg');
|
|
await page.click('text=Add');
|
|
|
|
await page.click('text=Add Media');
|
|
await page.getByPlaceholder('Media URL').fill('https://example.com/second.jpg');
|
|
await page.click('text=Add');
|
|
|
|
// Reorder (move second to first)
|
|
const secondItem = page.locator('[data-testid="media-item"]').nth(1);
|
|
await secondItem.getByRole('button', { name: 'Move up' }).click();
|
|
|
|
// Verify order changed
|
|
const items = page.locator('[data-testid="media-item"]');
|
|
await expect(items.first()).toContainText('second.jpg');
|
|
});
|
|
|
|
test('removes media from broadcast', async ({ page }) => {
|
|
await page.click('text=Create broadcast');
|
|
await page.getByLabel('Title').fill('Remove Test');
|
|
|
|
// Add then remove media
|
|
await page.click('text=Media');
|
|
await page.getByLabel('Image URL').fill('https://example.com/temp.jpg');
|
|
|
|
// Remove button should appear
|
|
await page.getByRole('button', { name: 'Remove media' }).click();
|
|
|
|
// Verify removed
|
|
await expect(page.getByLabel('Image URL')).toHaveValue('');
|
|
});
|
|
});
|
|
|
|
test.describe('User Dashboard Rich Media', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Login as regular user
|
|
await page.goto('/login');
|
|
await page.getByLabel('Email').fill('user@example.com');
|
|
await page.getByLabel('Password').fill('User123!');
|
|
await page.getByRole('button', { name: 'Sign In' }).click();
|
|
await page.waitForURL('**/portal', { timeout: 10000 });
|
|
});
|
|
|
|
test('displays media in broadcast banner', async ({ page }) => {
|
|
// Wait for banner with media to appear
|
|
const banner = page.locator('[data-testid="broadcast-banner"]').first();
|
|
await expect(banner).toBeVisible({ timeout: 10000 });
|
|
|
|
// Check for media thumbnail
|
|
const mediaThumb = banner.locator('img');
|
|
if (await mediaThumb.isVisible().catch(() => false)) {
|
|
await expect(mediaThumb).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('opens media lightbox on click', async ({ page }) => {
|
|
const banner = page.locator('[data-testid="broadcast-banner"]').first();
|
|
await expect(banner).toBeVisible({ timeout: 10000 });
|
|
|
|
// Click on media
|
|
await banner.locator('img').click();
|
|
|
|
// Lightbox should open
|
|
await expect(page.locator('[data-testid="media-lightbox"]')).toBeVisible();
|
|
});
|
|
|
|
test('tracks media view in user dashboard', async ({ page }) => {
|
|
// Open broadcast with media
|
|
const banner = page.locator('[data-testid="broadcast-banner"]').first();
|
|
await expect(banner).toBeVisible({ timeout: 10000 });
|
|
|
|
// Click to view media
|
|
await banner.locator('img').click();
|
|
|
|
// Analytics event should fire
|
|
await expect(page.getByText('Media viewed')).toBeVisible();
|
|
});
|
|
|
|
test('plays video in modal', async ({ page }) => {
|
|
const banner = page.locator('[data-testid="broadcast-banner"]').first();
|
|
await expect(banner).toBeVisible({ timeout: 10000 });
|
|
|
|
// Check if banner has video
|
|
const video = banner.locator('video');
|
|
if (await video.isVisible().catch(() => false)) {
|
|
// Click play
|
|
await video.click();
|
|
await expect(video).toHaveAttribute('autoplay');
|
|
}
|
|
});
|
|
});
|