test(e2e): migrate search-bar.ts to Playwright (#55157)

This commit is contained in:
Huyen Nguyen
2024-06-14 00:23:06 +07:00
committed by GitHub
parent 14df93bcef
commit 8f14c7e9f8
5 changed files with 389 additions and 2 deletions
@@ -668,7 +668,8 @@
"see-results": "See all results for {{searchQuery}}",
"no-tutorials": "No tutorials found",
"try": "Looking for something? Try the search bar on this page.",
"no-results": "We could not find anything relating to <0>{{query}}</0>"
"no-results": "We could not find anything relating to <0>{{query}}</0>",
"result-list": "Search results"
},
"misc": {
"offline": "You appear to be offline, your progress may not be saved",
@@ -63,7 +63,11 @@ const CustomHits = connectHits(
return (
<div className='ais-Hits'>
<ul className='ais-Hits-list' data-cy='ais-Hits-list'>
<ul
className='ais-Hits-list'
data-cy='ais-Hits-list'
aria-label={t('search.result-list')}
>
{allHits.map((hit: Hit, i: number) => (
<li
className={
+88
View File
@@ -0,0 +1,88 @@
{
"results": [
{
"hits": [
{
"title": "Article 1",
"objectID": "Article 1",
"_highlightResult": {
"title": {
"value": "<ais-highlight-0000000000>Article</ais-highlight-0000000000> 1",
"matchLevel": "full"
}
}
},
{
"title": "Article 2",
"objectID": "Article 2",
"_highlightResult": {
"title": {
"value": "<ais-highlight-0000000000>Article</ais-highlight-0000000000> 2",
"matchLevel": "full"
}
}
},
{
"title": "Article 3",
"objectID": "Article 3",
"_highlightResult": {
"title": {
"value": "<ais-highlight-0000000000>Article</ais-highlight-0000000000> 3",
"matchLevel": "full"
}
}
},
{
"title": "Article 4",
"objectID": "Article 4",
"_highlightResult": {
"title": {
"value": "<ais-highlight-0000000000>Article</ais-highlight-0000000000> 4",
"matchLevel": "full"
}
}
},
{
"title": "Article 5",
"objectID": "Article 5",
"_highlightResult": {
"title": {
"value": "<ais-highlight-0000000000>Article</ais-highlight-0000000000> 5",
"matchLevel": "full"
}
}
},
{
"title": "Article 6",
"objectID": "Article 6",
"_highlightResult": {
"title": {
"value": "<ais-highlight-0000000000>Article</ais-highlight-0000000000> 6",
"matchLevel": "full"
}
}
},
{
"title": "Article 7",
"objectID": "Article 7",
"_highlightResult": {
"title": {
"value": "<ais-highlight-0000000000>Article</ais-highlight-0000000000> 7",
"matchLevel": "full"
}
}
},
{
"title": "Article 8",
"objectID": "Article 8",
"_highlightResult": {
"title": {
"value": "<ais-highlight-0000000000>Article</ais-highlight-0000000000> 8",
"matchLevel": "full"
}
}
}
]
}
]
}
+58
View File
@@ -0,0 +1,58 @@
{
"results": [
{
"hits": [
{
"title": "Article 1",
"objectID": "Article 1",
"_highlightResult": {
"title": {
"value": "<ais-highlight-0000000000>Article</ais-highlight-0000000000> 1",
"matchLevel": "full"
}
}
},
{
"title": "Article 2",
"objectID": "Article 2",
"_highlightResult": {
"title": {
"value": "<ais-highlight-0000000000>Article</ais-highlight-0000000000> 2",
"matchLevel": "full"
}
}
},
{
"title": "Article 3",
"objectID": "Article 3",
"_highlightResult": {
"title": {
"value": "<ais-highlight-0000000000>Article</ais-highlight-0000000000> 3",
"matchLevel": "full"
}
}
},
{
"title": "Article 4",
"objectID": "Article 4",
"_highlightResult": {
"title": {
"value": "<ais-highlight-0000000000>Article</ais-highlight-0000000000> 4",
"matchLevel": "full"
}
}
},
{
"title": "Article 5",
"objectID": "Article 5",
"_highlightResult": {
"title": {
"value": "<ais-highlight-0000000000>Article</ais-highlight-0000000000> 5",
"matchLevel": "full"
}
}
}
]
}
]
}
+236
View File
@@ -0,0 +1,236 @@
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
});
});