fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled
Some checks failed
Deploy Application / deploy (push) Has been cancelled
This commit is contained in:
162
tests/e2e/live-components/progressive-enhancement.spec.ts
Normal file
162
tests/e2e/live-components/progressive-enhancement.spec.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* LiveComponents: Progressive Enhancement Tests
|
||||
*
|
||||
* Tests data-lc-boost functionality for automatic AJAX links and forms
|
||||
*/
|
||||
test.describe('LiveComponents Progressive Enhancement', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test('should find boost containers', async ({ page }) => {
|
||||
const boostContainers = page.locator('[data-lc-boost="true"]');
|
||||
const count = await boostContainers.count();
|
||||
|
||||
// This test just verifies the selector works
|
||||
// Actual boost containers may or may not exist
|
||||
expect(count).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
test('should mark links in boost containers', async ({ page }) => {
|
||||
const boostContainer = page.locator('[data-lc-boost="true"]').first();
|
||||
const containerCount = await boostContainer.count();
|
||||
|
||||
if (containerCount === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find links within boost container
|
||||
const links = boostContainer.locator('a[href]');
|
||||
const linkCount = await links.count();
|
||||
|
||||
if (linkCount === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Links should be marked (or at least exist)
|
||||
const firstLink = links.first();
|
||||
expect(firstLink).toBeVisible();
|
||||
});
|
||||
|
||||
test('should mark forms in boost containers', async ({ page }) => {
|
||||
const boostContainer = page.locator('[data-lc-boost="true"]').first();
|
||||
const containerCount = await boostContainer.count();
|
||||
|
||||
if (containerCount === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find forms within boost container
|
||||
const forms = boostContainer.locator('form[action]');
|
||||
const formCount = await forms.count();
|
||||
|
||||
if (formCount === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Forms should exist
|
||||
const firstForm = forms.first();
|
||||
expect(firstForm).toBeVisible();
|
||||
});
|
||||
|
||||
test('should handle link clicks in boost containers', async ({ page }) => {
|
||||
const boostContainer = page.locator('[data-lc-boost="true"]').first();
|
||||
const containerCount = await boostContainer.count();
|
||||
|
||||
if (containerCount === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const link = boostContainer.locator('a[href]:not([target="_blank"])').first();
|
||||
const linkCount = await link.count();
|
||||
|
||||
if (linkCount === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const href = await link.getAttribute('href');
|
||||
if (!href || href === '#' || href.startsWith('javascript:')) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Click link (should be handled via AJAX)
|
||||
await link.click();
|
||||
|
||||
// Wait for potential navigation
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Page should still be loaded (or navigated)
|
||||
expect(page.url()).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should handle form submissions in boost containers', async ({ page }) => {
|
||||
const boostContainer = page.locator('[data-lc-boost="true"]').first();
|
||||
const containerCount = await boostContainer.count();
|
||||
|
||||
if (containerCount === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find form without data-live-action (boost should handle it)
|
||||
const form = boostContainer.locator('form[action]:not([data-live-action])').first();
|
||||
const formCount = await form.count();
|
||||
|
||||
if (formCount === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Form should exist
|
||||
expect(form).toBeVisible();
|
||||
});
|
||||
|
||||
test('should respect opt-out (data-lc-boost="false")', async ({ page }) => {
|
||||
const optOutLink = page.locator('a[data-lc-boost="false"]').first();
|
||||
const count = await optOutLink.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Link should exist and be clickable
|
||||
expect(optOutLink).toBeVisible();
|
||||
});
|
||||
|
||||
test('should skip special links (mailto, tel, javascript)', async ({ page }) => {
|
||||
const boostContainer = page.locator('[data-lc-boost="true"]').first();
|
||||
const containerCount = await boostContainer.count();
|
||||
|
||||
if (containerCount === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find special links
|
||||
const mailtoLink = boostContainer.locator('a[href^="mailto:"]').first();
|
||||
const telLink = boostContainer.locator('a[href^="tel:"]').first();
|
||||
|
||||
// These should exist but not be boosted
|
||||
// We can't easily test the boost behavior, but we can verify they exist
|
||||
if (await mailtoLink.count() > 0) {
|
||||
expect(mailtoLink).toBeVisible();
|
||||
}
|
||||
if (await telLink.count() > 0) {
|
||||
expect(telLink).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
90
tests/e2e/live-components/scroll-behavior.spec.ts
Normal file
90
tests/e2e/live-components/scroll-behavior.spec.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* LiveComponents: Scroll Behavior Tests
|
||||
*
|
||||
* Tests automatic scrolling after updates
|
||||
*/
|
||||
test.describe('LiveComponents Scroll Behavior', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test('should support scroll after update', async ({ page }) => {
|
||||
const scrollButton = page.locator('[data-lc-scroll="true"]').first();
|
||||
const count = await scrollButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Scroll to top first
|
||||
await page.evaluate(() => window.scrollTo(0, 0));
|
||||
|
||||
// Click button
|
||||
await scrollButton.click();
|
||||
|
||||
// Wait for update and scroll
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Button should still be visible
|
||||
expect(scrollButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('should support scroll-target', async ({ page }) => {
|
||||
const scrollTargetButton = page.locator('[data-lc-scroll-target]').first();
|
||||
const count = await scrollTargetButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const targetSelector = await scrollTargetButton.getAttribute('data-lc-scroll-target');
|
||||
if (!targetSelector) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const targetElement = page.locator(targetSelector).first();
|
||||
const targetExists = await targetElement.count() > 0;
|
||||
|
||||
if (!targetExists) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Click button
|
||||
await scrollTargetButton.click();
|
||||
|
||||
// Wait for update and scroll
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Target should be visible
|
||||
expect(targetElement).toBeVisible();
|
||||
});
|
||||
|
||||
test('should support scroll-behavior', async ({ page }) => {
|
||||
const smoothScrollButton = page.locator('[data-lc-scroll-behavior="smooth"]').first();
|
||||
const instantScrollButton = page.locator('[data-lc-scroll-behavior="instant"]').first();
|
||||
|
||||
// Test smooth scroll
|
||||
if (await smoothScrollButton.count() > 0) {
|
||||
await smoothScrollButton.click();
|
||||
await page.waitForTimeout(1000);
|
||||
expect(smoothScrollButton).toBeVisible();
|
||||
}
|
||||
|
||||
// Test instant scroll
|
||||
if (await instantScrollButton.count() > 0) {
|
||||
await instantScrollButton.click();
|
||||
await page.waitForTimeout(1000);
|
||||
expect(instantScrollButton).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
207
tests/e2e/live-components/swap-strategies.spec.ts
Normal file
207
tests/e2e/live-components/swap-strategies.spec.ts
Normal file
@@ -0,0 +1,207 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* LiveComponents: Swap Strategies Tests
|
||||
*
|
||||
* Tests different swap strategies (innerHTML, outerHTML, beforebegin, afterbegin, afterend, beforeend, none)
|
||||
*/
|
||||
test.describe('LiveComponents Swap Strategies', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test('should support innerHTML swap (default)', async ({ page }) => {
|
||||
// This test assumes there's a component with a button that uses innerHTML swap
|
||||
// Since we don't have a test page yet, we'll skip if no components found
|
||||
const components = page.locator('[data-live-component]');
|
||||
const count = await components.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find a button with data-live-action
|
||||
const actionButton = page.locator('[data-live-action]').first();
|
||||
const buttonCount = await actionButton.count();
|
||||
|
||||
if (buttonCount === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get initial HTML
|
||||
const component = components.first();
|
||||
const initialHtml = await component.innerHTML();
|
||||
|
||||
// Click button (should use innerHTML by default)
|
||||
await actionButton.click();
|
||||
|
||||
// Wait for update
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// HTML should have changed (or stayed same if no update)
|
||||
const newHtml = await component.innerHTML();
|
||||
|
||||
// At minimum, the action should have executed
|
||||
expect(component).toBeVisible();
|
||||
});
|
||||
|
||||
test('should support outerHTML swap', async ({ page }) => {
|
||||
// This test requires a component with data-lc-swap="outerHTML"
|
||||
const swapButton = page.locator('[data-lc-swap="outerHTML"]');
|
||||
const count = await swapButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const targetElement = swapButton.first();
|
||||
const initialTag = await targetElement.evaluate(el => el.tagName);
|
||||
|
||||
// Click to trigger swap
|
||||
await targetElement.click();
|
||||
|
||||
// Wait for update
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Element should be replaced (tag might change)
|
||||
const newElement = page.locator(swapButton.first().locator('xpath=..')).first();
|
||||
expect(newElement).toBeVisible();
|
||||
});
|
||||
|
||||
test('should support beforebegin swap', async ({ page }) => {
|
||||
const swapButton = page.locator('[data-lc-swap="beforebegin"]');
|
||||
const count = await swapButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const button = swapButton.first();
|
||||
const parent = button.locator('xpath=..');
|
||||
|
||||
// Get initial child count
|
||||
const initialChildCount = await parent.evaluate(el => el.children.length);
|
||||
|
||||
// Click to trigger swap
|
||||
await button.click();
|
||||
|
||||
// Wait for update
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Should have more children (content inserted before button)
|
||||
const newChildCount = await parent.evaluate(el => el.children.length);
|
||||
expect(newChildCount).toBeGreaterThanOrEqual(initialChildCount);
|
||||
});
|
||||
|
||||
test('should support afterbegin swap', async ({ page }) => {
|
||||
const swapButton = page.locator('[data-lc-swap="afterbegin"]');
|
||||
const count = await swapButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const button = swapButton.first();
|
||||
const target = page.locator(button.getAttribute('data-lc-target') || '[data-live-component]').first();
|
||||
|
||||
// Get initial first child
|
||||
const initialFirstChild = await target.evaluate(el => el.firstElementChild?.tagName);
|
||||
|
||||
// Click to trigger swap
|
||||
await button.click();
|
||||
|
||||
// Wait for update
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// First child should have changed or new content added
|
||||
const newFirstChild = await target.evaluate(el => el.firstElementChild?.tagName);
|
||||
expect(target).toBeVisible();
|
||||
});
|
||||
|
||||
test('should support afterend swap', async ({ page }) => {
|
||||
const swapButton = page.locator('[data-lc-swap="afterend"]');
|
||||
const count = await swapButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const button = swapButton.first();
|
||||
const parent = button.locator('xpath=..');
|
||||
|
||||
// Get initial child count
|
||||
const initialChildCount = await parent.evaluate(el => el.children.length);
|
||||
|
||||
// Click to trigger swap
|
||||
await button.click();
|
||||
|
||||
// Wait for update
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Should have more children (content inserted after button)
|
||||
const newChildCount = await parent.evaluate(el => el.children.length);
|
||||
expect(newChildCount).toBeGreaterThanOrEqual(initialChildCount);
|
||||
});
|
||||
|
||||
test('should support beforeend swap', async ({ page }) => {
|
||||
const swapButton = page.locator('[data-lc-swap="beforeend"]');
|
||||
const count = await swapButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const button = swapButton.first();
|
||||
const target = page.locator(button.getAttribute('data-lc-target') || '[data-live-component]').first();
|
||||
|
||||
// Get initial last child
|
||||
const initialLastChild = await target.evaluate(el => el.lastElementChild?.tagName);
|
||||
|
||||
// Click to trigger swap
|
||||
await button.click();
|
||||
|
||||
// Wait for update
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Last child should have changed or new content added
|
||||
const newLastChild = await target.evaluate(el => el.lastElementChild?.tagName);
|
||||
expect(target).toBeVisible();
|
||||
});
|
||||
|
||||
test('should support none swap (no DOM update)', async ({ page }) => {
|
||||
const swapButton = page.locator('[data-lc-swap="none"]');
|
||||
const count = await swapButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const button = swapButton.first();
|
||||
const target = page.locator(button.getAttribute('data-lc-target') || '[data-live-component]').first();
|
||||
|
||||
// Get initial HTML
|
||||
const initialHtml = await target.innerHTML();
|
||||
|
||||
// Click to trigger action (should not update DOM)
|
||||
await button.click();
|
||||
|
||||
// Wait a bit
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// HTML should remain the same (no DOM update)
|
||||
const newHtml = await target.innerHTML();
|
||||
expect(newHtml).toBe(initialHtml);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
228
tests/e2e/live-components/target-support.spec.ts
Normal file
228
tests/e2e/live-components/target-support.spec.ts
Normal file
@@ -0,0 +1,228 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
73
tests/e2e/live-components/transitions.spec.ts
Normal file
73
tests/e2e/live-components/transitions.spec.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* LiveComponents: Transitions Tests
|
||||
*
|
||||
* Tests CSS transitions for swap operations
|
||||
*/
|
||||
test.describe('LiveComponents Transitions', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test('should support fade transition', async ({ page }) => {
|
||||
const fadeButton = page.locator('[data-lc-swap-transition="fade"]').first();
|
||||
const count = await fadeButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Click button
|
||||
await fadeButton.click();
|
||||
|
||||
// Wait for transition
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Button should still be visible
|
||||
expect(fadeButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('should support slide transition', async ({ page }) => {
|
||||
const slideButton = page.locator('[data-lc-swap-transition="slide"]').first();
|
||||
const count = await slideButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Click button
|
||||
await slideButton.click();
|
||||
|
||||
// Wait for transition
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Button should still be visible
|
||||
expect(slideButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('should combine transition with swap strategy', async ({ page }) => {
|
||||
const combinedButton = page.locator('[data-lc-swap-transition][data-lc-swap]').first();
|
||||
const count = await combinedButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Click button
|
||||
await combinedButton.click();
|
||||
|
||||
// Wait for transition
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Button should still be visible
|
||||
expect(combinedButton).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
167
tests/e2e/live-components/trigger-options.spec.ts
Normal file
167
tests/e2e/live-components/trigger-options.spec.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* LiveComponents: Trigger Options Tests
|
||||
*
|
||||
* Tests advanced trigger options (delay, throttle, once, changed, from, load)
|
||||
*/
|
||||
test.describe('LiveComponents Trigger Options', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test('should support trigger-delay', async ({ page }) => {
|
||||
const delayButton = page.locator('[data-lc-trigger-delay]').first();
|
||||
const count = await delayButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const delayValue = await delayButton.getAttribute('data-lc-trigger-delay');
|
||||
|
||||
// Click button
|
||||
const clickTime = Date.now();
|
||||
await delayButton.click();
|
||||
|
||||
// Wait a bit
|
||||
await page.waitForTimeout(100);
|
||||
|
||||
// Action should not have executed immediately (delay)
|
||||
// We can't easily test the exact delay, but we can verify the button exists
|
||||
expect(delayButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('should support trigger-throttle', async ({ page }) => {
|
||||
const throttleButton = page.locator('[data-lc-trigger-throttle]').first();
|
||||
const count = await throttleButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Click multiple times rapidly
|
||||
await throttleButton.click();
|
||||
await page.waitForTimeout(50);
|
||||
await throttleButton.click();
|
||||
await page.waitForTimeout(50);
|
||||
await throttleButton.click();
|
||||
|
||||
// Wait for throttle to complete
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Button should still be visible
|
||||
expect(throttleButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('should support trigger-once', async ({ page }) => {
|
||||
const onceButton = page.locator('[data-lc-trigger-once="true"]').first();
|
||||
const count = await onceButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Click multiple times
|
||||
await onceButton.click();
|
||||
await page.waitForTimeout(100);
|
||||
await onceButton.click();
|
||||
await page.waitForTimeout(100);
|
||||
await onceButton.click();
|
||||
|
||||
// Button should still be visible
|
||||
expect(onceButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('should support trigger-changed', async ({ page }) => {
|
||||
const changedInput = page.locator('[data-lc-trigger-changed="true"]').first();
|
||||
const count = await changedInput.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Type same value (should not trigger)
|
||||
await changedInput.fill('test');
|
||||
await page.waitForTimeout(100);
|
||||
await changedInput.fill('test'); // Same value
|
||||
|
||||
// Type different value (should trigger)
|
||||
await changedInput.fill('different');
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
expect(changedInput).toBeVisible();
|
||||
});
|
||||
|
||||
test('should support trigger-from', async ({ page }) => {
|
||||
const triggerFromButton = page.locator('[data-lc-trigger-from]').first();
|
||||
const count = await triggerFromButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const sourceSelector = await triggerFromButton.getAttribute('data-lc-trigger-from');
|
||||
if (!sourceSelector) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const sourceElement = page.locator(sourceSelector).first();
|
||||
const sourceExists = await sourceElement.count() > 0;
|
||||
|
||||
if (!sourceExists) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Click source element (should trigger action on triggerFromButton)
|
||||
await sourceElement.click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Both elements should be visible
|
||||
expect(sourceElement).toBeVisible();
|
||||
expect(triggerFromButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('should support trigger-load', async ({ page }) => {
|
||||
// This test is tricky because load happens on page load
|
||||
// We'll just verify the attribute exists
|
||||
const loadButton = page.locator('[data-lc-trigger-load="true"]').first();
|
||||
const count = await loadButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Button should exist
|
||||
expect(loadButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('should combine multiple trigger options', async ({ page }) => {
|
||||
const combinedButton = page.locator('[data-lc-trigger-delay][data-lc-trigger-once]').first();
|
||||
const count = await combinedButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
// Click button
|
||||
await combinedButton.click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Button should still be visible
|
||||
expect(combinedButton).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
91
tests/e2e/live-components/url-management.spec.ts
Normal file
91
tests/e2e/live-components/url-management.spec.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* LiveComponents: URL Management Tests
|
||||
*
|
||||
* Tests data-lc-push-url and data-lc-replace-url functionality
|
||||
*/
|
||||
test.describe('LiveComponents URL Management', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test('should support push-url', async ({ page }) => {
|
||||
const pushUrlButton = page.locator('[data-lc-push-url]').first();
|
||||
const count = await pushUrlButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const targetUrl = await pushUrlButton.getAttribute('data-lc-push-url');
|
||||
const initialUrl = page.url();
|
||||
|
||||
// Click button
|
||||
await pushUrlButton.click();
|
||||
|
||||
// Wait for URL update
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// URL should have changed (or stayed same if targetUrl is current)
|
||||
const newUrl = page.url();
|
||||
|
||||
// At minimum, the action should have executed
|
||||
expect(pushUrlButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('should support replace-url', async ({ page }) => {
|
||||
const replaceUrlButton = page.locator('[data-lc-replace-url]').first();
|
||||
const count = await replaceUrlButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const targetUrl = await replaceUrlButton.getAttribute('data-lc-replace-url');
|
||||
const initialUrl = page.url();
|
||||
|
||||
// Click button
|
||||
await replaceUrlButton.click();
|
||||
|
||||
// Wait for URL update
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Button should still be visible
|
||||
expect(replaceUrlButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('should handle browser back/forward', async ({ page }) => {
|
||||
// Navigate to a page first
|
||||
await page.goto('/');
|
||||
|
||||
// Find a button with push-url
|
||||
const pushUrlButton = page.locator('[data-lc-push-url]').first();
|
||||
const count = await pushUrlButton.count();
|
||||
|
||||
if (count === 0) {
|
||||
test.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const initialUrl = page.url();
|
||||
|
||||
// Click button to push URL
|
||||
await pushUrlButton.click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Go back
|
||||
await page.goBack();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Should be back at initial URL (or close to it)
|
||||
const backUrl = page.url();
|
||||
expect(page.url()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user