Playwright OTP Client
A versatile NPM package for retrieving One-Time Password (OTP) codes from SMS messages in Playwright-based E2E UI tests.
Features
- 🚀 Easily retrieve SMS OTP codes in your Playwright tests
- 🔄 Multiple SMS providers supported:
- 🧩 Modular architecture with clean provider abstractions
- 🔧 Simple configuration through constructor options
- ⚙️ Comprehensive test coverage
Installation
npm install pw-otp-client --save-dev
Quick Start
import { test, expect } from "@playwright/test";
import { PlaywrightOtpClient } from "pw-otp-client";
test("login with OTP", async ({ page }) => {
// Initialize the OTP client with MailSlurp (default provider)
const otpClient = new PlaywrightOtpClient({
providerConfig: {
apiKey: process.env.MAILSLURP_API_KEY,
},
});
await otpClient.initialize();
// Get a phone number for receiving SMS
const phoneNumber = await otpClient.getPhoneNumber();
// Navigate to your application and submit the phone number
await page.goto("https://your-app.com/login");
await page.fill("#phone-input", phoneNumber);
await page.click("#send-otp-button");
// Wait for the OTP code to arrive via SMS
const otpCode = await otpClient.getOtpCode(phoneNumber);
// Enter the OTP code in your application
await page.fill("#otp-input", otpCode);
await page.click("#verify-button");
// Verify successful login
await expect(page.locator(".welcome-message")).toBeVisible();
// Clean up
await otpClient.cleanup();
});
SMS Provider Setup Guide
MailSlurp (Default)
Getting API Key
Create an account:
- Go to MailSlurp website
- Sign up for an account (free tier is available)
Get your API key:
- Log in to your MailSlurp dashboard
- Navigate to "API Keys" section
- Copy your API key or generate a new one
Configure in your tests:
import { PlaywrightOtpClient } from "pw-otp-client";
const otpClient = new PlaywrightOtpClient({
provider: "mailslurp", // optional, this is the default
providerConfig: {
apiKey: "your-mailslurp-api-key",
},
});
Phone Number Setup
With MailSlurp, the library will:
- First try to use existing phone numbers in your account
- If no numbers exist, attempt to create a new one (requires paid plan)
Environment Variables
Add to your .env
file:
MAILSLURP_API_KEY=your_mailslurp_api_key_here
Common Issues and Solutions
Issue | Solution |
---|---|
"Failed to get phone number" | Verify your API key is correct and your account has SMS enabled |
"No SMS message received" | Ensure your test app is sending messages to the correct phone number |
Phone number creation fails | Check if your MailSlurp plan supports SMS (free tier has limited SMS functionality) |
Timeout waiting for OTP | Increase the timeout parameter in getOtpCode method |
Twilio
Getting API Credentials
Create an account:
- Sign up at Twilio
- Complete the verification process
Get your credentials:
- From your Twilio dashboard, find your Account SID and Auth Token
- These are located on your Twilio Console home page
Set up phone number:
- Purchase a phone number from the Twilio Console
- Make sure the number can receive SMS messages
Configure in your tests:
import { PlaywrightOtpClient } from "pw-otp-client";
const otpClient = new PlaywrightOtpClient({
provider: "twilio",
providerConfig: {
accountSid: "your-twilio-account-sid",
authToken: "your-twilio-auth-token",
fromPhoneNumber: "+12345678900", // Your Twilio phone number
},
});
Phone Number Management
Unlike MailSlurp and Mailosaur, Twilio requires you to:
- Purchase phone numbers manually through their console
- Provide the number in your configuration
- Ensure your account has sufficient funds for SMS reception
Environment Variables
Add to your .env
file:
TWILIO_ACCOUNT_SID=your_twilio_account_sid_here
TWILIO_AUTH_TOKEN=your_twilio_auth_token_here
TWILIO_FROM_PHONE_NUMBER=+12345678900
Common Issues and Solutions
Issue | Solution |
---|---|
Authentication failed | Double-check your Account SID and Auth Token |
"No phone number available" | Specify a fromPhoneNumber in the provider config |
SMS delivery failures | Ensure the phone number is correctly formatted with country code |
Rate limiting | Twilio has API rate limits - add backoff logic for high-volume testing |
Billing issues | Verify your Twilio account has sufficient credit |
Mailosaur
Getting API Key and Server ID
Create an account:
- Sign up at Mailosaur
- Create a new server from your dashboard
Get your API key:
- Go to API tab in your Mailosaur dashboard
- Copy your API key
Get your server ID:
- Select your server from the dashboard
- Copy the server ID from the URL or server settings
Configure in your tests:
import { PlaywrightOtpClient } from "pw-otp-client";
const otpClient = new PlaywrightOtpClient({
provider: "mailosaur",
providerConfig: {
apiKey: "your-mailosaur-api-key",
serverId: "your-mailosaur-server-id",
phoneCountryCode: "US", // Optional, defaults to 'US'
},
});
Phone Number Setup
Mailosaur will:
- Check for existing SMS numbers on your server
- If no numbers exist, attempt to create a new one (requires appropriate plan)
- Return the phone number for testing
Environment Variables
Add to your .env
file:
MAILOSAUR_API_KEY=your_mailosaur_api_key_here
MAILOSAUR_SERVER_ID=your_mailosaur_server_id_here
Common Issues and Solutions
Issue | Solution |
---|---|
"API key and server ID are required" | Ensure both values are provided in your configuration |
"Failed to create SMS number" | Check if your plan includes SMS functionality |
No SMS detected | Verify the server has SMS capability enabled |
Message searching fails | Check if your server exists and is active |
OTP extraction failure | Try increasing timeout or check the message format |
Sending and Receiving SMS Messages
Sending Test Messages
For testing your OTP client setup without integrating with your actual application:
Using Twilio for Sending Test Messages
import twilio from "twilio";
async function sendTestSMS(to: string, message: string) {
const client = twilio(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN
);
try {
const message = await client.messages.create({
body: `Your verification code is: 123456`,
from: process.env.TWILIO_FROM_PHONE_NUMBER,
to: to,
});
console.log(`Message sent with SID: ${message.sid}`);
return true;
} catch (error) {
console.error("Error sending test message:", error);
return false;
}
}
// Example usage
const phoneNumber = await otpClient.getPhoneNumber();
await sendTestSMS(phoneNumber, "Your verification code is: 123456");
const otpCode = await otpClient.getOtpCode(phoneNumber);
console.log(`Received OTP code: ${otpCode}`); // Should output: 123456
Receiving and Parsing Messages
The OTP client handles message receiving and parsing automatically, but here's what happens behind the scenes:
Message Polling:
- The client polls the SMS provider API at regular intervals
- For Twilio and Mailosaur, this is approximately every 2 seconds
- For MailSlurp, it uses the waitForLatestSms API when available
OTP Extraction:
- The client uses various regex patterns to extract OTP codes
- Default pattern looks for 4-8 digits after words like "code", "код", "otp", etc.
- Fallback patterns are used if the default pattern doesn't match
Timeout Handling:
- If no OTP is received within the timeout period, an error is thrown
- Default timeout is 30 seconds but can be configured
Troubleshooting
General Issues
No OTP code received:
- Verify the SMS was actually sent by your application
- Check if the phone number format is correct (international format with country code)
- Increase the timeout parameter (some providers or networks may be slow)
- Check provider account for any delivery issues or logs
Wrong OTP extracted:
- If the wrong digits are being extracted, provide a custom regex pattern:
// Direct use of OTP extractor with custom pattern
import { OtpExtractor } from "pw-otp-client";
// Extract using a custom pattern that matches exactly 6 digits after "code:"
const message = "Your verification code: 123456";
const otpCode = OtpExtractor.extractOtp(message, /code:\s*(\d{6})/i);
- Provider API errors:
- Make sure your internet connection is stable
- Check if provider services are operational
- Verify your API credentials are current (some providers rotate keys)
- Check if you've exceeded API rate limits or account quotas
Debugging Tips
Enable verbose logging:
// Add this before using the OTP client process.env.DEBUG = "pw-otp-client:*";
Manual message checking:
- Log in to your provider's dashboard to verify message delivery
- Check if messages are being delivered but not matched by the OTP extraction
Network issues:
- If testing behind corporate firewalls, ensure outbound connections to provider APIs are permitted
- Check if proxy configuration is needed for your environment
Custom SMS Provider
You can implement your own SMS provider by implementing the ISmsProvider
interface:
import {
PlaywrightOtpClient,
ISmsProvider,
BaseSmsProvider,
} from "pw-otp-client";
class MyCustomSmsProvider extends BaseSmsProvider implements ISmsProvider {
constructor(config: any) {
super(config);
}
async initialize(): Promise<void> {
// Initialize your SMS provider
}
async getPhoneNumber(): Promise<string> {
// Get a phone number for receiving SMS
return "+1234567890";
}
async waitForOtpCode(phoneNumber: string, timeout?: number): Promise<string> {
// Wait for and extract OTP code from SMS
return "123456";
}
async cleanup(): Promise<void> {
// Clean up resources
}
}
// Use your custom provider
const otpClient = new PlaywrightOtpClient({
provider: "custom",
customProvider: new MyCustomSmsProvider({
// Your custom config
}),
});
API Reference
PlaywrightOtpClient
The main class for interacting with SMS providers and retrieving OTP codes.
Constructor Options
interface OtpClientConfig {
provider?: "mailslurp" | "twilio" | "mailosaur" | "custom";
providerConfig?: any;
customProvider?: ISmsProvider;
timeout?: number; // Default: 30000 (30 seconds)
}
Methods
initialize()
: Initialize the OTP clientcleanup()
: Clean up resources used by the clientgetPhoneNumber()
: Get a phone number for receiving SMSgetOtpCode(phoneNumber?, timeout?)
: Wait for and extract OTP code from SMSgetPhoneNumberAndOtpCode(timeout?)
: Convenience method to get both phone number and OTP code
CI Integration
To use this package in CI environments, you'll need to provide your SMS provider API credentials as environment variables.
GitHub Actions Example
name: E2E Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "18"
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
env:
MAILSLURP_API_KEY: ${{ secrets.MAILSLURP_API_KEY }}
# Add other provider keys as needed
run: npx playwright test
Environment Variables
Create a .env
file in your project (and add it to .gitignore
):
# MailSlurp configuration
MAILSLURP_API_KEY=your_mailslurp_api_key_here
# Twilio configuration
TWILIO_ACCOUNT_SID=your_twilio_account_sid_here
TWILIO_AUTH_TOKEN=your_twilio_auth_token_here
TWILIO_FROM_PHONE_NUMBER=+12345678900 # Optional
# Mailosaur configuration
MAILOSAUR_API_KEY=your_mailosaur_api_key_here
MAILOSAUR_SERVER_ID=your_mailosaur_server_id_here
Then in your tests:
import dotenv from "dotenv";
dotenv.config();
const otpClient = new PlaywrightOtpClient({
provider: "mailslurp",
providerConfig: {
apiKey: process.env.MAILSLURP_API_KEY,
},
});
License
MIT © 2023-2024 Alex Bobyr
See LICENSE for more information.