external-services-automation 1.0.27 → 1.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,6 +12,17 @@ npm install external-services-automation
12
12
 
13
13
  ## Setup
14
14
 
15
+ ### Environment Variables
16
+
17
+ For email testing with GetTestMailService, configure:
18
+
19
+ ```bash
20
+ export TESTMAIL_API_KEY=your_api_key
21
+ export TESTMAIL_NAMESPACE=your_namespace
22
+ ```
23
+
24
+ Get your credentials from [testmail.app/console](https://testmail.app/console).
25
+
15
26
  ### Configure Your Test Framework
16
27
 
17
28
  The library can be used with any test framework. Here's an example for Playwright:
@@ -39,7 +50,8 @@ import {
39
50
  RegisterPage,
40
51
  KindeAuthService,
41
52
  StripeService,
42
- GuerrillaMailClient
53
+ GuerrillaMailService,
54
+ GetTestMailService
43
55
  } from 'external-services-automation';
44
56
  ```
45
57
 
@@ -88,11 +100,23 @@ await loginPage.fillCredentials(email, password);
88
100
  - **ConfirmUpdatePage** - Confirmation dialogs
89
101
  - **LeftPanelPage** - Navigation and sidebar interactions
90
102
 
91
- ### Utilities
92
- - **GuerrillaMailClient** - Temporary email handling for account verification
93
- - `createTempEmail()` - Creates temporary email address
94
- - `getVerificationCode()` - Retrieves verification emails
95
- - `cleanup()` - Cleans up temporary email resources
103
+ ### Email Services
104
+
105
+ #### GuerrillaMailService
106
+ Temporary email service (free, but can be unstable):
107
+ - `createMail(alias?)` - Creates temporary email address
108
+ - `readMailBySubject(regex)` - Waits for email matching subject
109
+ - `clearInbox()` - Cleans inbox
110
+
111
+ #### GetTestMailService ⭐ **Recommended**
112
+ Professional temporary email service with reliable API:
113
+ - `createEmailAddress(tag)` - Generates email with custom tag
114
+ - `getEmails(tag?, timestampFrom?, livequery?)` - Retrieves emails with filtering
115
+ - `readMailBySubject(tag, regex)` - Waits for email matching subject
116
+ - `extractCode(email, pattern?)` - Extracts verification codes
117
+ - Requires API key from [testmail.app](https://testmail.app)
118
+
119
+ See [GetTestMailService documentation](./docs/GetTestMailService.md) for detailed usage.
96
120
 
97
121
  ### World Extension
98
122
  - **ExternalServicesWorld** - Shared state management for test scenarios
@@ -102,29 +126,38 @@ await loginPage.fillCredentials(email, password);
102
126
  ```typescript
103
127
  // In your test files
104
128
  import { test, expect } from '@playwright/test';
105
- import { KindeAuthService, StripeService, GuerrillaMailClient } from 'external-services-automation';
129
+ import { KindeAuthService, StripeService, GetTestMailService } from 'external-services-automation';
106
130
 
107
131
  test.describe('External Services Integration', () => {
108
132
  let authService: KindeAuthService;
109
133
  let stripeService: StripeService;
110
- let tempEmail: GuerrillaMailClient;
134
+ let mailService: GetTestMailService;
111
135
 
112
136
  test.beforeEach(async () => {
113
137
  authService = new KindeAuthService();
114
138
  stripeService = new StripeService();
139
+ mailService = new GetTestMailService(
140
+ process.env.TESTMAIL_API_KEY!,
141
+ process.env.TESTMAIL_NAMESPACE!
142
+ );
115
143
  });
116
144
 
117
145
  test('should create account and upgrade subscription', async () => {
118
146
  // Create temporary email for account verification
119
- tempEmail = new GuerrillaMailClient();
120
- const email = await tempEmail.createTempEmail();
147
+ const tag = `signup-${Date.now()}`;
148
+ const email = mailService.createEmailAddress(tag);
121
149
 
122
150
  // Register new account
123
151
  await authService.register(email, 'John', 'Doe');
124
152
 
125
- // Verify email
126
- const verificationCode = await tempEmail.getVerificationCode();
127
- await authService.verifyEmail(verificationCode);
153
+ // Wait for verification email and extract code
154
+ const verificationEmail = await mailService.readMailBySubject(
155
+ tag,
156
+ /verification code/i,
157
+ 120000
158
+ );
159
+ const code = mailService.extractCode(verificationEmail);
160
+ await authService.verifyEmail(code);
128
161
 
129
162
  // Upgrade subscription
130
163
  await stripeService.upgradeSubscription('premium');
@@ -132,9 +165,6 @@ test.describe('External Services Integration', () => {
132
165
  // Verify subscription status
133
166
  const subscription = await stripeService.getCurrentSubscription();
134
167
  expect(subscription.status).toBe('active');
135
-
136
- // Cleanup
137
- await tempEmail.cleanup();
138
168
  });
139
169
 
140
170
  test('should handle login with existing account', async () => {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { KindeAuthService } from './services/KindeAuthService';
2
2
  export { StripeService } from './services/StripeService';
3
+ export { GuerrillaMailService, GuerrillaEmail } from './services/GuerrillaMailService';
4
+ export { GetTestMailService, TestMailEmail, TestMailResponse } from './services/GetTestMailService';
3
5
  export { LoginPage } from './pages/kinde/LoginPage';
4
6
  export { RegisterPage } from './pages/kinde/RegisterPage';
5
7
  export { PasswordSetupPage } from './pages/kinde/PasswordSetupPage';
@@ -9,5 +11,4 @@ export { UpdateSubscriptionPage, PlanType } from './pages/stripe/UpdateSubscript
9
11
  export { UpdatePaymentMethodPage } from './pages/stripe/UpdatePaymentMethodPage';
10
12
  export { ConfirmUpdatePage } from './pages/stripe/ConfirmUpdatePage';
11
13
  export { LeftPanelPage } from './pages/stripe/LeftPanelPage';
12
- export { GuerrillaMailClient } from './utils/temp-email-utils';
13
14
  export type { Page, BrowserContext, Locator } from 'playwright/test';
package/dist/index.js CHANGED
@@ -1,11 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GuerrillaMailClient = exports.LeftPanelPage = exports.ConfirmUpdatePage = exports.UpdatePaymentMethodPage = exports.UpdateSubscriptionPage = exports.CurrentSubscriptionPage = exports.ConfirmCodePage = exports.PasswordSetupPage = exports.RegisterPage = exports.LoginPage = exports.StripeService = exports.KindeAuthService = void 0;
3
+ exports.LeftPanelPage = exports.ConfirmUpdatePage = exports.UpdatePaymentMethodPage = exports.UpdateSubscriptionPage = exports.CurrentSubscriptionPage = exports.ConfirmCodePage = exports.PasswordSetupPage = exports.RegisterPage = exports.LoginPage = exports.GetTestMailService = exports.GuerrillaMailService = exports.StripeService = exports.KindeAuthService = void 0;
4
4
  // Export services
5
5
  var KindeAuthService_1 = require("./services/KindeAuthService");
6
6
  Object.defineProperty(exports, "KindeAuthService", { enumerable: true, get: function () { return KindeAuthService_1.KindeAuthService; } });
7
7
  var StripeService_1 = require("./services/StripeService");
8
8
  Object.defineProperty(exports, "StripeService", { enumerable: true, get: function () { return StripeService_1.StripeService; } });
9
+ var GuerrillaMailService_1 = require("./services/GuerrillaMailService");
10
+ Object.defineProperty(exports, "GuerrillaMailService", { enumerable: true, get: function () { return GuerrillaMailService_1.GuerrillaMailService; } });
11
+ var GetTestMailService_1 = require("./services/GetTestMailService");
12
+ Object.defineProperty(exports, "GetTestMailService", { enumerable: true, get: function () { return GetTestMailService_1.GetTestMailService; } });
9
13
  // Export all Kinde pages
10
14
  var LoginPage_1 = require("./pages/kinde/LoginPage");
11
15
  Object.defineProperty(exports, "LoginPage", { enumerable: true, get: function () { return LoginPage_1.LoginPage; } });
@@ -26,6 +30,3 @@ var ConfirmUpdatePage_1 = require("./pages/stripe/ConfirmUpdatePage");
26
30
  Object.defineProperty(exports, "ConfirmUpdatePage", { enumerable: true, get: function () { return ConfirmUpdatePage_1.ConfirmUpdatePage; } });
27
31
  var LeftPanelPage_1 = require("./pages/stripe/LeftPanelPage");
28
32
  Object.defineProperty(exports, "LeftPanelPage", { enumerable: true, get: function () { return LeftPanelPage_1.LeftPanelPage; } });
29
- // Export utilities
30
- var temp_email_utils_1 = require("./utils/temp-email-utils");
31
- Object.defineProperty(exports, "GuerrillaMailClient", { enumerable: true, get: function () { return temp_email_utils_1.GuerrillaMailClient; } });
@@ -5,8 +5,5 @@ export declare class ConfirmCodePage {
5
5
  readonly codeInput: Locator;
6
6
  readonly continueButton: Locator;
7
7
  constructor(page: Page);
8
- enterConfirmationCode(code: string): Promise<void>;
9
- clickContinue(): Promise<void>;
10
8
  submitConfirmationCode(code: string): Promise<void>;
11
- isOnConfirmCodePage(): Promise<void>;
12
9
  }
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ConfirmCodePage = void 0;
4
- const test_1 = require("@playwright/test");
5
4
  class ConfirmCodePage {
6
5
  constructor(page) {
7
6
  this.page = page;
@@ -9,20 +8,9 @@ class ConfirmCodePage {
9
8
  this.codeInput = page.locator('input[name="p_confirmation_code"]');
10
9
  this.continueButton = page.getByRole('button', { name: 'Continue' });
11
10
  }
12
- async enterConfirmationCode(code) {
11
+ async submitConfirmationCode(code) {
13
12
  await this.codeInput.fill(code);
14
- }
15
- async clickContinue() {
16
13
  await this.continueButton.click();
17
14
  }
18
- async submitConfirmationCode(code) {
19
- await this.enterConfirmationCode(code);
20
- await this.clickContinue();
21
- }
22
- async isOnConfirmCodePage() {
23
- await (0, test_1.expect)(this.pageTitle).toBeVisible();
24
- await (0, test_1.expect)(this.codeInput).toBeVisible();
25
- await (0, test_1.expect)(this.continueButton).toBeVisible();
26
- }
27
15
  }
28
16
  exports.ConfirmCodePage = ConfirmCodePage;
@@ -4,12 +4,9 @@ export declare class LoginPage {
4
4
  readonly emailInput: Locator;
5
5
  readonly nextButton: Locator;
6
6
  readonly passwordInput: Locator;
7
- readonly email_not_exists: Locator;
7
+ readonly emailErrorMessage: Locator;
8
8
  readonly createAccountLink: Locator;
9
9
  constructor(page: Page);
10
- navigate(): Promise<void>;
11
- enterEmail(email: string): Promise<void>;
12
- enterPassword(password: string): Promise<void>;
13
10
  getEmailNotExistsMessage(): Promise<string | null>;
14
11
  login(email: string, password?: string): Promise<void>;
15
12
  goToRegisterPage(): Promise<void>;
@@ -7,32 +7,18 @@ class LoginPage {
7
7
  this.emailInput = page.locator('#sign_up_sign_in_credentials_p_email');
8
8
  this.nextButton = page.locator('.kinde-button.kinde-button-variant-primary');
9
9
  this.passwordInput = page.locator('#verify_password_p_password');
10
- this.email_not_exists = page.locator('#sign_up_sign_in_credentials_p_email_error_msg');
10
+ this.emailErrorMessage = page.locator('#sign_up_sign_in_credentials_p_email_error_msg');
11
11
  this.createAccountLink = page.locator('//span[@class="signup-cta"]//a[text()="Sign up"]');
12
12
  }
13
- async navigate() {
14
- await this.page.goto('/login');
15
- }
16
- async enterEmail(email) {
17
- await this.emailInput.fill(email);
18
- await this.nextButton.click();
19
- await this.page.waitForTimeout(2000);
20
- }
21
- async enterPassword(password) {
22
- await this.passwordInput.fill(password);
23
- await this.nextButton.click();
24
- await this.page.waitForTimeout(3000);
25
- }
26
13
  async getEmailNotExistsMessage() {
27
- return await this.email_not_exists.textContent();
14
+ return await this.emailErrorMessage.textContent();
28
15
  }
29
16
  async login(email, password) {
30
- if (!email) {
31
- throw new Error(`${email} is not set`);
32
- }
33
- await this.enterEmail(email);
17
+ await this.emailInput.fill(email);
18
+ await this.nextButton.click();
34
19
  if (password) {
35
- await this.enterPassword(password);
20
+ await this.passwordInput.fill(password);
21
+ await this.nextButton.click();
36
22
  }
37
23
  }
38
24
  async goToRegisterPage() {
@@ -7,10 +7,6 @@ export declare class PasswordSetupPage {
7
7
  readonly continueButton: Locator;
8
8
  readonly dontUseMFAButton: Locator;
9
9
  constructor(page: Page);
10
- enterPassword(password: string): Promise<void>;
11
- enterConfirmPassword(password: string): Promise<void>;
12
- clickContinue(): Promise<void>;
13
- submitPasswordForm(password: string): Promise<void>;
14
- isOnPasswordSetupPage(): Promise<void>;
10
+ fillPassword(password: string): Promise<void>;
15
11
  dontUseMFA(): Promise<void>;
16
12
  }
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PasswordSetupPage = void 0;
4
- const test_1 = require("@playwright/test");
5
4
  class PasswordSetupPage {
6
5
  constructor(page) {
7
6
  this.page = page;
@@ -11,26 +10,11 @@ class PasswordSetupPage {
11
10
  this.continueButton = page.getByRole('button', { name: 'Continue' });
12
11
  this.dontUseMFAButton = page.locator('//button[contains(text(), "multi-factor authentication")]');
13
12
  }
14
- async enterPassword(password) {
13
+ async fillPassword(password) {
15
14
  await this.passwordInput.fill(password);
16
- }
17
- async enterConfirmPassword(password) {
18
15
  await this.confirmPasswordInput.fill(password);
19
- }
20
- async clickContinue() {
21
16
  await this.continueButton.click();
22
17
  }
23
- async submitPasswordForm(password) {
24
- await this.enterPassword(password);
25
- await this.enterConfirmPassword(password);
26
- await this.clickContinue();
27
- }
28
- async isOnPasswordSetupPage() {
29
- await (0, test_1.expect)(this.pageTitle).toBeVisible();
30
- await (0, test_1.expect)(this.passwordInput).toBeVisible();
31
- await (0, test_1.expect)(this.confirmPasswordInput).toBeVisible();
32
- await (0, test_1.expect)(this.continueButton).toBeVisible();
33
- }
34
18
  async dontUseMFA() {
35
19
  await this.page.waitForTimeout(3000);
36
20
  if (await this.dontUseMFAButton.isVisible()) {
@@ -8,13 +8,5 @@ export declare class RegisterPage {
8
8
  readonly acceptPoliciesCheckbox: Locator;
9
9
  readonly createAccountButton: Locator;
10
10
  constructor(page: Page);
11
- navigate(): Promise<void>;
12
- enterFirstName(firstName: string): Promise<void>;
13
- enterLastName(lastName: string): Promise<void>;
14
- enterEmail(email: string): Promise<void>;
15
- checkMarketingOptIn(): Promise<void>;
16
- checkAcceptPolicies(): Promise<void>;
17
- clickCreateAccount(): Promise<void>;
18
- fillAndSubmitCreateAccountForm(firstName: string, lastName: string, email: string, acceptMarketing?: boolean): Promise<void>;
19
- isOnRegisterPage(): Promise<void>;
11
+ register(firstName: string, lastName: string, email: string, acceptMarketing?: boolean): Promise<void>;
20
12
  }
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RegisterPage = void 0;
4
- const test_1 = require("@playwright/test");
5
4
  class RegisterPage {
6
5
  constructor(page) {
7
6
  this.page = page;
@@ -12,53 +11,15 @@ class RegisterPage {
12
11
  this.acceptPoliciesCheckbox = page.locator('input[name="p_has_clickwrap_accepted"]');
13
12
  this.createAccountButton = page.locator('//span[contains(text(), "account")]/parent::button');
14
13
  }
15
- async navigate() {
16
- await this.page.goto('/auth/register');
17
- await this.isOnRegisterPage();
18
- }
19
- async enterFirstName(firstName) {
14
+ async register(firstName, lastName, email, acceptMarketing = false) {
20
15
  await this.firstNameInput.fill(firstName);
21
- }
22
- async enterLastName(lastName) {
23
16
  await this.lastNameInput.fill(lastName);
24
- }
25
- async enterEmail(email) {
26
17
  await this.emailInput.fill(email);
27
- }
28
- async checkMarketingOptIn() {
29
- await this.marketingOptInCheckbox.check();
30
- }
31
- async checkAcceptPolicies() {
32
18
  await this.acceptPoliciesCheckbox.check();
33
- }
34
- async clickCreateAccount() {
35
- await this.createAccountButton.click();
36
- }
37
- async fillAndSubmitCreateAccountForm(firstName, lastName, email, acceptMarketing = false) {
38
- await this.enterFirstName(firstName);
39
- await this.enterLastName(lastName);
40
- await this.enterEmail(email);
41
19
  if (acceptMarketing) {
42
- await this.checkMarketingOptIn();
43
- }
44
- const checkboxCount = await this.acceptPoliciesCheckbox.count();
45
- if (checkboxCount > 0) {
46
- try {
47
- await this.checkAcceptPolicies();
48
- }
49
- catch (error) {
50
- // Ignore if checkbox is not interactable
51
- }
20
+ await this.marketingOptInCheckbox.check();
52
21
  }
53
- await this.clickCreateAccount();
54
- }
55
- async isOnRegisterPage() {
56
- await (0, test_1.expect)(this.firstNameInput).toBeVisible();
57
- await (0, test_1.expect)(this.lastNameInput).toBeVisible();
58
- await (0, test_1.expect)(this.emailInput).toBeVisible();
59
- await (0, test_1.expect)(this.marketingOptInCheckbox).toBeVisible();
60
- // await expect(this.acceptPoliciesCheckbox).toBeVisible();
61
- await (0, test_1.expect)(this.createAccountButton).toBeVisible();
22
+ await this.createAccountButton.click();
62
23
  }
63
24
  }
64
25
  exports.RegisterPage = RegisterPage;
@@ -0,0 +1,61 @@
1
+ export interface TestMailEmail {
2
+ _id: string;
3
+ from: string;
4
+ to: string;
5
+ subject: string;
6
+ text?: string;
7
+ html?: string;
8
+ timestamp: number;
9
+ attachments?: any[];
10
+ }
11
+ export interface TestMailResponse {
12
+ result: string;
13
+ message: string;
14
+ count: number;
15
+ emails: TestMailEmail[];
16
+ }
17
+ export declare class GetTestMailService {
18
+ private readonly API_URL;
19
+ private readonly apiKey;
20
+ private readonly namespace;
21
+ private client;
22
+ constructor(apiKey: string, namespace: string);
23
+ /**
24
+ * Generate an email address for testing
25
+ * @param tag - Unique identifier for this email address
26
+ * @returns Complete email address
27
+ */
28
+ createEmailAddress(tag: string): string;
29
+ /**
30
+ * Get emails from inbox
31
+ * @param tag - Filter by specific tag
32
+ * @param timestampFrom - Get emails from this timestamp onwards (in milliseconds)
33
+ * @param livequery - Wait for new emails if true
34
+ * @param timeoutMs - Timeout for the request
35
+ * @returns Array of emails
36
+ */
37
+ getEmails(tag?: string, timestampFrom?: number, livequery?: boolean, timeoutMs?: number): Promise<TestMailEmail[]>;
38
+ /**
39
+ * Wait for an email matching the subject regex
40
+ * @param tag - Tag to filter emails
41
+ * @param subjectRegex - Regex pattern to match email subject
42
+ * @param timeoutMs - Maximum time to wait
43
+ * @param pollMs - Polling interval (not used with livequery)
44
+ * @returns Matching email
45
+ */
46
+ readMailBySubject(tag: string, subjectRegex: RegExp, timeoutMs?: number, pollMs?: number): Promise<TestMailEmail>;
47
+ /**
48
+ * Extract verification code from email body
49
+ * @param email - Email object
50
+ * @param pattern - Regex pattern to extract code (default: 6 digits)
51
+ * @returns Extracted code
52
+ */
53
+ extractCode(email: TestMailEmail, pattern?: RegExp): string;
54
+ /**
55
+ * Create an isolated instance with different configuration
56
+ * @param apiKey - API key
57
+ * @param namespace - Namespace
58
+ * @returns New GetTestMailService instance
59
+ */
60
+ static createIsolatedClient(apiKey: string, namespace: string): GetTestMailService;
61
+ }
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GetTestMailService = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ class GetTestMailService {
9
+ constructor(apiKey, namespace) {
10
+ this.API_URL = 'https://api.testmail.app/api/json';
11
+ if (!apiKey || !namespace) {
12
+ throw new Error('API key and namespace are required for GetTestMailService');
13
+ }
14
+ this.apiKey = apiKey;
15
+ this.namespace = namespace;
16
+ this.client = axios_1.default.create({
17
+ timeout: 120000, // 2 minutes for livequery
18
+ headers: {
19
+ 'Accept': 'application/json',
20
+ },
21
+ });
22
+ }
23
+ /**
24
+ * Generate an email address for testing
25
+ * @param tag - Unique identifier for this email address
26
+ * @returns Complete email address
27
+ */
28
+ createEmailAddress(tag) {
29
+ if (!tag) {
30
+ tag = `test-${Date.now()}-${Math.random().toString(36).substring(7)}`;
31
+ }
32
+ return `${this.namespace}.${tag}@inbox.testmail.app`;
33
+ }
34
+ /**
35
+ * Get emails from inbox
36
+ * @param tag - Filter by specific tag
37
+ * @param timestampFrom - Get emails from this timestamp onwards (in milliseconds)
38
+ * @param livequery - Wait for new emails if true
39
+ * @param timeoutMs - Timeout for the request
40
+ * @returns Array of emails
41
+ */
42
+ async getEmails(tag, timestampFrom, livequery = false, timeoutMs = 120000) {
43
+ const params = {
44
+ apikey: this.apiKey,
45
+ namespace: this.namespace,
46
+ };
47
+ if (tag) {
48
+ params.tag = tag;
49
+ }
50
+ if (timestampFrom) {
51
+ params.timestamp_from = timestampFrom;
52
+ }
53
+ if (livequery) {
54
+ params.livequery = 'true';
55
+ }
56
+ const { data } = await this.client.get(this.API_URL, {
57
+ params,
58
+ timeout: timeoutMs,
59
+ });
60
+ if (data.result !== 'success') {
61
+ throw new Error(`API returned error: ${data.message}`);
62
+ }
63
+ return data.emails || [];
64
+ }
65
+ /**
66
+ * Wait for an email matching the subject regex
67
+ * @param tag - Tag to filter emails
68
+ * @param subjectRegex - Regex pattern to match email subject
69
+ * @param timeoutMs - Maximum time to wait
70
+ * @param pollMs - Polling interval (not used with livequery)
71
+ * @returns Matching email
72
+ */
73
+ async readMailBySubject(tag, subjectRegex, timeoutMs = 60000, pollMs = 5000) {
74
+ const startTime = Date.now();
75
+ const timestampFrom = startTime;
76
+ while (Date.now() - startTime < timeoutMs) {
77
+ const remainingTime = timeoutMs - (Date.now() - startTime);
78
+ try {
79
+ const emails = await this.getEmails(tag, timestampFrom, true, Math.min(remainingTime, 60000) // Max 60s per request
80
+ );
81
+ const matches = emails.filter(email => subjectRegex.test(email.subject));
82
+ if (matches.length > 0) {
83
+ // Sort by timestamp descending (most recent first)
84
+ matches.sort((a, b) => b.timestamp - a.timestamp);
85
+ return matches[0];
86
+ }
87
+ }
88
+ catch (error) {
89
+ // If timeout on request, continue polling
90
+ if (error.code === 'ECONNABORTED' || error.message?.includes('timeout')) {
91
+ if (Date.now() - startTime >= timeoutMs) {
92
+ break;
93
+ }
94
+ await new Promise(r => setTimeout(r, pollMs));
95
+ continue;
96
+ }
97
+ throw error;
98
+ }
99
+ await new Promise(r => setTimeout(r, pollMs));
100
+ }
101
+ throw new Error(`Timeout (${timeoutMs} ms) waiting for an email with subject matching ${subjectRegex}`);
102
+ }
103
+ /**
104
+ * Extract verification code from email body
105
+ * @param email - Email object
106
+ * @param pattern - Regex pattern to extract code (default: 6 digits)
107
+ * @returns Extracted code
108
+ */
109
+ extractCode(email, pattern = /\b\d{6}\b/) {
110
+ const text = email.text || email.html || '';
111
+ const match = text.match(pattern);
112
+ if (!match) {
113
+ throw new Error('Could not extract code from email');
114
+ }
115
+ return match[0];
116
+ }
117
+ /**
118
+ * Create an isolated instance with different configuration
119
+ * @param apiKey - API key
120
+ * @param namespace - Namespace
121
+ * @returns New GetTestMailService instance
122
+ */
123
+ static createIsolatedClient(apiKey, namespace) {
124
+ return new GetTestMailService(apiKey, namespace);
125
+ }
126
+ }
127
+ exports.GetTestMailService = GetTestMailService;
@@ -6,7 +6,7 @@ export interface GuerrillaEmail {
6
6
  mail_body?: string;
7
7
  mail_timestamp: number;
8
8
  }
9
- declare class GuerrillaMailClient {
9
+ declare class GuerrillaMailService {
10
10
  private readonly API_URL;
11
11
  private readonly ip;
12
12
  private readonly agent;
@@ -18,8 +18,8 @@ declare class GuerrillaMailClient {
18
18
  readMailBySubject(subjectRegex: RegExp, timeoutMs?: number, pollMs?: number): Promise<GuerrillaEmail>;
19
19
  deleteMail(mailId: string): Promise<void>;
20
20
  private fetchEmail;
21
- static createIsolatedClient(): GuerrillaMailClient;
21
+ static createIsolatedClient(): GuerrillaMailService;
22
22
  clearInbox(): Promise<void>;
23
23
  }
24
- export declare const email: GuerrillaMailClient;
25
- export { GuerrillaMailClient };
24
+ export declare const email: GuerrillaMailService;
25
+ export { GuerrillaMailService };
@@ -3,11 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.GuerrillaMailClient = exports.email = void 0;
6
+ exports.GuerrillaMailService = exports.email = void 0;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const axios_cookiejar_support_1 = require("axios-cookiejar-support");
9
9
  const tough_cookie_1 = require("tough-cookie");
10
- class GuerrillaMailClient {
10
+ class GuerrillaMailService {
11
11
  constructor() {
12
12
  this.API_URL = 'https://api.guerrillamail.com/ajax.php';
13
13
  this.ip = '127.0.0.1';
@@ -18,6 +18,7 @@ class GuerrillaMailClient {
18
18
  this.client = (0, axios_cookiejar_support_1.wrapper)(axios_1.default.create({
19
19
  jar,
20
20
  withCredentials: true,
21
+ timeout: 30000,
21
22
  headers: {
22
23
  'User-Agent': 'qa-automation-utils/1.0',
23
24
  },
@@ -93,7 +94,7 @@ class GuerrillaMailClient {
93
94
  return data;
94
95
  }
95
96
  static createIsolatedClient() {
96
- return new GuerrillaMailClient();
97
+ return new GuerrillaMailService();
97
98
  }
98
99
  async clearInbox() {
99
100
  const { data } = await this.client.get(this.API_URL, {
@@ -115,5 +116,5 @@ class GuerrillaMailClient {
115
116
  }
116
117
  }
117
118
  }
118
- exports.GuerrillaMailClient = GuerrillaMailClient;
119
- exports.email = new GuerrillaMailClient();
119
+ exports.GuerrillaMailService = GuerrillaMailService;
120
+ exports.email = new GuerrillaMailService();
@@ -1,14 +1,6 @@
1
- import { Page } from 'playwright/test';
2
- import { GuerrillaMailClient } from '../utils/temp-email-utils';
3
1
  export declare class KindeAuthService {
4
- private tempEmail?;
5
- private verificationCode?;
6
2
  private emailClient?;
7
- private loginPage;
8
- private registerPage;
9
- private confirmCodePage;
10
- private passwordSetupPage;
11
- constructor(page: Page);
12
- createAccountWithEmailVerification(referralCode?: string): Promise<GuerrillaMailClient>;
13
- deleteTemporaryEmail(): Promise<void>;
3
+ constructor();
4
+ createGuerrillaMail(): Promise<string>;
5
+ getConfirmationCodeFromEmail(): Promise<string>;
14
6
  }
@@ -1,57 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KindeAuthService = void 0;
4
- const ConfirmCodePage_1 = require("../pages/kinde/ConfirmCodePage");
5
- const LoginPage_1 = require("../pages/kinde/LoginPage");
6
- const PasswordSetupPage_1 = require("../pages/kinde/PasswordSetupPage");
7
- const RegisterPage_1 = require("../pages/kinde/RegisterPage");
8
- const temp_email_utils_1 = require("../utils/temp-email-utils");
4
+ const GuerrillaMailService_1 = require("./GuerrillaMailService");
9
5
  class KindeAuthService {
10
- constructor(page) {
11
- this.loginPage = new LoginPage_1.LoginPage(page);
12
- this.registerPage = new RegisterPage_1.RegisterPage(page);
13
- this.confirmCodePage = new ConfirmCodePage_1.ConfirmCodePage(page);
14
- this.passwordSetupPage = new PasswordSetupPage_1.PasswordSetupPage(page);
6
+ constructor() {
7
+ this.emailClient = GuerrillaMailService_1.GuerrillaMailService.createIsolatedClient();
15
8
  }
16
- async createAccountWithEmailVerification(referralCode) {
17
- await this.loginPage.page.goto(referralCode || '/');
18
- await this.loginPage.goToRegisterPage();
19
- await this.registerPage.isOnRegisterPage();
20
- this.emailClient = temp_email_utils_1.GuerrillaMailClient.createIsolatedClient();
21
- this.tempEmail = await this.emailClient.createMail();
22
- const firstName = 'Test';
23
- const lastName = 'User';
24
- await this.registerPage.fillAndSubmitCreateAccountForm(firstName, lastName, this.tempEmail, false // Don't accept marketing emails
25
- );
26
- await this.confirmCodePage.isOnConfirmCodePage();
9
+ async createGuerrillaMail() {
10
+ return this.emailClient.createMail();
11
+ }
12
+ async getConfirmationCodeFromEmail() {
27
13
  const emailData = await this.emailClient.readMailBySubject(/Email verification code/i, 120000, // 2 minutes timeout
28
- 5000 // Check every 5 seconds
29
- );
14
+ 5000);
30
15
  const codeMatch = emailData.mail_body?.match(/\b\d{6}\b/);
31
- if (codeMatch) {
32
- this.verificationCode = codeMatch[0];
33
- await this.confirmCodePage.submitConfirmationCode(this.verificationCode);
34
- }
35
- else {
36
- throw new Error('Could not extract verification code from email');
37
- }
38
- await this.passwordSetupPage.isOnPasswordSetupPage();
39
- const password = 'TestPassword123!';
40
- await this.passwordSetupPage.submitPasswordForm(password);
41
- await this.passwordSetupPage.dontUseMFA();
42
- return this.emailClient;
43
- }
44
- async deleteTemporaryEmail() {
45
- if (this.tempEmail) {
46
- try {
47
- this.tempEmail = undefined;
48
- this.verificationCode = undefined;
49
- this.emailClient = undefined;
50
- }
51
- catch (error) {
52
- console.warn("Could not clean up temporary email:", error);
53
- }
54
- }
16
+ return codeMatch[0];
55
17
  }
56
18
  }
57
19
  exports.KindeAuthService = KindeAuthService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "external-services-automation",
3
- "version": "1.0.27",
3
+ "version": "1.0.29",
4
4
  "description": "External services automation library for Playwright and Cucumber",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -28,16 +28,25 @@
28
28
  "build:watch": "tsc --watch",
29
29
  "clean": "rm -rf dist",
30
30
  "prebuild": "npm run clean",
31
- "test": "echo \"No tests specified\" && exit 0",
31
+ "test": "jest",
32
+ "test:watch": "jest --watch",
32
33
  "prepublishOnly": "npm run build"
33
34
  },
35
+ "dependencies": {
36
+ "axios": "^1.6.0"
37
+ },
34
38
  "peerDependencies": {
35
39
  "@playwright/test": "^1.40.0",
36
40
  "axios-cookiejar-support": "^6.0.2",
37
41
  "tough-cookie": "^5.1.2"
38
42
  },
39
43
  "devDependencies": {
44
+ "@jest/globals": "^29.7.0",
45
+ "@types/jest": "^29.5.12",
40
46
  "@types/node": "^22.5.4",
47
+ "dotenv": "^17.3.1",
48
+ "jest": "^29.7.0",
49
+ "ts-jest": "^29.1.2",
41
50
  "typescript": "^5.5.4"
42
51
  }
43
52
  }