refactor: added Mailosaur to manage sms

This commit is contained in:
2021-09-03 09:29:15 +02:00
parent bc6e1bc12e
commit e2a59a566f
13 changed files with 1021 additions and 7 deletions

View File

@@ -0,0 +1,408 @@
/// <reference types="cypress" />
/**
* @class
* Initializes a new instance of the SpamAssassinRule class.
* @constructor
* @member {number} [score]
* @member {string} [rule]
* @member {string} [description]
*/
export interface SpamAssassinRule {
score?: number;
rule?: string;
description?: string;
}
/**
* @class
* Initializes a new instance of the SpamFilterResults class.
* @constructor
* @member {array} [spamAssassin]
*/
export interface SpamFilterResults {
spamAssassin?: SpamAssassinRule[];
}
/**
* @class
* Initializes a new instance of the SpamAnalysisResult class.
* @constructor
* @member {object} [spamFilterResults]
* @member {array} [spamFilterResults.spamAssassin]
* @member {number} [score]
*/
export interface SpamAnalysisResult {
spamFilterResults?: SpamFilterResults;
score?: number;
}
/**
* @class
* Initializes a new instance of the MessageAddress class.
* @constructor
* @member {string} [name] Display name, if one is specified.
* @member {string} [email] Email address (applicable to email messages).
* @member {string} [phone] Phone number (applicable to SMS messages).
*/
export interface MessageAddress {
name?: string;
email?: string;
phone?: string;
}
/**
* @class
* Initializes a new instance of the Link class.
* @constructor
* @member {string} [href]
* @member {string} [text]
*/
export interface Link {
href?: string;
text?: string;
}
/**
* @class
* Initializes a new instance of the Image class.
* @constructor
* @member {string} [src]
* @member {string} [alt]
*/
export interface Image {
src?: string;
alt?: string;
}
/**
* @class
* Initializes a new instance of the MessageContent class.
* @constructor
* @member {array} [links]
* @member {array} [images]
* @member {string} [body]
*/
export interface MessageContent {
links?: Link[];
images?: Image[];
body?: string;
}
/**
* @class
* Initializes a new instance of the Attachment class.
* @constructor
* @member {uuid} id
* @member {string} [contentType]
* @member {string} [fileName]
* @member {string} [contentId]
* @member {number} [length]
* @member {string} [url]
*/
export interface Attachment {
id: string;
contentType?: string;
fileName?: string;
contentId?: string;
length?: number;
url?: string;
}
/**
* @class
* Initializes a new instance of the MessageHeader class.
* @constructor
* @member {string} [field] Header key.
* @member {string} [value] Header value.
*/
export interface MessageHeader {
field?: string;
value?: string;
}
/**
* @class
* Initializes a new instance of the Metadata class.
* @constructor
* Advanced use case content related to the message.
*
* @member {array} [headers] Email headers.
*/
export interface Metadata {
headers?: MessageHeader[];
}
/**
* @class
* Initializes a new instance of the Message class.
* @constructor
* @member {uuid} [id] Unique identifier for the message.
* @member {array} [from] The sender of the message.
* @member {array} [to] The messages recipient.
* @member {array} [cc] Carbon-copied recipients for email messages.
* @member {array} [bcc] Blind carbon-copied recipients for email messages.
* @member {date} [received] The datetime that this message was received by
* Mailosaur.
* @member {string} [subject] The messages subject.
* @member {object} [html] Message content that was sent in HTML format.
* @member {array} [html.links]
* @member {array} [html.images]
* @member {string} [html.body]
* @member {object} [text] Message content that was sent in plain text format.
* @member {array} [text.links]
* @member {array} [text.images]
* @member {string} [text.body]
* @member {array} [attachments] An array of attachment metadata for any
* attached files.
* @member {object} [metadata]
* @member {array} [metadata.headers] Email headers.
* @member {string} [server] Identifier for the server in which the message is
* located.
*/
export interface Message {
id?: string;
from?: MessageAddress[];
to?: MessageAddress[];
cc?: MessageAddress[];
bcc?: MessageAddress[];
received?: Date;
subject?: string;
html?: MessageContent;
text?: MessageContent;
attachments?: Attachment[];
metadata?: Metadata;
server?: string;
}
/**
* @class
* Initializes a new instance of the MessageSummary class.
* @constructor
* @member {uuid} id
* @member {string} [server]
* @member {array} [rcpt]
* @member {array} [from]
* @member {array} [to]
* @member {array} [cc]
* @member {array} [bcc]
* @member {date} [received]
* @member {string} [subject]
* @member {string} [summary]
* @member {number} [attachments]
*/
export interface MessageSummary {
id: string;
server?: string;
rcpt?: MessageAddress[];
from?: MessageAddress[];
to?: MessageAddress[];
cc?: MessageAddress[];
bcc?: MessageAddress[];
received?: Date;
subject?: string;
summary?: string;
attachments?: number;
}
/**
* @class
* Initializes a new instance of the MessageListResult class.
* @constructor
* The result of a message listing request.
*
* @member {array} [items] The individual summaries of each message forming the
* result. Summaries are returned sorted by received date, with the most
* recently-received messages appearing first.
*/
export interface MessageListResult {
items?: MessageSummary[];
}
/**
* @class
* Initializes a new instance of the SearchCriteria class.
* @constructor
* @member {string} [sentFrom] The full email address from which the target email
* was sent.
* @member {string} [sentTo] The full email address to which the target email
* was sent.
* @member {string} [subject] The value to seek within the target email's
* subject line.
* @member {string} [body] The value to seek within the target email's HTML or
* text body.
* @member {string} [match] If set to ALL (default), then only results that match all
* specified criteria will be returned. If set to ANY, results that match any of the
* specified criteria will be returned.
*/
export interface SearchCriteria {
sentFrom?: string;
sentTo?: string;
subject?: string;
body?: string;
match?: "ALL" | "ANY";
}
/**
* @class
* Initializes a new instance of the Server class.
* @constructor
* @member {string} [id] Unique identifier for the server. Used as username for
* SMTP/POP3 authentication.
* @member {string} [name] A name used to identify the server.
* @member {array} [users] Users (excluding administrators) who have access to
* the server.
* @member {number} [messages] The number of messages currently in the server.
*/
export interface Server {
id?: string;
name?: string;
users?: string[];
messages?: number;
}
/**
* @class
* Initializes a new instance of the ServerListResult class.
* @constructor
* The result of a server listing request.
*
* @member {array} [items] The individual servers forming the result. Servers
* are returned sorted by creation date, with the most recently-created server
* appearing first.
*/
export interface ServerListResult {
items?: Server[];
}
/**
* @class
* Initializes a new instance of the ServerCreateOptions class.
* @constructor
* @member {string} [name] A name used to identify the server.
*/
export interface ServerCreateOptions {
name?: string;
}
export interface SearchOptions {
timeout?: number,
receivedAfter?: Date,
page?: number,
itemsPerPage?: number,
suppressError?: boolean
}
declare global {
namespace Cypress {
interface Chainable {
/**
* @summary List all servers
*
* Returns a list of your virtual SMTP servers. Servers are returned sorted in
* alphabetical order.
*
*/
mailosaurListServers(
): Cypress.Chainable<ServerListResult>;
mailosaurCreateServer(
options: ServerCreateOptions
): Cypress.Chainable<Server>;
mailosaurGetServer(
serverId: string
): Cypress.Chainable<Server>;
mailosaurGetServerPassword(
serverId: string
): Cypress.Chainable<string>;
mailosaurUpdateServer(
server: Server
): Cypress.Chainable<Server>;
mailosaurDeleteServer(
serverId: string
): Cypress.Chainable<null>;
mailosaurDeleteAllMessages(
serverId: string
): Cypress.Chainable<null>;
mailosaurListMessages(
serverId: string
): Cypress.Chainable<MessageListResult>;
mailosaurCreateMessage(
serverId: string
): Cypress.Chainable<Message>;
mailosaurGetMessage(
serverId: string,
criteria: SearchCriteria,
options?: SearchOptions
): Cypress.Chainable<Message>;
mailosaurGetMessageById(
messageId: string
): Cypress.Chainable<Message>;
mailosaurSearchMessages(
serverId: string,
criteria: SearchCriteria,
options?: SearchOptions
): Cypress.Chainable<MessageListResult>;
mailosaurGetMessagesBySubject(
serverId: string,
subject: string
): Cypress.Chainable<MessageListResult>;
mailosaurGetMessagesByBody(
serverId: string,
body: string
): Cypress.Chainable<MessageListResult>;
mailosaurGetMessagesBySentFrom(
serverId: string,
sentFrom: string
): Cypress.Chainable<MessageListResult>;
mailosaurGetMessagesBySentTo(
serverId: string,
sentTo: string
): Cypress.Chainable<MessageListResult>;
mailosaurDownloadAttachment(
attachmentId: string
): Cypress.Chainable<Attachment>;
mailosaurDownloadMessage(
messageId: string
): Cypress.Chainable<string>;
mailosaurDeleteMessage(
messageId: string
): Cypress.Chainable<null>;
/**
* @summary Perform a spam test
*
* Perform spam testing on the specified email
*
* @param {string} messageId The identifier of the email to be analyzed.
*
* @returns {Chainable<SpamAnalysisResult>}
*/
mailosaurGetSpamAnalysis(
messageId: string
): Chainable<SpamAnalysisResult>;
mailosaurGenerateEmailAddress(
serverId: string
): Cypress.Chainable<string>;
}
}
}

