TIDE/frontend/blog_portal/cypress/support/commands.ts
Yisroel Baum 178193bfd8
add cypress api-driven seeding helpers
cy.signupViaApi, cy.fetchLatestConfirmToken (parses the mailpit
inbox), cy.confirmViaApi, cy.loginViaApi, cy.logoutViaApi, plus
composed seedConfirmedUser/seedAdmin/seedPostAs/seedFeaturedPost
so each spec can build its own fixture without going through
the UI for setup.
2026-05-06 23:24:26 +03:00

235 lines
5.7 KiB
TypeScript

/// <reference types="cypress" />
interface MailpitAddress {
Address: string;
Name: string;
}
interface MailpitMessageSummary {
ID: string;
From: MailpitAddress;
To: MailpitAddress[];
Subject: string;
Snippet: string;
Created: string;
}
interface MailpitMessages {
total: number;
unread: number;
count: number;
messages: MailpitMessageSummary[];
}
interface MailpitMessageBody {
ID: string;
Text: string;
HTML: string;
}
interface SignupArgs {
email: string;
displayName: string;
}
interface ConfirmArgs {
token: string;
password: string;
}
interface LoginArgs {
email: string;
password: string;
}
interface SeedConfirmedUserArgs {
email: string;
displayName: string;
password: string;
}
type SeedAdminArgs = SeedConfirmedUserArgs;
interface SeedPostArgs {
email: string;
password: string;
title: string;
body: string;
}
interface CreatedPost {
id: number;
userId: number;
authorDisplayName: string;
title: string;
body: string;
createdAt: string;
featureSlot: number | null;
}
interface SeedFeaturedPostArgs {
adminEmail: string;
adminPassword: string;
postId: number;
slot: number;
}
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
interface Chainable {
resetDb(): Chainable<null>;
seedDb(): Chainable<null>;
promoteAdmin(email: string): Chainable<null>;
clearMail(): Chainable<null>;
getMail(): Chainable<MailpitMessages>;
getMailBody(id: string): Chainable<MailpitMessageBody>;
signupViaApi(args: SignupArgs): Chainable<void>;
fetchLatestConfirmToken(email: string): Chainable<string>;
confirmViaApi(args: ConfirmArgs): Chainable<void>;
loginViaApi(args: LoginArgs): Chainable<void>;
logoutViaApi(): Chainable<void>;
seedConfirmedUser(args: SeedConfirmedUserArgs): Chainable<void>;
seedAdmin(args: SeedAdminArgs): Chainable<void>;
seedPostAs(args: SeedPostArgs): Chainable<CreatedPost>;
seedFeaturedPost(args: SeedFeaturedPostArgs): Chainable<void>;
}
}
}
const apiBase = "/api";
Cypress.Commands.add("resetDb", function () {
return cy.task<null>("db:reset");
});
Cypress.Commands.add("seedDb", function () {
return cy.task<null>("db:seed");
});
Cypress.Commands.add("promoteAdmin", function (email: string) {
return cy.task<null>("db:promote", email);
});
Cypress.Commands.add("clearMail", function () {
return cy.task<null>("mailpit:clear");
});
Cypress.Commands.add("getMail", function () {
return cy.task<MailpitMessages>("mailpit:messages");
});
Cypress.Commands.add("getMailBody", function (id: string) {
return cy.task<MailpitMessageBody>("mailpit:message", id);
});
Cypress.Commands.add("signupViaApi", function (args: SignupArgs) {
cy.request({
method: "POST",
url: `${apiBase}/signup`,
body: { email: args.email, displayName: args.displayName },
}).then(function (response) {
expect(response.status).to.equal(201);
});
});
Cypress.Commands.add("fetchLatestConfirmToken", function (email: string) {
return cy.getMail().then(function (inbox) {
const match = inbox.messages.find(function (message) {
return message.To.some(function (to) {
return to.Address.toLowerCase() === email.toLowerCase();
});
});
if (!match) {
throw new Error(`no mailpit message for ${email}`);
}
return cy.getMailBody(match.ID).then(function (body) {
const text = body.Text || body.HTML;
const tokenMatch = text.match(/token=([a-f0-9]+)/);
if (!tokenMatch) {
throw new Error("confirmation token not found in mail body");
}
return tokenMatch[1];
});
});
});
Cypress.Commands.add("confirmViaApi", function (args: ConfirmArgs) {
cy.request({
method: "POST",
url: `${apiBase}/confirm-email`,
body: { token: args.token, password: args.password },
}).then(function (response) {
expect(response.status).to.equal(200);
});
});
Cypress.Commands.add("loginViaApi", function (args: LoginArgs) {
cy.request({
method: "POST",
url: `${apiBase}/login`,
body: { email: args.email, password: args.password },
}).then(function (response) {
expect(response.status).to.equal(200);
});
});
Cypress.Commands.add("logoutViaApi", function () {
cy.request({
method: "POST",
url: `${apiBase}/logout`,
failOnStatusCode: false,
});
});
Cypress.Commands.add(
"seedConfirmedUser",
function (args: SeedConfirmedUserArgs) {
cy.signupViaApi({ email: args.email, displayName: args.displayName });
cy.fetchLatestConfirmToken(args.email).then(function (token) {
cy.confirmViaApi({ token, password: args.password });
});
cy.logoutViaApi();
},
);
Cypress.Commands.add("seedAdmin", function (args: SeedAdminArgs) {
cy.seedConfirmedUser({
email: args.email,
displayName: args.displayName,
password: args.password,
});
cy.promoteAdmin(args.email);
});
Cypress.Commands.add("seedPostAs", function (args: SeedPostArgs) {
cy.loginViaApi({ email: args.email, password: args.password });
return cy
.request({
method: "POST",
url: `${apiBase}/posts`,
body: { title: args.title, body: args.body },
})
.then(function (response) {
expect(response.status).to.equal(201);
cy.logoutViaApi();
return response.body.post as CreatedPost;
});
});
Cypress.Commands.add(
"seedFeaturedPost",
function (args: SeedFeaturedPostArgs) {
cy.loginViaApi({ email: args.adminEmail, password: args.adminPassword });
cy.request({
method: "POST",
url: `${apiBase}/admin/posts/feature`,
body: { postId: args.postId, slot: args.slot },
}).then(function (response) {
expect(response.status).to.equal(200);
});
cy.logoutViaApi();
},
);
export {};