Skip to content

Instantly share code, notes, and snippets.

@peteonrails
Created December 16, 2025 01:04
Show Gist options
  • Select an option

  • Save peteonrails/bc4c46a6ec3a7ee0196edb6981d95245 to your computer and use it in GitHub Desktop.

Select an option

Save peteonrails/bc4c46a6ec3a7ee0196edb6981d95245 to your computer and use it in GitHub Desktop.
Playwright test for datepicker calendar positioning fix (PR #10638)
const { chromium } = require('playwright');
const TARGET_URL = 'http://localhost:3000';
const LOGIN_EMAIL = 'emmie@tern.travel';
const LOGIN_PASSWORD = 'abc12345!1!1';
(async () => {
console.log('Testing EXTREME edge case - datepicker at very top of viewport...\n');
const browser = await chromium.launch({
headless: false,
slowMo: 50
});
const page = await browser.newPage();
await page.setViewportSize({ width: 1280, height: 600 }); // Smaller height to test more easily
try {
// Login
console.log('Logging in...');
await page.goto(TARGET_URL, { waitUntil: 'networkidle' });
if (page.url().includes('session')) {
await page.getByLabel('Email Address').fill(LOGIN_EMAIL);
await page.getByLabel('Password').fill(LOGIN_PASSWORD);
await page.getByRole('button', { name: 'Sign In', exact: true }).click();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
}
console.log('Navigating to Commission page...');
await page.click('text=Commission');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
// Click on Filter to open the filter panel
const filterBtn = page.locator('button:has-text("Filter")');
if (await filterBtn.isVisible()) {
await filterBtn.click();
await page.waitForTimeout(500);
}
// Find the first visible datepicker in the filter panel
const datepickers = await page.locator('[data-controller="datepicker"]').all();
console.log(`Found ${datepickers.length} datepicker elements`);
// Find a datepicker that's visible and can be interacted with
let testDatepicker = null;
for (let i = 0; i < datepickers.length; i++) {
const dp = page.locator('[data-controller="datepicker"]').nth(i);
if (await dp.isVisible().catch(() => false)) {
const toggle = dp.locator('[data-datepicker-target="toggle"]');
if (await toggle.isVisible().catch(() => false)) {
testDatepicker = dp;
console.log(`Using datepicker at index ${i}`);
break;
}
}
}
if (!testDatepicker) {
console.log('No suitable datepicker found');
await page.screenshot({ path: '/tmp/datepicker-extreme-noselector.png' });
await browser.close();
return;
}
// Scroll so the datepicker is positioned right at the top edge (within 50-70px)
console.log('\n=== TEST: Positioning datepicker at extreme top ===');
await page.evaluate(() => {
// Scroll the filter panel contents or the page so datepicker is at top
const dp = document.querySelector('[data-controller="datepicker"]:not([style*="display: none"])');
if (dp) {
const rect = dp.getBoundingClientRect();
// Position it very close to top (around 50px from top - just below header)
window.scrollBy(0, rect.top - 55);
}
});
await page.waitForTimeout(300);
// Get the datepicker's new position
const bounds = await testDatepicker.boundingBox();
console.log(`Datepicker input is now at y=${bounds.y}px (aiming for ~50-60px from top)`);
// Take screenshot before opening
await page.screenshot({ path: '/tmp/datepicker-extreme-1-before.png' });
// Open the calendar
const toggle = testDatepicker.locator('[data-datepicker-target="toggle"]');
await toggle.click();
await page.waitForTimeout(500);
// Take screenshot with calendar open
await page.screenshot({ path: '/tmp/datepicker-extreme-2-calendar.png' });
// Check calendar position
const calendar = page.locator('[data-datepicker-target="calendar"]');
if (await calendar.isVisible()) {
const calBounds = await calendar.boundingBox();
console.log(`\nCalendar positioned at y=${calBounds.y}px`);
console.log(`Calendar height: ${calBounds.height}px`);
console.log(`Calendar bottom: ${calBounds.y + calBounds.height}px`);
// The fix ensures calendar y is at least 60px
if (calBounds.y >= 60) {
console.log('\n✅ PASS: Calendar y position is >= 60px');
console.log(' The fix is working - calendar stays below the header!');
} else if (calBounds.y >= 0) {
console.log('\n⚠️ WARNING: Calendar y position is ' + calBounds.y + 'px (< 60px)');
console.log(' Calendar might partially overlap with header');
} else {
console.log('\n❌ FAIL: Calendar y position is negative!');
console.log(' Calendar is cut off at the top');
}
// Check if month/year navigation is visible (the main issue from the ticket)
const monthNav = calendar.locator('button').first();
if (await monthNav.isVisible()) {
console.log('\n✅ Calendar navigation controls are visible');
}
} else {
console.log('Calendar not visible');
}
// Close and test with even more extreme positioning
await page.keyboard.press('Escape');
await page.waitForTimeout(200);
console.log('\n=== TEST 2: Even more extreme - datepicker at y=10px ===');
await page.evaluate(() => {
const dp = document.querySelector('[data-controller="datepicker"]:not([style*="display: none"])');
if (dp) {
const rect = dp.getBoundingClientRect();
window.scrollBy(0, rect.top - 10);
}
});
await page.waitForTimeout(300);
const bounds2 = await testDatepicker.boundingBox();
console.log(`Datepicker input is now at y=${bounds2.y}px`);
await toggle.click();
await page.waitForTimeout(500);
await page.screenshot({ path: '/tmp/datepicker-extreme-3-very-top.png' });
if (await calendar.isVisible()) {
const calBounds2 = await calendar.boundingBox();
console.log(`Calendar positioned at y=${calBounds2.y}px`);
if (calBounds2.y >= 60) {
console.log('\n✅ PASS: Even with extreme positioning, calendar stays at y >= 60px');
console.log(' The limiter in the code is working correctly!');
} else {
console.log('\n❌ FAIL: Calendar at y=' + calBounds2.y + 'px is too high');
}
}
console.log('\n=== All tests completed ===');
console.log('Check screenshots in /tmp/datepicker-extreme-*.png');
} catch (error) {
console.error('Test error:', error.message);
await page.screenshot({ path: '/tmp/datepicker-extreme-error.png' });
} finally {
await page.waitForTimeout(2000);
await browser.close();
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment