diff --git a/e2e/auth.spec.ts b/e2e/auth.spec.ts index ad2fee64..b0d7720d 100644 --- a/e2e/auth.spec.ts +++ b/e2e/auth.spec.ts @@ -192,9 +192,7 @@ test('shows error for invalid login credentials', async ({ page }) => { await page.getByLabel('Password').fill('wrongpassword123'); await page.getByRole('button', { name: 'Log in' }).click(); - await expect( - page.getByText('These credentials do not match our records.') - ).toBeVisible(); + await expect(page.getByText('These credentials do not match our records.')).toBeVisible(); }); test('shows error when registering with existing email', async ({ page }) => { diff --git a/e2e/profile.spec.ts b/e2e/profile.spec.ts index 81aed742..93d9248c 100644 --- a/e2e/profile.spec.ts +++ b/e2e/profile.spec.ts @@ -44,6 +44,28 @@ test('test that user can create an API key', async ({ page }) => { await createNewApiToken(page); }); +test('test that creating an API key with empty name shows validation error', async ({ page }) => { + await page.goto(PLAYWRIGHT_BASE_URL + '/user/profile'); + + // Wait for the API Key Name input to be visible before interacting + const nameInput = page.getByLabel('API Key Name'); + await expect(nameInput).toBeVisible(); + + // Ensure the API Key Name input is empty + await nameInput.fill(''); + + // Click the create button and wait for the 422 response + const [response] = await Promise.all([ + page.waitForResponse('**/users/me/api-tokens'), + page.getByRole('button', { name: 'Create API Key' }).click(), + ]); + + expect(response.status()).toBe(422); + + // Verify that an error notification is shown with validation message about the name field + await expect(page.getByText('name field is required')).toBeVisible({ timeout: 5000 }); +}); + test('test that user can delete an API key', async ({ page }) => { await page.goto(PLAYWRIGHT_BASE_URL + '/user/profile'); await createNewApiToken(page); diff --git a/e2e/reporting.spec.ts b/e2e/reporting.spec.ts index 2e635a5d..7537030f 100644 --- a/e2e/reporting.spec.ts +++ b/e2e/reporting.spec.ts @@ -158,12 +158,9 @@ test('test that deselecting a project removes the filter', async ({ page, ctx }) page.getByRole('button', { name: 'Projects' }).first().getByText('1') ).toBeVisible(); - // Deselect project + // Deselect project (no network request expected — TanStack Query serves cached unfiltered data) await page.getByRole('button', { name: 'Projects' }).first().click(); - await Promise.all([ - page.getByRole('option').filter({ hasText: project1Name }).click(), - waitForReportingUpdate(page), - ]); + await page.getByRole('option').filter({ hasText: project1Name }).click(); await page.keyboard.press('Escape'); // Verify badge count is gone (no count displayed when 0) @@ -281,12 +278,9 @@ test('test that deselecting a client removes the filter', async ({ page, ctx }) page.getByRole('button', { name: 'Clients' }).first().getByText('1') ).toBeVisible(); - // Deselect client + // Deselect client (no network request expected — TanStack Query serves cached unfiltered data) await page.getByRole('button', { name: 'Clients' }).first().click(); - await Promise.all([ - page.getByRole('option').filter({ hasText: client1Name }).click(), - waitForReportingUpdate(page), - ]); + await page.getByRole('option').filter({ hasText: client1Name }).click(); await page.keyboard.press('Escape'); await expect( @@ -445,12 +439,9 @@ test('test that deselecting a member removes the filter', async ({ page, ctx }) page.getByRole('button', { name: 'Members' }).first().getByText('1') ).toBeVisible(); - // Deselect member + // Deselect member (no network request expected — TanStack Query serves cached unfiltered data) await page.getByRole('button', { name: 'Members' }).first().click(); - await Promise.all([ - page.getByRole('option').filter({ hasText: 'John Doe' }).click(), - waitForReportingUpdate(page), - ]); + await page.getByRole('option').filter({ hasText: 'John Doe' }).click(); await page.keyboard.press('Escape'); // Verify badge count is gone @@ -544,12 +535,9 @@ test('test that deselecting a tag removes the filter', async ({ page, ctx }) => await expect(page.getByRole('button', { name: 'Tags' }).getByText('1')).toBeVisible(); - // Deselect tag + // Deselect tag (no network request expected — TanStack Query serves cached unfiltered data) await page.getByRole('button', { name: 'Tags' }).click(); - await Promise.all([ - page.getByRole('option').filter({ hasText: tag1Name }).click(), - waitForReportingUpdate(page), - ]); + await page.getByRole('option').filter({ hasText: tag1Name }).click(); await page.keyboard.press('Escape'); await expect(page.getByRole('button', { name: 'Tags' }).getByText(/^\d+$/)).not.toBeVisible(); diff --git a/e2e/utils/api.ts b/e2e/utils/api.ts index 6d9cdc0d..f8d9e02c 100644 --- a/e2e/utils/api.ts +++ b/e2e/utils/api.ts @@ -417,10 +417,9 @@ export async function updateOrganizationCurrencyViaWeb( currency: string, name: string = 'Test Organization' ) { - const response = await ctx.request.put( - `${PLAYWRIGHT_BASE_URL}/teams/${ctx.orgId}`, - { data: { name, currency } } - ); + const response = await ctx.request.put(`${PLAYWRIGHT_BASE_URL}/teams/${ctx.orgId}`, { + data: { name, currency }, + }); expect(response.status()).toBe(200); } diff --git a/e2e/utils/mailpit.ts b/e2e/utils/mailpit.ts index 2ece5802..8d5c807a 100644 --- a/e2e/utils/mailpit.ts +++ b/e2e/utils/mailpit.ts @@ -78,4 +78,4 @@ export async function getPasswordResetUrl( expect(resetUrlMatch).toBeTruthy(); return resetUrlMatch![1].replace(/&/g, '&'); -} \ No newline at end of file +} diff --git a/resources/js/Components/Common/Client/ClientCreateModal.vue b/resources/js/Components/Common/Client/ClientCreateModal.vue index f7a3a453..6ce70036 100644 --- a/resources/js/Components/Common/Client/ClientCreateModal.vue +++ b/resources/js/Components/Common/Client/ClientCreateModal.vue @@ -7,7 +7,7 @@ import type { CreateClientBody } from '@/packages/api/src'; import PrimaryButton from '@/packages/ui/src/Buttons/PrimaryButton.vue'; import { useFocus } from '@vueuse/core'; import { useClientsStore } from '@/utils/useClients'; -import InputLabel from '../../../packages/ui/src/Input/InputLabel.vue'; +import { Field, FieldLabel } from '@/packages/ui/src/field'; const { createClient } = useClientsStore(); const show = defineModel('show', { default: false }); @@ -37,19 +37,19 @@ useFocus(clientNameInput, { initialValue: true });