mirror of
https://github.com/quay/quay.git
synced 2026-01-26 06:21:37 +03:00
* 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>
154 lines
4.1 KiB
TypeScript
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);
|
|
},
|
|
};
|