-
Notifications
You must be signed in to change notification settings - Fork 75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat: Email Validation #135
base: main
Are you sure you want to change the base?
Conversation
…e met before AI contineuing execution
… the peer dependencies are handled by the package managers
…ht browsers if it's not found
Merge main
Merge update-readme branch
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
@slavingia FYI I ended up using Mailosaur as it provides more features and is faster. |
… and render it in the new page for AI to see
…ypto to work with mailosaur
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @slavingia . Here's my implementation for Email Validation. Below is a simple test case in Helper. I added explanation for important parts of the code for you. Let me know if you'd like me explain any parts in more details. Before I finalize my PR please let me know if you'd like to adjust something. Here's the demo:
helper.mp4
@@ -29,36 +29,38 @@ | |||
"build:types": "tsc --emitDeclarationOnly --outDir dist/types && cp index.d.ts dist/", | |||
"build:js": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:esbuild --external:punycode --external:playwright --external:@anthropic-ai/sdk --external:expect --external:dotenv", | |||
"build:cjs": "esbuild src/index.ts --bundle --platform=node --format=cjs --outfile=dist/index.cjs --external:esbuild --external:punycode --external:playwright --external:@anthropic-ai/sdk --external:expect --external:dotenv", | |||
"build:cli": "esbuild src/cli/bin.ts src/cli/setup.ts --bundle --platform=node --format=esm --outdir=dist/cli --metafile=dist/meta-cli.json --external:fsevents --external:chokidar --external:glob --external:esbuild --external:events --external:path --external:fs --external:util --external:stream --external:os --external:assert --external:url --external:playwright --external:@anthropic-ai/sdk --external:expect --external:dotenv --external:otplib --external:picocolors --external:punycode", | |||
"build:cli": "esbuild src/cli/bin.ts src/cli/setup.ts --bundle --platform=node --format=esm --outdir=dist/cli --metafile=dist/meta-cli.json --external:fsevents --external:chokidar --external:glob --external:esbuild --external:events --external:path --external:fs --external:util --external:stream --external:os --external:assert --external:url --external:playwright --external:@anthropic-ai/sdk --external:expect --external:dotenv --external:otplib --external:picocolors --external:punycode --external:https --external:http --external:net --external:tls --external:crypto --external:mailosaur", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we are just marking some more nodejs built ins as external so that they will work with Mailosaur implementation:. Here is the list:
https
http
net
tls
crypto
{ | ||
name: "render_email", | ||
description: "Fetch, decode and render received email in browser", | ||
input_schema: { | ||
type: "object", | ||
properties: { | ||
action: { | ||
type: "string", | ||
enum: ["render_email"], | ||
description: "Render the received email in a new tab" | ||
} | ||
}, | ||
required: ["action"] | ||
} | ||
}, | ||
{ | ||
name: "sleep_milliseconds", | ||
description: "Pause test execution for specified duration", | ||
input_schema: { | ||
type: "object", | ||
properties: { | ||
action: { | ||
type: "string", | ||
enum: ["sleep_milliseconds"], | ||
description: "The action to perform" | ||
}, | ||
duration: { | ||
type: "number", | ||
description: "Duration to sleep in milliseconds (e.g. 5000 for 5 seconds)", | ||
minimum: 0, | ||
maximum: 60000 | ||
} | ||
}, | ||
required: ["action", "duration"] | ||
} | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added two new tools here: render_email and sleep_miliseconds
render_email is used to execute Mailosaur email validation in browser-tools.ts
file.
sleep_miliseconds
is used to help AI to wait for a condition to be met. Either instructed by the user or based on AI judgement.
Why both of them needed in this PR:
getting the latest email requires AI to wait for a few seconds to so that the user receives the email, otherwise the test case might fail prematurely
case 'sleep_milliseconds': { | ||
const defaultDuration = 1000; | ||
const maxDuration = 60000; | ||
let duration = input.duration ?? defaultDuration; | ||
|
||
// Enforce maximum duration | ||
if (duration > maxDuration) { | ||
console.warn(`Requested sleep duration ${duration}ms exceeds maximum of ${maxDuration}ms. Using maximum.`); | ||
duration = maxDuration; | ||
} | ||
|
||
// Convert to seconds for logging | ||
const seconds = Math.round(duration / 1000); | ||
console.log(`⏳ Waiting for ${seconds} second${seconds !== 1 ? 's' : ''}...`); | ||
|
||
await this.page.waitForTimeout(duration); | ||
output = `Finished waiting for ${seconds} second${seconds !== 1 ? 's' : ''}`; | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implementation for sleep_miliseconds: max time is 60 seconds and min is 1 second
case 'render_email': { | ||
if (!this.mailosaurTool) { | ||
if (!this.config.mailosaur) { | ||
throw new ToolError('Mailosaur configuration required'); | ||
} | ||
this.mailosaurTool = new MailosaurTool(this.config.mailosaur); | ||
} | ||
|
||
const newPage = await this.page.context().newPage(); | ||
|
||
try { | ||
const email = await this.mailosaurTool.getLatestEmail(); | ||
|
||
// Render email in new tab | ||
await newPage.setContent(email.html, { | ||
waitUntil: 'domcontentloaded' | ||
}); | ||
|
||
await newPage.waitForLoadState('load', { | ||
timeout: 5000 | ||
}).catch(e => { | ||
console.log('⚠️ Load timeout, continuing anyway'); | ||
}); | ||
|
||
// Switch focus | ||
this.page = newPage; | ||
|
||
output = `Email received successfully. Navigated to new tab to display email: ${email.subject}`; | ||
metadata = { | ||
window_info: { | ||
title: email.subject, | ||
content: email.html, | ||
size: this.page.viewportSize() || { width: this.width, height: this.height } | ||
} | ||
}; | ||
|
||
break; | ||
} catch (error) { | ||
await newPage.close(); | ||
throw new ToolError(`Failed to render email: ${error}`); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implementation for render_email: This tool will grab the latest email from Mailosaur in HTML format and will render it in a new tab. It will then give the control back to AI to validate the email.
Why render it in a new tab?
Some users may want to test the links in the email. This gives AI a chance to interact with the email and click on it since it's in the browser.
Just a note that this is indeterministic. Perhaps we could implement a mechanism to manually compare the content of the email and the received email in the inbox.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is the implementation of Mailosaur in terms of grabbing the latest email from inbox. We can use this file to add more features like SMS validation and other types of email validations.
mailosaur?: { | ||
apiKey: string; | ||
serverId: string; | ||
emailAddress: string; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
apiKey and serverId are necessary but not emailAddress. I will remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready for review!
await newPage.close(); | ||
const errorMessage = error instanceof Error ? error.message : String(error); | ||
|
||
if (errorMessage.includes('Email content missing')) { | ||
return { | ||
output: `Email was found but content is missing. This might be due to malformed email. Moving to next test.`, | ||
error: 'EMAIL_CONTENT_MISSING' | ||
}; | ||
} | ||
|
||
if (errorMessage.includes('Mailosaur email address is required')) { | ||
return { | ||
output: `Email address is required but was not provided.`, | ||
error: 'EMAIL_ADDRESS_MISSING' | ||
}; | ||
} | ||
|
||
if (errorMessage.includes('No matching messages found')) { | ||
return { | ||
output: `No email found for ${input.email}. The email might not have been sent yet or is older than 1 hour. Moving to next test.`, | ||
error: 'EMAIL_NOT_FOUND' | ||
}; | ||
} | ||
|
||
// Generic error case | ||
return { | ||
output: `Failed to fetch or render email: ${errorMessage}. Moving to next test.`, | ||
error: 'EMAIL_OPERATION_FAILED' | ||
}; | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit more robust error handling to handle validation failures more accurately.
## [0.1.2] - 2024-12-26 | ||
|
||
### Added | ||
- Added support for Mailosaur email validation | ||
- Added email rendering feature in the browser | ||
- Added sleep_milliseconds tool to add delays in the test execution | ||
- Added more robust error handling for Mailosaur email validation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding the features that was added to in this PR. Since we have added a new feature, I used the wording Added
. Maybe we can have a consistent wording for this file!?
8. **Testing Email**: | ||
- If you need to test a condition that involves seeing the contents of an email, use the "render_email" tool. | ||
- This tool will grab the latest email from the email address given to you and will render it in a new tab for you to see. | ||
- Once you are done with validating the email, navigate back to the original tab. | ||
- You MUST pass the email address that is given to you to the tool as a parameter otherwise it will fail. | ||
- If no email address is given to you for this test, you should fail the test. | ||
- For email validation, you MUST always use 'Click' and 'Mouse' action instead of using keyboard shortcuts. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a new rule to system prompt to instruct AI how to use Email validation. Specifically instructing AI to pass email address as ActionInput and use 'Click' and 'Mouse' action instead of using keyboard shortcuts because shortcuts may cause unexpected events when dealing with an Inbox UI.
Needs to be updated as not in progress? |
@@ -3,19 +3,14 @@ import { clerk, clerkSetup } from "@clerk/testing/playwright"; | |||
|
|||
let frontendUrl = process.env.SHORTEST_TEST_BASE_URL ?? "http://localhost:3000"; | |||
|
|||
shortest.beforeAll(async ({ page }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Self-review would be helpful to me. Why isn't this needed?
This PR addresses issue #104
Changes
sleep_miliseconds
that helps AI to wait for a condition to be met before proceeding