dynamicweb/cms/tests/frontend/integration/touch-action.spec.js

231 lines
8.2 KiB
JavaScript

// @ts-check
const { test, expect, settings } = require('./helpers/fixtures');
/**
* Test: Touch Action CSS Classes
*
* Verifies that touch-action CSS classes are properly applied
* instead of inline data-touch-action attributes.
* This ensures CSP compliance while maintaining touch gesture control.
*/
test.describe('Touch Action CSS Classes', () => {
test.beforeEach(async ({ cms, page }) => {
await cms.login();
await cms.addPage();
});
test.afterEach(async ({ cms }) => {
await cms.logout();
});
test('should apply touch-action classes to toolbar elements', async ({ cms, page }) => {
await page.goto(settings.editUrl);
await page.waitForSelector('.cms-toolbar');
// Check main toolbar container has touch-action-none class
const toolbar = page.locator('#cms-top');
await expect(toolbar).toHaveClass(/cms-touch-action-none/);
// Verify no data-touch-action attributes exist
const elementsWithDataAttr = await page.locator('[data-touch-action]').count();
expect(elementsWithDataAttr).toBe(0);
// Check computed style has touch-action: none
const touchAction = await toolbar.evaluate(el =>
window.getComputedStyle(el).touchAction
);
expect(touchAction).toBe('none');
});
test('should apply touch-action classes to modal elements', async ({ cms, page }) => {
await page.goto(settings.editUrl);
await page.waitForSelector('.cms-toolbar');
// Open a modal (e.g., page settings)
await cms.openMenu('Page');
await page.click('.cms-toolbar-item-navigation a[href*="/change/"][data-rel="modal"]');
await page.waitForSelector('.cms-modal', { state: 'visible' });
// Check modal has touch-action-none class
const modal = page.locator('.cms-modal');
await expect(modal).toHaveClass(/cms-touch-action-none/);
// Check modal head has touch-action-none class
const modalHead = page.locator('.cms-modal-head');
await expect(modalHead).toHaveClass(/cms-touch-action-none/);
// Check modal title has touch-action-none class
const modalTitle = page.locator('.cms-modal-title');
await expect(modalTitle).toHaveClass(/cms-touch-action-none/);
// Check modal breadcrumb has touch-action-pan-x class
const modalBreadcrumb = page.locator('.cms-modal-breadcrumb');
await expect(modalBreadcrumb).toHaveClass(/cms-touch-action-pan-x/);
// Verify computed styles
const modalTouchAction = await modal.evaluate(el =>
window.getComputedStyle(el).touchAction
);
expect(modalTouchAction).toBe('none');
const breadcrumbTouchAction = await modalBreadcrumb.evaluate(el =>
window.getComputedStyle(el).touchAction
);
expect(breadcrumbTouchAction).toBe('pan-x');
});
test('should apply touch-action classes to structure board', async ({ cms, page }) => {
await page.goto(settings.editUrl);
await page.waitForSelector('.cms-toolbar');
// Switch to structure mode
await cms.enterEditMode();
await cms.openStructureBoard();
// Check structure content has touch-action-pan-y class
const structureContent = page.locator('.cms-structure-content');
await expect(structureContent).toHaveClass(/cms-touch-action-pan-y/);
// Verify computed style
const touchAction = await structureContent.evaluate(el =>
window.getComputedStyle(el).touchAction
);
expect(touchAction).toBe('pan-y');
// Check plugin pickers have touch-action-pan-y class
const pluginPickers = page.locator('.cms-plugin-picker');
const count = await pluginPickers.count();
if (count > 0) {
const firstPicker = pluginPickers.first();
await expect(firstPicker).toHaveClass(/cms-touch-action-pan-y/);
const pickerTouchAction = await firstPicker.evaluate(el =>
window.getComputedStyle(el).touchAction
);
expect(pickerTouchAction).toBe('pan-y');
}
});
test('should apply touch-action classes to dropdown menus', async ({ cms, page }) => {
await page.goto(settings.editUrl);
await page.waitForSelector('.cms-toolbar');
// Switch to structure mode to find dropdown menus
await cms.enterEditMode();
await cms.openStructureBoard();
// Find and check submenu dropdowns
const dropdowns = page.locator('.cms-submenu-dropdown-settings');
const count = await dropdowns.count();
if (count > 0) {
const firstDropdown = dropdowns.first();
await expect(firstDropdown).toHaveClass(/cms-touch-action-pan-y/);
const touchAction = await firstDropdown.evaluate(el =>
window.getComputedStyle(el).touchAction
);
expect(touchAction).toBe('pan-y');
}
});
test('should not have any data-touch-action attributes in DOM', async ({ cms, page }) => {
await page.goto(settings.editUrl);
await page.waitForSelector('.cms-toolbar');
// Switch to structure mode for full UI
await cms.enterEditMode();
await cms.openStructureBoard();
// Comprehensive check: no data-touch-action attributes should exist
const allElementsWithAttr = await page.locator('[data-touch-action]').count();
expect(allElementsWithAttr).toBe(0);
// Verify all expected classes exist instead
const touchActionNone = await page.locator('.cms-touch-action-none').count();
const touchActionPanY = await page.locator('.cms-touch-action-pan-y').count();
const touchActionPanX = await page.locator('.cms-touch-action-pan-x').count();
// At least some elements should have touch-action classes
expect(touchActionNone).toBeGreaterThan(0);
expect(touchActionPanY).toBeGreaterThan(0);
// pan-x might not always be visible depending on modal state
expect(touchActionPanX).toBeGreaterThanOrEqual(0);
});
test('should maintain touch-action during drag operations', async ({ cms, page }) => {
await page.goto(settings.editUrl);
await page.waitForSelector('.cms-toolbar');
// Switch to structure mode
await cms.enterEditMode();
await cms.openStructureBoard();
// Get structure content element
const structureContent = page.locator('.cms-structure-content').first();
// Verify initial touch-action class
await expect(structureContent).toHaveClass(/cms-touch-action-pan-y/);
// Check that touch-action style is set via CSS class, not inline
const hasInlineStyle = await structureContent.evaluate(el =>
el.style.touchAction !== ''
);
// Should NOT have inline style (unless dynamically set by JS during drag)
// This test ensures we're using CSS classes as the default
const inlineTouchAction = await structureContent.evaluate(el =>
el.style.touchAction || 'not-set'
);
// Either not set or set to 'pan-y' via class
if (inlineTouchAction !== 'not-set') {
expect(['pan-y', 'none']).toContain(inlineTouchAction);
}
});
test('should have proper CSS rules defined', async ({ cms, page }) => {
await page.goto(settings.editUrl);
await page.waitForSelector('.cms-toolbar');
// Create test elements to verify CSS rules
const testResults = await page.evaluate(() => {
const results = {};
// Create temporary test elements
const container = document.createElement('div');
container.className = 'cms cms-reset';
document.body.appendChild(container);
// Test cms-touch-action-none
const noneEl = document.createElement('div');
noneEl.className = 'cms-touch-action-none';
container.appendChild(noneEl);
results.none = window.getComputedStyle(noneEl).touchAction;
// Test cms-touch-action-pan-y
const panYEl = document.createElement('div');
panYEl.className = 'cms-touch-action-pan-y';
container.appendChild(panYEl);
results.panY = window.getComputedStyle(panYEl).touchAction;
// Test cms-touch-action-pan-x
const panXEl = document.createElement('div');
panXEl.className = 'cms-touch-action-pan-x';
container.appendChild(panXEl);
results.panX = window.getComputedStyle(panXEl).touchAction;
// Cleanup
document.body.removeChild(container);
return results;
});
// Verify CSS rules produce correct computed styles
expect(testResults.none).toBe('none');
expect(testResults.panY).toBe('pan-y');
expect(testResults.panX).toBe('pan-x');
});
});