237 lines
7.4 KiB
TypeScript
237 lines
7.4 KiB
TypeScript
import { test, expect, type Page } from '@playwright/test';
|
|
import translations from '../client/i18n/locales/english/translations.json';
|
|
import algoliaEightHits from './fixtures/algolia-eight-hits.json';
|
|
import algoliaFiveHits from './fixtures/algolia-five-hits.json';
|
|
|
|
const haveApiKeys =
|
|
process.env.ALGOLIA_APP_ID !== 'app_id_from_algolia_dashboard' &&
|
|
process.env.ALGOLIA_API_KEY !== 'api_key_from_algolia_dashboard';
|
|
|
|
const getSearchInput = async ({
|
|
page,
|
|
isMobile
|
|
}: {
|
|
page: Page;
|
|
isMobile: boolean;
|
|
}) => {
|
|
if (isMobile) {
|
|
const menuButton = page.getByRole('button', {
|
|
name: translations.buttons.menu
|
|
});
|
|
await expect(menuButton).toBeVisible();
|
|
await menuButton.click();
|
|
}
|
|
|
|
return page.getByLabel('Search');
|
|
};
|
|
|
|
const search = async ({
|
|
page,
|
|
isMobile,
|
|
query
|
|
}: {
|
|
page: Page;
|
|
isMobile: boolean;
|
|
query: string;
|
|
}) => {
|
|
const searchInput = await getSearchInput({ page, isMobile });
|
|
await searchInput.fill(query);
|
|
};
|
|
|
|
const mockAlgolia = async ({
|
|
page,
|
|
hitsPerPage
|
|
}: {
|
|
page: Page;
|
|
hitsPerPage: number;
|
|
}) => {
|
|
if (hitsPerPage === 8) {
|
|
await page.route(/\w+(\.algolia\.net|\.algolianet\.com)/, async route => {
|
|
await route.fulfill({ json: algoliaEightHits });
|
|
});
|
|
} else if (hitsPerPage === 5) {
|
|
await page.route(/\w+(\.algolia\.net|\.algolianet\.com)/, async route => {
|
|
await route.fulfill({ json: algoliaFiveHits });
|
|
});
|
|
} else if (hitsPerPage === 0) {
|
|
await page.route(/\w+(\.algolia\.net|\.algolianet\.com)/, async route => {
|
|
await route.fulfill({ json: {} });
|
|
});
|
|
}
|
|
};
|
|
|
|
test.describe('Search bar', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('/learn');
|
|
|
|
// Mock Algolia requests to prevent hitting Algolia server unnecessarily.
|
|
// Comment out this line if you want to test against the real server.
|
|
await mockAlgolia({ page, hitsPerPage: 8 });
|
|
});
|
|
|
|
test('should display correctly', async ({ page, isMobile }) => {
|
|
const searchInput = await getSearchInput({ page, isMobile });
|
|
|
|
await expect(searchInput).toBeVisible();
|
|
await expect(searchInput).toHaveAttribute(
|
|
'placeholder',
|
|
translations.search.placeholder
|
|
);
|
|
await expect(
|
|
page.getByRole('button', { name: 'Submit search terms' })
|
|
).toBeVisible();
|
|
});
|
|
|
|
test('should return the search results when the user presses Enter', async ({
|
|
page,
|
|
isMobile
|
|
}) => {
|
|
test.skip(!haveApiKeys, 'This test requires Algolia API keys');
|
|
|
|
await search({ page, isMobile, query: 'article' });
|
|
|
|
// Wait for the search results to show up
|
|
const resultList = page.getByRole('list', { name: 'Search results' });
|
|
// Initially, the dropdown contains an `li` with the text "No tutorials found",
|
|
// so we need to check the text content to ensure the correct `li` is displayed.
|
|
await expect(resultList.getByRole('listitem').first()).toContainText(
|
|
/article/i
|
|
);
|
|
|
|
await page.keyboard.press('Enter');
|
|
|
|
await page.waitForURL(
|
|
'https://www.freecodecamp.org/news/search/?query=article'
|
|
);
|
|
const title = await page.title();
|
|
expect(title).toBe('Search - freeCodeCamp.org');
|
|
});
|
|
|
|
test('should return the search results when the user clicks the search button', async ({
|
|
page,
|
|
isMobile
|
|
}) => {
|
|
test.skip(!haveApiKeys, 'This test requires Algolia API keys');
|
|
|
|
await search({ page, isMobile, query: 'article' });
|
|
|
|
// Wait for the search results to show up
|
|
const resultList = page.getByRole('list', { name: 'Search results' });
|
|
// Initially, the dropdown contains an `li` with the text "No tutorials found",
|
|
// so we need to check the text content to ensure the correct `li` is displayed.
|
|
await expect(resultList.getByRole('listitem').first()).toContainText(
|
|
/article/i
|
|
);
|
|
|
|
await page.getByRole('button', { name: 'Submit search terms' }).click();
|
|
|
|
await page.waitForURL(
|
|
'https://www.freecodecamp.org/news/search/?query=article'
|
|
);
|
|
const title = await page.title();
|
|
expect(title).toBe('Search - freeCodeCamp.org');
|
|
});
|
|
|
|
test('should show an empty result list if no results found', async ({
|
|
page,
|
|
isMobile
|
|
}) => {
|
|
await mockAlgolia({ page, hitsPerPage: 0 });
|
|
await search({ page, isMobile, query: '!@#$%^' });
|
|
|
|
const resultList = page.getByRole('list', { name: 'Search results' });
|
|
await expect(resultList.getByRole('listitem')).toHaveCount(1);
|
|
await expect(resultList.getByRole('listitem')).toHaveText(
|
|
'No tutorials found'
|
|
);
|
|
});
|
|
|
|
test('should clear the input and hide the result dropdown when the user clicks the clear button', async ({
|
|
page,
|
|
isMobile
|
|
}) => {
|
|
const searchInput = await getSearchInput({ page, isMobile });
|
|
await expect(searchInput).toBeVisible();
|
|
|
|
await searchInput.fill('test');
|
|
await page.getByRole('button', { name: 'Clear search terms' }).click();
|
|
|
|
await expect(searchInput).toHaveValue('');
|
|
await expect(
|
|
page.getByRole('list', { name: 'Search results' })
|
|
).toBeHidden();
|
|
});
|
|
});
|
|
|
|
test.describe('Search results when viewport height is greater than 768px', () => {
|
|
test.use({
|
|
viewport: { width: 1600, height: 1200 }
|
|
});
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('/learn');
|
|
|
|
// Mock Algolia requests to prevent hitting Algolia server unnecessarily.
|
|
// Comment out this line if you want to test against the real server.
|
|
await mockAlgolia({ page, hitsPerPage: 8 });
|
|
});
|
|
|
|
test('should display 8 items', async ({ page, isMobile }) => {
|
|
test.skip(!haveApiKeys, 'This test requires Algolia API keys');
|
|
|
|
await search({ page, isMobile, query: 'article' });
|
|
|
|
// Wait for the search results to show up
|
|
const results = page.getByRole('list', { name: 'Search results' });
|
|
await expect(results.getByRole('listitem')).toHaveCount(9); // 8 results + the footer
|
|
});
|
|
});
|
|
|
|
test.describe('Search results when viewport height is equal to 768px', () => {
|
|
test.use({
|
|
viewport: { width: 1600, height: 768 }
|
|
});
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('/learn');
|
|
|
|
// Mock Algolia requests to prevent hitting Algolia server unnecessarily.
|
|
// Comment out this line if you want to test against the real server.
|
|
await mockAlgolia({ page, hitsPerPage: 8 });
|
|
});
|
|
|
|
test('should display 8 items', async ({ page, isMobile }) => {
|
|
test.skip(!haveApiKeys, 'This test requires Algolia API keys');
|
|
|
|
await search({ page, isMobile, query: 'article' });
|
|
|
|
// Wait for the search results to show up
|
|
const results = page.getByRole('list', { name: 'Search results' });
|
|
await expect(results.getByRole('listitem')).toHaveCount(9); // 8 results + the footer
|
|
});
|
|
});
|
|
|
|
test.describe('Search results when viewport height is less than 768px', () => {
|
|
test.use({
|
|
viewport: { width: 1600, height: 500 }
|
|
});
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('/learn');
|
|
|
|
// Mock Algolia requests to prevent hitting Algolia server unnecessarily.
|
|
// Comment out this line if you want to test against the real server.
|
|
await mockAlgolia({ page, hitsPerPage: 5 });
|
|
});
|
|
|
|
test('should display 5 items', async ({ page, isMobile }) => {
|
|
test.skip(!haveApiKeys, 'This test requires Algolia API keys');
|
|
|
|
await search({ page, isMobile, query: 'article' });
|
|
|
|
// Wait for the search results to show up
|
|
const results = page.getByRole('list', { name: 'Search results' });
|
|
await expect(results.getByRole('listitem')).toHaveCount(6); // 5 results + the footer
|
|
});
|
|
});
|