refactor: standardize API error handling (#1635)

This commit is contained in:
Elias Schneider
2026-08-02 23:36:06 +02:00
committed by GitHub
parent 281bea54d3
commit 7a4d0dd275
108 changed files with 3163 additions and 1629 deletions
+2 -2
View File
@@ -134,8 +134,8 @@ test('Email verification fails with expired token', async ({ page, context }) =>
await page.getByRole('button', { name: 'Authenticate' }).click();
await page.waitForURL(
'/settings/account?emailVerificationState=Invalid+email+verification+token'
'/settings/account?emailVerificationState=Email+verification+token+is+invalid'
);
await expect(page.getByText('Invalid email verification token')).toBeVisible();
await expect(page.getByText('Email verification token is invalid')).toBeVisible();
});
+3 -1
View File
@@ -60,7 +60,9 @@ test('Cannot create an API with the issuer as resource', async ({ page }) => {
await page.getByLabel('Resource').fill(issuer);
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.locator('[data-type="error"]')).toContainText('reserved');
await expect(page.locator('[data-type="error"]')).toHaveText(
'Resource is reserved by Pocket ID and cannot be used for a custom API'
);
});
test('Edit the name of an API', async ({ page }) => {
@@ -174,7 +174,7 @@ test.describe('Update application images', () => {
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.locator('[data-type="error"]')).toHaveText(
'File must be of type .png or .jpg/jpeg'
'File must be of type PNG or JPEG'
);
});
});
+1
View File
@@ -173,6 +173,7 @@ test('Filter OIDC clients by PAR requirement', async ({ page, request }) => {
// Deselect "Yes" and select "No" to invert the filter
await page.getByTestId('facet-par-option-true').click();
await expect(page.getByRole('row', { name: oidcClients.nextcloud.name })).toBeVisible();
await page.getByTestId('facet-par-option-false').click();
// PAR client should be hidden, others visible
+9 -11
View File
@@ -128,7 +128,7 @@ test('Authorize new client while not signed in', async ({ page }) => {
);
});
test('Authorize new client fails with user group not allowed', async ({ page }) => {
test('Authorize new client shows Pocket ID error when user group not allowed', async ({ page }) => {
const oidcClient = oidcClients.immich;
const urlParams = createUrlParams(oidcClient);
@@ -140,15 +140,12 @@ test('Authorize new client fails with user group not allowed', async ({ page })
await expectScopes(page, ['Email', 'Profile']);
const callbackUrl = await expectCallbackRedirect(page, oidcClient.callbackUrl, () =>
page.getByRole('button', { name: 'Sign in' }).click()
);
await page.getByRole('button', { name: 'Sign in' }).click();
expect(callbackUrl.searchParams.get('error')).toBe('access_denied');
expect(callbackUrl.searchParams.get('error_description')).toContain(
'You are not allowed to access this service.'
);
expect(callbackUrl.searchParams.get('state')).toBe(urlParams.get('state'));
await expect(page).toHaveURL(/\/interaction\/error\?error=/);
await expect(
page.getByRole('paragraph').filter({ hasText: 'You are not allowed to access this service.' })
).toBeVisible();
});
function createUrlParams(oidcClient: { id: string; callbackUrl: string }) {
@@ -761,7 +758,7 @@ test('Authorize client with device authorization flow with invalid code', async
await page.goto('/device?user_code=invalid-code');
await expect(
page.getByRole('paragraph').filter({ hasText: 'Invalid device code.' })
page.getByRole('paragraph').filter({ hasText: 'Device code is invalid. Please try again.' })
).toBeVisible();
});
@@ -1101,7 +1098,7 @@ test.describe('OIDC prompt parameter', () => {
await expect(selectionCard).toContainText('Tim Cook');
await expectCallbackRedirect(page, oidcClient.callbackUrl, () =>
page.getByRole('button', { name: 'Sign In' }).click()
page.getByRole('button', { name: /sign in/i }).click()
);
});
@@ -1473,6 +1470,7 @@ test.describe('Pushed Authorization Requests (PAR)', () => {
}
await page.getByRole('button', { name: /save/i }).click();
await expect(page.getByText('OIDC client updated successfully', { exact: true })).toBeVisible();
await page.reload();
await page.getByRole('button', { name: 'Show Advanced Options' }).click();
+16 -10
View File
@@ -98,11 +98,11 @@ test.describe('SCIM Sync', () => {
test('Sync client', async ({ page }) => {
await syncScimServiceProvider(page);
await expectScimResourceCount('Users', 2);
const scimUsers = await getScimResources('Users');
await expect(scimUsers.length).toBe(2);
await expectScimResourceCount('Groups', 2);
const groups = await getScimResources('Groups');
await expect(groups.length).toBe(2);
const timUser = scimUsers.find((u: any) => u.userName === 'tim');
await expect(timUser).toBeDefined();
@@ -135,16 +135,18 @@ test.describe('SCIM Sync', () => {
await expect(developersCheckbox).toHaveAttribute('data-state', 'unchecked');
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.getByText('Allowed user groups updated successfully', { exact: true })).toBeVisible();
await expect(
page.getByText('Allowed user groups updated successfully', { exact: true })
).toBeVisible();
await syncScimServiceProvider(page);
await expectScimResourceCount('Users', 1);
const scimUsers = await getScimResources('Users');
await expect(scimUsers.length).toBe(1);
await expect(scimUsers.find((u: any) => u.userName === users.tim.username)).toBeDefined();
await expectScimResourceCount('Groups', 1);
const scimGroups = await getScimResources('Groups');
await expect(scimGroups.length).toBe(1);
await expect(
scimGroups.find((g: any) => g.displayName === userGroups.designers.friendlyName)
).toBeDefined();
@@ -157,17 +159,21 @@ test.describe('SCIM Sync', () => {
await page.getByRole('button', { name: 'Unrestrict' }).click();
await page.getByRole('button', { name: 'Unrestrict' }).nth(1).click();
await expect(
page.getByText('User groups restriction updated successfully', { exact: true })
).toBeVisible();
await syncScimServiceProvider(page);
const scimUsers = await getScimResources('Users');
await expect(scimUsers.length).toBe(3);
const scimGroups = await getScimResources('Groups');
await expect(scimGroups.length).toBe(2);
await expectScimResourceCount('Users', 3);
await expectScimResourceCount('Groups', 2);
});
});
async function expectScimResourceCount(resourceType: 'Users' | 'Groups', count: number) {
await expect.poll(async () => (await getScimResources(resourceType)).length).toBe(count);
}
async function getScimResources(resourceType: 'Users' | 'Groups') {
const response = await fetch(`${process.env.SCIM_SERVICE_PROVIDER_URL}/${resourceType}`).then(
(res) => res.json()
+1 -1
View File
@@ -99,7 +99,7 @@ test.describe('Initial User Signup', () => {
);
const successfulResponses = responses.filter((response) => response.status() === 200);
const rejectedResponses = responses.filter((response) => response.status() === 404);
const rejectedResponses = responses.filter((response) => response.status() === 409);
expect(successfulResponses).toHaveLength(1);
expect(rejectedResponses).toHaveLength(requestCount - 1);