252 lines
9.6 KiB
TypeScript
252 lines
9.6 KiB
TypeScript
import { expect, test, type Locator, type Page } from './support/test.ts';
|
|
import { signInAsAdmin } from './support/auth.ts';
|
|
|
|
test('an unreachable worker shows plain SMTP test feedback without retrying', async ({ page }) => {
|
|
await signInAsAdmin(page);
|
|
await page.goto('/admin/health');
|
|
|
|
let emailPosts = 0;
|
|
page.on('request', (request) => {
|
|
if (request.method() === 'POST' && request.url().endsWith('/admin/health?/email')) {
|
|
emailPosts++;
|
|
}
|
|
});
|
|
|
|
const smtpCard = page.locator('.card').filter({
|
|
has: page.getByRole('heading', { name: 'smtp.example.test', exact: true })
|
|
});
|
|
const form = smtpCard.locator('form.email-form');
|
|
await expect(form).toHaveAttribute('action', '?/email');
|
|
await expect(form.getByRole('textbox')).toHaveValue('e2e@example.test');
|
|
await form.getByRole('button', { name: 'Send', exact: true }).click();
|
|
|
|
const feedback = smtpCard.getByRole('status');
|
|
await expect(feedback).toHaveText('Worker not reachable.');
|
|
await expect(feedback).toHaveClass(/\bemail-result\b/);
|
|
await expect(smtpCard.getByRole('alert')).toHaveCount(0);
|
|
const status = smtpCard
|
|
.locator('dt')
|
|
.filter({ hasText: /^Status$/ })
|
|
.locator('+ dd');
|
|
await expect(status).toHaveText('not observed');
|
|
await expect(form.getByRole('button', { name: 'Send', exact: true })).toBeEnabled();
|
|
expect(emailPosts).toBe(1);
|
|
});
|
|
|
|
test('an unreachable worker shows provider auth feedback only on its card without retrying', async ({
|
|
page
|
|
}) => {
|
|
await signInAsAdmin(page);
|
|
await page.goto('/admin/health');
|
|
|
|
let providerPosts = 0;
|
|
page.on('request', (request) => {
|
|
if (request.method() === 'POST' && request.url().endsWith('/admin/health?/testProvider')) {
|
|
providerPosts++;
|
|
}
|
|
});
|
|
|
|
const providerCard = page.locator('.card').filter({
|
|
has: page.getByRole('heading', { name: 'fixture-service', exact: true })
|
|
});
|
|
const otherProviderCard = page.locator('.card').filter({
|
|
has: page.getByRole('heading', { name: 'fixture-video', exact: true })
|
|
});
|
|
const form = providerCard.locator("form[action='?/testProvider']");
|
|
await form.getByRole('button', { name: 'Test', exact: true }).click();
|
|
|
|
const feedback = providerCard.getByRole('status');
|
|
await expect(feedback).toHaveText('Worker not reachable.');
|
|
await expect(feedback).toHaveClass(/\bprovider-test-result\b/);
|
|
await expect(providerCard.getByRole('alert')).toHaveCount(0);
|
|
const status = providerCard
|
|
.locator('dt')
|
|
.filter({ hasText: /^Status$/ })
|
|
.locator('+ dd');
|
|
await expect(status).toHaveText('not observed');
|
|
await expect(otherProviderCard.getByRole('status')).toHaveCount(0);
|
|
await expect(form.getByRole('button', { name: 'Test', exact: true })).toBeEnabled();
|
|
expect(providerPosts).toBe(1);
|
|
});
|
|
|
|
test('an unreachable worker shows calendar test feedback without changing refresh health or retrying', async ({
|
|
page
|
|
}) => {
|
|
await signInAsAdmin(page);
|
|
await page.goto('/admin/health');
|
|
|
|
let calendarPosts = 0;
|
|
page.on('request', (request) => {
|
|
if (request.method() === 'POST' && request.url().endsWith('/admin/health?/testCalendar')) {
|
|
calendarPosts++;
|
|
}
|
|
});
|
|
|
|
const calendarCard = page.locator('.card').filter({
|
|
has: page.getByRole('heading', { name: 'main', exact: true })
|
|
});
|
|
const status = calendarCard
|
|
.locator('dt')
|
|
.filter({ hasText: /^Status$/ })
|
|
.locator('+ dd');
|
|
const lastRefresh = calendarCard
|
|
.locator('dt')
|
|
.filter({ hasText: /^Last refresh$/ })
|
|
.locator('+ dd');
|
|
await expect(status).toHaveText('refresh freshness unknown');
|
|
const refreshBefore = await lastRefresh.innerText();
|
|
const form = calendarCard.locator("form[action='?/testCalendar']");
|
|
await expect(form.locator('input[name="calendar"]')).toHaveValue('main');
|
|
const button = form.getByRole('button', { name: 'Test', exact: true });
|
|
await button.click();
|
|
|
|
const feedback = calendarCard.getByRole('status');
|
|
await expect(feedback).toHaveText('Worker not reachable.');
|
|
await expect(feedback).toHaveClass(/\bcalendar-test-result\b/);
|
|
await expect(feedback).toHaveClass(/\bfeedback-error\b/);
|
|
await expect(calendarCard.getByRole('alert')).toHaveCount(0);
|
|
await expect(status).toHaveText('refresh freshness unknown');
|
|
await expect(lastRefresh).toHaveText(refreshBefore);
|
|
await expect(calendarCard.locator('.failed')).toHaveCount(0);
|
|
await expect(button).toBeEnabled();
|
|
expect(calendarPosts).toBe(1);
|
|
});
|
|
|
|
async function feedbackStyle(feedback: Locator) {
|
|
const appearance = await feedback.evaluate((element) => {
|
|
const style = getComputedStyle(element);
|
|
const secondary = element.closest('.card')!.querySelector('dd')!;
|
|
return {
|
|
style: {
|
|
fontFamily: style.fontFamily,
|
|
fontSize: style.fontSize,
|
|
fontWeight: style.fontWeight,
|
|
lineHeight: style.lineHeight,
|
|
padding: style.padding,
|
|
background: style.backgroundColor,
|
|
border: style.borderTop,
|
|
color: style.color
|
|
},
|
|
secondaryColor: getComputedStyle(secondary).color
|
|
};
|
|
});
|
|
expect(appearance.style.color).toBe(appearance.secondaryColor);
|
|
return appearance.style;
|
|
}
|
|
|
|
async function feedbackThemes(page: Page, feedback: Locator) {
|
|
await expect(feedback).toHaveText('Worker not reachable.');
|
|
await expect(feedback).toHaveClass(/\bfeedback-error\b/);
|
|
await page.emulateMedia({ colorScheme: 'light' });
|
|
const light = await feedbackStyle(feedback);
|
|
await page.emulateMedia({ colorScheme: 'dark' });
|
|
const dark = await feedbackStyle(feedback);
|
|
await page.emulateMedia({ colorScheme: 'light' });
|
|
return { light, dark };
|
|
}
|
|
|
|
test('unreachable discovery settles with consistent error feedback, not an empty calendar list', async ({
|
|
page
|
|
}, testInfo) => {
|
|
await page.setViewportSize({ width: 1280, height: 900 });
|
|
await page.emulateMedia({ colorScheme: 'light' });
|
|
await signInAsAdmin(page);
|
|
await page.goto('/admin/health');
|
|
|
|
const card = (name: string) =>
|
|
page.locator('.card').filter({
|
|
has: page.getByRole('heading', { name, exact: true })
|
|
});
|
|
const providerCard = card('fixture-service');
|
|
const otherProviderCard = card('fixture-video');
|
|
const smtpCard = card('smtp.example.test');
|
|
const calendarCard = card('main');
|
|
|
|
// Each real action replaces the last result; compare its computed styles before moving on.
|
|
await smtpCard.getByRole('button', { name: 'Send', exact: true }).click();
|
|
const smtpStyles = await feedbackThemes(page, smtpCard.getByRole('status'));
|
|
await providerCard.locator("form[action='?/testProvider'] button").click();
|
|
const providerStyles = await feedbackThemes(page, providerCard.getByRole('status'));
|
|
expect(providerStyles).toEqual(smtpStyles);
|
|
await calendarCard.locator("form[action='?/testCalendar'] button").click();
|
|
const calendarStyles = await feedbackThemes(page, calendarCard.getByRole('status'));
|
|
expect(calendarStyles).toEqual(smtpStyles);
|
|
|
|
let discoveryPosts = 0;
|
|
const isDiscovery = (url: URL) => url.pathname === '/admin/health' && url.search === '?/discover';
|
|
page.on('request', (request) => {
|
|
if (request.method() === 'POST' && isDiscovery(new URL(request.url()))) discoveryPosts++;
|
|
});
|
|
// Hold only the browser's POST, then let the fixture server produce the actual failure.
|
|
const gate = Promise.withResolvers<void>();
|
|
await page.route(isDiscovery, async (route) => {
|
|
await gate.promise;
|
|
await route.continue();
|
|
});
|
|
const form = providerCard.locator("form[action='?/discover']");
|
|
await expect(form.locator('input[name="provider"]')).toHaveValue('fixture-service');
|
|
const button = form.getByRole('button', { name: 'List calendars', exact: true });
|
|
const requestStarted = page.waitForRequest(
|
|
(request) => request.method() === 'POST' && isDiscovery(new URL(request.url()))
|
|
);
|
|
try {
|
|
await button.click();
|
|
const request = await requestStarted;
|
|
expect(request.method()).toBe('POST');
|
|
await expect(button).toBeDisabled();
|
|
await expect(button.locator('.spinner')).toBeVisible();
|
|
await expect(providerCard.locator('.found')).toHaveCount(0);
|
|
} finally {
|
|
gate.resolve();
|
|
}
|
|
|
|
const feedback = providerCard.getByRole('status');
|
|
await expect(feedback).toHaveClass(/\bdiscovery-result\b/);
|
|
const discoveryStyles = await feedbackThemes(page, feedback);
|
|
expect(discoveryStyles).toEqual(smtpStyles);
|
|
await expect(button).toBeEnabled();
|
|
await expect(button.locator('.spinner')).toHaveCount(0);
|
|
await expect(button).not.toHaveClass(/\bbusy\b/);
|
|
await expect(providerCard.locator('.found')).toHaveCount(0);
|
|
await expect(page.getByRole('heading', { name: 'Available calendars', exact: true })).toHaveCount(
|
|
0
|
|
);
|
|
await expect(page.getByText('This provider exposes no calendars.', { exact: true })).toHaveCount(
|
|
0
|
|
);
|
|
await expect(page.getByText(/Asking/)).toHaveCount(0);
|
|
await expect(providerCard.getByRole('alert')).toHaveCount(0);
|
|
await expect(
|
|
providerCard
|
|
.locator('dt')
|
|
.filter({ hasText: /^Status$/ })
|
|
.locator('+ dd')
|
|
).toHaveText('not observed');
|
|
await expect(otherProviderCard.getByRole('status')).toHaveCount(0);
|
|
await expect(otherProviderCard.getByRole('alert')).toHaveCount(0);
|
|
await expect(
|
|
otherProviderCard.getByRole('button', { name: 'List calendars', exact: true })
|
|
).toBeEnabled();
|
|
await expect(otherProviderCard.getByRole('button', { name: 'Test', exact: true })).toBeEnabled();
|
|
expect(discoveryPosts).toBe(1);
|
|
|
|
await page.screenshot({
|
|
path: testInfo.outputPath('health-feedback-desktop.png'),
|
|
fullPage: true
|
|
});
|
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
await page.emulateMedia({ colorScheme: 'dark' });
|
|
const mobileStyle = await feedbackStyle(feedback);
|
|
expect(mobileStyle).toEqual(discoveryStyles.dark);
|
|
const dimensions = await page.evaluate(() => ({
|
|
content: document.documentElement.scrollWidth,
|
|
viewport: document.documentElement.clientWidth
|
|
}));
|
|
expect(dimensions.content).toBeLessThanOrEqual(dimensions.viewport);
|
|
await page.screenshot({
|
|
path: testInfo.outputPath('health-feedback-mobile.png'),
|
|
fullPage: true
|
|
});
|
|
expect(discoveryPosts).toBe(1);
|
|
});
|