From e447e4037b11919f15fc9f32741ba7d31ca125c8 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 22 Feb 2025 17:13:12 +1100 Subject: [PATCH] UI cleanup (#9140) * Refactor SecurityContext page: - Add Accordion to separate different groups - Fix "make primary" button (requires PATCH) - Responsive grid design * Add splash screen background to more pages * Adds playwright testing for email setup * Refactoring * Fix playwright tests --- src/frontend/src/components/SplashScreen.tsx | 24 ++ src/frontend/src/functions/auth.tsx | 2 +- .../src/pages/Auth/ChangePassword.tsx | 91 +++-- src/frontend/src/pages/Auth/Login.tsx | 25 +- src/frontend/src/pages/Auth/MFALogin.tsx | 72 ++-- .../AccountSettings/SecurityContent.tsx | 375 ++++++++++-------- src/frontend/tests/helpers.ts | 11 + src/frontend/tests/pages/pui_build.spec.ts | 23 +- src/frontend/tests/pages/pui_company.spec.ts | 16 +- src/frontend/tests/pages/pui_part.spec.ts | 81 ++-- .../tests/pages/pui_purchase_order.spec.ts | 40 +- .../tests/pages/pui_sales_order.spec.ts | 59 +-- src/frontend/tests/pages/pui_stock.spec.ts | 27 +- src/frontend/tests/pui_plugins.spec.ts | 11 +- src/frontend/tests/pui_printing.spec.ts | 8 +- src/frontend/tests/pui_settings.spec.ts | 82 ++-- 16 files changed, 517 insertions(+), 430 deletions(-) create mode 100644 src/frontend/src/components/SplashScreen.tsx diff --git a/src/frontend/src/components/SplashScreen.tsx b/src/frontend/src/components/SplashScreen.tsx new file mode 100644 index 0000000000..79bb9992ac --- /dev/null +++ b/src/frontend/src/components/SplashScreen.tsx @@ -0,0 +1,24 @@ +import { BackgroundImage } from '@mantine/core'; +import { generateUrl } from '../functions/urls'; +import { useServerApiState } from '../states/ApiState'; + +/** + * Render content within a "splash screen" container. + */ +export default function SplashScreen({ + children +}: { + children: React.ReactNode; +}) { + const [server] = useServerApiState((state) => [state.server]); + + if (server.customize?.splash) { + return ( + + {children} + + ); + } else { + return <>{children}; + } +} diff --git a/src/frontend/src/functions/auth.tsx b/src/frontend/src/functions/auth.tsx index 788f205a86..c94b3cec09 100644 --- a/src/frontend/src/functions/auth.tsx +++ b/src/frontend/src/functions/auth.tsx @@ -342,7 +342,7 @@ export async function ProviderLogin( export function authApi( url: string, config: AxiosRequestConfig | undefined = undefined, - method: 'get' | 'post' | 'put' | 'delete' = 'get', + method: 'get' | 'patch' | 'post' | 'put' | 'delete' = 'get', data?: any ) { const requestConfig = config || {}; diff --git a/src/frontend/src/pages/Auth/ChangePassword.tsx b/src/frontend/src/pages/Auth/ChangePassword.tsx index 4e2087501c..0c2d47a329 100644 --- a/src/frontend/src/pages/Auth/ChangePassword.tsx +++ b/src/frontend/src/pages/Auth/ChangePassword.tsx @@ -15,6 +15,7 @@ import { notifications } from '@mantine/notifications'; import { useNavigate } from 'react-router-dom'; import { api } from '../../App'; +import SplashScreen from '../../components/SplashScreen'; import { StylishText } from '../../components/items/StylishText'; import { ProtectedRoute } from '../../components/nav/Layout'; import { LanguageContext } from '../../contexts/LanguageContext'; @@ -104,49 +105,53 @@ export default function Set_Password() { return ( -
- - - {t`Reset Password`} - - {user.username() && ( - - - {t`User`} - {user.username()} - - - )} - - - - - - - - - -
+ +
+ + + + {t`Reset Password`} + + {user.username() && ( + + + {t`Username`} + {user.username()} + + + )} + + + + + + + + + + +
+
); diff --git a/src/frontend/src/pages/Auth/Login.tsx b/src/frontend/src/pages/Auth/Login.tsx index a4dba6c4ff..67a032ab03 100644 --- a/src/frontend/src/pages/Auth/Login.tsx +++ b/src/frontend/src/pages/Auth/Login.tsx @@ -1,16 +1,10 @@ import { t } from '@lingui/macro'; -import { - BackgroundImage, - Center, - Container, - Divider, - Paper, - Text -} from '@mantine/core'; +import { Center, Container, Divider, Paper, Text } from '@mantine/core'; import { useDisclosure, useToggle } from '@mantine/hooks'; import { useEffect, useMemo } from 'react'; import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'; import { setApiDefaults } from '../../App'; +import SplashScreen from '../../components/SplashScreen'; import { AuthFormOptions } from '../../components/forms/AuthFormOptions'; import { AuthenticationForm, @@ -25,7 +19,6 @@ import { doBasicLogin, followRedirect } from '../../functions/auth'; -import { generateUrl } from '../../functions/urls'; import { useServerApiState } from '../../states/ApiState'; import { useLocalState } from '../../states/LocalState'; @@ -73,16 +66,6 @@ export default function Login() { return null; }, [server.customize]); - const SplashComponent = useMemo(() => { - const temp = server.customize?.splash; - if (temp) { - return ({ children }: { children: React.ReactNode }) => ( - {children} - ); - } - return ({ children }: { children: React.ReactNode }) => <>{children}; - }, [server.customize]); - // Data manipulation functions function ChangeHost(newHost: string | null): void { if (newHost === null) return; @@ -120,7 +103,7 @@ export default function Login() { // Main rendering block return ( - +
-
+ ); } diff --git a/src/frontend/src/pages/Auth/MFALogin.tsx b/src/frontend/src/pages/Auth/MFALogin.tsx index 4a832db3c0..7bed8b29f1 100644 --- a/src/frontend/src/pages/Auth/MFALogin.tsx +++ b/src/frontend/src/pages/Auth/MFALogin.tsx @@ -3,14 +3,16 @@ import { Button, Center, Container, + Paper, Stack, - TextInput, - Title + TextInput } from '@mantine/core'; import { useForm } from '@mantine/form'; import { useLocation, useNavigate } from 'react-router-dom'; import { useState } from 'react'; +import SplashScreen from '../../components/SplashScreen'; +import { StylishText } from '../../components/items/StylishText'; import { LanguageContext } from '../../contexts/LanguageContext'; import { handleMfaLogin } from '../../functions/auth'; @@ -22,38 +24,40 @@ export default function MFALogin() { return ( -
- - - - <Trans>MFA Login</Trans> - - - - - - - -
+ +
+ + + + {t`Multi-Factor Login`} + + + + + + + +
+
); } diff --git a/src/frontend/src/pages/Index/Settings/AccountSettings/SecurityContent.tsx b/src/frontend/src/pages/Index/Settings/AccountSettings/SecurityContent.tsx index 1ef92b58d3..1d23e34503 100644 --- a/src/frontend/src/pages/Index/Settings/AccountSettings/SecurityContent.tsx +++ b/src/frontend/src/pages/Index/Settings/AccountSettings/SecurityContent.tsx @@ -1,5 +1,6 @@ import { Trans, t } from '@lingui/macro'; import { + Accordion, Alert, Badge, Button, @@ -9,11 +10,13 @@ import { Loader, Modal, Radio, + SimpleGrid, Stack, Table, Text, TextInput, - Title + Title, + Tooltip } from '@mantine/core'; import { useDisclosure } from '@mantine/hooks'; import { hideNotification, showNotification } from '@mantine/notifications'; @@ -43,91 +46,116 @@ export function SecurityContent() { return ( - - <Trans>Email Addresses</Trans> - - - - <Trans>Single Sign On</Trans> - - {sso_enabled() ? ( - - ) : ( - } - title={t`Not enabled`} - color='yellow' - > - Single Sign On is not enabled for this server - - )} - - <Trans>Multifactor authentication</Trans> - - - - <Trans>Access Tokens</Trans> - - + + + + {t`Email Addresses`} + + + + + + + + {t`Single Sign On`} + + + {sso_enabled() ? ( + + ) : ( + } + title={t`Not enabled`} + color='yellow' + > + Single Sign On is not enabled for this server + + )} + + + + + {t`Multi-Factor Authentication`} + + + + + + + + {t`Access Tokens`} + + + + + + ); } function EmailSection() { - const [value, setValue] = useState(''); + const [selectedEmail, setSelectedEmail] = useState(''); const [newEmailValue, setNewEmailValue] = useState(''); const { isLoading, data, refetch } = useQuery({ queryKey: ['emails'], queryFn: () => authApi(apiUrl(ApiEndpoints.auth_email)).then((res) => res.data.data) }); + const emailAvailable = useMemo(() => { return data == undefined || data.length == 0; }, [data]); function runServerAction( - action: 'post' | 'put' | 'delete' = 'post', + action: 'patch' | 'post' | 'put' | 'delete' = 'post', data?: any ) { - const vals: any = data || { email: value }; - return authApi( - apiUrl(ApiEndpoints.auth_email), - undefined, - action, - vals - ).then(() => { - refetch(); - }); + const vals: any = data || { email: selectedEmail }; + return authApi(apiUrl(ApiEndpoints.auth_email), undefined, action, vals) + .then(() => { + refetch(); + }) + .catch((err) => { + hideNotification('email-error'); + + showNotification({ + id: 'email-error', + title: t`Error`, + message: t`Error while updating email`, + color: 'red' + }); + }); } if (isLoading) return ; return ( - - - {emailAvailable ? ( - } - title={t`Not configured`} - color='yellow' - > - Currently no email addresses are registered. - - ) : ( - - - {data.map((email: any) => ( - - {email.email} + + {emailAvailable ? ( + } + title={t`Not Configured`} + color='yellow' + > + Currently no email addresses are registered. + + ) : ( + + + {data.map((email: any) => ( + + {email.email} + {email.primary && ( Primary @@ -143,53 +171,51 @@ function EmailSection() { )} - } - /> - ))} - - - )} - - - - - Add Email Address - - } - value={newEmailValue} - onChange={(event) => setNewEmailValue(event.currentTarget.value)} - /> - - - - - - - - - - + + } + /> + ))} + + + + + + + + )} + + {t`Add Email Address`} + } + aria-label='email-address-input' + value={newEmailValue} + onChange={(event) => setNewEmailValue(event.currentTarget.value)} + /> - - + + ); } @@ -263,7 +289,7 @@ function ProviderSection({ {data.length == 0 ? ( } - title={t`Not configured`} + title={t`Not Configured`} color='yellow' > There are no providers connected to this account. @@ -395,61 +421,61 @@ function MfaSection() { return ( <> - - - {data.length == 0 ? ( - } color='yellow'> - No factors configured - - ) : ( - - - - - Type - - - Last used at - - - Created at - - - Actions - - - - {rows} -
- )} -
- - - { - refetch(); - closeRecoveryCodes(); - }} - title={t`Recovery Codes`} - centered + + {data.length == 0 ? ( + } + color='yellow' > - - <Trans>Unused Codes</Trans> - - {recoveryCodes?.unused_codes?.join('\n')} + No multi-factor tokens configured for this account + + ) : ( + + + + + Type + + + Last used at + + + Created at + + + Actions + + + + {rows} +
+ )} + + { + refetch(); + closeRecoveryCodes(); + }} + title={t`Recovery Codes`} + centered + > + + <Trans>Unused Codes</Trans> + + {recoveryCodes?.unused_codes?.join('\n')} - - <Trans>Used Codes</Trans> - - {recoveryCodes?.used_codes?.join('\n')} - -
-
+ + <Trans>Used Codes</Trans> + + {recoveryCodes?.used_codes?.join('\n')} + + ); } @@ -554,16 +580,17 @@ function MfaAddSection({ return ( - Add Factor + {t`Add Token`} {possibleFactors.map((factor) => ( - + + + ))} { await page.waitForLoadState('networkidle'); }; +/** + * CLick on the 'tab' element with the provided name + */ +export const loadTab = async (page, tabName) => { + await page + .getByLabel(/panel-tabs-/) + .getByRole('tab', { name: tabName }) + .click(); + await page.waitForLoadState('networkidle'); +}; + /** * Perform a 'global search' on the provided page, for the provided query text */ diff --git a/src/frontend/tests/pages/pui_build.spec.ts b/src/frontend/tests/pages/pui_build.spec.ts index 6f112c882e..385dffc5a2 100644 --- a/src/frontend/tests/pages/pui_build.spec.ts +++ b/src/frontend/tests/pages/pui_build.spec.ts @@ -2,6 +2,7 @@ import { test } from '../baseFixtures.ts'; import { clearTableFilters, getRowFromCell, + loadTab, navigate, setTableChoiceFilter } from '../helpers.ts'; @@ -12,7 +13,7 @@ test('Build Order - Basic Tests', async ({ page }) => { // Navigate to the correct build order await page.getByRole('tab', { name: 'Manufacturing', exact: true }).click(); - await page.getByRole('tab', { name: 'Build Orders', exact: true }).click(); + await loadTab(page, 'Build Orders'); await clearTableFilters(page); @@ -57,11 +58,11 @@ test('Build Order - Basic Tests', async ({ page }) => { await page.getByRole('button', { name: 'Cancel' }).click(); // Click on some tabs - await page.getByRole('tab', { name: 'Attachments' }).click(); - await page.getByRole('tab', { name: 'Notes' }).click(); - await page.getByRole('tab', { name: 'Incomplete Outputs' }).click(); - await page.getByRole('tab', { name: 'Line Items' }).click(); - await page.getByRole('tab', { name: 'Allocated Stock' }).click(); + await loadTab(page, 'Attachments'); + await loadTab(page, 'Notes'); + await loadTab(page, 'Incomplete Outputs'); + await loadTab(page, 'Line Items'); + await loadTab(page, 'Allocated Stock'); // Check for expected text in the table await page.getByText('R_10R_0402_1%').waitFor(); @@ -70,7 +71,7 @@ test('Build Order - Basic Tests', async ({ page }) => { .waitFor(); // Check "test results" - await page.getByRole('tab', { name: 'Test Results' }).click(); + await loadTab(page, 'Test Results'); await page.getByText('Quantity: 25').waitFor(); await page.getByText('Continuity Checks').waitFor(); await page @@ -80,7 +81,7 @@ test('Build Order - Basic Tests', async ({ page }) => { await page.getByText('Add Test Result').waitFor(); // Click through to the "parent" build - await page.getByRole('tab', { name: 'Build Details' }).click(); + await loadTab(page, 'Build Details'); await page.getByRole('link', { name: 'BO0010' }).click(); await page .getByLabel('Build Details') @@ -119,7 +120,7 @@ test('Build Order - Build Outputs', async ({ page }) => { await doQuickLogin(page); await navigate(page, 'manufacturing/index/'); - await page.getByRole('tab', { name: 'Build Orders', exact: true }).click(); + await loadTab(page, 'Build Orders'); await clearTableFilters(page); @@ -128,7 +129,7 @@ test('Build Order - Build Outputs', async ({ page }) => { await page.getByText('Pending').first().waitFor(); await page.getByRole('cell', { name: 'BO0011' }).click(); - await page.getByRole('tab', { name: 'Incomplete Outputs' }).click(); + await loadTab(page, 'Incomplete Outputs'); // Create a new build output await page.getByLabel('action-button-add-build-output').click(); @@ -213,7 +214,7 @@ test('Build Order - Allocation', async ({ page }) => { await page.getByRole('cell', { name: 'Reel Storage', exact: true }).waitFor(); // Navigate to the "Incomplete Outputs" tab - await page.getByRole('tab', { name: 'Incomplete Outputs' }).click(); + await loadTab(page, 'Incomplete Outputs'); // Find output #7 const output7 = await page diff --git a/src/frontend/tests/pages/pui_company.spec.ts b/src/frontend/tests/pages/pui_company.spec.ts index 548ab81819..9f32e8af6a 100644 --- a/src/frontend/tests/pages/pui_company.spec.ts +++ b/src/frontend/tests/pages/pui_company.spec.ts @@ -1,5 +1,5 @@ import { test } from '../baseFixtures.js'; -import { navigate } from '../helpers.js'; +import { loadTab, navigate } from '../helpers.js'; import { doQuickLogin } from '../login.js'; test('Company', async ({ page }) => { @@ -8,23 +8,23 @@ test('Company', async ({ page }) => { await navigate(page, 'company/1/details'); await page.getByLabel('Details').getByText('DigiKey Electronics').waitFor(); await page.getByRole('cell', { name: 'https://www.digikey.com/' }).waitFor(); - await page.getByRole('tab', { name: 'Supplied Parts' }).click(); + await loadTab(page, 'Supplied Parts'); await page .getByRole('cell', { name: 'RR05P100KDTR-ND', exact: true }) .waitFor(); - await page.getByRole('tab', { name: 'Purchase Orders' }).click(); + await loadTab(page, 'Purchase Orders'); await page.getByRole('cell', { name: 'Molex connectors' }).first().waitFor(); - await page.getByRole('tab', { name: 'Stock Items' }).click(); + await loadTab(page, 'Stock Items'); await page .getByRole('cell', { name: 'Blue plastic enclosure' }) .first() .waitFor(); - await page.getByRole('tab', { name: 'Contacts' }).click(); + await loadTab(page, 'Contacts'); await page.getByRole('cell', { name: 'jimmy.mcleod@digikey.com' }).waitFor(); - await page.getByRole('tab', { name: 'Addresses' }).click(); + await loadTab(page, 'Addresses'); await page.getByRole('cell', { name: 'Carla Tunnel' }).waitFor(); - await page.getByRole('tab', { name: 'Attachments' }).click(); - await page.getByRole('tab', { name: 'Notes' }).click(); + await loadTab(page, 'Attachments'); + await loadTab(page, 'Notes'); // Let's edit the company details await page.getByLabel('action-menu-company-actions').click(); diff --git a/src/frontend/tests/pages/pui_part.spec.ts b/src/frontend/tests/pages/pui_part.spec.ts index 48a848bfa6..0310f8d503 100644 --- a/src/frontend/tests/pages/pui_part.spec.ts +++ b/src/frontend/tests/pages/pui_part.spec.ts @@ -1,5 +1,10 @@ import { test } from '../baseFixtures'; -import { clearTableFilters, getRowFromCell, navigate } from '../helpers'; +import { + clearTableFilters, + getRowFromCell, + loadTab, + navigate +} from '../helpers'; import { doQuickLogin } from '../login'; /** @@ -19,35 +24,35 @@ test('Parts - Tabs', async ({ page }) => { await page.getByPlaceholder('Search').fill('1551'); await page.getByText('1551ABK').click(); - await page.getByRole('tab', { name: 'Allocations' }).click(); - await page.getByRole('tab', { name: 'Used In' }).click(); - await page.getByRole('tab', { name: 'Pricing' }).click(); - await page.getByRole('tab', { name: 'Suppliers' }).click(); - await page.getByRole('tab', { name: 'Purchase Orders' }).click(); - await page.getByRole('tab', { name: 'Scheduling' }).click(); - await page.getByRole('tab', { name: 'Stock History' }).click(); - await page.getByRole('tab', { name: 'Attachments' }).click(); - await page.getByRole('tab', { name: 'Notes' }).click(); - await page.getByRole('tab', { name: 'Related Parts' }).click(); + await loadTab(page, 'Allocations'); + await loadTab(page, 'Used In'); + await loadTab(page, 'Pricing'); + await loadTab(page, 'Suppliers'); + await loadTab(page, 'Purchase Orders'); + await loadTab(page, 'Scheduling'); + await loadTab(page, 'Stock History'); + await loadTab(page, 'Attachments'); + await loadTab(page, 'Notes'); + await loadTab(page, 'Related Parts'); // Related Parts await page.getByText('1551ACLR').click(); - await page.getByRole('tab', { name: 'Part Details' }).click(); - await page.getByRole('tab', { name: 'Parameters' }).click(); + await loadTab(page, 'Part Details'); + await loadTab(page, 'Parameters'); await page .getByLabel('panel-tabs-part') .getByRole('tab', { name: 'Stock', exact: true }) .click(); - await page.getByRole('tab', { name: 'Allocations' }).click(); - await page.getByRole('tab', { name: 'Used In' }).click(); - await page.getByRole('tab', { name: 'Pricing' }).click(); + await loadTab(page, 'Allocations'); + await loadTab(page, 'Used In'); + await loadTab(page, 'Pricing'); await navigate(page, 'part/category/index/parts'); await page.getByText('Blue Chair').click(); - await page.getByRole('tab', { name: 'Bill of Materials' }).click(); - await page.getByRole('tab', { name: 'Build Orders' }).click(); + await loadTab(page, 'Bill of Materials'); + await loadTab(page, 'Build Orders'); }); test('Parts - Manufacturer Parts', async ({ page }) => { @@ -55,11 +60,11 @@ test('Parts - Manufacturer Parts', async ({ page }) => { await navigate(page, 'part/84/suppliers'); - await page.getByRole('tab', { name: 'Suppliers' }).click(); + await loadTab(page, 'Suppliers'); await page.getByText('Hammond Manufacturing').click(); - await page.getByRole('tab', { name: 'Parameters' }).click(); - await page.getByRole('tab', { name: 'Suppliers' }).click(); - await page.getByRole('tab', { name: 'Attachments' }).click(); + await loadTab(page, 'Parameters'); + await loadTab(page, 'Suppliers'); + await loadTab(page, 'Attachments'); await page.getByText('1551ACLR - 1551ACLR').waitFor(); }); @@ -68,11 +73,11 @@ test('Parts - Supplier Parts', async ({ page }) => { await navigate(page, 'part/15/suppliers'); - await page.getByRole('tab', { name: 'Suppliers' }).click(); + await loadTab(page, 'Suppliers'); await page.getByRole('cell', { name: 'DIG-84670-SJI' }).click(); - await page.getByRole('tab', { name: 'Received Stock' }).click(); // - await page.getByRole('tab', { name: 'Purchase Orders' }).click(); - await page.getByRole('tab', { name: 'Pricing' }).click(); + await loadTab(page, 'Received Stock'); // + await loadTab(page, 'Purchase Orders'); + await loadTab(page, 'Pricing'); await page.getByText('DIG-84670-SJI - R_550R_0805_1%').waitFor(); }); @@ -81,14 +86,14 @@ test('Parts - Locking', async ({ page }) => { // Navigate to a known assembly which is *not* locked await navigate(page, 'part/104/bom'); - await page.getByRole('tab', { name: 'Bill of Materials' }).click(); + await loadTab(page, 'Bill of Materials'); await page.getByLabel('action-button-add-bom-item').waitFor(); - await page.getByRole('tab', { name: 'Parameters' }).click(); + await loadTab(page, 'Parameters'); await page.getByLabel('action-button-add-parameter').waitFor(); // Navigate to a known assembly which *is* locked await navigate(page, 'part/100/bom'); - await page.getByRole('tab', { name: 'Bill of Materials' }).click(); + await loadTab(page, 'Bill of Materials'); await page.getByLabel('part-lock-icon').waitFor(); await page.getByText('Part is Locked', { exact: true }).waitFor(); @@ -98,7 +103,7 @@ test('Parts - Locking', async ({ page }) => { await page.getByText('In Production: 50').waitFor(); // Check the "parameters" tab also - await page.getByRole('tab', { name: 'Parameters' }).click(); + await loadTab(page, 'Parameters'); await page.getByText('Part parameters cannot be').waitFor(); }); @@ -107,7 +112,7 @@ test('Parts - Allocations', async ({ page }) => { // Let's look at the allocations for a single stock item await navigate(page, 'stock/item/324/'); - await page.getByRole('tab', { name: 'Allocations' }).click(); + await loadTab(page, 'Allocations'); await page.getByRole('button', { name: 'Build Order Allocations' }).waitFor(); await page.getByRole('cell', { name: 'Making some blue chairs' }).waitFor(); @@ -124,7 +129,7 @@ test('Parts - Allocations', async ({ page }) => { await page.waitForTimeout(500); await page.waitForLoadState('networkidle'); - await page.getByRole('tab', { name: 'Allocations' }).click(); + await loadTab(page, 'Allocations'); await page.getByRole('button', { name: 'Build Order Allocations' }).waitFor(); await page.getByRole('button', { name: 'Sales Order Allocations' }).waitFor(); @@ -177,7 +182,7 @@ test('Parts - Pricing (Nothing, BOM)', async ({ page }) => { await navigate(page, 'part/82/pricing'); await page.getByText('Small plastic enclosure, black').waitFor(); - await page.getByRole('tab', { name: 'Part Pricing' }).click(); + await loadTab(page, 'Part Pricing'); await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor(); await page.getByRole('button', { name: 'Pricing Overview' }).waitFor(); await page.getByText('Last Updated').waitFor(); @@ -188,7 +193,7 @@ test('Parts - Pricing (Nothing, BOM)', async ({ page }) => { // Part with history await navigate(page, 'part/108/pricing'); await page.getByText('A chair - with blue paint').waitFor(); - await page.getByRole('tab', { name: 'Part Pricing' }).click(); + await loadTab(page, 'Part Pricing'); await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor(); await page.getByRole('button', { name: 'Pricing Overview' }).waitFor(); await page.getByText('Last Updated').waitFor(); @@ -226,7 +231,7 @@ test('Parts - Pricing (Supplier)', async ({ page }) => { // Part await navigate(page, 'part/55/pricing'); await page.getByText('Ceramic capacitor, 100nF in').waitFor(); - await page.getByRole('tab', { name: 'Part Pricing' }).click(); + await loadTab(page, 'Part Pricing'); await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor(); await page.getByRole('button', { name: 'Pricing Overview' }).waitFor(); await page.getByText('Last Updated').waitFor(); @@ -252,7 +257,7 @@ test('Parts - Pricing (Variant)', async ({ page }) => { // Part await navigate(page, 'part/106/pricing'); await page.getByText('A chair - available in multiple colors').waitFor(); - await page.getByRole('tab', { name: 'Part Pricing' }).click(); + await loadTab(page, 'Part Pricing'); await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor(); await page.getByRole('button', { name: 'Pricing Overview' }).waitFor(); await page.getByText('Last Updated').waitFor(); @@ -278,7 +283,7 @@ test('Parts - Pricing (Internal)', async ({ page }) => { // Part await navigate(page, 'part/65/pricing'); await page.getByText('Socket head cap screw, M2').waitFor(); - await page.getByRole('tab', { name: 'Part Pricing' }).click(); + await loadTab(page, 'Part Pricing'); await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor(); await page.getByRole('button', { name: 'Pricing Overview' }).waitFor(); await page.getByText('Last Updated').waitFor(); @@ -303,7 +308,7 @@ test('Parts - Pricing (Purchase)', async ({ page }) => { // Part await navigate(page, 'part/69/pricing'); await page.getByText('1.25mm Pitch, PicoBlade PCB').waitFor(); - await page.getByRole('tab', { name: 'Part Pricing' }).click(); + await loadTab(page, 'Part Pricing'); await page.getByLabel('Part Pricing').getByText('Part Pricing').waitFor(); await page.getByRole('button', { name: 'Pricing Overview' }).waitFor(); await page.getByText('Last Updated').waitFor(); diff --git a/src/frontend/tests/pages/pui_purchase_order.spec.ts b/src/frontend/tests/pages/pui_purchase_order.spec.ts index ce72489db9..11bdb5c8fe 100644 --- a/src/frontend/tests/pages/pui_purchase_order.spec.ts +++ b/src/frontend/tests/pages/pui_purchase_order.spec.ts @@ -3,6 +3,7 @@ import { clearTableFilters, clickButtonIfVisible, clickOnRowMenu, + loadTab, navigate, openFilterDrawer, setTableChoiceFilter @@ -13,7 +14,7 @@ test('Purchase Orders - List', async ({ page }) => { await doQuickLogin(page); await page.getByRole('tab', { name: 'Purchasing' }).click(); - await page.getByRole('tab', { name: 'Purchase Orders' }).click(); + await loadTab(page, 'Purchase Orders'); await clearTableFilters(page); @@ -101,29 +102,32 @@ test('Purchase Orders - General', async ({ page }) => { await doQuickLogin(page); await page.getByRole('tab', { name: 'Purchasing' }).click(); + await page.getByRole('cell', { name: 'PO0012' }).click(); await page.waitForTimeout(200); - await page.getByRole('tab', { name: 'Line Items' }).click(); - await page.getByRole('tab', { name: 'Received Stock' }).click(); - await page.getByRole('tab', { name: 'Attachments' }).click(); + await loadTab(page, 'Line Items'); + await loadTab(page, 'Received Stock'); + await loadTab(page, 'Attachments'); + await page.getByRole('tab', { name: 'Purchasing' }).click(); - await page.getByRole('tab', { name: 'Suppliers' }).click(); + await loadTab(page, 'Suppliers'); await page.getByText('Arrow', { exact: true }).click(); await page.waitForTimeout(200); - await page.getByRole('tab', { name: 'Supplied Parts' }).click(); - await page.getByRole('tab', { name: 'Purchase Orders' }).click(); - await page.getByRole('tab', { name: 'Stock Items' }).click(); - await page.getByRole('tab', { name: 'Contacts' }).click(); - await page.getByRole('tab', { name: 'Addresses' }).click(); - await page.getByRole('tab', { name: 'Attachments' }).click(); + await loadTab(page, 'Supplied Parts'); + await loadTab(page, 'Purchase Orders'); + await loadTab(page, 'Stock Items'); + await loadTab(page, 'Contacts'); + await loadTab(page, 'Addresses'); + await loadTab(page, 'Attachments'); + await page.getByRole('tab', { name: 'Purchasing' }).click(); - await page.getByRole('tab', { name: 'Manufacturers' }).click(); + await loadTab(page, 'Manufacturers'); await page.getByText('AVX Corporation').click(); await page.waitForTimeout(200); - await page.getByRole('tab', { name: 'Addresses' }).click(); + await loadTab(page, 'Addresses'); await page.getByRole('cell', { name: 'West Branch' }).click(); await page.locator('.mantine-ScrollArea-root').click(); await page @@ -151,7 +155,7 @@ test('Purchase Orders - Filters', async ({ page }) => { await doQuickLogin(page, 'reader', 'readonly'); await page.getByRole('tab', { name: 'Purchasing' }).click(); - await page.getByRole('tab', { name: 'Purchase Orders' }).click(); + await loadTab(page, 'Purchase Orders'); // Open filters drawer await openFilterDrawer(page); @@ -197,7 +201,7 @@ test('Purchase Orders - Order Parts', async ({ page }) => { // Open "Order Parts" wizard from the "Stock Items" table await page.getByRole('tab', { name: 'Stock' }).click(); - await page.getByRole('tab', { name: 'Stock Items' }).click(); + await loadTab(page, 'Stock Items'); // Select multiple stock items for (let ii = 2; ii < 7; ii += 2) { @@ -257,10 +261,10 @@ test('Purchase Orders - Receive Items', async ({ page }) => { await page.getByRole('tab', { name: 'Purchasing' }).click(); await page.getByRole('cell', { name: 'PO0014' }).click(); - await page.getByRole('tab', { name: 'Order Details' }).click(); + await loadTab(page, 'Order Details'); // Select all line items to receive - await page.getByRole('tab', { name: 'Line Items' }).click(); + await loadTab(page, 'Line Items'); await page.getByLabel('Select all records').click(); await page.waitForTimeout(200); @@ -311,7 +315,7 @@ test('Purchase Orders - Receive Items', async ({ page }) => { await page.getByRole('button', { name: 'Submit' }).click(); await page.getByText('Items received').waitFor(); - await page.getByRole('tab', { name: 'Received Stock' }).click(); + await loadTab(page, 'Received Stock'); await clearTableFilters(page); await page.getByRole('cell', { name: 'my-batch-code' }).first().waitFor(); diff --git a/src/frontend/tests/pages/pui_sales_order.spec.ts b/src/frontend/tests/pages/pui_sales_order.spec.ts index ab766899b5..0be3b233bb 100644 --- a/src/frontend/tests/pages/pui_sales_order.spec.ts +++ b/src/frontend/tests/pages/pui_sales_order.spec.ts @@ -2,6 +2,7 @@ import { test } from '../baseFixtures.ts'; import { clearTableFilters, globalSearch, + loadTab, navigate, setTableChoiceFilter } from '../helpers.ts'; @@ -13,27 +14,27 @@ test('Sales Orders - Tabs', async ({ page }) => { await navigate(page, 'sales/index/'); await page.waitForURL('**/platform/sales/**'); - await page.getByRole('tab', { name: 'Sales Orders' }).click(); + await loadTab(page, 'Sales Orders'); await page.waitForURL('**/platform/sales/index/salesorders'); - await page.getByRole('tab', { name: 'Return Orders' }).click(); + await loadTab(page, 'Return Orders'); // Customers - await page.getByRole('tab', { name: 'Customers' }).click(); + await loadTab(page, 'Customers'); await page.getByText('Customer A').click(); - await page.getByRole('tab', { name: 'Notes' }).click(); - await page.getByRole('tab', { name: 'Attachments' }).click(); - await page.getByRole('tab', { name: 'Contacts' }).click(); - await page.getByRole('tab', { name: 'Assigned Stock' }).click(); - await page.getByRole('tab', { name: 'Return Orders' }).click(); - await page.getByRole('tab', { name: 'Sales Orders' }).click(); - await page.getByRole('tab', { name: 'Contacts' }).click(); + await loadTab(page, 'Notes'); + await loadTab(page, 'Attachments'); + await loadTab(page, 'Contacts'); + await loadTab(page, 'Assigned Stock'); + await loadTab(page, 'Return Orders'); + await loadTab(page, 'Sales Orders'); + await loadTab(page, 'Contacts'); await page.getByRole('cell', { name: 'Dorathy Gross' }).waitFor(); await page .getByRole('row', { name: 'Dorathy Gross dorathy.gross@customer.com' }) .waitFor(); // Sales Order Details - await page.getByRole('tab', { name: 'Sales Orders' }).click(); + await loadTab(page, 'Sales Orders'); await clearTableFilters(page); @@ -42,30 +43,30 @@ test('Sales Orders - Tabs', async ({ page }) => { .getByLabel('Order Details') .getByText('Selling some stuff') .waitFor(); - await page.getByRole('tab', { name: 'Line Items' }).click(); - await page.getByRole('tab', { name: 'Shipments' }).click(); - await page.getByRole('tab', { name: 'Build Orders' }).click(); + await loadTab(page, 'Line Items'); + await loadTab(page, 'Shipments'); + await loadTab(page, 'Build Orders'); await page.getByText('No records found').first().waitFor(); - await page.getByRole('tab', { name: 'Attachments' }).click(); + await loadTab(page, 'Attachments'); await page.getByText('No attachments found').first().waitFor(); - await page.getByRole('tab', { name: 'Notes' }).click(); - await page.getByRole('tab', { name: 'Order Details' }).click(); + await loadTab(page, 'Notes'); + await loadTab(page, 'Order Details'); // Return Order Details await page.getByRole('link', { name: 'Customer A' }).click(); - await page.getByRole('tab', { name: 'Return Orders' }).click(); + await loadTab(page, 'Return Orders'); await page.getByRole('cell', { name: 'RMA-' }).click(); await page.getByText('RMA-0001', { exact: true }).waitFor(); - await page.getByRole('tab', { name: 'Line Items' }).click(); - await page.getByRole('tab', { name: 'Attachments' }).click(); - await page.getByRole('tab', { name: 'Notes' }).click(); + await loadTab(page, 'Line Items'); + await loadTab(page, 'Attachments'); + await loadTab(page, 'Notes'); }); test('Sales Orders - Basic Tests', async ({ page }) => { await doQuickLogin(page); await page.getByRole('tab', { name: 'Sales' }).click(); - await page.getByRole('tab', { name: 'Sales Orders' }).click(); + await loadTab(page, 'Sales Orders'); await clearTableFilters(page); @@ -104,12 +105,12 @@ test('Sales Orders - Shipments', async ({ page }) => { await doQuickLogin(page); await page.getByRole('tab', { name: 'Sales' }).click(); - await page.getByRole('tab', { name: 'Sales Orders' }).click(); + await loadTab(page, 'Sales Orders'); await clearTableFilters(page); // Click through to a particular sales order await page.getByRole('cell', { name: 'SO0006' }).first().click(); - await page.getByRole('tab', { name: 'Shipments' }).click(); + await loadTab(page, 'Shipments'); // Create a new shipment await page.getByLabel('action-button-add-shipment').click(); @@ -155,14 +156,14 @@ test('Sales Orders - Shipments', async ({ page }) => { await page.getByRole('menuitem', { name: 'View Shipment' }).click(); // Click through the various tabs - await page.getByRole('tab', { name: 'Attachments' }).click(); - await page.getByRole('tab', { name: 'Notes' }).click(); - await page.getByRole('tab', { name: 'Allocated Stock' }).click(); + await loadTab(page, 'Attachments'); + await loadTab(page, 'Notes'); + await loadTab(page, 'Allocated Stock'); // Ensure assigned items table loads correctly await page.getByRole('cell', { name: 'BATCH-001' }).first().waitFor(); - await page.getByRole('tab', { name: 'Shipment Details' }).click(); + await loadTab(page, 'Shipment Details'); // The "new" tracking number should be visible await page.getByText(tracking_number).waitFor(); @@ -171,7 +172,7 @@ test('Sales Orders - Shipments', async ({ page }) => { await page.getByRole('link', { name: 'SO0006' }).click(); // Let's try to allocate some stock - await page.getByRole('tab', { name: 'Line Items' }).click(); + await loadTab(page, 'Line Items'); await page.getByLabel('row-action-menu-1').click(); await page.getByRole('menuitem', { name: 'Allocate stock' }).click(); await page diff --git a/src/frontend/tests/pages/pui_stock.spec.ts b/src/frontend/tests/pages/pui_stock.spec.ts index b42817b4b5..97381c5d6e 100644 --- a/src/frontend/tests/pages/pui_stock.spec.ts +++ b/src/frontend/tests/pages/pui_stock.spec.ts @@ -2,6 +2,7 @@ import { test } from '../baseFixtures.js'; import { clearTableFilters, clickButtonIfVisible, + loadTab, navigate, openFilterDrawer, setTableChoiceFilter @@ -14,28 +15,28 @@ test('Stock - Basic Tests', async ({ page }) => { await navigate(page, 'stock/location/index/'); await page.waitForURL('**/platform/stock/location/**'); - await page.getByRole('tab', { name: 'Location Details' }).click(); + await loadTab(page, 'Location Details'); await page.waitForURL('**/platform/stock/location/index/details'); - await page.getByRole('tab', { name: 'Stock Items' }).click(); + await loadTab(page, 'Stock Items'); await page.getByText('1551ABK').first().click(); await page.getByRole('tab', { name: 'Stock', exact: true }).click(); await page.waitForURL('**/platform/stock/**'); - await page.getByRole('tab', { name: 'Stock Locations' }).click(); + await loadTab(page, 'Stock Locations'); await page.getByRole('cell', { name: 'Electronics Lab' }).first().click(); - await page.getByRole('tab', { name: 'Default Parts' }).click(); - await page.getByRole('tab', { name: 'Stock Locations' }).click(); - await page.getByRole('tab', { name: 'Stock Items' }).click(); - await page.getByRole('tab', { name: 'Location Details' }).click(); + await loadTab(page, 'Default Parts'); + await loadTab(page, 'Stock Locations'); + await loadTab(page, 'Stock Items'); + await loadTab(page, 'Location Details'); await navigate(page, 'stock/item/1194/details'); await page.getByText('D.123 | Doohickey').waitFor(); await page.getByText('Batch Code: BX-123-2024-2-7').waitFor(); - await page.getByRole('tab', { name: 'Stock Tracking' }).click(); - await page.getByRole('tab', { name: 'Test Data' }).click(); + await loadTab(page, 'Stock Tracking'); + await loadTab(page, 'Test Data'); await page.getByText('395c6d5586e5fb656901d047be27e1f7').waitFor(); - await page.getByRole('tab', { name: 'Installed Items' }).click(); + await loadTab(page, 'Installed Items'); }); test('Stock - Location Tree', async ({ page }) => { @@ -43,7 +44,7 @@ test('Stock - Location Tree', async ({ page }) => { await navigate(page, 'stock/location/index/'); await page.waitForURL('**/platform/stock/location/**'); - await page.getByRole('tab', { name: 'Location Details' }).click(); + await loadTab(page, 'Location Details'); await page.getByLabel('nav-breadcrumb-action').click(); await page.getByLabel('nav-tree-toggle-1}').click(); @@ -59,7 +60,7 @@ test('Stock - Filters', async ({ page }) => { await doQuickLogin(page, 'steven', 'wizardstaff'); await navigate(page, 'stock/location/index/'); - await page.getByRole('tab', { name: 'Stock Items' }).click(); + await loadTab(page, 'Stock Items'); await openFilterDrawer(page); await clickButtonIfVisible(page, 'Clear Filters'); @@ -238,7 +239,7 @@ test('Stock - Tracking', async ({ page }) => { await page.getByRole('link', { name: 'Widget Assembly # 2' }).waitFor(); // Navigate to the "stock tracking" tab - await page.getByRole('tab', { name: 'Stock Tracking' }).click(); + await loadTab(page, 'Stock Tracking'); await page.getByText('- - Factory/Office Block/Room').first().waitFor(); await page.getByRole('link', { name: 'Widget Assembly' }).waitFor(); await page.getByRole('cell', { name: 'Installed into assembly' }).waitFor(); diff --git a/src/frontend/tests/pui_plugins.spec.ts b/src/frontend/tests/pui_plugins.spec.ts index 6234421e00..887a21fe6a 100644 --- a/src/frontend/tests/pui_plugins.spec.ts +++ b/src/frontend/tests/pui_plugins.spec.ts @@ -1,6 +1,6 @@ import test from 'playwright/test'; -import { navigate } from './helpers.js'; +import { loadTab, navigate } from './helpers.js'; import { doQuickLogin } from './login.js'; import { setPluginState, setSettingState } from './settings.js'; @@ -29,19 +29,18 @@ test('Plugins - Panels', async ({ page, request }) => { await navigate(page, 'part/69/'); // Ensure basic part tab is available - await page.getByRole('tab', { name: 'Part Details' }).waitFor(); + await loadTab(page, 'Part Details'); // Allow time for the plugin panels to load (they are loaded asynchronously) await page.waitForTimeout(1000); // Check out each of the plugin panels - - await page.getByRole('tab', { name: 'Broken Panel' }).click(); + await loadTab(page, 'Broken Panel'); await page.waitForTimeout(500); await page.getByText('Error occurred while loading plugin content').waitFor(); - await page.getByRole('tab', { name: 'Dynamic Panel' }).click(); + await loadTab(page, 'Dynamic Panel'); await page.waitForTimeout(500); await page.getByText('Instance ID: 69'); @@ -49,7 +48,7 @@ test('Plugins - Panels', async ({ page, request }) => { .getByText('This panel has been dynamically rendered by the plugin system') .waitFor(); - await page.getByRole('tab', { name: 'Part Panel', exact: true }).click(); + await loadTab(page, 'Part Panel'); await page.waitForTimeout(500); await page.getByText('This content has been rendered by a custom plugin'); diff --git a/src/frontend/tests/pui_printing.spec.ts b/src/frontend/tests/pui_printing.spec.ts index 945dc7e74c..04c762416c 100644 --- a/src/frontend/tests/pui_printing.spec.ts +++ b/src/frontend/tests/pui_printing.spec.ts @@ -1,5 +1,5 @@ import { expect, test } from './baseFixtures.js'; -import { navigate } from './helpers.js'; +import { loadTab, navigate } from './helpers.js'; import { doQuickLogin } from './login.js'; import { setPluginState } from './settings.js'; @@ -14,7 +14,7 @@ test('Label Printing', async ({ page }) => { await navigate(page, 'stock/location/index/'); await page.waitForURL('**/platform/stock/location/**'); - await page.getByRole('tab', { name: 'Stock Items' }).click(); + await loadTab(page, 'Stock Items'); // Select some labels await page.getByLabel('Select record 1', { exact: true }).click(); @@ -60,7 +60,7 @@ test('Report Printing', async ({ page }) => { // Navigate to a specific PurchaseOrder await page.getByRole('tab', { name: 'Purchasing' }).click(); - await page.getByRole('tab', { name: 'Purchase Orders' }).click(); + await loadTab(page, 'Purchase Orders'); await page.getByRole('cell', { name: 'PO0009' }).click(); @@ -98,7 +98,7 @@ test('Report Editing', async ({ page, request }) => { // Navigate to the admin center await page.getByRole('button', { name: 'admin' }).click(); await page.getByRole('menuitem', { name: 'Admin Center' }).click(); - await page.getByRole('tab', { name: 'Label Templates' }).click(); + await loadTab(page, 'Label Templates'); await page .getByRole('cell', { name: 'InvenTree Stock Item Label (' }) .click(); diff --git a/src/frontend/tests/pui_settings.spec.ts b/src/frontend/tests/pui_settings.spec.ts index deaedea25b..9afe28f219 100644 --- a/src/frontend/tests/pui_settings.spec.ts +++ b/src/frontend/tests/pui_settings.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from './baseFixtures.js'; import { apiUrl } from './defaults.js'; -import { getRowFromCell, navigate } from './helpers.js'; +import { getRowFromCell, loadTab, navigate } from './helpers.js'; import { doQuickLogin } from './login.js'; import { setSettingState } from './settings.js'; @@ -49,47 +49,47 @@ test('Settings - Admin', async ({ page }) => { // User settings await page.getByRole('button', { name: 'admin' }).click(); await page.getByRole('menuitem', { name: 'Account settings' }).click(); - await page.getByRole('tab', { name: 'Security' }).click(); + await loadTab(page, 'Security'); - await page.getByRole('tab', { name: 'Display Options' }).click(); + await loadTab(page, 'Display Options'); await page.getByText('Date Format').waitFor(); - await page.getByRole('tab', { name: 'Search' }).click(); + await loadTab(page, 'Search'); await page.getByText('Regex Search').waitFor(); - await page.getByRole('tab', { name: 'Notifications' }).click(); - await page.getByRole('tab', { name: 'Reporting' }).click(); + await loadTab(page, 'Notifications'); + await loadTab(page, 'Reporting'); await page.getByText('Inline report display').waitFor(); // System Settings await page.locator('label').filter({ hasText: 'System Settings' }).click(); await page.getByText('Base URL', { exact: true }).waitFor(); - await page.getByRole('tab', { name: 'Login' }).click(); - await page.getByRole('tab', { name: 'Barcodes' }).click(); - await page.getByRole('tab', { name: 'Notifications' }).click(); - await page.getByRole('tab', { name: 'Pricing' }).click(); - await page.getByRole('tab', { name: 'Labels' }).click(); - await page.getByRole('tab', { name: 'Reporting' }).click(); + await loadTab(page, 'Login'); + await loadTab(page, 'Barcodes'); + await loadTab(page, 'Notifications'); + await loadTab(page, 'Pricing'); + await loadTab(page, 'Labels'); + await loadTab(page, 'Reporting'); - await page.getByRole('tab', { name: 'Build Orders' }).click(); - await page.getByRole('tab', { name: 'Purchase Orders' }).click(); - await page.getByRole('tab', { name: 'Sales Orders' }).click(); - await page.getByRole('tab', { name: 'Return Orders' }).click(); + await loadTab(page, 'Build Orders'); + await loadTab(page, 'Purchase Orders'); + await loadTab(page, 'Sales Orders'); + await loadTab(page, 'Return Orders'); // Admin Center await page.getByRole('button', { name: 'admin' }).click(); await page.getByRole('menuitem', { name: 'Admin Center' }).click(); - await page.getByRole('tab', { name: 'Background Tasks' }).click(); - await page.getByRole('tab', { name: 'Error Reports' }).click(); - await page.getByRole('tab', { name: 'Currencies' }).click(); - await page.getByRole('tab', { name: 'Project Codes' }).click(); - await page.getByRole('tab', { name: 'Custom Units' }).click(); - await page.getByRole('tab', { name: 'Part Parameters' }).click(); - await page.getByRole('tab', { name: 'Category Parameters' }).click(); - await page.getByRole('tab', { name: 'Label Templates' }).click(); - await page.getByRole('tab', { name: 'Report Templates' }).click(); - await page.getByRole('tab', { name: 'Plugins' }).click(); + await loadTab(page, 'Background Tasks'); + await loadTab(page, 'Error Reports'); + await loadTab(page, 'Currencies'); + await loadTab(page, 'Project Codes'); + await loadTab(page, 'Custom Units'); + await loadTab(page, 'Part Parameters'); + await loadTab(page, 'Category Parameters'); + await loadTab(page, 'Label Templates'); + await loadTab(page, 'Report Templates'); + await loadTab(page, 'Plugins'); // Adjust some "location type" items - await page.getByRole('tab', { name: 'Location Types' }).click(); + await loadTab(page, 'Location Types'); // Edit first item ('Room') const roomCell = await page.getByRole('cell', { name: 'Room', exact: true }); @@ -166,7 +166,7 @@ test('Settings - Admin - Barcode History', async ({ page, request }) => { await page.getByRole('button', { name: 'admin' }).click(); await page.getByRole('menuitem', { name: 'Admin Center' }).click(); - await page.getByRole('tab', { name: 'Barcode Scans' }).click(); + await loadTab(page, 'Barcode Scans'); await page.waitForTimeout(500); @@ -193,8 +193,8 @@ test('Settings - Admin - Unauthorized', async ({ page }) => { await navigate(page, 'settings/user/'); await page.waitForURL('**/settings/user/**'); - await page.getByRole('tab', { name: 'Display Options' }).click(); - await page.getByRole('tab', { name: 'Account' }).click(); + await loadTab(page, 'Display Options'); + await loadTab(page, 'Account'); // Try to access global settings page await navigate(page, 'settings/system/'); @@ -205,3 +205,25 @@ test('Settings - Admin - Unauthorized', async ({ page }) => { .getByRole('button', { name: 'Return to the index page' }) .waitFor(); }); + +// Test for user auth configuration +test('Settings - Auth - Email', async ({ page }) => { + await doQuickLogin(page, 'allaccess', 'nolimits'); + await navigate(page, 'settings/user/'); + + await loadTab(page, 'Security'); + + await page.getByText('Currently no email addresses are registered').waitFor(); + await page.getByLabel('email-address-input').fill('test-email@domain.org'); + await page.getByLabel('email-address-submit').click(); + + await page.getByText('Unverified', { exact: true }).waitFor(); + await page.getByLabel('test-email@domain.').click(); + await page.getByRole('button', { name: 'Make Primary' }).click(); + await page.getByText('Primary', { exact: true }).waitFor(); + await page.getByRole('button', { name: 'Remove' }).click(); + + await page.getByText('Currently no email addresses are registered').waitFor(); + + await page.waitForTimeout(2500); +});