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 +46 -16
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -4
- package/dist/pages/kinde/ConfirmCodePage.d.ts +0 -3
- package/dist/pages/kinde/ConfirmCodePage.js +1 -13
- package/dist/pages/kinde/LoginPage.d.ts +1 -4
- package/dist/pages/kinde/LoginPage.js +6 -20
- package/dist/pages/kinde/PasswordSetupPage.d.ts +1 -5
- package/dist/pages/kinde/PasswordSetupPage.js +1 -17
- package/dist/pages/kinde/RegisterPage.d.ts +1 -9
- package/dist/pages/kinde/RegisterPage.js +3 -42
- package/dist/services/GetTestMailService.d.ts +61 -0
- package/dist/services/GetTestMailService.js +127 -0
- package/dist/{utils/temp-email-utils.d.ts → services/GuerrillaMailService.d.ts} +4 -4
- package/dist/{utils/temp-email-utils.js → services/GuerrillaMailService.js} +6 -5
- package/dist/services/KindeAuthService.d.ts +3 -11
- package/dist/services/KindeAuthService.js +9 -47
- package/package.json +11 -2
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
|
-
|
|
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
|
-
###
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
120
|
-
const email =
|
|
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
|
-
//
|
|
126
|
-
const
|
|
127
|
-
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
14
|
+
return await this.emailErrorMessage.textContent();
|
|
28
15
|
}
|
|
29
16
|
async login(email, password) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
await this.enterEmail(email);
|
|
17
|
+
await this.emailInput.fill(email);
|
|
18
|
+
await this.nextButton.click();
|
|
34
19
|
if (password) {
|
|
35
|
-
await this.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
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():
|
|
21
|
+
static createIsolatedClient(): GuerrillaMailService;
|
|
22
22
|
clearInbox(): Promise<void>;
|
|
23
23
|
}
|
|
24
|
-
export declare const email:
|
|
25
|
-
export {
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
119
|
-
exports.email = new
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
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(
|
|
11
|
-
this.
|
|
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
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
29
|
-
);
|
|
14
|
+
5000);
|
|
30
15
|
const codeMatch = emailData.mail_body?.match(/\b\d{6}\b/);
|
|
31
|
-
|
|
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.
|
|
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": "
|
|
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
|
}
|