1import { test, expect } from '@playwright/test'; 2 3const httpHost = process.env.HTTP_HOST 4 5if (typeof httpHost !== 'string') { 6 throw new Error('Environment variable "HTTP_HOST" is not set.') 7} 8 9test.beforeEach(async ({ page }) => { 10 await page.goto(httpHost); 11}); 12 13const openSearchModal = async (page) => { 14 await page.getByRole('button', {name: 'Search'}).click(); 15 const modal = await page.getByRole('dialog', { name: 'Search modal' }); 16 17 // Wait for the modal animation to finish 18 await expect(page.locator('#search-modal__backdrop.show')).not.toHaveClass('showing'); 19 20 expect(modal).toBeVisible(); 21 return modal; 22} 23 24const expectModalToBeHidden = async (page, modal) => { 25 await expect(page.locator('#search-modal__backdrop')).not.toHaveClass(['show', 'hiding']); 26 await expect(modal).toBeHidden(); 27} 28 29const expectOption = async (modal, name) => { 30 await expect(modal.getByRole('option', { name })).toBeVisible(); 31} 32 33const expectSelectedOption = async (modal, name) => { 34 await expect(modal.getByRole('option', { name, selected: true })).toBeVisible(); 35} 36 37test('should open search modal when search button is clicked', async ({ page }) => { 38 const searchModal = await openSearchModal(page); 39 await expect(searchModal).toBeVisible(); 40}); 41 42test('should disable window scroll when search modal is open', async ({ page }) => { 43 await openSearchModal(page); 44 await page.mouse.wheel(0, 100); 45 await page.waitForTimeout(100); 46 const currentScrollY = await page.evaluate(() => window.scrollY); 47 expect(currentScrollY).toBe(0); 48}); 49 50test('should focus on search input when modal is opened', async ({ page }) => { 51 const modal = await openSearchModal(page); 52 const searchInput = modal.getByRole('searchbox', { name: 'Search docs' }); 53 await expect(searchInput).toBeFocused(); 54 await expect(searchInput).toHaveValue(''); 55}); 56 57test('should close search modal when close button is clicked', async ({ page }) => { 58 const modal = await openSearchModal(page); 59 await modal.getByRole('button', { name: 'Close' }).click(); 60 await expectModalToBeHidden(page, modal); 61}); 62 63test('should re-enable window scroll when search modal is closed', async ({ page }) => { 64 const modal = await openSearchModal(page); 65 await modal.getByRole('button', { name: 'Close' }).click(); 66 await expectModalToBeHidden(page, modal); 67 await page.mouse.wheel(0, 100); 68 await page.waitForTimeout(100); // wait for scroll event to be processed 69 const currentScrollY = await page.evaluate(() => window.scrollY); 70 expect(currentScrollY).toBe(100); 71}); 72 73test('should close search modal when Escape key is pressed', async ({ page }) => { 74 const modal = await openSearchModal(page); 75 await page.keyboard.press('Escape'); 76 await expectModalToBeHidden(page, modal); 77}); 78 79test('should close search modal when clicking outside of it', async ({ page }) => { 80 const modal = await openSearchModal(page); 81 await page.click('#search-modal__backdrop', { position: { x: 10, y: 10 } }); 82 await expectModalToBeHidden(page, modal); 83}); 84 85test('should perform search and display results', async ({ page }) => { 86 const modal = await openSearchModal(page); 87 await modal.getByRole('searchbox').fill('array'); 88 await expect( 89 await modal.getByRole('listbox', { name: 'Search results' }).getByRole('option') 90 ).toHaveCount(30); 91}); 92 93test('should navigate through search results with arrow keys', async ({ page }) => { 94 const modal = await openSearchModal(page); 95 await modal.getByRole('searchbox').fill('strlen'); 96 await expectOption(modal, /^strlen$/); 97 98 await page.keyboard.press('ArrowDown'); 99 await expectSelectedOption(modal, /^strlen$/); 100 101 await page.keyboard.press('ArrowDown'); 102 await page.keyboard.press('ArrowDown'); 103 await page.keyboard.press('ArrowDown'); 104 await expectSelectedOption(modal, /^mb_strlen$/); 105 106 await page.keyboard.press('ArrowUp'); 107 await expectSelectedOption(modal, /^iconv_strlen$/); 108}); 109 110test('should navigate to selected result page when Enter is pressed', async ({ page }) => { 111 const modal = await openSearchModal(page); 112 await modal.getByRole('searchbox').fill('strpos'); 113 await expectOption(modal, /^strpos$/); 114 115 await page.keyboard.press('ArrowDown'); 116 await page.keyboard.press('Enter'); 117 await expect(page).toHaveURL(`http://${httpHost}/manual/en/function.strpos.php`); 118}); 119 120test('should navigate to search page when Enter is pressed with no selection', async ({ page }) => { 121 const modal = await openSearchModal(page); 122 await modal.getByRole('searchbox').fill('php basics'); 123 await page.keyboard.press('Enter'); 124 await expect(page).toHaveURL(`http://${httpHost}/search.php?lang=en&q=php%20basics`); 125}); 126