feat/ab test landing google auth (#62538)

This commit is contained in:
Ahmad Abdolsaheb
2025-10-29 16:55:08 +03:00
committed by GitHub
parent 2bd3ec1db8
commit 03c775ac2d
14 changed files with 464 additions and 240 deletions

View File

@@ -42,6 +42,37 @@ describe('auth0 plugin', () => {
await fastify.register(prismaPlugin);
});
describe('GET /signin/google', () => {
test('should redirect directly to Google via Auth0 with connection param', async () => {
const res = await fastify.inject({
method: 'GET',
url: '/signin/google'
});
const redirectUrl = new URL(res.headers.location!);
expect(redirectUrl.host).toMatch(AUTH0_DOMAIN);
expect(redirectUrl.pathname).toBe('/authorize');
expect(redirectUrl.searchParams.get('connection')).toBe('google-oauth2');
expect(res.statusCode).toBe(302);
});
test('sets a login-returnto cookie', async () => {
const returnTo = 'http://localhost:3000/learn';
const res = await fastify.inject({
method: 'GET',
url: '/signin/google',
headers: { referer: returnTo }
});
const cookie = res.cookies.find(c => c.name === 'login-returnto');
expect(unsign(cookie!.value).value).toBe(returnTo);
expect(cookie).toMatchObject({
domain: COOKIE_DOMAIN,
httpOnly: true,
secure: true,
sameSite: 'Lax'
});
});
});
afterAll(async () => {
await fastify.prisma.$runCommandRaw({ dropDatabase: 1 });
await fastify.close();

View File

@@ -82,6 +82,25 @@ export const auth0Client: FastifyPluginCallbackTypebox = fp(
);
void reply.redirect(redirectUrl);
});
fastify.get('/signin/google', async function (request, reply) {
const returnTo = request.headers.referer ?? `${HOME_LOCATION}/learn`;
void reply.setCookie('login-returnto', returnTo, {
domain: COOKIE_DOMAIN,
httpOnly: true,
secure: true,
signed: true,
sameSite: 'lax'
});
const authorizationEndpoint =
await this.auth0OAuth.generateAuthorizationUri(request, reply);
const url = new URL(authorizationEndpoint);
url.searchParams.set('connection', 'google-oauth2');
void reply.redirect(url.toString());
});
done();
});

View File

