diff --git a/e2e/completion-modal.spec.ts b/e2e/completion-modal.spec.ts index 65b9494c2bb..063639d87d0 100644 --- a/e2e/completion-modal.spec.ts +++ b/e2e/completion-modal.spec.ts @@ -1,6 +1,7 @@ import { test, expect } from '@playwright/test'; import translations from '../client/i18n/locales/english/translations.json'; import { authedRequest } from './utils/request'; +import { allowTrailingSlash } from './utils/url'; const nextChallengeURL = '/learn/data-analysis-with-python/data-analysis-with-python-projects/demographic-data-analyzer'; @@ -72,7 +73,7 @@ test.describe('Challenge Completion Modal Tests (Signed Out)', () => { await page .getByRole('link', { name: translations.learn['sign-in-save'] }) .click(); - await expect(page).toHaveURL(/.*\/learn\/?$/); + await expect(page).toHaveURL(allowTrailingSlash('/learn')); }); test('should redirect to next challenge', async ({ page }) => { diff --git a/e2e/delete-modal.spec.ts b/e2e/delete-modal.spec.ts index b58b098418b..7d060dc44be 100644 --- a/e2e/delete-modal.spec.ts +++ b/e2e/delete-modal.spec.ts @@ -5,6 +5,7 @@ import { test, expect } from '@playwright/test'; import translations from '../client/i18n/locales/english/translations.json'; import { alertToBeVisible } from './utils/alerts'; +import { allowTrailingSlash } from './utils/url'; const execP = promisify(exec); @@ -145,7 +146,7 @@ test.describe('Delete Modal component', () => { }) ).not.toBeVisible(); - await expect(page).toHaveURL(/.*\/learn\/?/); + await expect(page).toHaveURL(allowTrailingSlash('/learn')); await alertToBeVisible(page, translations.flash['account-deleted']); // The user is signed out after their account is deleted await expect(page.getByRole('link', { name: 'Sign in' })).toHaveCount(2); diff --git a/e2e/donation-modal.spec.ts b/e2e/donation-modal.spec.ts index 3ad2f31d556..a5c42d87516 100644 --- a/e2e/donation-modal.spec.ts +++ b/e2e/donation-modal.spec.ts @@ -3,6 +3,7 @@ import { test, expect, type Page } from '@playwright/test'; import { addGrowthbookCookie } from './utils/add-growthbook-cookie'; import { clearEditor, focusEditor } from './utils/editor'; +import { allowTrailingSlash } from './utils/url'; const slowExpect = expect.configure({ timeout: 25000 }); @@ -22,7 +23,9 @@ const completeFrontEndCert = async (page: Page, number?: number) => { const loopNumber = number || projects.length; for (let i = 0; i < loopNumber; i++) { await page.waitForURL( - `/learn/front-end-development-libraries/front-end-development-libraries-projects/build-a-${projects[i]}` + allowTrailingSlash( + `/learn/front-end-development-libraries/front-end-development-libraries-projects/build-a-${projects[i]}` + ) ); await page .getByRole('textbox', { name: 'solution' }) @@ -92,7 +95,7 @@ const completeChallenges = async ({ }) => { await page.goto(challenges[0].url); for (const challenge of challenges.slice(0, number)) { - await page.waitForURL(challenge.url); + await page.waitForURL(allowTrailingSlash(challenge.url)); await focusEditor({ page, isMobile }); await clearEditor({ page, browserName }); await page.evaluate( diff --git a/e2e/quincy-email-sign-up.spec.ts b/e2e/quincy-email-sign-up.spec.ts index f21431bbc50..1ee51c19aed 100644 --- a/e2e/quincy-email-sign-up.spec.ts +++ b/e2e/quincy-email-sign-up.spec.ts @@ -3,6 +3,7 @@ import { execSync } from 'child_process'; import { test, expect } from '@playwright/test'; import translations from '../client/i18n/locales/english/translations.json'; +import { allowTrailingSlash } from './utils/url'; const apiLocation = process.env.API_LOCATION || 'http://localhost:3000'; @@ -73,7 +74,7 @@ test.describe('Email sign-up page when user is not signed in', () => { // The user is signed in and automatically redirected to /learn after clicking the button. // We wait for this navigation to complete before moving onto the next. - await page.waitForURL('**/learn'); + await page.waitForURL(allowTrailingSlash('/learn')); await expect( page.getByRole('heading', { name: 'Welcome back, Full Stack User' }) ).toBeVisible(); @@ -144,7 +145,7 @@ test.describe('Email sign-up page when user is signed in', () => { // The user is signed in and automatically redirected to /learn after clicking the button. // We wait for the navigation to complete. - await page.waitForURL('**/learn'); + await page.waitForURL('/learn'); await expect( page.getByRole('heading', { name: 'Welcome back, Full Stack User' }) ).toBeVisible(); @@ -180,7 +181,7 @@ test.describe('Email sign-up page when user is signed in', () => { // The user is signed in and automatically redirected to /learn after clicking the button. // We wait for the navigation to complete. - await page.waitForURL('**/learn'); + await page.waitForURL('/learn'); await expect( page.getByRole('heading', { name: 'Welcome back, Full Stack User' }) ).toBeVisible(); diff --git a/e2e/quiz-challenge.spec.ts b/e2e/quiz-challenge.spec.ts index 5263910feca..b0d333990ff 100644 --- a/e2e/quiz-challenge.spec.ts +++ b/e2e/quiz-challenge.spec.ts @@ -1,4 +1,5 @@ import { test, expect } from '@playwright/test'; +import { allowTrailingSlash } from './utils/url'; test.describe('Quiz challenge', () => { test.beforeEach(async ({ page }) => { @@ -66,7 +67,9 @@ test.describe('Quiz challenge', () => { // The navigation should be blocked, the user should stay on the same page await expect(page).toHaveURL( - '/learn/full-stack-developer/quiz-basic-html/quiz-basic-html' + allowTrailingSlash( + '/learn/full-stack-developer/quiz-basic-html/quiz-basic-html' + ) ); await expect(page.getByRole('dialog')).toBeVisible(); diff --git a/e2e/redirect.spec.ts b/e2e/redirect.spec.ts index 3c15b80cea9..4b349504a71 100644 --- a/e2e/redirect.spec.ts +++ b/e2e/redirect.spec.ts @@ -1,4 +1,5 @@ import { test, expect } from '@playwright/test'; +import { allowTrailingSlash } from './utils/url'; // To run this test locally you will need to run: pnpm run start-ci; // Also, make sure that you have pm2 installed globally via: pnpm install -g pm2 @@ -74,7 +75,7 @@ test.describe('Legacy Challenge Path Redirection Tests', () => { for (const [input, expected] of pathsToTest) { test(`should redirect from ${input} to ${expected}`, async ({ page }) => { await page.goto(input); - await expect(page).toHaveURL(new RegExp(`${expected}/?`)); + await expect(page).toHaveURL(allowTrailingSlash(expected)); }); } }); diff --git a/e2e/signout-modal.spec.ts b/e2e/signout-modal.spec.ts index 9f9e30126fe..4c90cc8542c 100644 --- a/e2e/signout-modal.spec.ts +++ b/e2e/signout-modal.spec.ts @@ -1,5 +1,6 @@ import { test, expect } from '@playwright/test'; import translations from '../client/i18n/locales/english/translations.json'; +import { allowTrailingSlash } from './utils/url'; test.beforeEach(async ({ page }) => { await page.goto('/'); @@ -49,7 +50,7 @@ test.describe('Signout Modal component', () => { await expect( page.getByRole('dialog', { name: translations.signout.heading }) ).not.toBeVisible(); - await expect(page).toHaveURL(/.*\/learn\/?$/); + await expect(page).toHaveURL(allowTrailingSlash('/learn')); }); test('closes modal after user cancels signing out', async ({ page }) => { diff --git a/e2e/update-email.spec.ts b/e2e/update-email.spec.ts index 43435001f7d..5c87322bbb7 100644 --- a/e2e/update-email.spec.ts +++ b/e2e/update-email.spec.ts @@ -1,5 +1,6 @@ import { test, expect } from '@playwright/test'; import translations from '../client/i18n/locales/english/translations.json'; +import { allowTrailingSlash } from './utils/url'; test.describe('The update-email page when the user is signed in', () => { test.beforeEach(async ({ page }) => { @@ -64,7 +65,7 @@ test.describe('The update-email page when the user is not signed in', () => { // Ref: https://github.com/microsoft/playwright/issues/20749 test.skip(browserName === 'firefox'); - await page.waitForURL(/\/learn\/?/); + await page.waitForURL(allowTrailingSlash('/learn')); await expect( page.getByRole('heading', { name: 'Welcome back, Full Stack User' }) diff --git a/e2e/utils/url.ts b/e2e/utils/url.ts new file mode 100644 index 00000000000..053061055e1 --- /dev/null +++ b/e2e/utils/url.ts @@ -0,0 +1 @@ +export const allowTrailingSlash = (url: string) => RegExp(url + '[/]?$');