210
node_modules/cypress-mailosaur/src/mailosaurCommands.js generated vendored Normal file
View File

@@ -0,0 +1,210 @@
const Request = require('./request');
class MailosaurCommands {
static get cypressCommands() {
return [
'mailosaurSetApiKey',
'mailosaurListServers',
'mailosaurCreateServer',
'mailosaurGetServer',
'mailosaurGetServerPassword',
'mailosaurUpdateServer',
'mailosaurDeleteServer',
'mailosaurListMessages',
'mailosaurCreateMessage',
'mailosaurGetMessage',
'mailosaurGetMessageById',
'mailosaurSearchMessages',
'mailosaurGetMessagesBySubject',
'mailosaurGetMessagesByBody',
'mailosaurGetMessagesBySentFrom',
'mailosaurGetMessagesBySentTo',
'mailosaurDeleteMessage',
'mailosaurDeleteAllMessages',
'mailosaurDownloadAttachment',
'mailosaurDownloadMessage',
'mailosaurGetSpamAnalysis',
'mailosaurGenerateEmailAddress'
];
}
constructor() {
const defaultApiKey = Cypress.env('MAILOSAUR_API_KEY');
this.mailosaurSetApiKey(defaultApiKey);
}
mailosaurSetApiKey(apiKey) {
this.request = new Request({ apiKey, baseUrl: Cypress.env('MAILOSAUR_BASE_URL') });
}
mailosaurListServers() {
return this.request.get(`api/servers`);
}
mailosaurCreateServer(params) {
return this.request.post(`api/servers`, params);
}
mailosaurGetServer(serverId) {
return this.request.get(`api/servers/${serverId}`);
}
mailosaurGetServerPassword(serverId) {
return this.request.get(`api/servers/${serverId}/password`)
.then((result) => (result.value));
}
mailosaurUpdateServer(server) {
return this.request.put(`api/servers/${server.id}`, server);
}
mailosaurDeleteServer(serverId) {
return this.request.del(`api/servers/${serverId}`);
}
mailosaurDeleteAllMessages(serverId) {
return this.request.del(`api/messages?server=${serverId}`);
}
mailosaurListMessages(serverId) {
return this.request.get(`api/messages?server=${serverId}`);
}
mailosaurCreateMessage(serverId) {
return this.request.post(`api/messages?server=${serverId}`, {});
}
mailosaurGetMessage(server, criteria, options = {}) {
// Only return 1 result
options.page = 0;
options.itemsPerPage = 1;
// Default timeout to 10s
options.timeout = options.timeout || 10000; // eslint-disable-line no-param-reassign
// Default receivedAfter to 1h
options.receivedAfter = options.receivedAfter || new Date(Date.now() - 3600000); // eslint-disable-line no-param-reassign
return cy.mailosaurSearchMessages(server, criteria, options)
.then((result) => (
cy.mailosaurGetMessageById(result.items[0].id)
));
}
mailosaurGetMessageById(messageId) {
return this.request.get(`api/messages/${messageId}`);
}
mailosaurSearchMessages(serverId, searchCriteria, options = {}) {
let pollCount = 0;
const startTime = Date.now();
const qs = {
server: serverId,
page: options.page,
itemsPerPage: options.itemsPerPage,
receivedAfter: options.receivedAfter
};
if (!Number.isInteger(options.timeout)) {
options.timeout = 0; // eslint-disable-line no-param-reassign
}
if (typeof options.errorOnTimeout !== 'boolean') {
options.errorOnTimeout = true; // eslint-disable-line no-param-reassign
}
const fn = (resolve, reject) => () => {
const reqOptions = this.request.buildOptions('POST', `api/messages/search`);
reqOptions.qs = qs;
reqOptions.json = searchCriteria;
return Cypress.backend('http:request', reqOptions)
.timeout(10000)
.then((result) => {
const { body, status, headers } = result;
switch (status) {
case 200:
break;
case 400:
return reject(new Error(JSON.stringify(result.body)));
case 401:
return reject(new Error('Cannot authenticate with Mailosaur (401). Please check your API key.'));
default:
return reject(new Error(`Status: ${status}, Result: ${JSON.stringify(result)}`));
}
if (options.timeout && !body.items.length) {
const delayPattern = (headers['x-ms-delay'] || '1000')
.split(',')
.map(x => parseInt(x, 10));
const delay = (pollCount >= delayPattern.length) ?
delayPattern[delayPattern.length - 1] :
delayPattern[pollCount];
pollCount += 1;
// Stop if timeout will be exceeded
if (((Date.now() - startTime) + delay) > options.timeout) {
return (options.errorOnTimeout === false) ?
resolve(body) :
reject(new Error('No matching messages found in time. By default, only messages received in the last hour are checked (use receivedAfter to override this).'));
}
return setTimeout(fn(resolve, reject), delay);
}
resolve(body);
});
};
cy.wrap(new Cypress.Promise((resolve, reject) => {
fn(resolve, reject)();
}), {
log: false,
timeout: options.timeout + 10000
});
}
mailosaurGetMessagesBySubject(serverId, subject) {
return cy.mailosaurSearchMessages(serverId, { subject });
}
mailosaurGetMessagesByBody(serverId, body) {
return cy.mailosaurSearchMessages(serverId, { body });
}
mailosaurGetMessagesBySentFrom(serverId, sentFrom) {
return cy.mailosaurSearchMessages(serverId, { sentFrom });
}
mailosaurGetMessagesBySentTo(serverId, sentTo) {
return cy.mailosaurSearchMessages(serverId, { sentTo });
}
mailosaurDownloadAttachment(attachmentId) {
return this.request.get(`api/files/attachments/${attachmentId}`);
}
mailosaurDownloadMessage(messageId) {
return this.request.get(`api/files/email/${messageId}`);
}
mailosaurDeleteMessage(messageId) {
return this.request.del(`api/messages/${messageId}`);
}
mailosaurGetSpamAnalysis(messageId) {
return this.request.get(`api/analysis/spam/${messageId}`);
}
mailosaurGenerateEmailAddress(serverId) {
const host = Cypress.env('MAILOSAUR_SMTP_HOST') || 'mailosaur.net';
const random = (Math.random() + 1).toString(36).substring(7);
return cy.wrap(`${random}@${serverId}.${host}`);
}
}
module.exports = MailosaurCommands;

