import { test, expect } from '../playwright/fixtures'; import { PLAYWRIGHT_BASE_URL, TEST_USER_PASSWORD } from '../playwright/config'; import type { Page } from '@playwright/test'; async function goToProfilePage(page: Page) { await page.goto(PLAYWRIGHT_BASE_URL + '/user/profile'); } test('test that user name can be updated', async ({ page }) => { await page.goto(PLAYWRIGHT_BASE_URL + '/user/profile'); await page.getByLabel('Name', { exact: true }).fill('NEW NAME'); await Promise.all([ page.getByRole('button', { name: 'Save' }).first().click(), page.waitForResponse('**/user/profile-information'), ]); await page.reload(); await expect(page.getByLabel('Name', { exact: true })).toHaveValue('NEW NAME'); }); test.skip('test that user email can be updated', async ({ page }) => { // this does not work because of email verification currently await page.goto(PLAYWRIGHT_BASE_URL + '/user/profile'); const emailId = Math.round(Math.random() * 10000); await page.getByLabel('Email').fill(`newemail+${emailId}@test.com`); await page.getByRole('button', { name: 'Save' }).first().click(); await page.reload(); await expect(page.getByLabel('Email')).toHaveValue(`newemail+${emailId}@test.com`); }); async function createNewApiToken(page) { await page.getByLabel('API Key Name').fill('NEW API KEY'); await Promise.all([ page.getByRole('button', { name: 'Create API Key' }).click(), page.waitForResponse('**/users/me/api-tokens'), ]); await expect(page.locator('body')).toContainText('API Token created successfully'); await page.getByRole('dialog').getByText('Close').click(); await expect(page.locator('body')).toContainText('NEW API KEY'); } test('test that user can create an API key', async ({ page }) => { await page.goto(PLAYWRIGHT_BASE_URL + '/user/profile'); 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); page.getByLabel('Delete API Token NEW API KEY').click(); await expect(page.getByRole('dialog')).toContainText( 'Are you sure you would like to delete this API token?' ); await Promise.all([ page.getByRole('dialog').getByRole('button', { name: 'Delete' }).click(), page.waitForResponse('**/users/me/api-tokens'), ]); await expect(page.locator('body')).not.toContainText('NEW API KEY'); }); test('test that user can revoke an API key', async ({ page }) => { await page.goto(PLAYWRIGHT_BASE_URL + '/user/profile'); await createNewApiToken(page); page.getByLabel('Revoke API Token NEW API KEY').click(); await expect(page.getByRole('dialog')).toContainText( 'Are you sure you would like to revoke this API token?' ); await Promise.all([ page.getByRole('dialog').getByRole('button', { name: 'Revoke' }).click(), page.waitForResponse('**/users/me/api-tokens'), ]); await expect(page.getByRole('button', { name: 'Revoke' })).toBeHidden(); await expect(page.locator('body')).toContainText('NEW API KEY'); await expect(page.locator('body')).toContainText('Revoked'); }); // ============================================= // Update Password Form Tests // ============================================= test('test that password mismatch shows error', async ({ page }) => { await goToProfilePage(page); // Fill in with mismatched passwords await page.getByLabel('Current Password').fill(TEST_USER_PASSWORD); await page.getByLabel('New Password').fill('newSecurePassword456'); await page.getByLabel('Confirm Password').fill('differentPassword789'); // Find the form containing the Confirm Password field and click its Save button const passwordForm = page.getByLabel('Confirm Password').locator('xpath=ancestor::form'); await Promise.all([ page.waitForResponse( (response) => response.url().includes('/user/password') && response.request().method() === 'PUT' ), passwordForm.getByRole('button', { name: 'Save' }).click(), ]); // Verify error message about password confirmation await expect(page.getByText('confirmation does not match')).toBeVisible(); }); test('test that short password shows validation error', async ({ page }) => { await goToProfilePage(page); // Fill in with a too short password await page.getByLabel('Current Password').fill(TEST_USER_PASSWORD); await page.getByLabel('New Password').fill('short'); await page.getByLabel('Confirm Password').fill('short'); // Find the form containing the Confirm Password field and click its Save button const passwordForm = page.getByLabel('Confirm Password').locator('xpath=ancestor::form'); await Promise.all([ page.waitForResponse( (response) => response.url().includes('/user/password') && response.request().method() === 'PUT' ), passwordForm.getByRole('button', { name: 'Save' }).click(), ]); // Verify error message about password length await expect(page.getByText('must be at least')).toBeVisible(); }); test('test that incorrect current password shows validation error', async ({ page }) => { await goToProfilePage(page); // Fill in with wrong current password await page.getByLabel('Current Password').fill('wrongCurrentPassword123'); await page.getByLabel('New Password').fill('newSecurePassword456'); await page.getByLabel('Confirm Password').fill('newSecurePassword456'); // Find the form containing the Confirm Password field and click its Save button const passwordForm = page.getByLabel('Confirm Password').locator('xpath=ancestor::form'); await Promise.all([ page.waitForResponse( (response) => response.url().includes('/user/password') && response.request().method() === 'PUT' ), passwordForm.getByRole('button', { name: 'Save' }).click(), ]); // Verify error message about incorrect password await expect(page.getByText('does not match')).toBeVisible(); }); test('test that password can be updated successfully', async ({ page }) => { await goToProfilePage(page); const newPassword = 'newSecurePassword456'; // Change password to new password await page.getByLabel('Current Password').fill(TEST_USER_PASSWORD); await page.getByLabel('New Password').fill(newPassword); await page.getByLabel('Confirm Password').fill(newPassword); const passwordForm = page.getByLabel('Confirm Password').locator('xpath=ancestor::form'); const responsePromise = page.waitForResponse( (response) => response.url().includes('/user/password') && response.request().method() === 'PUT' ); await passwordForm.getByRole('button', { name: 'Save' }).click(); const response = await responsePromise; // Verify successful response (303 is Inertia redirect on success, means password was updated) expect(response.status()).toBe(303); // Verify no error messages are displayed await expect(page.getByText('does not match')).not.toBeVisible(); await expect(page.getByText('must be at least')).not.toBeVisible(); }); // ============================================= // Theme Selection Tests // ============================================= test('test that theme can be changed to dark and light', async ({ page }) => { await goToProfilePage(page); // The theme select is a Reka UI combobox (button), not a native