Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5da11f0
added timeouts to createAccount test to ensure test functions
Mel000000 Jun 26, 2026
d57069b
added deletion test
Mel000000 Jun 26, 2026
8e04e81
expanded on cleanUp script to cleanUp test users
Mel000000 Jun 26, 2026
44d78df
added mailosaur api key to test workflow
Mel000000 Jun 26, 2026
a50cf59
debugging env variable issues
Mel000000 Jun 26, 2026
11517b9
added warmup for render server in workflow
Mel000000 Jun 26, 2026
376694c
added more steps to deletion test and increased timeout lengths of cr…
Mel000000 Jun 27, 2026
f37a493
added wait for render deploy step to testing workflow
Mel000000 Jun 27, 2026
74f45eb
increasing the timeout even more
Mel000000 Jun 27, 2026
4232096
removing the deployment checking step
Mel000000 Jun 27, 2026
5c20af3
extended the sendMail controller to send verification emails to mailp…
Mel000000 Jun 30, 2026
3fcfc74
add the from credentials of the testing emailsender
Mel000000 Jun 30, 2026
70993d7
changed email sending direction for development
Mel000000 Jul 2, 2026
a87948d
added error toast for unverified email password reset
Mel000000 Jul 4, 2026
a0e41ce
added unverified Email tests including one that runs the cleanUp script
Mel000000 Jul 4, 2026
adb4ea6
added more feedback to the reset-password component
Mel000000 Jul 4, 2026
9d45313
added to the CRUD tests by testing error messaging
Mel000000 Jul 4, 2026
e8c1ac1
changed order of Run tests workflow step to see if it will fix the se…
Mel000000 Jul 4, 2026
054adb8
added mongodb uri secret to workflow env
Mel000000 Jul 4, 2026
e539407
updated globalSetup variable in playwright.config.js
Mel000000 Jul 4, 2026
c25c058
remove setup file
Mel000000 Jul 4, 2026
d3fc143
trying to negate the workflow issue by sending current code
Mel000000 Jul 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions .github/scripts/cleanUp.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@ const User = require("../../server/models/user");
const { deleteImageFromCloudinary } = require("../../server/config/cloudinary.js");
const path = require("path");
const dotenv = require("dotenv");
dotenv.config({ path: path.resolve(__dirname, '../../.env') }); // Load .env file
dotenv.config({ path: path.resolve(__dirname, '../../.env') }); // Load .env file (local dev only; CI supplies MONGODB_URI directly)

const uri = process.env.MONGODB_URI;

async function connectDB() {
try {
await mongoose.connect(uri, {dbName: "Authentication-User-Dashboard-App" });
await mongoose.connect(uri, { dbName: "Authentication-User-Dashboard-App" });
console.log("Successfully connected to MongoDB via Mongoose!");
} catch (error) {
console.error("MongoDB connection error:", error);
process.exit(1); // Stop the server if database connection fails
process.exit(1); // Stop the script if database connection fails
}
}

connectDB();

const fetchUsersWithUnverifiedEmailsAndDelete = async () =>{
const users = await User.find()
const fetchEmailsToDelete = async () => {
const users = await User.find();
for (const user of users) {
if (!user.email_verified) {
try {
Expand All @@ -34,14 +34,13 @@ const fetchUsersWithUnverifiedEmailsAndDelete = async () =>{
}
await User.deleteOne({ _id: user._id });
console.log(`Deleted user with unverified email: ${user.email}`);

} catch (err) {
console.error(`Error deleting user ${user.email}:`, err);
}
}
}console.log("Finished checking for unverified emails and deleting users.");
}
console.log("Finished checking for users to delete.");
mongoose.connection.close(); // Close the connection after the operation is complete
};


fetchUsersWithUnverifiedEmailsAndDelete ();
fetchEmailsToDelete();
21 changes: 15 additions & 6 deletions .github/workflows/tests-automation.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
name: Tests Automation

# Normally I would instead use a webhook as the trigger so the test runs on the latest deployment,
# but I will instead use the Render API to fetch the current status of deployment to save costs.
on:
workflow_dispatch:
push:
Expand Down Expand Up @@ -37,16 +39,23 @@ jobs:
- name: Install dependencies
run: npm install --legacy-peer-deps

# Installs OS system dependencies every run, but skips downloading the
# actual browser binaries if they were found in our cache storage step.
- name: Install Playwright browsers
run: npx playwright install --with-deps

- name: Warm up Render instance
run: |
# NOTE: there is no /health route on the server, so this always got a 404
# and only "worked" because of the || true swallowing the failure.
# Hitting the actual signup page still wakes a cold Render instance.
curl -s https://audaf-testing.onrender.com/signup || true
sleep 5

- name: Run tests
run: npx playwright test
env:
CI: true

MONGODB_URI: ${{ secrets.MONGODB_URI }}

- name: Format test results
run: |
if [ -f results.json ]; then
Expand All @@ -63,7 +72,7 @@ jobs:
echo "| ❌ Failed | $FAILED |" >> $GITHUB_STEP_SUMMARY
echo "| ⏭️ Skipped | $SKIPPED |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

