feat(client): add settings side nav (#63034)

Co-authored-by: Huyen Nguyen <25715018+huyenltnguyen@users.noreply.github.com>
Co-authored-by: ahmad abdolsaheb <ahmad.abdolsaheb@gmail.com>
This commit is contained in:
Sem Bauke
2025-12-03 10:28:20 +01:00
committed by GitHub
parent ebf5a8463e
commit e056608d82
12 changed files with 546 additions and 135 deletions

View File

@@ -0,0 +1,61 @@
import { test, expect } from '@playwright/test';
test.use({ storageState: 'playwright/.auth/certified-user.json' });
test.beforeEach(async ({ page }) => {
// Set viewport to desktop size to ensure sideNav is visible
await page.setViewportSize({ width: 1280, height: 720 });
await page.goto('/settings');
// Wait for the main heading to appear
await expect(
page.getByRole('heading', { level: 1, name: 'Settings for certifieduser' })
).toBeVisible();
});
test.describe('Settings SideNav Component', () => {
test('should display the settings sideNav with links to all main sections', async ({
page,
isMobile
}) => {
test.setTimeout(30000);
test.skip(isMobile, 'Sidebar is hidden on mobile');
const sideNav = page.getByRole('complementary');
const main = page.getByRole('main');
// Get all h2 and h3 heading in the main section
const h2Texts = await main
.getByRole('heading', { level: 2 })
.allTextContents();
const h3Texts = await main
.getByRole('heading', { level: 3 })
.allTextContents();
const headingTexts = [...h2Texts, ...h3Texts];
// Make sure the sideNav contains the same number of links as headings
const sideNavLinks = sideNav.getByRole('link');
await expect(sideNavLinks).toHaveCount(headingTexts.length);
// For each heading text, find the link by accessible name and test click behavior
for (const headingText of headingTexts) {
const link = sideNav.getByRole('link', {
name: headingText,
exact: true
});
await expect(link).toBeVisible();
// Get the href and assert the URL ends with it after click
const href = await link.getAttribute('href');
await link.click();
// Wait for scroll animation
// Playwright performs click very fast, which could lead to URL check before scroll ends
await page.waitForTimeout(300);
await expect(page).toHaveURL(new RegExp(href + '$'));
}
});
});

View File

@@ -232,7 +232,7 @@ test.describe('Settings - Certified User', () => {
}
// Danger Zone
await expect(page.getByText('Danger Zone')).toBeVisible();
await expect(page.getByRole('main').getByText('Danger Zone')).toBeVisible();
await expect(
page.getByText(
'Please be careful. Changes in this section are permanent.'

View File

@@ -18,7 +18,7 @@ test.describe('Initially', () => {
test('should not render', async ({ page }) => {
await page.goto('/settings');
await expect(
page.getByText('User Token', { exact: true })
page.getByRole('main').getByText('User Token', { exact: true })
).not.toBeVisible();
});
});
@@ -32,7 +32,9 @@ test.describe('After creating token', () => {
await page.goto('/settings');
// Set `exact` to `true` to only match the panel heading
await expect(page.getByText('User Token', { exact: true })).toBeVisible();
await expect(
page.getByRole('main').getByText('User Token', { exact: true })
).toBeVisible();
await expect(
page.getByText(
'Your user token is used to save your progress on curriculum sections that use a virtual machine. If you suspect it has been compromised, you can delete it without losing any progress. A new one will be created automatically the next time you open a project.'
@@ -42,7 +44,7 @@ test.describe('After creating token', () => {
await alertToBeVisible(page, translations.flash['token-deleted']);
await expect(
page.getByText('User Token', { exact: true })
page.getByRole('main').getByText('User Token', { exact: true })
).not.toBeVisible();
});
});