mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-07-17 17:21:48 +00:00
222 lines
7.0 KiB
TypeScript
222 lines
7.0 KiB
TypeScript
import {expect, test} from '@playwright/test';
|
|
import {setupGedcomRoute} from './helpers';
|
|
|
|
test.describe('Search functionality and global shortcut', () => {
|
|
test.beforeEach(async ({context}) => {
|
|
await setupGedcomRoute(context);
|
|
});
|
|
|
|
test('Search works and shortcut "/" focuses and selects all text', async ({
|
|
page,
|
|
}) => {
|
|
await page.goto('/#/view?url=https://example.org/family.ged');
|
|
await expect(page.locator('#content')).not.toContainText('Chike');
|
|
|
|
const searchInput = page.getByPlaceholder('Search for people', {
|
|
exact: false,
|
|
});
|
|
await expect(searchInput).toBeVisible();
|
|
|
|
// Fill some text, blur, and press '/'
|
|
await searchInput.fill('Test Query');
|
|
await searchInput.blur();
|
|
await expect(searchInput).not.toBeFocused();
|
|
|
|
await page.keyboard.press('/');
|
|
await expect(searchInput).toBeFocused();
|
|
|
|
// Assert that all text is selected
|
|
const isTextSelected = await searchInput.evaluate(
|
|
(el: HTMLInputElement) => {
|
|
return el.selectionStart === 0 && el.selectionEnd === el.value.length;
|
|
},
|
|
);
|
|
expect(isTextSelected).toBe(true);
|
|
|
|
// Perform search
|
|
await searchInput.fill('chik');
|
|
await expect(page.locator('.results')).toContainText('Chike');
|
|
await searchInput.press('Enter');
|
|
await expect(page.locator('#content')).toContainText('Chike');
|
|
});
|
|
|
|
test('Viewport switching: Search query is preserved when resizing between desktop and mobile', async ({
|
|
page,
|
|
}) => {
|
|
await page.setViewportSize({width: 1200, height: 800});
|
|
await page.goto('/#/view?url=https://example.org/family.ged');
|
|
|
|
const desktopInput = page.getByPlaceholder("Search for people (press '/')");
|
|
await desktopInput.fill('chik');
|
|
await expect(page.locator('.results')).toContainText('Chike');
|
|
|
|
// Resize to mobile viewport
|
|
await page.setViewportSize({width: 500, height: 800});
|
|
const mobileInput = page.getByPlaceholder('Search for people', {
|
|
exact: true,
|
|
});
|
|
await expect(mobileInput).toBeVisible();
|
|
await expect(mobileInput).toHaveValue('chik');
|
|
await expect(page.locator('.results')).toContainText('Chike');
|
|
});
|
|
|
|
test('Collision safety: Typing "/" inside an active modal input types "/" and does not trigger shortcut', async ({
|
|
page,
|
|
}) => {
|
|
await page.goto('/#/view?url=https://example.org/family.ged');
|
|
|
|
await page.getByText('Open', {exact: true}).click();
|
|
await page.getByText('Load from URL').click();
|
|
|
|
const modalInput = page.getByPlaceholder('https://');
|
|
await expect(modalInput).toBeVisible();
|
|
await expect(modalInput).toBeFocused();
|
|
|
|
await page.keyboard.type('http://test/data');
|
|
await expect(modalInput).toHaveValue('http://test/data');
|
|
|
|
const searchInput = page.getByPlaceholder('Search for people', {
|
|
exact: false,
|
|
});
|
|
await expect(searchInput).not.toBeFocused();
|
|
});
|
|
|
|
test('Modal safety: Pressing "/" while a modal is open does not steal focus even if modal input is blurred', async ({
|
|
page,
|
|
}) => {
|
|
await page.goto('/#/view?url=https://example.org/family.ged');
|
|
|
|
await page.getByText('Open', {exact: true}).click();
|
|
await page.getByText('Load from URL').click();
|
|
|
|
const modalInput = page.getByPlaceholder('https://');
|
|
await expect(modalInput).toBeVisible();
|
|
await modalInput.blur();
|
|
|
|
await page.keyboard.press('/');
|
|
|
|
const searchInput = page.getByPlaceholder('Search for people', {
|
|
exact: false,
|
|
});
|
|
await expect(searchInput).not.toBeFocused();
|
|
});
|
|
|
|
test('Form control protection: Pressing "/" while focused on a button does not steal focus', async ({
|
|
page,
|
|
}) => {
|
|
await page.goto('/#/view?url=https://example.org/family.ged');
|
|
|
|
await page.getByText('Open', {exact: true}).click();
|
|
await page.getByText('Load from URL').click();
|
|
|
|
const cancelButton = page.getByRole('button', {name: 'Cancel'});
|
|
await cancelButton.focus();
|
|
await expect(cancelButton).toBeFocused();
|
|
|
|
await page.keyboard.press('/');
|
|
|
|
await expect(cancelButton).toBeFocused();
|
|
const searchInput = page.getByPlaceholder('Search for people', {
|
|
exact: false,
|
|
});
|
|
await expect(searchInput).not.toBeFocused();
|
|
});
|
|
|
|
test('Modifier exclusion: Modifier combinations do not trigger the shortcut', async ({
|
|
page,
|
|
}) => {
|
|
await page.goto('/#/view?url=https://example.org/family.ged');
|
|
|
|
const searchInput = page.getByPlaceholder('Search for people', {
|
|
exact: false,
|
|
});
|
|
await expect(searchInput).not.toBeFocused();
|
|
|
|
await page.keyboard.press('Control+/');
|
|
await expect(searchInput).not.toBeFocused();
|
|
|
|
await page.keyboard.press('Alt+/');
|
|
await expect(searchInput).not.toBeFocused();
|
|
|
|
await page.keyboard.press('Meta+/');
|
|
await expect(searchInput).not.toBeFocused();
|
|
});
|
|
|
|
test('Landing page safety: Pressing "/" on landing page has no effect and doesn\'t throw errors', async ({
|
|
page,
|
|
}) => {
|
|
const errors: string[] = [];
|
|
page.on('pageerror', (err) => {
|
|
errors.push(err.message);
|
|
});
|
|
page.on('console', (msg) => {
|
|
if (msg.type() === 'error' && !msg.text().includes('Warning:')) {
|
|
errors.push(msg.text());
|
|
}
|
|
});
|
|
|
|
await page.goto('/');
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.keyboard.press('/');
|
|
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
test('Question mark safety: Pressing "?" (Shift+/) does not trigger search shortcut', async ({
|
|
page,
|
|
}) => {
|
|
await page.goto('/#/view?url=https://example.org/family.ged');
|
|
const searchInput = page.getByPlaceholder('Search for people', {
|
|
exact: false,
|
|
});
|
|
await expect(searchInput).not.toBeFocused();
|
|
|
|
await page.keyboard.press('?');
|
|
await expect(searchInput).not.toBeFocused();
|
|
});
|
|
|
|
test('Dropdown menu interaction: Pressing "/" while dropdown is open focuses search bar', async ({
|
|
page,
|
|
}) => {
|
|
await page.goto('/#/view?url=https://example.org/family.ged');
|
|
|
|
await page.getByText('View', {exact: true}).click();
|
|
await expect(page.getByText('Hourglass chart')).toBeVisible();
|
|
|
|
await page.keyboard.press('/');
|
|
|
|
const searchInput = page.getByPlaceholder('Search for people', {
|
|
exact: false,
|
|
});
|
|
await expect(searchInput).toBeFocused();
|
|
});
|
|
|
|
test('Combobox wrapper interaction: Clicking inside search bar wrapper and pressing "/" focuses input', async ({
|
|
page,
|
|
}) => {
|
|
await page.goto('/#/view?url=https://example.org/family.ged');
|
|
|
|
const searchInput = page.getByPlaceholder('Search for people', {
|
|
exact: false,
|
|
});
|
|
await page.locator('.ui.search').first().click();
|
|
await searchInput.blur();
|
|
|
|
await page.keyboard.press('/');
|
|
await expect(searchInput).toBeFocused();
|
|
});
|
|
|
|
test('AltGr compatibility: Control+Alt+/ triggers the shortcut', async ({
|
|
page,
|
|
}) => {
|
|
await page.goto('/#/view?url=https://example.org/family.ged');
|
|
const searchInput = page.getByPlaceholder('Search for people', {
|
|
exact: false,
|
|
});
|
|
await expect(searchInput).toBeVisible();
|
|
|
|
await page.keyboard.press('Control+Alt+/');
|
|
await expect(searchInput).toBeFocused();
|
|
});
|
|
});
|