@@ -110,13 +110,13 @@
}
]
},
"show-benefits": {
"landing-two-button-cta": {
"defaultValue": false,
"rules": [
{
"coverage": 1,
"hashAttribute": "id",
"seed": "show-benefits",
"seed": "landing-two-button-cta",
"hashVersion": 2,
"variations": [
false,
@@ -126,7 +126,7 @@
0.5,
0.5
],
"key": "show-benefits",
"key": "landing-two-button-cta",
"meta": [
{
"key": "0",
@@ -138,7 +138,7 @@
}
],
"phase": "0",
"name": "prod-show-benefits"
"name": "prod-landing-two-button-cta"
}
]
}

View File

@@ -118,6 +118,8 @@
"share-on-threads": "Share on Threads",
"play-scene": "Press Play",
"download-latest-version": "Download the Latest Version",
"more-ways-to-sign-in": "More ways to sign in",
"sign-in-with-google": "Sign in with Google",
"go-to-dcc-today": "Go to Today's Challenge",
"go-to-dcc-archive": "Go to Daily Coding Challenge Archive"
},
@@ -893,7 +895,8 @@
"fsd-b-benefit-2-title": "Professional Certification",
"fsd-b-benefit-2-description": "Prove your skills with an official, verifiable certification.",
"fsd-b-benefit-3-title": "500+ Exercises",
"fsd-b-benefit-3-description": "Solidify your knowledge with plenty of practice."
"fsd-b-benefit-3-description": "Solidify your knowledge with plenty of practice.",
"or": "OR"
},
"icons": {
"gold-cup": "Gold Cup",

View File

@@ -29,13 +29,14 @@ const Login = ({
isSignedIn
}: LoginProps): JSX.Element => {
const { t } = useTranslation();
const href = isSignedIn ? `${homeLocation}/learn` : `${apiLocation}/signin`;
return (
<a
className={(block ? 'btn-cta-big btn-block' : '') + ' signup-btn btn-cta'}
data-test-label={dataTestLabel}
data-playwright-test-label='header-sign-in-button'
data-playwright-test-label={
dataTestLabel ? dataTestLabel : 'sign-in-button'
}
href={href}
onClick={() => {
callGA({
@@ -43,9 +44,8 @@ const Login = ({
});
}}
>
<span className='login-btn-icon'>
<span className='login-btn-icon' aria-hidden='true'>
<FontAwesomeIcon icon={faRightToBracket} />
<span className='sr-only'> {t('buttons.sign-in')}</span>
</span>
<span className='login-btn-text'>{children || t('buttons.sign-in')}</span>
</a>

View File

@@ -2,14 +2,20 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import Login from '../../Header/components/login';
const BigCallToAction = ({ text }: { text?: string }): JSX.Element => {
const BigCallToAction = ({
text,
testLabel
}: {
text?: string;
testLabel?: string;
}): JSX.Element => {
const { t } = useTranslation();
return (
<Login
block={true}
data-test-label='landing-big-cta'
data-playwright-test-label='landing-big-cta'
data-test-label={testLabel}
data-playwright-test-label={testLabel}
>
{text ? text : t('buttons.logged-in-cta-btn')}
</Login>

View File

@@ -1,89 +0,0 @@
import React from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { Container, Col, Row, Spacer } from '@freecodecamp/ui';
import { clientLocale } from '../../../../config/env.json';
import {
AmazonLogo,
AppleLogo,
MicrosoftLogo,
SpotifyLogo,
GoogleLogo,
TencentLogo,
AlibabaLogo
} from '../../../assets/images/components';
import BigCallToAction from './big-call-to-action';
import CampersImage from './campers-image';
function LandingTop(): JSX.Element {
const { t } = useTranslation();
const showChineseLogos = ['chinese', 'chinese-tradition'].includes(
clientLocale
);
return (
<Container
fluid={true}
className='landing-top lading-top-c gradient-container'
>
<Container>
<Row className='landing-top-two-column'>
<Spacer size='m' />
<Col className='landing-top-left'>
<h1
id='content-start'
className='ultra-heading'
data-test-label='landing-header'
data-playwright-test-label='big-heading-1-b'
>
{t('landing.big-heading-1-b')}
</h1>
<p data-playwright-test-label='advance-career'>
{t('landing.advance-career')}
</p>
<Spacer size='m' />
<BigCallToAction />
<Spacer size='m' />
</Col>
<Col className='landing-top-right'>
<CampersImage />
<Spacer size='m' />
</Col>
</Row>
</Container>
<Container>
<Row>
<Col sm={10} smOffset={1} xs={12} className='brands-container'>
<Spacer size='l' />
<p data-playwright-test-label='graduates-work'>
<Trans>landing.graduates-work</Trans>
</p>
<Spacer size='s' />
<div
className='logo-row'
data-playwright-test-label='brand-logo-container'
>
<AppleLogo />
<GoogleLogo />
<MicrosoftLogo />
{showChineseLogos ? (
<>
<TencentLogo />
<AlibabaLogo />
</>
) : (
<>
<SpotifyLogo />
<AmazonLogo />
</>
)}
</div>
<Spacer size='m' />
</Col>
</Row>
</Container>
</Container>
);
}
LandingTop.displayName = 'LandingTop';
export default LandingTop;

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { useFeature } from '@growthbook/growthbook-react';
import { Container, Col, Row, Spacer } from '@freecodecamp/ui';
import { clientLocale } from '../../../../config/env.json';
import {
@@ -12,78 +13,78 @@ import {
AlibabaLogo
} from '../../../assets/images/components';
import BigCallToAction from './big-call-to-action';
import TwoButtonCTA from './two-button-cta';
import CampersImage from './campers-image';
const LogoRow = (): JSX.Element => {
function LandingTop(): JSX.Element {
const { t } = useTranslation();
const showTwoButtonCTA = useFeature('landing-two-button-cta').on;
const showChineseLogos = ['chinese', 'chinese-tradition'].includes(
clientLocale
);
return (
<>
<p className='logo-row-title' data-playwright-test-label='h2-heading'>
<Trans>landing.h2-heading</Trans>
</p>
<div
className='logo-row'
data-playwright-test-label='brand-logo-container'
>
<AppleLogo />
<GoogleLogo />
<MicrosoftLogo />
{showChineseLogos ? (
<>
<TencentLogo />
<AlibabaLogo />
</>
) : (
<>
<SpotifyLogo />
<AmazonLogo />
</>
)}
</div>
</>
);
};
function LandingTop(): JSX.Element {
const { t } = useTranslation();
return (
<Container fluid={true} className='gradient-container'>
<Container className='landing-top'>
<Spacer size='m' />
<Row>
<Col lg={8} lgOffset={2} sm={10} smOffset={1} xs={12}>
<Container
fluid={true}
className='landing-top lading-top-c gradient-container'
>
<Container>
<Row className='landing-top-two-column' data-test-label='landing-top'>
<Spacer size='m' />
<Col className='landing-top-left'>
<h1
id='content-start'
className='mega-heading'
className='ultra-heading'
data-test-label='landing-header'
data-playwright-test-label='landing-big-heading-1'
data-playwright-test-label='big-heading-1-b'
>
{t('landing.big-heading-1')}
{t('landing.big-heading-1-b')}
</h1>
<p
className='mega-heading'
data-playwright-test-label='landing-big-heading-2'
>
{t('landing.big-heading-2')}
<p data-playwright-test-label='advance-career'>
{t('landing.advance-career')}
</p>
<p
className='mega-heading'
data-playwright-test-label='landing-big-heading-3'
>
{t('landing.big-heading-3')}
</p>
<LogoRow />
<Spacer size='m' />
<BigCallToAction />
{showTwoButtonCTA ? (
<TwoButtonCTA />
) : (
<BigCallToAction testLabel='landing-top-big-cta' />
)}
<Spacer size='m' />
</Col>
<Col className='landing-top-right'>
<CampersImage />
<Spacer size='m' />
</Col>
</Row>
</Container>
<Container>
<Row>
<Col lg={8} lgOffset={2} sm={10} smOffset={1} xs={12}>
<CampersImage />
<Col sm={10} smOffset={1} xs={12} className='brands-container'>
<Spacer size='l' />
<p data-playwright-test-label='graduates-work'>
<Trans>landing.graduates-work</Trans>
</p>
<Spacer size='s' />
<div
className='logo-row'
data-playwright-test-label='brand-logo-container'
>
<AppleLogo />
<GoogleLogo />
<MicrosoftLogo />
{showChineseLogos ? (
<>
<TencentLogo />
<AlibabaLogo />
</>
) : (
<>
<SpotifyLogo />
<AmazonLogo />
</>
)}
</div>
<Spacer size='m' />
</Col>
</Row>

View File

@@ -0,0 +1,70 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { Provider } from 'react-redux';
import { describe, expect, test, vi } from 'vitest';
import { createStore } from '../../../redux/create-store';
import TwoButtonCTA from './two-button-cta';
vi.mock('../../../utils/get-words');
const renderWithStore = ({ isSignedIn }: { isSignedIn: boolean }) => {
const preloadedState = {
app: {
user: {
sessionUser: isSignedIn ? { id: 'user-id', username: 'fcc-user' } : null
}
}
};
const store = createStore(preloadedState);
render(
<Provider store={store}>
<TwoButtonCTA />
</Provider>
);
const googleCta = screen.getByRole('link', {
name: 'buttons.sign-in-with-google'
});
const moreWaysCta = screen.getByRole('link', {
name: 'buttons.more-ways-to-sign-in'
});
return { googleCta, moreWaysCta };
};
describe('Stacked landing CTA', () => {
test('renders Google and More ways CTAs', () => {
const { googleCta, moreWaysCta } = renderWithStore({ isSignedIn: false });
expect(googleCta).toBeInTheDocument();
expect(moreWaysCta).toBeInTheDocument();
});
test('links go to API when signed out', () => {
const { googleCta, moreWaysCta } = renderWithStore({ isSignedIn: false });
expect(googleCta).toHaveAttribute(
'href',
expect.stringMatching(/\/signin\/google$/)
);
expect(moreWaysCta).toHaveAttribute(
'href',
expect.stringMatching(/\/signin$/)
);
});
test('links go to learn when signed in', () => {
const { googleCta, moreWaysCta } = renderWithStore({ isSignedIn: true });
expect(googleCta).toHaveAttribute(
'href',
expect.stringMatching(/\/learn$/)
);
expect(moreWaysCta).toHaveAttribute(
'href',
expect.stringMatching(/\/learn$/)
);
});
});

View File

@@ -0,0 +1,98 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import envData from '../../../../config/env.json';
import { isSignedInSelector } from '../../../redux/selectors';
import callGA from '../../../analytics/call-ga';
const { apiLocation, homeLocation } = envData as {
apiLocation: string;
homeLocation: string;
};
interface TwoButtonCTAProps {
isSignedIn?: boolean;
}
const TwoButtonCTA = ({ isSignedIn }: TwoButtonCTAProps): JSX.Element => {
const { t } = useTranslation();
const handleGoogleClick = () => callGA({ event: 'sign_in' });
const handleMoreWaysClick = () => callGA({ event: 'sign_in' });
const googleHref = isSignedIn
? `${homeLocation}/learn`
: `${apiLocation}/signin/google`;
const moreWaysHref = isSignedIn
? `${homeLocation}/learn`
: `${apiLocation}/signin`;
return (
<div className='two-button-cta'>
<a
className='signup-btn btn btn-sm btn-block google-btn'
data-test-label='landing-google-cta'
data-playwright-test-label='landing-google-cta'
href={googleHref}
onClick={handleGoogleClick}
>
<span className='google-btn-icon' aria-hidden='true'>
{/* Google "G" logo box per brand guidelines */}
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 48 48'
width='18'
height='18'
>
<path
fill='#EA4335'
d='M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.91-6.91C35.9 2.3 30.37 0 24 0 14.62 0 6.51 5.38 2.56 13.22l8.9 6.91C13.42 14.83 18.27 9.5 24 9.5z'
/>
<path
fill='#4285F4'
d='M46.5 24c0-1.64-.15-3.2-.44-4.71H24v8.92h12.65c-.55 2.96-2.2 5.47-4.71 7.17l7.23 5.61C43.9 36.78 46.5 30.86 46.5 24z'
/>
<path
fill='#FBBC05'
d='M11.46 28.13A14.5 14.5 0 0 1 10.5 24c0-1.46.25-2.87.7-4.19l-8.9-6.91A23.93 23.93 0 0 0 0 24c0 3.91.94 7.6 2.6 10.86l8.86-6.73z'
/>
<path
fill='#34A853'
d='M24 48c6.48 0 11.93-2.14 15.9-5.86l-7.23-5.61c-2 1.35-4.62 2.13-8.67 2.13-5.73 0-10.58-5.33-12.53-10.63l-8.9 6.91C6.51 42.62 14.62 48 24 48z'
/>
<path fill='none' d='M0 0h48v48H0z' />
</svg>
</span>
<span>{t('buttons.sign-in-with-google')}</span>
</a>
<div className='cta-divider' role='separator' aria-label='or'>
<span className='cta-divider-line' />
<span>{t('misc.or')}</span>
<span className='cta-divider-line' />
</div>
<a
className='signup-btn btn btn-sm btn-block more-ways-btn'
data-test-label='landing-more-ways-cta'
data-playwright-test-label='landing-more-ways-cta'
href={moreWaysHref}
onClick={handleMoreWaysClick}
>
<span className='login-btn-text'>
{t('buttons.more-ways-to-sign-in')}
</span>
</a>
</div>
);
};
TwoButtonCTA.displayName = 'TwoButtonCTA';
const mapStateToProps = createSelector(isSignedInSelector, isSignedIn => ({
isSignedIn
}));
export default connect(mapStateToProps)(TwoButtonCTA);

View File

@@ -217,7 +217,6 @@ figcaption.caption {
}
}
/* AB testing styles */
.landing-top .mega-heading {
font-size: 2.2rem;
margin: 0px 0px 2rem;
@@ -230,6 +229,109 @@ figcaption.caption {
padding: 12px 20px;
}
.two-button-cta {
display: grid;
gap: 10px;
max-width: 500px;
}
@media (max-width: 1200px) {
.two-button-cta {
margin: 0 auto;
}
}
.google-btn {
align-items: center;
background: #fff;
border: 3px solid #ffffff;
color: #000;
display: inline-flex;
font-family: var(--font-family-sans-serif);
font-size: 1rem;
min-height: 48px;
padding: 10px 16px;
font-weight: 600;
letter-spacing: 0.01em;
}
.google-btn:hover,
.google-btn:focus {
background: #000;
border-color: #ffffff;
color: #fff;
}
.light-palette .google-btn {
background: #000;
border-color: #000000;
color: #fff;
}
.light-palette .google-btn:hover,
.light-palette .google-btn:focus {
background: #ffffff;
border-color: #000000;
color: #000000;
text-decoration: none;
}
.google-btn svg {
display: block;
height: 18px;
width: 18px;
margin: 0 10px;
}
.google-btn-icon {
border-radius: 2px;
display: inline-flex;
align-items: center;
justify-content: center;
height: 20px;
min-width: 20px;
padding-inline-end: 4px;
position: relative;
}
.more-ways-btn {
align-items: center;
background-image: linear-gradient(#fecc4c, #ffac33);
border: 3px solid #feac32;
color: #0a0a23;
display: inline-flex;
font-weight: 600;
justify-content: center;
min-height: 48px;
padding: 12px 20px;
text-decoration: none;
font-size: 1.1rem;
}
.more-ways-btn:hover,
.more-ways-btn:focus {
background-color: #fecc4c;
background-image: none;
border-color: #f1a02a;
color: #0a0a23;
text-decoration: none;
}
.cta-divider {
display: flex;
align-items: center;
gap: 8px;
color: var(--secondary-color);
text-transform: lowercase;
font-size: 0.95rem;
margin-block: 12px;
width: 100%;
}
.cta-divider-line {
height: 1px;
background: none;
border-top: 1px dashed var(--gray-45);
flex: 1 1 auto;
}
@media (min-width: 500px) {
.landing-top .mega-heading {
font-size: 3rem;

View File

@@ -3,7 +3,6 @@ import { useTranslation } from 'react-i18next';
import { useGrowthBook } from '@growthbook/growthbook-react';
import SEO from '../components/seo';
import { Loader } from '../components/helpers';
import LandingTopB from '../components/landing/components/landing-top-b';
import LandingTop from '../components/landing/components/landing-top';
import Testimonials from '../components/landing/components/testimonials';
import Certifications from '../components/landing/components/certifications';
@@ -11,17 +10,13 @@ import Faq from '../components/landing/components/faq';
import Benefits from '../components/landing/components/benefits';
import '../components/landing/landing.css';
type LandingProps = {
showLandingPageRedesign: boolean;
};
const Landing = ({ showLandingPageRedesign }: LandingProps) => (
const Landing = () => (
<main
id='landing-content'
data-testid='landing-content'
className={`landing-page`}
>
{showLandingPageRedesign ? <LandingTopB /> : <LandingTop />}
<LandingTop />
<Benefits />
<Testimonials />
<Certifications />
@@ -33,20 +28,14 @@ function IndexPage(): JSX.Element {
const { t } = useTranslation();
const growthbook = useGrowthBook();
if (growthbook && growthbook.ready) {
console.error('GrowthBook Ready', growthbook);
const showLandingPageRedesign = growthbook.getFeatureValue(
'landing-top-skill-focused',
false
);
growthbook.getFeatureValue('landing-aa-test', false);
return (
<>
<SEO title={t('metaTags:title')} />
<Landing showLandingPageRedesign={showLandingPageRedesign} />
<Landing />
</>
);
} else {
console.error('GrowthBook not ready yet', growthbook);
return (
<>
<SEO title={t('metaTags:title')} />

View File

@@ -15,7 +15,7 @@ const headerComponentElements = {
languageButton: 'header-lang-list-option',
menuButton: 'header-menu-button',
menu: 'header-menu',
signInButton: 'header-sign-in-button'
signInButton: 'sign-in-button'
} as const;
const examUrl =

View File

@@ -6,13 +6,15 @@ import { addGrowthbookCookie } from './utils/add-growthbook-cookie';
const landingPageElements = {
heading: 'landing-header',
callToAction: 'landing-big-cta',
certifications: 'certifications',
curriculumBtns: 'curriculum-map-button',
testimonials: 'testimonial-card',
landingPageImage: 'landing-page-figure',
faq: 'landing-page-faq',
jobs: 'More than <strong>100,000</strong> freeCodeCamp.org graduates have gotten <strong>jobs</strong> at tech companies including:'
jobs: 'More than <strong>100,000</strong> freeCodeCamp.org graduates have gotten <strong>jobs</strong> at tech companies including:',
googleCTA: 'landing-google-cta',
moreWaysCTA: 'landing-more-ways-cta',
landingTopCta: 'landing-top-big-cta'
} as const;
const nonArchivedSuperBlocks = [
@@ -30,11 +32,68 @@ async function goToLandingPage(page: Page) {
await page.goto('/');
}
test.describe('Landing Top - Variation B', () => {
test.describe('Main CTA - Variation A', () => {
test.beforeEach(async ({ context, page }) => {
await addGrowthbookCookie({ context, variation: 'A' });
await goToLandingPage(page);
});
test('Five main CTAs render correctly', async ({ page }) => {
const landingTopCta = page.getByTestId(landingPageElements.landingTopCta);
const googleCTA = page.getByTestId(landingPageElements.googleCTA);
const moreWaysCTA = page.getByTestId(landingPageElements.moreWaysCTA);
const ctas = page.getByRole('link', {
name: translations.buttons['logged-in-cta-btn']
});
const benefitsCtas = page.getByRole('link', {
name: translations.landing.benefits.cta
});
await expect(benefitsCtas).toHaveCount(1);
await expect(landingTopCta).toHaveText(
translations.buttons['logged-in-cta-btn']
);
await expect(ctas).toHaveCount(4);
for (const cta of await ctas.all()) {
await expect(cta).toBeVisible();
}
await expect(googleCTA).toBeHidden();
await expect(moreWaysCTA).toBeHidden();
});
});
test.describe('Main CTA - Variation B', () => {
test.beforeEach(async ({ context, page }) => {
await addGrowthbookCookie({ context, variation: 'B' });
await goToLandingPage(page);
});
test('Four main and two stacked CTAs render correctly', async ({ page }) => {
const landingTopCta = page.getByTestId(landingPageElements.landingTopCta);
const googleCTA = page.getByTestId(landingPageElements.googleCTA);
const moreWaysCTA = page.getByTestId(landingPageElements.moreWaysCTA);
const ctas = page.getByRole('link', {
name: translations.buttons['logged-in-cta-btn']
});
const benefitsCtas = page.getByRole('link', {
name: translations.landing.benefits.cta
});
await expect(benefitsCtas).toHaveCount(1);
await expect(landingTopCta).toBeHidden();
await expect(ctas).toHaveCount(3);
for (const cta of await ctas.all()) {
await expect(cta).toBeVisible();
}
await expect(googleCTA).toHaveText(
translations.buttons['sign-in-with-google']
);
await expect(moreWaysCTA).toHaveText(
translations.buttons['more-ways-to-sign-in']
);
});
});
test.describe('Landing Page', () => {
test.beforeEach(async ({ page }) => {
await goToLandingPage(page);
});
test('Main heading copy renders correctly', async ({ page }) => {
const bigHeading = page.getByTestId('big-heading-1-b');
@@ -54,61 +113,6 @@ test.describe('Landing Top - Variation B', () => {
translations.landing['graduates-work'].replace(/<\/?strong>/g, '')
);
});
});
/*
*
* not currently in use after https://github.com/freeCodeCamp/freeCodeCamp/pull/61359
* bring back after we fix GB
*/
// test.describe('Landing Top - Variation A', () => {
// test.beforeEach(async ({ context, page }) => {
// await addGrowthbookCookie({ context, variation: 'newA' });
// await goToLandingPage(page);
// });
// test('The headline renders correctly', async ({ page }) => {
// const landingHeading1 = page.getByTestId('landing-big-heading-1');
// await expect(landingHeading1).toHaveText(
// translations.landing['big-heading-1']
// );
// const landingHeading2 = page.getByTestId('landing-big-heading-2');
// await expect(landingHeading2).toHaveText(
// translations.landing['big-heading-2']
// );
// const landingHeading3 = page.getByTestId('landing-big-heading-3');
// await expect(landingHeading3).toHaveText(
// translations.landing['big-heading-3']
// );
// });
// test('Logo row copy renders correctly', async ({ page }) => {
// const landingH2Heading = page.getByTestId('h2-heading');
// await expect(landingH2Heading).toHaveText(
// translations.landing['h2-heading'].replace(/<\/?strong>/g, '')
// );
// });
// test('Hero image should have a description', async ({ isMobile, page }) => {
// const captionText = page.getByText(
// translations.landing['hero-img-description']
// );
// if (isMobile) {
// await expect(captionText).toBeHidden();
// } else {
// await expect(captionText).toBeVisible();
// }
// });
// });
test.describe('Landing Page', () => {
test.beforeEach(async ({ page }) => {
await goToLandingPage(page);
});
test('The component Why learn with freeCodeCamp renders correctly', async ({
context,
@@ -123,16 +127,6 @@ test.describe('Landing Page', () => {
await expect(h2Element).toBeVisible();
});
test('Call to action buttons should render correctly', async ({ page }) => {
const ctas = page.getByRole('link', {
name: translations.buttons['logged-in-cta-btn']
});
await expect(ctas).toHaveCount(4);
for (const cta of await ctas.all()) {
await expect(cta).toBeVisible();
}
});
test('Hero image should have an alt', async ({ isMobile, page }) => {
const campersImage = page.getByAltText(
translations.landing['hero-img-alt']