website: more e2e tests for signin

Fixed issue where existing sign in test fails due to the existence of dev test login.

Added reusable cy.signInWithEmail() to login before testing rest of UI.
This commit is contained in:
Adrian Cowan
2023-01-02 00:27:36 +11:00
parent a196af7ca8
commit 0df6d7fd31
9 changed files with 118 additions and 8 deletions
+7
View File
@@ -22,4 +22,11 @@ export default defineConfig({
getCompareSnapshotsPlugin(on, config);
},
},
env: {
MAILDEV_PROTOCOL: "http",
MAILDEV_HOST: "localhost",
MAILDEV_SMTP_PORT: "1025",
MAILDEV_API_PORT: "1080",
},
});
+33 -2
View File
@@ -1,10 +1,41 @@
import { faker } from "@faker-js/faker";
describe("signin flow", () => {
it("redirects to a confirmation page on submit of valid email address", () => {
cy.visit("/auth/signin");
cy.get(".chakra-input").type(`test@example.com`);
cy.get(".chakra-stack > .chakra-button").click();
cy.get('form[data-cy="signin-email"').within(() => {
cy.get(".chakra-input").type(`test@example.com`);
cy.get(".chakra-stack > .chakra-button").click();
});
cy.url().should("contain", "/auth/verify");
});
it("emails a login link to the user when signing in with email", () => {
// Use random email to avoid possibility of tests passing just due to other tests or previous runs also causing emails to be sent
const emailAddress = faker.internet.email();
cy.log("emailAddress", emailAddress);
cy.request("GET", "/api/auth/csrf")
.then((response) => {
const csrfToken = response.body.csrfToken;
cy.request("POST", "/api/auth/signin/email", {
callbackUrl: "/",
email: emailAddress,
csrfToken,
json: "true",
});
})
.then((response) => {
cy.signInUsingEmailedLink(emailAddress).then(() => {
cy.get('[data-cy="username"]').should("exist");
});
});
});
it("shows the logged in users email address if logged in with email", () => {
const emailAddress = "user@example.com";
cy.signInWithEmail(emailAddress);
// The user will only see the email address if the window is wide enough, not technically required as even when hidden this will find it in the page.
cy.viewport(1920, 1000);
cy.contains('[data-cy="username"]', emailAddress);
});
});
export {};
+34
View File
@@ -36,4 +36,38 @@
// }
// }
Cypress.Commands.add("signInUsingEmailedLink", (emailAddress) => {
const mailDevApi = `${Cypress.env("MAILDEV_PROTOCOL")}://${Cypress.env(
"MAILDEV_HOST"
)}:${Cypress.env("MAILDEV_API_PORT")}`;
cy.request(
"GET",
`${mailDevApi}/email?headers.to=${emailAddress.toLowerCase()}`
).then((response) => {
const emails = response.body;
// Find and use login link
const loginLink = emails
.pop()
.html.match(/href="[^"]+(\/api\/auth\/callback\/[^"]+?)"/)[1];
cy.visit(loginLink);
});
});
Cypress.Commands.add("signInWithEmail", (emailAddress) => {
cy.request("GET", "/api/auth/csrf")
.then((response) => {
const csrfToken = response.body.csrfToken;
cy.request("POST", "/api/auth/signin/email", {
callbackUrl: "/",
email: emailAddress,
csrfToken,
json: "true",
});
})
.then(() => {
cy.signInUsingEmailedLink(emailAddress);
});
});
export {};
-4
View File
@@ -13,12 +13,8 @@
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import "./commands";
import compareSnapshotCommand from "cypress-image-diff-js/dist/command";
compareSnapshotCommand();
// Alternatively you can use CommonJS syntax:
// require('./commands')
export {};
+22
View File
@@ -0,0 +1,22 @@
// load type definitions that come with Cypress module
/// <reference types="cypress" />
declare global {
namespace Cypress {
interface Chainable {
/**
* Custom command to sign in with a given email address
* @example cy.signInWithEmail('user@example.com')
*/
signInWithEmail(emailAddress: string): Chainable<Element>;
/**
* Custom command to sign in with the link emailed to the given email address
* @example cy.signInUsingEmailedLink('user@example.com')
*/
signInUsingEmailedLink(emailAddress: string): Chainable<Element>;
}
}
}
export {};
+17
View File
@@ -44,6 +44,7 @@
"devDependencies": {
"@babel/core": "^7.20.7",
"@chakra-ui/storybook-addon": "^4.0.16",
"@faker-js/faker": "^7.6.0",
"@storybook/addon-actions": "^6.5.15",
"@storybook/addon-essentials": "^6.5.15",
"@storybook/addon-interactions": "^6.5.15",
@@ -3591,6 +3592,16 @@
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/@faker-js/faker": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-7.6.0.tgz",
"integrity": "sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==",
"dev": true,
"engines": {
"node": ">=14.0.0",
"npm": ">=6.0.0"
}
},
"node_modules/@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
@@ -31099,6 +31110,12 @@
}
}
},
"@faker-js/faker": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-7.6.0.tgz",
"integrity": "sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==",
"dev": true
},
"@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
+1
View File
@@ -54,6 +54,7 @@
"devDependencies": {
"@babel/core": "^7.20.7",
"@chakra-ui/storybook-addon": "^4.0.16",
"@faker-js/faker": "^7.6.0",
"@storybook/addon-actions": "^6.5.15",
"@storybook/addon-essentials": "^6.5.15",
"@storybook/addon-interactions": "^6.5.15",
+3 -1
View File
@@ -34,7 +34,9 @@ export function UserMenu() {
height="40"
className="rounded-full"
></Image>
<p className="hidden lg:flex">{session.user.name || session.user.email}</p>
<p data-cy="username" className="hidden lg:flex">
{session.user.name || session.user.email}
</p>
</div>
</Popover.Button>
<AnimatePresence initial={false}>
+1 -1
View File
@@ -41,7 +41,7 @@ export default function Signin({ csrfToken, providers }) {
</form>
)}
{email && (
<form onSubmit={signinWithEmail}>
<form data-cy="signin-email" onSubmit={signinWithEmail}>
<Stack>
<Input variant="outline" size="lg" placeholder="Email Address" ref={emailEl} />
<Button size={"lg"} leftIcon={<FaEnvelope />} colorScheme="gray" type="submit">