Files
pocket-id/tests/specs/api-key.spec.ts
T

128 lines
4.1 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: 'Create 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'
})
);
await page.getByRole('dialog').getByRole('button', { name: 'Create API key' }).click();
// The sheet turns into the one-time reveal of the new key
const sheet = page.getByRole('dialog', { name: 'API key created' });
await expect(sheet).toBeVisible();
await expect(sheet.getByText(name)).toBeVisible();
const token = await sheet.getByRole('textbox', { name: 'API key' }).textContent();
expect(token?.trim().length).toBe(32);
await sheet.getByRole('button', { name: 'Done' }).click();
await page.reload();
await expect(page.getByRole('row', { name })).toContainText('Expires in 30 days');
});
test('Renew API key', async ({ page }) => {
const apiKey = apiKeys[1];
await page
.getByRole('row', { name: apiKey.name })
.getByRole('button', { name: /^Actions for / })
.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 key' }).click();
await expect(page.getByRole('heading', { name: 'API key renewed' })).toBeVisible();
await page.getByRole('button', { name: 'Done' }).click();
await expect(page.getByRole('row', { name: apiKey.name })).toContainText('Expires in 30 days');
});
test('Revoke API key', async ({ page }) => {
const apiKey = apiKeys[0];
await page
.getByRole('row', { name: apiKey.name })
.getByRole('button', { name: /^Actions for / })
.click();
await page.getByRole('menuitem', { name: 'Revoke' }).click();
await page.getByRole('alertdialog').getByRole('button', { name: 'Revoke' }).click();
await expect(page.locator('[data-type="success"]')).toHaveText(`${apiKey.name} revoked`);
// Verify key is no longer in the list
await expect(page.getByRole('row', { 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);
}