mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-09-24 23:01:18 +08:00
test(e2e,playwright): Header (#52005)
This commit is contained in:
@@ -134,6 +134,7 @@ const LanguageList = ({ t, navigate }: LanguageListProps): JSX.Element => {
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
data-playwright-test-label='header-toggle-lang-button'
|
||||
id='toggle-lang-button'
|
||||
className='lang-button-nav'
|
||||
title={t('buttons.change-language')}
|
||||
@@ -147,6 +148,7 @@ const LanguageList = ({ t, navigate }: LanguageListProps): JSX.Element => {
|
||||
<LanguageGlobe />
|
||||
</button>
|
||||
<ul
|
||||
data-playwright-test-label='header-lang-list'
|
||||
id='nav-lang-list'
|
||||
className='nav-list'
|
||||
aria-labelledby='toggle-lang-button'
|
||||
@@ -154,6 +156,7 @@ const LanguageList = ({ t, navigate }: LanguageListProps): JSX.Element => {
|
||||
{locales.map((lang, index) => (
|
||||
<li key={'lang-' + lang}>
|
||||
<button
|
||||
data-playwright-test-label='header-lang-list-option'
|
||||
className='nav-link nav-lang-list-option'
|
||||
data-value={lang}
|
||||
onClick={handleLanguageChange}
|
||||
|
||||
@@ -36,6 +36,7 @@ const Login = ({
|
||||
bsStyle='default'
|
||||
className={(block ? 'btn-cta-big btn-block' : '') + ' signup-btn btn-cta'}
|
||||
data-test-label={dataTestLabel}
|
||||
data-playwright-test-label='header-sign-in-button'
|
||||
href={href}
|
||||
>
|
||||
<span className='login-btn-icon'>
|
||||
|
||||
@@ -46,6 +46,7 @@ const MenuButton = ({
|
||||
aria-expanded={displayMenu}
|
||||
className='exposed-button-nav'
|
||||
id='toggle-button-nav'
|
||||
data-playwright-test-label='header-menu-button'
|
||||
onBlur={handleBlur}
|
||||
onClick={handleClick}
|
||||
ref={innerRef}
|
||||
|
||||
@@ -153,6 +153,7 @@ function NavLinks({
|
||||
return (
|
||||
<ul
|
||||
aria-labelledby='toggle-button-nav'
|
||||
data-playwright-test-label='header-menu'
|
||||
className={`nav-list${displayMenu ? ' display-menu' : ''}`}
|
||||
>
|
||||
<DonateButton
|
||||
|
||||
@@ -4,7 +4,12 @@ import FreeCodeCampLogo from '../../../assets/icons/freecodecamp';
|
||||
|
||||
const NavLogo = (): JSX.Element => {
|
||||
const { t } = useTranslation();
|
||||
return <FreeCodeCampLogo aria-label={t('aria.fcc-curriculum')} />;
|
||||
return (
|
||||
<FreeCodeCampLogo
|
||||
aria-label={t('aria.fcc-curriculum')}
|
||||
data-playwright-test-label='header-logo'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
NavLogo.displayName = 'NavLogo';
|
||||
|
||||
@@ -45,6 +45,7 @@ const SearchBarOptimized = ({
|
||||
autoComplete='off'
|
||||
autoCorrect='off'
|
||||
id='ais-SearchBox-input'
|
||||
data-playwright-test-label='header-search'
|
||||
className='ais-SearchBox-input'
|
||||
maxLength={512}
|
||||
onChange={onChange}
|
||||
|
||||
@@ -208,6 +208,7 @@ export class SearchBar extends Component<SearchBarProps, SearchBarState> {
|
||||
<ObserveKeys except={['Space']}>
|
||||
<div onFocus={this.handleFocus} role='textbox'>
|
||||
<SearchBox
|
||||
data-playwright-test-label='header-search'
|
||||
focusShortcuts={['83', '191']}
|
||||
onChange={this.handleChange}
|
||||
onSubmit={e => {
|
||||
|
||||
+159
-32
@@ -1,12 +1,21 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import translations from '../client/i18n/locales/english/translations.json';
|
||||
import { availableLangs, hiddenLangs, LangNames } from '../shared/config/i18n';
|
||||
import links from '../client/i18n/locales/english/links.json';
|
||||
|
||||
const headerComponentElements = {
|
||||
skipContent: 'header-skip-content',
|
||||
examNav: 'header-exam-nav',
|
||||
examNavLogo: 'header-exam-nav-microsoft-logo',
|
||||
universalNav: 'header-universal-nav',
|
||||
universalNavLogo: 'header-universal-nav-logo'
|
||||
universalNavLogo: 'header-universal-nav-logo',
|
||||
search: 'header-search',
|
||||
toggleLangButton: 'header-toggle-lang-button',
|
||||
languageList: 'header-lang-list',
|
||||
languageButton: 'header-lang-list-option',
|
||||
menuButton: 'header-menu-button',
|
||||
menu: 'header-menu',
|
||||
signInButton: 'header-sign-in-button'
|
||||
} as const;
|
||||
|
||||
const examUrl =
|
||||
@@ -15,40 +24,158 @@ const examUrl =
|
||||
test.use({ storageState: 'playwright/.auth/certified-user.json' });
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(examUrl);
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page }) => {
|
||||
await page.close();
|
||||
});
|
||||
test.describe('Header Component', () => {
|
||||
test('Should contain a skip link', async ({ page }) => {
|
||||
const skipContent = page.getByTestId(headerComponentElements.skipContent);
|
||||
await expect(skipContent).toBeVisible();
|
||||
await expect(skipContent).toHaveAttribute('href', '#content-start');
|
||||
});
|
||||
|
||||
test('Has link for skip content', async ({ page }) => {
|
||||
const skipContent = page.getByTestId(headerComponentElements.skipContent);
|
||||
await expect(skipContent).toBeVisible();
|
||||
await expect(skipContent).toHaveAttribute('href', '#content-start');
|
||||
});
|
||||
test('Renders universal nav by default', async ({ page }) => {
|
||||
const universalNavigation = page.getByTestId(
|
||||
headerComponentElements.universalNav
|
||||
);
|
||||
const universalNavigationLogo = page.getByTestId(
|
||||
headerComponentElements.universalNavLogo
|
||||
);
|
||||
await expect(universalNavigation).toBeVisible();
|
||||
await expect(universalNavigationLogo).toBeVisible();
|
||||
await expect(universalNavigationLogo).toHaveAttribute('href', '/learn');
|
||||
});
|
||||
|
||||
test('Has nav for universal', async ({ page }) => {
|
||||
const universalNavigation = page.getByTestId(
|
||||
headerComponentElements.universalNav
|
||||
);
|
||||
const universalNavigationLogo = page.getByTestId(
|
||||
headerComponentElements.universalNavLogo
|
||||
);
|
||||
await expect(universalNavigation).toBeVisible();
|
||||
await expect(universalNavigationLogo).toBeVisible();
|
||||
await expect(universalNavigationLogo).toHaveAttribute('href', '/learn');
|
||||
});
|
||||
test('Renders exam nav for Foundational C# with Microsoft exam', async ({
|
||||
page
|
||||
}) => {
|
||||
await page.goto(examUrl);
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: translations.buttons['click-start-exam']
|
||||
})
|
||||
.click();
|
||||
await expect(page).toHaveURL(examUrl);
|
||||
await expect(
|
||||
page.getByTestId(headerComponentElements.examNav)
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId(headerComponentElements.examNavLogo)
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('Has nav for exams', async ({ page }) => {
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: translations.buttons['click-start-exam']
|
||||
})
|
||||
.click();
|
||||
await expect(page).toHaveURL(examUrl);
|
||||
await expect(page.getByTestId(headerComponentElements.examNav)).toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId(headerComponentElements.examNavLogo)
|
||||
).toBeVisible();
|
||||
test('Should display search in header on desktop and in menu on mobile', async ({
|
||||
page,
|
||||
isMobile
|
||||
}) => {
|
||||
const search = page.getByTestId(headerComponentElements.search);
|
||||
const menuButton = page.getByTestId(headerComponentElements.menuButton);
|
||||
|
||||
if (isMobile) {
|
||||
await expect(search).toBeHidden();
|
||||
await menuButton.click();
|
||||
await expect(search).toBeVisible();
|
||||
} else {
|
||||
await expect(search).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('Clicking the "Change Language" button should open the language list', async ({
|
||||
page
|
||||
}) => {
|
||||
const toggleLangButton = page.getByTestId(
|
||||
headerComponentElements.toggleLangButton
|
||||
);
|
||||
await expect(toggleLangButton).toBeVisible();
|
||||
await toggleLangButton.click();
|
||||
const langList = page.getByTestId(headerComponentElements.languageList);
|
||||
await expect(langList).toBeVisible();
|
||||
});
|
||||
|
||||
test('The language list should contain a button for each available language', async ({
|
||||
page
|
||||
}) => {
|
||||
const locales = availableLangs.client.filter(
|
||||
lang => !hiddenLangs.includes(lang)
|
||||
);
|
||||
|
||||
const toggleLangButton = page.getByTestId(
|
||||
headerComponentElements.toggleLangButton
|
||||
);
|
||||
await expect(toggleLangButton).toBeVisible();
|
||||
await toggleLangButton.click();
|
||||
const langList = page.getByTestId(headerComponentElements.languageList);
|
||||
await expect(langList).toBeVisible();
|
||||
|
||||
const langButtons = page.getByTestId(
|
||||
headerComponentElements.languageButton
|
||||
);
|
||||
await expect(langButtons).toHaveCount(locales.length);
|
||||
|
||||
for (let i = 0; i < locales.length; i++) {
|
||||
const btn = langButtons.nth(i);
|
||||
await expect(btn).toContainText(LangNames[locales[i]]);
|
||||
}
|
||||
});
|
||||
|
||||
test('Clicking the menu button should open the menu', async ({ page }) => {
|
||||
const menuButton = page.getByTestId(headerComponentElements.menuButton);
|
||||
const menu = page.getByTestId(headerComponentElements.menu);
|
||||
await expect(menuButton).toBeVisible();
|
||||
await menuButton.click();
|
||||
await expect(menu).toBeVisible();
|
||||
});
|
||||
|
||||
test('The menu should contain links to: donate, curriculum, forum, news, radio, and contribute', async ({
|
||||
page
|
||||
}) => {
|
||||
const menuButton = page.getByTestId(headerComponentElements.menuButton);
|
||||
const menu = page.getByTestId(headerComponentElements.menu);
|
||||
await expect(menuButton).toBeVisible();
|
||||
await menuButton.click();
|
||||
await expect(menu).toBeVisible();
|
||||
|
||||
const menuLinks = [
|
||||
{
|
||||
name: translations.buttons.donate,
|
||||
href: '/donate'
|
||||
},
|
||||
{ name: translations.buttons.curriculum, href: '/learn' },
|
||||
{ name: translations.buttons.forum, href: links.nav.forum },
|
||||
{ name: translations.buttons.news, href: links.nav.news },
|
||||
{
|
||||
name: translations.buttons.radio,
|
||||
href: process.env.RADIO_LOCATION || 'https://coderadio.freecodecamp.org'
|
||||
},
|
||||
{
|
||||
name: translations.buttons.contribute,
|
||||
href: links.nav.contribute
|
||||
}
|
||||
];
|
||||
|
||||
for (const menuLink of menuLinks) {
|
||||
const link = page.getByRole('link', { name: menuLink.name, exact: true });
|
||||
await expect(link).toBeVisible();
|
||||
await expect(link).toHaveAttribute('href', menuLink.href);
|
||||
}
|
||||
});
|
||||
|
||||
test('The Sign In button should redirect to api/signin', async ({
|
||||
browser
|
||||
}) => {
|
||||
// Sign out user in order to test Sign In button
|
||||
const context = await browser.newContext({
|
||||
storageState: { cookies: [], origins: [] }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto('/');
|
||||
|
||||
const signInButton = page.getByRole('link', {
|
||||
name: translations.buttons['sign-in']
|
||||
});
|
||||
|
||||
const apiLocation = process.env.API_LOCATION || 'http://localhost:3000';
|
||||
|
||||
await expect(signInButton).toHaveAttribute('href', `${apiLocation}/signin`);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user