@bhavinkumarvegad/playwright-email-utils
Version:
Reusable utilities for handling emails in Playwright tests from Yopmail, Gmail and other providers.
187 lines (162 loc) • 5.76 kB
text/typescript
import { google } from 'googleapis';
import { EmailQuery, EmailExtractionRule } from '../types';
import { EmailBodyParser } from '../utils/emailBodyParser';
export interface GmailMessage {
id?: string;
raw?: string;
headers?: Array<{
name: string;
value: string;
}>;
labelIds?: string[];
snippet?: string;
body?: string;
subject?: string;
from?: string;
to?: string;
}
interface GmailConfig {
clientId: string;
clientSecret: string;
refreshToken: string;
redirectUri?: string;
}
async function getOAuth2Client(config: GmailConfig) {
if (!config.refreshToken) {
throw new Error('GMAIL_REFRESH_TOKEN is required in your configuration.');
}
const oAuth2Client = new google.auth.OAuth2(
config.clientId,
config.clientSecret,
config.redirectUri || 'http://localhost'
);
oAuth2Client.setCredentials({ refresh_token: config.refreshToken });
return oAuth2Client;
}
async function getGmail(config: GmailConfig) {
const auth = await getOAuth2Client(config);
return google.gmail({ version: 'v1', auth });
}
async function listMessages(config: GmailConfig, query: string): Promise<GmailMessage[]> {
const gmail = await getGmail(config);
const res = await gmail.users.messages.list({
userId: 'me',
q: query,
});
return (res.data.messages || []).map(msg => ({
id: msg.id || undefined,
labelIds: msg.labelIds || undefined
}));
}
async function getMessage(config: GmailConfig, msgId: string): Promise<GmailMessage> {
const gmail = await getGmail(config);
const res = await gmail.users.messages.get({
userId: 'me',
id: msgId,
format: 'full',
});
const headers = res.data.payload?.headers?.map(h => ({
name: h.name || '',
value: h.value || ''
})) || [];
const subject = headers.find(h => h.name.toLowerCase() === 'subject')?.value;
const from = headers.find(h => h.name.toLowerCase() === 'from')?.value;
const to = headers.find(h => h.name.toLowerCase() === 'to')?.value;
let body = '';
if (res.data.payload?.body?.data) {
body = Buffer.from(res.data.payload.body.data, 'base64').toString('utf8');
} else if (res.data.payload?.parts) {
const textPart = res.data.payload.parts.find(part => part.mimeType === 'text/plain');
if (textPart?.body?.data) {
body = Buffer.from(textPart.body.data, 'base64').toString('utf8');
}
}
return {
id: res.data.id || undefined,
headers,
labelIds: res.data.labelIds || undefined,
snippet: res.data.snippet || undefined,
body,
subject,
from,
to
};
}
export async function fromGmail(config: GmailConfig) {
return {
findEmail: async ({ subject, from, to, afterTime, beforeTime }: EmailQuery): Promise<GmailMessage | null> => {
let query = 'label:inbox';
if (subject) query += ` subject:\"${subject}\"`;
if (from) query += ` from:${from}`;
if (to) query += ` to:${to}`;
if (afterTime) {
const timestamp = Math.floor(new Date(afterTime).getTime() / 1000);
query += ` after:${timestamp}`;
}
if (beforeTime) {
const timestamp = Math.floor(new Date(beforeTime).getTime() / 1000);
query += ` before:${timestamp}`;
}
const messages = await listMessages(config, query);
if (!messages.length || !messages[0].id) return null;
return getMessage(config, messages[0].id);
},
findEmailWithDetails: async (
config: GmailConfig,
query: EmailQuery,
extractionRules: EmailExtractionRule[]
): Promise<{ email: GmailMessage | null; extractedDetails: Record<string, string | null> }> => {
const email = await findEmail(config, query);
if (!email) {
return { email: null, extractedDetails: {} };
}
const extractedDetails: Record<string, string | null> = {};
for (const rule of extractionRules) {
extractedDetails[rule.field] = EmailBodyParser.extractInfo(email, {
type: rule.type,
startMarker: rule.startMarker,
endMarker: rule.endMarker,
regex: rule.regex,
});
}
return { email, extractedDetails };
}
};
}
export async function findEmail(config: GmailConfig, { subject, from, to, afterTime, beforeTime }: EmailQuery): Promise<GmailMessage | null> {
let query = 'label:inbox';
if (subject) query += ` subject:\"${subject}\"`;
if (from) query += ` from:${from}`;
if (to) query += ` to:${to}`;
if (afterTime) {
const timestamp = Math.floor(new Date(afterTime).getTime() / 1000);
query += ` after:${timestamp}`;
}
if (beforeTime) {
const timestamp = Math.floor(new Date(beforeTime).getTime() / 1000);
query += ` before:${timestamp}`;
}
const messages = await listMessages(config, query);
if (!messages.length || !messages[0].id) return null;
return getMessage(config, messages[0].id);
}
export async function findEmailWithDetails(
config: GmailConfig,
query: EmailQuery,
extractionRules: EmailExtractionRule[]
): Promise<{ email: GmailMessage | null; extractedDetails: Record<string, string | null> }> {
const email = await findEmail(config, query);
if (!email) {
return { email: null, extractedDetails: {} };
}
const extractedDetails: Record<string, string | null> = {};
for (const rule of extractionRules) {
extractedDetails[rule.field] = EmailBodyParser.extractInfo(email, {
type: rule.type,
startMarker: rule.startMarker,
endMarker: rule.endMarker,
regex: rule.regex,
});
}
return { email, extractedDetails };
}