15
node_modules/cypress-mailosaur/src/register.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
const MailosaurCommands = require('./mailosaurCommands');
const register = (Cypress) => {
const mailosaurCommands = new MailosaurCommands();
MailosaurCommands.cypressCommands.forEach((commandName) => {
Cypress.Commands.add(
commandName,
mailosaurCommands[commandName].bind(mailosaurCommands)
);
});
};
module.exports = {
register,
};

56
node_modules/cypress-mailosaur/src/request.js generated vendored Normal file
View File

@@ -0,0 +1,56 @@
const pkg = require('../package.json');
/* eslint-disable max-classes-per-file */
class Request {
constructor(options) {
this.baseUrl = options.baseUrl || 'https://mailosaur.com/';
this.apiKey = options.apiKey;
const encodedKey = Buffer.from(`${this.apiKey}:`).toString('base64');
this.headers = {
Accept: 'application/json',
Authorization: `Basic ${encodedKey}`,
'User-Agent': `cypress-mailosaur/${pkg.version}`
};
}
buildOptions(method, path) {
if (!this.apiKey) {
// CYPRESS_ prefix necessary per https://docs.cypress.io/guides/guides/environment-variables.html#Option-3-CYPRESS
throw new Error('You must set the CYPRESS_MAILOSAUR_API_KEY environment variable to use the Mailosaur plugin.');
}
return {
method,
url: `${this.baseUrl}${path}`,
headers: {
Accept: this.headers.Accept,
Authorization: this.headers.Authorization,
'User-Agent': this.headers['User-Agent']
}
};
}
request(method, path, body) {
const options = this.buildOptions(method, path);
options.body = body || undefined;
return cy.request(options).its('body');
}
get(path) {
return this.request('GET', path);
}
post(path, body) {
return this.request('POST', path, body);
}
put(path, body) {
return this.request('PUT', path, body);
}
del(path) {
return this.request('DELETE', path);
}
}
module.exports = Request;