mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-07-18 08:49:54 +00:00
145 lines
4.5 KiB
TypeScript
145 lines
4.5 KiB
TypeScript
// frontend/tests/api-key.spec.ts
|
|
import { expect, type Page, test } from '@playwright/test';
|
|
import { apiKeys } from '../data';
|
|
import { cleanupBackend } from '../utils/cleanup.util';
|
|
|
|
test.describe('API Key Management', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await cleanupBackend();
|
|
await page.goto('/settings/admin/api-keys');
|
|
});
|
|
|
|
test('Create new API key', async ({ page }) => {
|
|
await page.getByRole('button', { name: 'Add API Key' }).click();
|
|
|
|
// Fill out the API key form
|
|
const name = 'New Test API Key';
|
|
await page.getByLabel('Name').fill(name);
|
|
await page.getByLabel('Description').fill('Created by automated test');
|
|
|
|
const expectedDate = getDefaultApiKeyExpirationDate();
|
|
await expect(page.getByRole('button', { name: 'Select a date' })).toHaveText(
|
|
expectedDate.toLocaleDateString('en-US', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
})
|
|
);
|
|
|
|
// Submit the form
|
|
await page.getByRole('button', { name: 'Save' }).click();
|
|
|
|
// Verify the success dialog appears
|
|
await expect(page.getByRole('heading', { name: 'API Key Created' })).toBeVisible();
|
|
|
|
// Verify the key details are shown
|
|
await expect(page.getByRole('cell', { name })).toBeVisible();
|
|
|
|
// Verify the token is displayed (should be 32 characters)
|
|
const token = await page.locator('.font-mono').textContent();
|
|
expect(token?.length).toBe(32);
|
|
|
|
// Close the dialog
|
|
await page.getByRole('button', { name: 'Close', exact: true }).nth(1).click();
|
|
|
|
await page.reload();
|
|
|
|
// Verify the key appears in the list
|
|
await expect(page.getByRole('cell', { name }).first()).toContainText(name);
|
|
await expect(
|
|
page.getByRole('row', { name }).getByRole('cell', { name: expectedDate.toLocaleString() })
|
|
).toBeVisible();
|
|
});
|
|
|
|
test('Renew API key', async ({ page }) => {
|
|
const apiKey = apiKeys[1];
|
|
|
|
await page
|
|
.getByRole('row', { name: apiKey.name })
|
|
.getByRole('button', { name: 'Toggle menu' })
|
|
.click();
|
|
|
|
await page.getByRole('menuitem', { name: 'Renew' }).click();
|
|
|
|
const expectedDate = getDefaultApiKeyExpirationDate();
|
|
await selectApiKeyExpirationDate(page, expectedDate);
|
|
await expect(page.getByRole('button', { name: 'Select a date' })).toHaveText(
|
|
expectedDate.toLocaleDateString('en-US', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
})
|
|
);
|
|
|
|
await page.getByRole('button', { name: 'Renew' }).click();
|
|
|
|
await expect(page.getByRole('heading', { name: 'API key renewed' })).toBeVisible();
|
|
|
|
// Verify the new expiration date is shown
|
|
const row = page.getByRole('row', { name: apiKey.name });
|
|
await expect(
|
|
row.getByRole('cell', { name: new RegExp(escapeRegExp(expectedDate.toLocaleDateString())) })
|
|
).toBeVisible();
|
|
});
|
|
|
|
test('Revoke API key', async ({ page }) => {
|
|
const apiKey = apiKeys[0];
|
|
|
|
await page
|
|
.getByRole('row', { name: apiKey.name })
|
|
.getByRole('button', { name: 'Toggle menu' })
|
|
.click();
|
|
|
|
await page.getByRole('menuitem', { name: 'Revoke' }).click();
|
|
|
|
await page.getByRole('button', { name: 'Revoke' }).click();
|
|
|
|
// Verify success message
|
|
await expect(page.locator('[data-type="success"]')).toHaveText('API key revoked successfully');
|
|
|
|
// Verify key is no longer in the list
|
|
await expect(page.getByRole('cell', { name: apiKey.name })).not.toBeVisible();
|
|
});
|
|
});
|
|
|
|
function getDefaultApiKeyExpirationDate() {
|
|
const date = new Date();
|
|
date.setDate(date.getDate() + 30);
|
|
date.setHours(0, 0, 0, 0);
|
|
return date;
|
|
}
|
|
|
|
async function selectApiKeyExpirationDate(page: Page, date: Date) {
|
|
await page.getByRole('button', { name: 'Select a date' }).click();
|
|
|
|
const targetDay = page.getByRole('button', { name: getCalendarDayName(date) });
|
|
const nextButton = page.getByRole('button', { name: 'Next', exact: true });
|
|
|
|
// The calendar opens on the month of the field's current value, which can be an
|
|
// arbitrary number of months away from the target date (e.g. when renewing an
|
|
// already-expired key, whose proposed date is in the past). Advance one month at
|
|
// a time until the target day is rendered, rather than assuming it is exactly one
|
|
// month ahead — that assumption is date-dependent and fails on most days.
|
|
await expect(async () => {
|
|
if (!(await targetDay.isVisible())) {
|
|
await nextButton.click();
|
|
}
|
|
await expect(targetDay).toBeVisible({ timeout: 500 });
|
|
}).toPass({ timeout: 8000 });
|
|
|
|
await targetDay.click();
|
|
}
|
|
|
|
function getCalendarDayName(date: Date) {
|
|
return new Intl.DateTimeFormat('en-US', {
|
|
weekday: 'long',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
year: 'numeric'
|
|
}).format(date);
|
|
}
|
|
|
|
function escapeRegExp(value: string) {
|
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
}
|