1
0
mirror of https://github.com/quay/quay.git synced 2026-01-26 06:21:37 +03:00
Files
quay/web/playwright/utils/mailpit.ts
jbpratt 0638c19183 test(web): migrate more tests to playwright (#4767)
* test(web): migrate repository-autopruning to Playwright

Consolidate 17 Cypress tests into 6 Playwright tests:
- policy lifecycle (create, update, delete)
- policy with tag pattern filter
- multiple policies lifecycle
- namespace policy display in repository settings
- registry policy display
- error handling (load failure)

Uses @feature:AUTO_PRUNE tag for automatic test skipping.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* test: enable features by default

Signed-off-by: Brady Pratt <bpratt@redhat.com>

* test(web): migrate create-account.cy.ts to Playwright

Migrates the Create Account Cypress tests to Playwright following
the MIGRATION.md guide:

- Consolidates 10 Cypress tests into 6 focused Playwright tests
- Uses real API calls instead of mocks
- Adds data-testid attributes to CreateAccount component
- Uses @feature:MAILING and @feature:QUOTA_MANAGEMENT tags
  to skip tests when features are not enabled
- Creates custom fixtures for unauthenticated page access
- Implements proper user cleanup after tests

Tests:
- form validation prevents invalid submissions
- creates account with valid inputs and redirects
- shows error for existing username
- navigates to signin page via link
- shows verification message (requires MAILING)
- redirects to updateuser (requires QUOTA_MANAGEMENT)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Brady Pratt <bpratt@redhat.com>

* chore(dev) add Mailpit for local email testing

Add Mailpit email testing server to the local development environment
to enable testing of FEATURE_MAILING functionality with Playwright.

Changes:
- Add mailpit service to docker-compose.yaml (ports 8025/1025)
- Enable FEATURE_MAILING and configure SMTP settings in config.yaml
- Add mailpit utilities to Playwright fixtures (getEmails, clearInbox,
  waitForEmail, getEmailBody, isAvailable)

Usage:
  podman-compose up mailpit -d
  # Access Web UI at http://localhost:8025

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test(web) use mailpit helpers for email confirmation support

Test updates:
- "creates account and redirects to organization" now confirms email
- "redirects to updateuser when user has prompts" now confirms email
- Tests detect FEATURE_MAILING at runtime and adapt accordingly
- Email search uses recipient address for parallel test safety

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Brady Pratt <bpratt@redhat.com>

* test(web): use mailpit for email notification test

Replace mocked email authorization with real Mailpit verification
in the notifications.spec.ts test.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Brady Pratt <bpratt@redhat.com>

* test(web): mock user response in cypress test

this broke when mailing was enabled

Signed-off-by: Brady Pratt <bpratt@redhat.com>

---------

Signed-off-by: Brady Pratt <bpratt@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-05 09:47:29 -08:00

154 lines
4.1 KiB
TypeScript

/**
* Mailpit: Local email testing utilities
*
* Requires mailpit to be running (docker-compose up -d mailpit).
*
* @example
* ```typescript
* import { mailpit } from './utils/mailpit';
*
* await mailpit.clearInbox();
* const email = await mailpit.waitForEmail(msg => msg.Subject.includes('Verify'));
* const link = await mailpit.extractLink(email.ID);
* ```
*/
const MAILPIT_API = 'http://localhost:8025/api/v1';
/**
* Email message from Mailpit API
*/
export interface MailpitMessage {
ID: string;
From: {Address: string; Name: string};
To: {Address: string; Name: string}[];
Subject: string;
Snippet: string;
Created: string;
}
/**
* Response from Mailpit messages endpoint
*/
export interface MailpitMessagesResponse {
messages: MailpitMessage[];
total: number;
}
/**
* Mailpit utilities for testing email functionality.
*/
export const mailpit = {
/**
* Get all emails in the inbox
*/
async getEmails(): Promise<MailpitMessagesResponse> {
const response = await fetch(`${MAILPIT_API}/messages`);
if (!response.ok) {
throw new Error(`Mailpit API error: ${response.status}`);
}
return response.json();
},
/**
* Clear all emails from the inbox
*/
async clearInbox(): Promise<void> {
const response = await fetch(`${MAILPIT_API}/messages`, {method: 'DELETE'});
if (!response.ok) {
throw new Error(`Mailpit API error: ${response.status}`);
}
},
/**
* Wait for an email matching the predicate
*
* @param predicate - Function to match the desired email
* @param timeout - Max wait time in ms (default: 10000)
* @param interval - Poll interval in ms (default: 500)
* @returns Matching email or null if not found within timeout
*/
async waitForEmail(
predicate: (msg: MailpitMessage) => boolean,
timeout = 10000,
interval = 500,
): Promise<MailpitMessage | null> {
const start = Date.now();
while (Date.now() - start < timeout) {
const {messages} = await this.getEmails();
const found = messages.find(predicate);
if (found) return found;
await new Promise((r) => setTimeout(r, interval));
}
return null;
},
/**
* Get the full body of an email
*
* @param id - Email ID from MailpitMessage.ID
* @returns Email body (plain text if available, otherwise HTML)
*/
async getEmailBody(id: string): Promise<string> {
const response = await fetch(`${MAILPIT_API}/message/${id}`);
if (!response.ok) {
throw new Error(`Mailpit API error: ${response.status}`);
}
const data = await response.json();
return data.Text || data.HTML;
},
/**
* Check if Mailpit is available
*
* @returns true if Mailpit is running and accessible
*/
async isAvailable(): Promise<boolean> {
try {
const response = await fetch(`${MAILPIT_API}/messages`, {
signal: AbortSignal.timeout(1000),
});
return response.ok;
} catch {
return false;
}
},
/**
* Extract a confirmation/action link from an email body
*
* @param emailId - Email ID from MailpitMessage.ID
* @param linkPattern - Regex pattern to match the link (default: URLs with code= parameter)
* @returns The extracted URL or null if not found
*/
async extractLink(
emailId: string,
linkPattern = /https?:\/\/[^\s\])]+[?&]code=[^\s\])]+/,
): Promise<string | null> {
const body = await this.getEmailBody(emailId);
const match = body.match(linkPattern);
return match ? match[0] : null;
},
/**
* Wait for a confirmation email and extract the confirmation link
*
* @param emailAddress - Email address to look for
* @param timeout - Max wait time in ms (default: 10000)
* @returns The confirmation URL or null if not found
*/
async waitForConfirmationLink(
emailAddress: string,
timeout = 10000,
): Promise<string | null> {
const email = await this.waitForEmail(
(msg) =>
msg.To.some((to) => to.Address === emailAddress) &&
msg.Subject.includes('confirm'),
timeout,
);
if (!email) return null;
return this.extractLink(email.ID);
},
};