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(); }); });