# Generate a native Markdown Pie Chart using Mermaid
echo "#### Visual Breakdown" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`mermaid" >> $GITHUB_STEP_SUMMARY
Expand All @@ -82,7 +91,7 @@ jobs:

# Deep-scans the JSON file, filters for unexpected results, and formats with the project name
jq -r '.. | .specs? // empty | .[] | select(.tests[].results[].status != "expected") | .file as $file | .title as $title | .tests[] | select(.results[].status != "expected") | "* ❌ **" + $title + "** — 🌐 \`🏼" + .projectName + "\` *(in \`" + $file + "\`)*"' results.json >> $GITHUB_STEP_SUMMARY

echo "" >> $GITHUB_STEP_SUMMARY
echo "❌ Failing tests detected. Halting pipeline automation."
exit 1
Expand All @@ -102,4 +111,4 @@ jobs:
source_branch: testing
target_branch: main
title: 'Automated Merge from testing to main'
body: 'This PR was automatically created by the Tests Automation workflow after successful tests.'
body: 'This PR was automatically created by the Tests Automation workflow after successful tests.'
4 changes: 2 additions & 2 deletions client/src/components/ResetPassword.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function ResetPasswordCard() {


const validForm = () => {
return password.length >= 6 && password === confirmPassword;
return password.length >= 6 && password === confirmPassword && /[0-9]/.test(password);
};

const handleSubmit = async (e) => {
Expand Down Expand Up @@ -93,7 +93,7 @@ function ResetPasswordCard() {
style={{ borderRadius: '0.75rem', padding: '0.75rem' }}
/>
<Form.Control.Feedback type="invalid">
Password must be at least 6 characters
Password must be at least 6 characters long and contain at least one number
</Form.Control.Feedback>
</Form.Group>

Expand Down
3 changes: 2 additions & 1 deletion client/src/components/VerifyCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ function VerifyCard() {
navigate('/reset-password', { state: { email, resetToken } });
}, 1000);
} catch (error) {
let errorMsg = "Invalid verification code. Please try again.";
let errorMsg = error.response?.data?.error || "Verification failed. Please try again.";
toast.error(errorMsg);
if (error.response) {
if (error.response.status === 429) {
errorMsg = "Too many attempts. Please wait 15 minutes.";
Expand Down
21 changes: 21 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"jsonwebtoken": "^9.0.3",
"mongoose": "^9.1.2",
"multer": "^2.1.1",
"nodemailer": "^9.0.3",
"path": "^0.12.7",
"rate-limit-redis": "^5.0.0",
"react": "^18.2.0",
Expand All @@ -33,6 +34,7 @@
"@playwright/test": "^1.60.0",
"@types/node": "^25.9.1",
"mailosaur": "^11.1.1",
"mailpit-api": "^2.1.0",
"nodemon": "^3.1.14"
},
"scripts": {}
Expand Down
62 changes: 8 additions & 54 deletions playwright.config.js
Original file line number Diff line number Diff line change
@@ -1,75 +1,29 @@
// @ts-check
import { defineConfig, devices } from '@playwright/test';

/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// import dotenv from 'dotenv';
// import path from 'path';
// dotenv.config({ path: path.resolve(__dirname, '.env') });

/**
* @see https://playwright.dev/docs/test-configuration
*/
export default defineConfig({
testDir: './tests',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: process.env.CI
reporter: process.env.CI
? [['list'], ['json', { outputFile: 'results.json' }]]
: [['html']],
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('')`. */
// baseURL: 'http://localhost:3000',

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},

/* Configure projects for major browsers */
projects: [

{
name: 'setup',
testMatch: /.*1-signup\.spec\.ts/,
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
dependencies: ['setup'],
testIgnore: /.*1-signup\.spec\.ts/,
},


/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },

/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],

/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://localhost:3000',
// reuseExistingServer: !process.env.CI,
// },
});

});
34 changes: 31 additions & 3 deletions server/controllers/emailSender.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
require("dotenv").config(); // load .env

const isTest = process.env.NODE_ENV === "test";
const isdevelopment = process.env.NODE_ENV === "development";

module.exports.sendMail = async (email, code) => {
if (isTest || isdevelopment) {
const response = await fetch(`${process.env.MAILPIT_URL}/api/v1/send`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
From: { Email: "authApp@gmail.com", Name: "Auth App" },
To: [{ Email: email }],
Subject: 'Your Verification Code',
HTML: `
<h3>Your Verification Code</h3>
<p>Your verification code is: <strong>${code}</strong></p>
`,
}),
});

if (!response.ok) {
const errorText = await response.text();
console.error('Mailpit send error:', response.status, errorText);
throw new Error('Failed to send email via Mailpit');
}

return response.json();
}

const response = await fetch('https://api.brevo.com/v3/smtp/email', {
method: 'POST',
headers: {
Expand All @@ -27,6 +56,5 @@ module.exports.sendMail = async (email, code) => {
throw new Error('Failed to send email via Brevo API');
}

const data = await response.json();
return data;
};
return response.json();
};
4 changes: 4 additions & 0 deletions server/routes/codeRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ router.post("/resetPassword", doubleCsrfProtection, async (req, res) => {
return res.status(400).json({ error: "Invalid payload input rules" });
}

if(!/[0-9]/.test(newPassword)){
return res.status(400).json({ error: "Password must contain at least one number" });
}

try {
const decoded = jwt.verify(resetToken, process.env.JWT_SECRET_RESET_PASSWORD);
if (!decoded || decoded.email !== email) {
Expand Down
50 changes: 0 additions & 50 deletions tests/00-createAccount.spec.ts

This file was deleted.

Loading
Loading