Files
michaelschiemer/tests/e2e/live-components/target-support.spec.ts
2025-11-24 21:28:25 +01:00

229 lines
6.4 KiB
TypeScript

import { test, expect } from '@playwright/test';
/**
* LiveComponents: Target Support Tests
*
* Tests data-lc-target attribute for updating elements outside the component
*/
test.describe('LiveComponents Target Support', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test('should update target element within component', async ({ page }) => {
// Find a component with a button that has data-lc-target
const targetButton = page.locator('[data-lc-target]').first();
const count = await targetButton.count();
if (count === 0) {
test.skip();
return;
}
// Get target selector
const targetSelector = await targetButton.getAttribute('data-lc-target');
if (!targetSelector) {
test.skip();
return;
}
// Find target element
const targetElement = page.locator(targetSelector).first();
const targetExists = await targetElement.count() > 0;
if (!targetExists) {
test.skip();
return;
}
// Get initial content
const initialContent = await targetElement.textContent();
// Click button
await targetButton.click();
// Wait for update
await page.waitForTimeout(1000);
// Target should still exist and be visible
expect(targetElement).toBeVisible();
});
test('should update target element outside component', async ({ page }) => {
// This test requires a component that updates an element outside itself
// Look for a component with data-lc-target pointing to an element outside
const components = page.locator('[data-live-component]');
const componentCount = await components.count();
if (componentCount === 0) {
test.skip();
return;
}
// Find a button with data-lc-target that points outside its component
const buttons = page.locator('[data-live-action][data-lc-target]');
const buttonCount = await buttons.count();
if (buttonCount === 0) {
test.skip();
return;
}
// Test first button
const button = buttons.first();
const targetSelector = await button.getAttribute('data-lc-target');
if (!targetSelector) {
test.skip();
return;
}
// Find target element (should exist in document)
const targetElement = page.locator(targetSelector).first();
const targetExists = await targetElement.count() > 0;
if (!targetExists) {
test.skip();
return;
}
// Get component element
const component = button.locator('xpath=ancestor::*[@data-live-component][1]').first();
const componentExists = await component.count() > 0;
// Check if target is outside component (if component found)
if (componentExists) {
const targetInComponent = await component.locator(targetSelector).count() > 0;
// If target is inside component, skip this test (tested in previous test)
if (targetInComponent) {
test.skip();
return;
}
}
// Get initial content
const initialContent = await targetElement.textContent();
// Click button
await button.click();
// Wait for update
await page.waitForTimeout(1000);
// Target should still exist and be visible
expect(targetElement).toBeVisible();
});
test('should fallback to component element if target not found', async ({ page }) => {
// This test requires a component with an invalid target selector
// Since we can't easily create invalid selectors in tests, we'll test the fallback behavior
const components = page.locator('[data-live-component]');
const componentCount = await components.count();
if (componentCount === 0) {
test.skip();
return;
}
// Find any action button
const actionButton = page.locator('[data-live-action]').first();
const buttonCount = await actionButton.count();
if (buttonCount === 0) {
test.skip();
return;
}
// Get component
const component = actionButton.locator('xpath=ancestor::*[@data-live-component][1]').first();
const componentId = await component.getAttribute('data-live-component');
if (!componentId) {
test.skip();
return;
}
// Click button (should work even if target not found - falls back to component)
await actionButton.click();
// Wait for update
await page.waitForTimeout(1000);
// Component should still exist
expect(component).toBeVisible();
});
test('should support CSS selector targets', async ({ page }) => {
// Test ID selector
const idTargetButton = page.locator('[data-lc-target^="#"]').first();
const idCount = await idTargetButton.count();
if (idCount > 0) {
const targetId = await idTargetButton.getAttribute('data-lc-target');
const targetElement = page.locator(targetId || '').first();
if (await targetElement.count() > 0) {
await idTargetButton.click();
await page.waitForTimeout(1000);
expect(targetElement).toBeVisible();
}
}
// Test class selector
const classTargetButton = page.locator('[data-lc-target^="."]').first();
const classCount = await classTargetButton.count();
if (classCount > 0) {
const targetClass = await classTargetButton.getAttribute('data-lc-target');
const targetElement = page.locator(targetClass || '').first();
if (await targetElement.count() > 0) {
await classTargetButton.click();
await page.waitForTimeout(1000);
expect(targetElement).toBeVisible();
}
}
});
test('should combine target with swap strategies', async ({ page }) => {
// Find a button with both data-lc-target and data-lc-swap
const combinedButton = page.locator('[data-lc-target][data-lc-swap]').first();
const count = await combinedButton.count();
if (count === 0) {
test.skip();
return;
}
const targetSelector = await combinedButton.getAttribute('data-lc-target');
const swapStrategy = await combinedButton.getAttribute('data-lc-swap');
if (!targetSelector || !swapStrategy) {
test.skip();
return;
}
const targetElement = page.locator(targetSelector).first();
const targetExists = await targetElement.count() > 0;
if (!targetExists) {
test.skip();
return;
}
// Click button
await combinedButton.click();
// Wait for update
await page.waitForTimeout(1000);
// Target should still exist (swap strategy applied)
expect(targetElement).toBeVisible();
});
});