external-services-automation 1.0.0
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 +216 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +31 -0
- package/dist/pages/kinde/ConfirmCodePage.d.ts +12 -0
- package/dist/pages/kinde/ConfirmCodePage.js +28 -0
- package/dist/pages/kinde/LoginPage.d.ts +16 -0
- package/dist/pages/kinde/LoginPage.js +42 -0
- package/dist/pages/kinde/PasswordSetupPage.d.ts +14 -0
- package/dist/pages/kinde/PasswordSetupPage.js +34 -0
- package/dist/pages/kinde/RegisterPage.d.ts +20 -0
- package/dist/pages/kinde/RegisterPage.js +56 -0
- package/dist/pages/stripe/ConfirmUpdatePage.d.ts +9 -0
- package/dist/pages/stripe/ConfirmUpdatePage.js +27 -0
- package/dist/pages/stripe/CurrentSubscriptionPage.d.ts +11 -0
- package/dist/pages/stripe/CurrentSubscriptionPage.js +20 -0
- package/dist/pages/stripe/LeftPanelPage.d.ts +11 -0
- package/dist/pages/stripe/LeftPanelPage.js +17 -0
- package/dist/pages/stripe/UpdatePaymentMethodPage.d.ts +11 -0
- package/dist/pages/stripe/UpdatePaymentMethodPage.js +53 -0
- package/dist/pages/stripe/UpdateSubscriptionPage.d.ts +14 -0
- package/dist/pages/stripe/UpdateSubscriptionPage.js +25 -0
- package/dist/services/KindeAuthService.d.ts +13 -0
- package/dist/services/KindeAuthService.js +55 -0
- package/dist/services/StripeService.d.ts +12 -0
- package/dist/services/StripeService.js +41 -0
- package/dist/utils/temp-email-utils.d.ts +49 -0
- package/dist/utils/temp-email-utils.js +147 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# External Services Automation Library
|
|
2
|
+
|
|
3
|
+
A TypeScript library for test automation with Playwright, focused on integrations with external services like Kinde (authentication) and Stripe (payments).
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
This library is designed to be used as a Git submodule. Follow these steps to integrate it into your project:
|
|
8
|
+
|
|
9
|
+
### 1. Add Submodule
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Add the submodule
|
|
13
|
+
git submodule add https://bitbucket.org/connectist/external-services-automation.git external-services
|
|
14
|
+
|
|
15
|
+
# Initialize and update
|
|
16
|
+
git submodule update --init --recursive
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 2. Build Library
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
cd external-services
|
|
23
|
+
npm install
|
|
24
|
+
npm run build
|
|
25
|
+
cd ..
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 3. Configure Your Test Framework
|
|
29
|
+
|
|
30
|
+
The library can be used with any test framework. Here's an example for Playwright:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
// playwright.config.ts
|
|
34
|
+
import { defineConfig } from '@playwright/test';
|
|
35
|
+
|
|
36
|
+
export default defineConfig({
|
|
37
|
+
testDir: './tests',
|
|
38
|
+
use: {
|
|
39
|
+
baseURL: process.env.BASE_URL || 'https://your-app.com',
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
### Import Components
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import {
|
|
50
|
+
ExternalServicesWorld,
|
|
51
|
+
LoginPage,
|
|
52
|
+
RegisterPage,
|
|
53
|
+
KindeAuthService,
|
|
54
|
+
StripeService,
|
|
55
|
+
GuerrillaMailClient
|
|
56
|
+
} from './external-services/dist/index';
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Use Services and Page Objects
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// Authentication with Kinde
|
|
63
|
+
const authService = new KindeAuthService();
|
|
64
|
+
await authService.login(email, password);
|
|
65
|
+
|
|
66
|
+
// Payment processing with Stripe
|
|
67
|
+
const stripeService = new StripeService();
|
|
68
|
+
await stripeService.upgradeSubscription(planType);
|
|
69
|
+
|
|
70
|
+
// Page interactions
|
|
71
|
+
const loginPage = new LoginPage(page);
|
|
72
|
+
await loginPage.fillCredentials(email, password);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Available Components
|
|
76
|
+
|
|
77
|
+
### Services
|
|
78
|
+
|
|
79
|
+
#### KindeAuthService
|
|
80
|
+
- `login(email: string, password: string)` - Authenticates user with Kinde
|
|
81
|
+
- `register(email: string, firstName: string, lastName: string)` - Creates new account
|
|
82
|
+
- `verifyEmail(code: string)` - Verifies email with confirmation code
|
|
83
|
+
|
|
84
|
+
#### StripeService
|
|
85
|
+
- `upgradeSubscription(planType: string)` - Upgrades subscription to specified plan
|
|
86
|
+
- `updatePaymentMethod(cardDetails: object)` - Updates payment method
|
|
87
|
+
- `getCurrentSubscription()` - Retrieves current subscription details
|
|
88
|
+
|
|
89
|
+
### Page Objects
|
|
90
|
+
|
|
91
|
+
#### Kinde Pages
|
|
92
|
+
- **LoginPage** - Handles login form interactions
|
|
93
|
+
- **RegisterPage** - Manages account registration
|
|
94
|
+
- **ConfirmCodePage** - Email verification code entry
|
|
95
|
+
- **PasswordSetupPage** - Password creation and setup
|
|
96
|
+
|
|
97
|
+
#### Stripe Pages
|
|
98
|
+
- **CurrentSubscriptionPage** - Displays current subscription details
|
|
99
|
+
- **UpdateSubscriptionPage** - Subscription plan selection
|
|
100
|
+
- **UpdatePaymentMethodPage** - Payment method management
|
|
101
|
+
- **ConfirmUpdatePage** - Confirmation dialogs
|
|
102
|
+
- **LeftPanelPage** - Navigation and sidebar interactions
|
|
103
|
+
|
|
104
|
+
### Utilities
|
|
105
|
+
- **GuerrillaMailClient** - Temporary email handling for account verification
|
|
106
|
+
- `createTempEmail()` - Creates temporary email address
|
|
107
|
+
- `getVerificationCode()` - Retrieves verification emails
|
|
108
|
+
- `cleanup()` - Cleans up temporary email resources
|
|
109
|
+
|
|
110
|
+
### World Extension
|
|
111
|
+
- **ExternalServicesWorld** - Shared state management for test scenarios
|
|
112
|
+
|
|
113
|
+
## Example Implementation
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// In your test files
|
|
117
|
+
import { test, expect } from '@playwright/test';
|
|
118
|
+
import { KindeAuthService, StripeService, GuerrillaMailClient } from './external-services/dist/index';
|
|
119
|
+
|
|
120
|
+
test.describe('External Services Integration', () => {
|
|
121
|
+
let authService: KindeAuthService;
|
|
122
|
+
let stripeService: StripeService;
|
|
123
|
+
let tempEmail: GuerrillaMailClient;
|
|
124
|
+
|
|
125
|
+
test.beforeEach(async () => {
|
|
126
|
+
authService = new KindeAuthService();
|
|
127
|
+
stripeService = new StripeService();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('should create account and upgrade subscription', async () => {
|
|
131
|
+
// Create temporary email for account verification
|
|
132
|
+
tempEmail = new GuerrillaMailClient();
|
|
133
|
+
const email = await tempEmail.createTempEmail();
|
|
134
|
+
|
|
135
|
+
// Register new account
|
|
136
|
+
await authService.register(email, 'John', 'Doe');
|
|
137
|
+
|
|
138
|
+
// Verify email
|
|
139
|
+
const verificationCode = await tempEmail.getVerificationCode();
|
|
140
|
+
await authService.verifyEmail(verificationCode);
|
|
141
|
+
|
|
142
|
+
// Upgrade subscription
|
|
143
|
+
await stripeService.upgradeSubscription('premium');
|
|
144
|
+
|
|
145
|
+
// Verify subscription status
|
|
146
|
+
const subscription = await stripeService.getCurrentSubscription();
|
|
147
|
+
expect(subscription.status).toBe('active');
|
|
148
|
+
|
|
149
|
+
// Cleanup
|
|
150
|
+
await tempEmail.cleanup();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('should handle login with existing account', async () => {
|
|
154
|
+
const result = await authService.login('user@example.com', 'password123');
|
|
155
|
+
expect(result.success).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Update Submodule
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Update to latest
|
|
164
|
+
git submodule update --remote external-services
|
|
165
|
+
|
|
166
|
+
# Build updated library
|
|
167
|
+
cd external-services && npm run build && cd ..
|
|
168
|
+
|
|
169
|
+
# Commit update
|
|
170
|
+
git add external-services
|
|
171
|
+
git commit -m "Update external-services submodule"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## CI/CD
|
|
175
|
+
|
|
176
|
+
### Bitbucket Pipelines
|
|
177
|
+
|
|
178
|
+
```yaml
|
|
179
|
+
script:
|
|
180
|
+
- git submodule update --init --recursive
|
|
181
|
+
- npm install
|
|
182
|
+
- cd external-services && npm install && npm run build && cd ..
|
|
183
|
+
- npm test
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Dependencies
|
|
187
|
+
|
|
188
|
+
The library requires the following peer dependencies:
|
|
189
|
+
- `@playwright/test` ^1.40.0
|
|
190
|
+
- `axios-cookiejar-support` ^6.0.2
|
|
191
|
+
- `dotenv` ^16.4.5
|
|
192
|
+
- `tough-cookie` ^5.1.2
|
|
193
|
+
|
|
194
|
+
## Troubleshooting
|
|
195
|
+
|
|
196
|
+
### Submodule Not Found
|
|
197
|
+
```bash
|
|
198
|
+
git submodule update --init --recursive
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Import Errors
|
|
202
|
+
```bash
|
|
203
|
+
cd external-services && npm run build && cd ..
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Missing Dependencies
|
|
207
|
+
```bash
|
|
208
|
+
npm install @playwright/test axios-cookiejar-support dotenv tough-cookie
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Development
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
npm install
|
|
215
|
+
npm run build
|
|
216
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { KindeAuthService } from './services/KindeAuthService';
|
|
2
|
+
export { StripeService } from './services/StripeService';
|
|
3
|
+
export { LoginPage } from './pages/kinde/LoginPage';
|
|
4
|
+
export { RegisterPage } from './pages/kinde/RegisterPage';
|
|
5
|
+
export { PasswordSetupPage } from './pages/kinde/PasswordSetupPage';
|
|
6
|
+
export { ConfirmCodePage } from './pages/kinde/ConfirmCodePage';
|
|
7
|
+
export { CurrentSubscriptionPage } from './pages/stripe/CurrentSubscriptionPage';
|
|
8
|
+
export { UpdateSubscriptionPage, PlanType } from './pages/stripe/UpdateSubscriptionPage';
|
|
9
|
+
export { UpdatePaymentMethodPage } from './pages/stripe/UpdatePaymentMethodPage';
|
|
10
|
+
export { ConfirmUpdatePage } from './pages/stripe/ConfirmUpdatePage';
|
|
11
|
+
export { LeftPanelPage } from './pages/stripe/LeftPanelPage';
|
|
12
|
+
export { GuerrillaMailClient } from './utils/temp-email-utils';
|
|
13
|
+
export type { Page, BrowserContext, Locator } from 'playwright/test';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
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;
|
|
4
|
+
// Export services
|
|
5
|
+
var KindeAuthService_1 = require("./services/KindeAuthService");
|
|
6
|
+
Object.defineProperty(exports, "KindeAuthService", { enumerable: true, get: function () { return KindeAuthService_1.KindeAuthService; } });
|
|
7
|
+
var StripeService_1 = require("./services/StripeService");
|
|
8
|
+
Object.defineProperty(exports, "StripeService", { enumerable: true, get: function () { return StripeService_1.StripeService; } });
|
|
9
|
+
// Export all Kinde pages
|
|
10
|
+
var LoginPage_1 = require("./pages/kinde/LoginPage");
|
|
11
|
+
Object.defineProperty(exports, "LoginPage", { enumerable: true, get: function () { return LoginPage_1.LoginPage; } });
|
|
12
|
+
var RegisterPage_1 = require("./pages/kinde/RegisterPage");
|
|
13
|
+
Object.defineProperty(exports, "RegisterPage", { enumerable: true, get: function () { return RegisterPage_1.RegisterPage; } });
|
|
14
|
+
var PasswordSetupPage_1 = require("./pages/kinde/PasswordSetupPage");
|
|
15
|
+
Object.defineProperty(exports, "PasswordSetupPage", { enumerable: true, get: function () { return PasswordSetupPage_1.PasswordSetupPage; } });
|
|
16
|
+
var ConfirmCodePage_1 = require("./pages/kinde/ConfirmCodePage");
|
|
17
|
+
Object.defineProperty(exports, "ConfirmCodePage", { enumerable: true, get: function () { return ConfirmCodePage_1.ConfirmCodePage; } });
|
|
18
|
+
// Export all Stripe pages
|
|
19
|
+
var CurrentSubscriptionPage_1 = require("./pages/stripe/CurrentSubscriptionPage");
|
|
20
|
+
Object.defineProperty(exports, "CurrentSubscriptionPage", { enumerable: true, get: function () { return CurrentSubscriptionPage_1.CurrentSubscriptionPage; } });
|
|
21
|
+
var UpdateSubscriptionPage_1 = require("./pages/stripe/UpdateSubscriptionPage");
|
|
22
|
+
Object.defineProperty(exports, "UpdateSubscriptionPage", { enumerable: true, get: function () { return UpdateSubscriptionPage_1.UpdateSubscriptionPage; } });
|
|
23
|
+
var UpdatePaymentMethodPage_1 = require("./pages/stripe/UpdatePaymentMethodPage");
|
|
24
|
+
Object.defineProperty(exports, "UpdatePaymentMethodPage", { enumerable: true, get: function () { return UpdatePaymentMethodPage_1.UpdatePaymentMethodPage; } });
|
|
25
|
+
var ConfirmUpdatePage_1 = require("./pages/stripe/ConfirmUpdatePage");
|
|
26
|
+
Object.defineProperty(exports, "ConfirmUpdatePage", { enumerable: true, get: function () { return ConfirmUpdatePage_1.ConfirmUpdatePage; } });
|
|
27
|
+
var LeftPanelPage_1 = require("./pages/stripe/LeftPanelPage");
|
|
28
|
+
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; } });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
export declare class ConfirmCodePage {
|
|
3
|
+
readonly page: Page;
|
|
4
|
+
readonly pageTitle: Locator;
|
|
5
|
+
readonly codeInput: Locator;
|
|
6
|
+
readonly continueButton: Locator;
|
|
7
|
+
constructor(page: Page);
|
|
8
|
+
enterConfirmationCode(code: string): Promise<void>;
|
|
9
|
+
clickContinue(): Promise<void>;
|
|
10
|
+
submitConfirmationCode(code: string): Promise<void>;
|
|
11
|
+
isOnConfirmCodePage(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConfirmCodePage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
class ConfirmCodePage {
|
|
6
|
+
constructor(page) {
|
|
7
|
+
this.page = page;
|
|
8
|
+
this.pageTitle = page.locator('h1:has-text("Almost there — check your inbox")');
|
|
9
|
+
this.codeInput = page.locator('input[name="p_confirmation_code"]');
|
|
10
|
+
this.continueButton = page.locator('button[type="submit"]:has-text("Continue")');
|
|
11
|
+
}
|
|
12
|
+
async enterConfirmationCode(code) {
|
|
13
|
+
await this.codeInput.fill(code);
|
|
14
|
+
}
|
|
15
|
+
async clickContinue() {
|
|
16
|
+
await this.continueButton.click();
|
|
17
|
+
}
|
|
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
|
+
}
|
|
28
|
+
exports.ConfirmCodePage = ConfirmCodePage;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Locator, Page } from '@playwright/test';
|
|
2
|
+
export declare class LoginPage {
|
|
3
|
+
readonly page: Page;
|
|
4
|
+
readonly emailInput: Locator;
|
|
5
|
+
readonly nextButton: Locator;
|
|
6
|
+
readonly passwordInput: Locator;
|
|
7
|
+
readonly email_not_exists: Locator;
|
|
8
|
+
readonly createAccountLink: Locator;
|
|
9
|
+
constructor(page: Page);
|
|
10
|
+
navigate(): Promise<void>;
|
|
11
|
+
enterEmail(email: string): Promise<void>;
|
|
12
|
+
enterPassword(password: string): Promise<void>;
|
|
13
|
+
getEmailNotExistsMessage(): Promise<string | null>;
|
|
14
|
+
login(email: string, password?: string): Promise<void>;
|
|
15
|
+
goToRegisterPage(): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LoginPage = void 0;
|
|
4
|
+
class LoginPage {
|
|
5
|
+
constructor(page) {
|
|
6
|
+
this.page = page;
|
|
7
|
+
this.emailInput = page.locator('#sign_up_sign_in_credentials_p_email');
|
|
8
|
+
this.nextButton = page.locator('.kinde-button.kinde-button-variant-primary');
|
|
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');
|
|
11
|
+
this.createAccountLink = page.locator('.kinde-text-link[href*="register"]');
|
|
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
|
+
async getEmailNotExistsMessage() {
|
|
27
|
+
return await this.email_not_exists.textContent();
|
|
28
|
+
}
|
|
29
|
+
async login(email, password) {
|
|
30
|
+
if (!email) {
|
|
31
|
+
throw new Error(`${email} is not set`);
|
|
32
|
+
}
|
|
33
|
+
await this.enterEmail(email);
|
|
34
|
+
if (password) {
|
|
35
|
+
await this.enterPassword(password);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async goToRegisterPage() {
|
|
39
|
+
await this.createAccountLink.click();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.LoginPage = LoginPage;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
export declare class PasswordSetupPage {
|
|
3
|
+
readonly page: Page;
|
|
4
|
+
readonly pageTitle: Locator;
|
|
5
|
+
readonly passwordInput: Locator;
|
|
6
|
+
readonly confirmPasswordInput: Locator;
|
|
7
|
+
readonly continueButton: Locator;
|
|
8
|
+
constructor(page: Page);
|
|
9
|
+
enterPassword(password: string): Promise<void>;
|
|
10
|
+
enterConfirmPassword(password: string): Promise<void>;
|
|
11
|
+
clickContinue(): Promise<void>;
|
|
12
|
+
submitPasswordForm(password: string): Promise<void>;
|
|
13
|
+
isOnPasswordSetupPage(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PasswordSetupPage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
class PasswordSetupPage {
|
|
6
|
+
constructor(page) {
|
|
7
|
+
this.page = page;
|
|
8
|
+
this.pageTitle = page.locator('h1:has-text("Password setup")');
|
|
9
|
+
this.passwordInput = page.locator('input[name="p_first_password"]');
|
|
10
|
+
this.confirmPasswordInput = page.locator('input[name="p_second_password"]');
|
|
11
|
+
this.continueButton = page.locator('button[type="submit"]:has-text("Continue")');
|
|
12
|
+
}
|
|
13
|
+
async enterPassword(password) {
|
|
14
|
+
await this.passwordInput.fill(password);
|
|
15
|
+
}
|
|
16
|
+
async enterConfirmPassword(password) {
|
|
17
|
+
await this.confirmPasswordInput.fill(password);
|
|
18
|
+
}
|
|
19
|
+
async clickContinue() {
|
|
20
|
+
await this.continueButton.click();
|
|
21
|
+
}
|
|
22
|
+
async submitPasswordForm(password) {
|
|
23
|
+
await this.enterPassword(password);
|
|
24
|
+
await this.enterConfirmPassword(password);
|
|
25
|
+
await this.clickContinue();
|
|
26
|
+
}
|
|
27
|
+
async isOnPasswordSetupPage() {
|
|
28
|
+
await (0, test_1.expect)(this.pageTitle).toBeVisible();
|
|
29
|
+
await (0, test_1.expect)(this.passwordInput).toBeVisible();
|
|
30
|
+
await (0, test_1.expect)(this.confirmPasswordInput).toBeVisible();
|
|
31
|
+
await (0, test_1.expect)(this.continueButton).toBeVisible();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.PasswordSetupPage = PasswordSetupPage;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
export declare class RegisterPage {
|
|
3
|
+
readonly page: Page;
|
|
4
|
+
readonly firstNameInput: Locator;
|
|
5
|
+
readonly lastNameInput: Locator;
|
|
6
|
+
readonly emailInput: Locator;
|
|
7
|
+
readonly marketingOptInCheckbox: Locator;
|
|
8
|
+
readonly acceptPoliciesCheckbox: Locator;
|
|
9
|
+
readonly createAccountButton: Locator;
|
|
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>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RegisterPage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
class RegisterPage {
|
|
6
|
+
constructor(page) {
|
|
7
|
+
this.page = page;
|
|
8
|
+
this.firstNameInput = page.locator('input[name="p_first_name"]');
|
|
9
|
+
this.lastNameInput = page.locator('input[name="p_last_name"]');
|
|
10
|
+
this.emailInput = page.locator('input[name="p_email"]');
|
|
11
|
+
this.marketingOptInCheckbox = page.locator('input[name="p_kp_usr_is_marketing_opt_in"]');
|
|
12
|
+
this.acceptPoliciesCheckbox = page.locator('input[name="p_has_clickwrap_accepted"]');
|
|
13
|
+
this.createAccountButton = page.locator('button[type="submit"]:has-text("Create your account")');
|
|
14
|
+
}
|
|
15
|
+
async navigate() {
|
|
16
|
+
await this.page.goto('/auth/register');
|
|
17
|
+
await this.isOnRegisterPage();
|
|
18
|
+
}
|
|
19
|
+
async enterFirstName(firstName) {
|
|
20
|
+
await this.firstNameInput.fill(firstName);
|
|
21
|
+
}
|
|
22
|
+
async enterLastName(lastName) {
|
|
23
|
+
await this.lastNameInput.fill(lastName);
|
|
24
|
+
}
|
|
25
|
+
async enterEmail(email) {
|
|
26
|
+
await this.emailInput.fill(email);
|
|
27
|
+
}
|
|
28
|
+
async checkMarketingOptIn() {
|
|
29
|
+
await this.marketingOptInCheckbox.check();
|
|
30
|
+
}
|
|
31
|
+
async checkAcceptPolicies() {
|
|
32
|
+
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
|
+
if (acceptMarketing) {
|
|
42
|
+
await this.checkMarketingOptIn();
|
|
43
|
+
}
|
|
44
|
+
await this.checkAcceptPolicies();
|
|
45
|
+
await this.clickCreateAccount();
|
|
46
|
+
}
|
|
47
|
+
async isOnRegisterPage() {
|
|
48
|
+
await (0, test_1.expect)(this.firstNameInput).toBeVisible();
|
|
49
|
+
await (0, test_1.expect)(this.lastNameInput).toBeVisible();
|
|
50
|
+
await (0, test_1.expect)(this.emailInput).toBeVisible();
|
|
51
|
+
await (0, test_1.expect)(this.marketingOptInCheckbox).toBeVisible();
|
|
52
|
+
await (0, test_1.expect)(this.acceptPoliciesCheckbox).toBeVisible();
|
|
53
|
+
await (0, test_1.expect)(this.createAccountButton).toBeVisible();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.RegisterPage = RegisterPage;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
export declare class ConfirmUpdatePage {
|
|
3
|
+
readonly page: Page;
|
|
4
|
+
readonly pageContainer: Locator;
|
|
5
|
+
readonly confirmButton: Locator;
|
|
6
|
+
constructor(page: Page);
|
|
7
|
+
clickConfirm(): Promise<void>;
|
|
8
|
+
isOnConfirmUpdatePage(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConfirmUpdatePage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
class ConfirmUpdatePage {
|
|
6
|
+
constructor(page) {
|
|
7
|
+
this.page = page;
|
|
8
|
+
this.pageContainer = page
|
|
9
|
+
.locator('[data-testid="page-container-main"], .Box-root, [data-testid="confirm-container"]')
|
|
10
|
+
.first();
|
|
11
|
+
this.confirmButton = page.locator('button:has-text("Confirm"), ' +
|
|
12
|
+
'[data-testid="confirm-button"], ' +
|
|
13
|
+
'[data-test="confirm-button"], ' +
|
|
14
|
+
'a:has-text("Confirm"), ' +
|
|
15
|
+
'button[type="submit"]:has-text("Confirm"), ' +
|
|
16
|
+
'.confirm-button, ' +
|
|
17
|
+
'#confirm-button');
|
|
18
|
+
}
|
|
19
|
+
async clickConfirm() {
|
|
20
|
+
await this.confirmButton.click();
|
|
21
|
+
}
|
|
22
|
+
async isOnConfirmUpdatePage() {
|
|
23
|
+
await (0, test_1.expect)(this.pageContainer).toBeVisible();
|
|
24
|
+
await (0, test_1.expect)(this.confirmButton).toBeVisible();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.ConfirmUpdatePage = ConfirmUpdatePage;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
export declare class CurrentSubscriptionPage {
|
|
3
|
+
readonly page: Page;
|
|
4
|
+
readonly pageContainer: Locator;
|
|
5
|
+
readonly planName: Locator;
|
|
6
|
+
readonly updateSubscriptionButton: Locator;
|
|
7
|
+
readonly currentSubscriptionPageTitle: Locator;
|
|
8
|
+
constructor(page: Page);
|
|
9
|
+
navigateToUpdateSubscription(): Promise<void>;
|
|
10
|
+
isOnCurrentSubscriptionPage(): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CurrentSubscriptionPage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
class CurrentSubscriptionPage {
|
|
6
|
+
constructor(page) {
|
|
7
|
+
this.page = page;
|
|
8
|
+
this.pageContainer = page.locator('[data-testid="page-container-main"]');
|
|
9
|
+
this.planName = page.locator('[class*="Text-fontSize--20"]:has-text("plan")').first();
|
|
10
|
+
this.updateSubscriptionButton = page.locator('[data-test="update-subscription"]');
|
|
11
|
+
this.currentSubscriptionPageTitle = page.locator('//span[text()= "Current subscription"]');
|
|
12
|
+
}
|
|
13
|
+
async navigateToUpdateSubscription() {
|
|
14
|
+
await this.updateSubscriptionButton.click();
|
|
15
|
+
}
|
|
16
|
+
async isOnCurrentSubscriptionPage() {
|
|
17
|
+
await (0, test_1.expect)(this.currentSubscriptionPageTitle).toBeVisible();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.CurrentSubscriptionPage = CurrentSubscriptionPage;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
export declare class LeftPanelPage {
|
|
3
|
+
readonly page: Page;
|
|
4
|
+
readonly panelContainer: Locator;
|
|
5
|
+
readonly emailAwesomeLogo: Locator;
|
|
6
|
+
readonly sandboxIndicator: Locator;
|
|
7
|
+
readonly businessTitle: Locator;
|
|
8
|
+
readonly returnToBusinessButton: Locator;
|
|
9
|
+
constructor(page: Page);
|
|
10
|
+
navigateBackToEmailAwesome(): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LeftPanelPage = void 0;
|
|
4
|
+
class LeftPanelPage {
|
|
5
|
+
constructor(page) {
|
|
6
|
+
this.page = page;
|
|
7
|
+
this.panelContainer = page.locator('div[style*="background-color: rgb(232, 245, 233)"]');
|
|
8
|
+
this.emailAwesomeLogo = page.locator('img[data-test="logo-img"]');
|
|
9
|
+
this.sandboxIndicator = page.locator('span:has-text("Sandbox")');
|
|
10
|
+
this.businessTitle = page.locator('span:has-text("Email Awesome")').first();
|
|
11
|
+
this.returnToBusinessButton = page.locator('[data-testid="return-to-business-link"]');
|
|
12
|
+
}
|
|
13
|
+
async navigateBackToEmailAwesome() {
|
|
14
|
+
await this.returnToBusinessButton.click();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.LeftPanelPage = LeftPanelPage;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
export declare class UpdatePaymentMethodPage {
|
|
3
|
+
readonly page: Page;
|
|
4
|
+
readonly pageContainer: Locator;
|
|
5
|
+
readonly stripeIframe: Locator;
|
|
6
|
+
readonly continueButton: Locator;
|
|
7
|
+
constructor(page: Page);
|
|
8
|
+
clickContinue(): Promise<void>;
|
|
9
|
+
isOnUpdatePaymentMethodPage(): Promise<void>;
|
|
10
|
+
fillCardInformation(): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UpdatePaymentMethodPage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
class UpdatePaymentMethodPage {
|
|
6
|
+
constructor(page) {
|
|
7
|
+
this.page = page;
|
|
8
|
+
this.pageContainer = page.locator('[data-testid="page-container-main"]').first();
|
|
9
|
+
this.stripeIframe = page.locator('iframe[src*="stripe.com"]').first();
|
|
10
|
+
this.continueButton = page.locator('button[data-testid="confirm"]');
|
|
11
|
+
}
|
|
12
|
+
async clickContinue() {
|
|
13
|
+
await this.continueButton.click();
|
|
14
|
+
}
|
|
15
|
+
async isOnUpdatePaymentMethodPage() {
|
|
16
|
+
await (0, test_1.expect)(this.pageContainer).toBeVisible();
|
|
17
|
+
await (0, test_1.expect)(this.stripeIframe).toBeVisible();
|
|
18
|
+
await (0, test_1.expect)(this.continueButton).toBeVisible();
|
|
19
|
+
}
|
|
20
|
+
async fillCardInformation() {
|
|
21
|
+
const cardNumber = '4242424242424242';
|
|
22
|
+
const expirationDate = '12/34';
|
|
23
|
+
const securityCode = '567';
|
|
24
|
+
try {
|
|
25
|
+
await this.stripeIframe.waitFor({ state: 'visible' });
|
|
26
|
+
// Get the iframe element and switch to it
|
|
27
|
+
const iframeElement = await this.stripeIframe.elementHandle();
|
|
28
|
+
if (!iframeElement) {
|
|
29
|
+
throw new Error('Stripe iframe not found');
|
|
30
|
+
}
|
|
31
|
+
// Switch to the iframe context
|
|
32
|
+
const iframePage = await iframeElement.contentFrame();
|
|
33
|
+
if (!iframePage) {
|
|
34
|
+
throw new Error('Could not access iframe content');
|
|
35
|
+
}
|
|
36
|
+
const cardNumberField = iframePage.locator('input[name="number"]');
|
|
37
|
+
const expirationDateField = iframePage.locator('input[name="expiry"]');
|
|
38
|
+
const securityCodeField = iframePage.locator('input[name="cvc"]');
|
|
39
|
+
await cardNumberField.fill(cardNumber);
|
|
40
|
+
await expirationDateField.fill(expirationDate);
|
|
41
|
+
await securityCodeField.fill(securityCode);
|
|
42
|
+
await this.page.bringToFront();
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error('Error filling card information:', error);
|
|
46
|
+
if (!cardNumber || !expirationDate || !securityCode) {
|
|
47
|
+
throw new Error(`Cannot use keyboard fallback - missing data: cardNumber=${cardNumber}, expirationDate=${expirationDate}, securityCode=${securityCode}`);
|
|
48
|
+
}
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.UpdatePaymentMethodPage = UpdatePaymentMethodPage;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
export type PlanType = 'lite' | 'basic' | 'growth' | 'pro' | 'business' | 'enterprise';
|
|
3
|
+
export declare class UpdateSubscriptionPage {
|
|
4
|
+
readonly page: Page;
|
|
5
|
+
readonly pageContainer: Locator;
|
|
6
|
+
readonly planCardsContainer: Locator;
|
|
7
|
+
readonly continueButton: Locator;
|
|
8
|
+
readonly backButton: Locator;
|
|
9
|
+
readonly selectPlanButton: (planType: PlanType) => Locator;
|
|
10
|
+
constructor(page: Page);
|
|
11
|
+
selectPlan(planType: PlanType): Promise<void>;
|
|
12
|
+
clickContinue(): Promise<void>;
|
|
13
|
+
isOnUpdateSubscriptionPage(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UpdateSubscriptionPage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
class UpdateSubscriptionPage {
|
|
6
|
+
constructor(page) {
|
|
7
|
+
this.page = page;
|
|
8
|
+
this.pageContainer = page.locator('[data-testid="page-container-main"], .Box-root').first();
|
|
9
|
+
this.planCardsContainer = page.locator('[data-testid="pricing-table"]');
|
|
10
|
+
this.continueButton = page.locator('button:has-text("Continue"), [data-testid="continue-button"], a:has-text("Continue"), a[role="button"]:has-text("Continue")');
|
|
11
|
+
this.backButton = page.locator('button:has-text("Back"), [data-testid="back-button"], a:has-text("Back"), a[role="button"]:has-text("Back")');
|
|
12
|
+
this.selectPlanButton = (planType) => this.page.locator(`//div[contains(text(),'${planType.charAt(0).toUpperCase() + planType.slice(1)}')]/ancestor::div[@data-testid='pricing-table-card']//span[text()='Select']`);
|
|
13
|
+
}
|
|
14
|
+
async selectPlan(planType) {
|
|
15
|
+
await this.selectPlanButton(planType).click();
|
|
16
|
+
}
|
|
17
|
+
async clickContinue() {
|
|
18
|
+
await this.continueButton.click();
|
|
19
|
+
}
|
|
20
|
+
async isOnUpdateSubscriptionPage() {
|
|
21
|
+
await (0, test_1.expect)(this.pageContainer).toBeVisible();
|
|
22
|
+
await (0, test_1.expect)(this.planCardsContainer).toBeVisible();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.UpdateSubscriptionPage = UpdateSubscriptionPage;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Page } from 'playwright/test';
|
|
2
|
+
export declare class KindeAuthService {
|
|
3
|
+
private tempEmail?;
|
|
4
|
+
private verificationCode?;
|
|
5
|
+
private emailClient?;
|
|
6
|
+
private loginPage;
|
|
7
|
+
private registerPage;
|
|
8
|
+
private confirmCodePage;
|
|
9
|
+
private passwordSetupPage;
|
|
10
|
+
constructor(page: Page);
|
|
11
|
+
createAccountWithEmailVerification(): Promise<void>;
|
|
12
|
+
deleteTemporaryEmail(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
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");
|
|
9
|
+
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);
|
|
15
|
+
}
|
|
16
|
+
async createAccountWithEmailVerification() {
|
|
17
|
+
await this.loginPage.navigate();
|
|
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();
|
|
27
|
+
const emailData = await this.emailClient.readMailBySubject(/Email verification code/i, 120000, // 2 minutes timeout
|
|
28
|
+
5000 // Check every 5 seconds
|
|
29
|
+
);
|
|
30
|
+
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
|
+
}
|
|
42
|
+
async deleteTemporaryEmail() {
|
|
43
|
+
if (this.tempEmail) {
|
|
44
|
+
try {
|
|
45
|
+
this.tempEmail = undefined;
|
|
46
|
+
this.verificationCode = undefined;
|
|
47
|
+
this.emailClient = undefined;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.warn("Could not clean up temporary email:", error);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.KindeAuthService = KindeAuthService;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Page } from 'playwright/test';
|
|
2
|
+
export declare class StripeService {
|
|
3
|
+
private page;
|
|
4
|
+
private stripeCurrentSubscriptionPage;
|
|
5
|
+
private stripeUpdateSubscriptionPage;
|
|
6
|
+
private stripeConfirmUpdatePage;
|
|
7
|
+
private stripeUpdatePaymentMethodPage;
|
|
8
|
+
private stripeLeftPanelPage;
|
|
9
|
+
constructor(page: Page);
|
|
10
|
+
upgradePlan(planType: string): Promise<void>;
|
|
11
|
+
completePayment(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StripeService = void 0;
|
|
4
|
+
const test_1 = require("playwright/test");
|
|
5
|
+
const ConfirmUpdatePage_1 = require("../pages/stripe/ConfirmUpdatePage");
|
|
6
|
+
const CurrentSubscriptionPage_1 = require("../pages/stripe/CurrentSubscriptionPage");
|
|
7
|
+
const LeftPanelPage_1 = require("../pages/stripe/LeftPanelPage");
|
|
8
|
+
const UpdatePaymentMethodPage_1 = require("../pages/stripe/UpdatePaymentMethodPage");
|
|
9
|
+
const UpdateSubscriptionPage_1 = require("../pages/stripe/UpdateSubscriptionPage");
|
|
10
|
+
class StripeService {
|
|
11
|
+
constructor(page) {
|
|
12
|
+
this.page = page;
|
|
13
|
+
this.stripeCurrentSubscriptionPage = new CurrentSubscriptionPage_1.CurrentSubscriptionPage(page);
|
|
14
|
+
this.stripeUpdateSubscriptionPage = new UpdateSubscriptionPage_1.UpdateSubscriptionPage(page);
|
|
15
|
+
this.stripeConfirmUpdatePage = new ConfirmUpdatePage_1.ConfirmUpdatePage(page);
|
|
16
|
+
this.stripeUpdatePaymentMethodPage = new UpdatePaymentMethodPage_1.UpdatePaymentMethodPage(page);
|
|
17
|
+
this.stripeLeftPanelPage = new LeftPanelPage_1.LeftPanelPage(page);
|
|
18
|
+
}
|
|
19
|
+
async upgradePlan(planType) {
|
|
20
|
+
await (0, test_1.expect)(this.page).toHaveURL(/billing\.stripe\.com/, { timeout: 20000 });
|
|
21
|
+
await this.page.waitForLoadState('domcontentloaded');
|
|
22
|
+
await this.stripeCurrentSubscriptionPage.navigateToUpdateSubscription();
|
|
23
|
+
await this.stripeUpdateSubscriptionPage.isOnUpdateSubscriptionPage();
|
|
24
|
+
const planTypeFormatted = planType.toLowerCase();
|
|
25
|
+
await this.stripeUpdateSubscriptionPage.selectPlan(planTypeFormatted);
|
|
26
|
+
await this.stripeUpdateSubscriptionPage.clickContinue();
|
|
27
|
+
await this.stripeConfirmUpdatePage.isOnConfirmUpdatePage();
|
|
28
|
+
await this.stripeConfirmUpdatePage.clickConfirm();
|
|
29
|
+
}
|
|
30
|
+
async completePayment() {
|
|
31
|
+
await (0, test_1.expect)(this.page).toHaveURL(/update-payment-method/, { timeout: 10000 });
|
|
32
|
+
await this.stripeUpdatePaymentMethodPage.isOnUpdatePaymentMethodPage();
|
|
33
|
+
await this.stripeUpdatePaymentMethodPage.fillCardInformation();
|
|
34
|
+
await this.stripeUpdatePaymentMethodPage.clickContinue();
|
|
35
|
+
await this.stripeCurrentSubscriptionPage.isOnCurrentSubscriptionPage();
|
|
36
|
+
await this.stripeLeftPanelPage.navigateBackToEmailAwesome();
|
|
37
|
+
await (0, test_1.expect)(this.page).toHaveURL(/emailawesome\.com/, { timeout: 10000 });
|
|
38
|
+
await (0, test_1.expect)(this.page).not.toHaveURL(/stripe\.com/);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.StripeService = StripeService;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Structure returned by Guerrilla Mail for a single message */
|
|
2
|
+
export interface GuerrillaEmail {
|
|
3
|
+
mail_id: string;
|
|
4
|
+
mail_subject: string;
|
|
5
|
+
mail_from: string;
|
|
6
|
+
mail_excerpt?: string;
|
|
7
|
+
mail_body?: string;
|
|
8
|
+
mail_timestamp: number;
|
|
9
|
+
}
|
|
10
|
+
declare class GuerrillaMailClient {
|
|
11
|
+
private readonly API_URL;
|
|
12
|
+
private readonly ip;
|
|
13
|
+
private readonly agent;
|
|
14
|
+
private seq;
|
|
15
|
+
private emailAddress;
|
|
16
|
+
private client;
|
|
17
|
+
constructor();
|
|
18
|
+
/**
|
|
19
|
+
* Function 1: Crea un email temporal. Si se pasa un alias se fuerza ese usuario.
|
|
20
|
+
* @param alias Nombre de usuario deseado (sin dominio).
|
|
21
|
+
* @returns Dirección de email completa.
|
|
22
|
+
*/
|
|
23
|
+
createMail(alias?: string): Promise<string>;
|
|
24
|
+
/**
|
|
25
|
+
* Function 2: Lee la bandeja y devuelve el email MÁS RECIENTE cuyo subject matchee con subjectRegex.
|
|
26
|
+
* Hace polling hasta timeout.
|
|
27
|
+
* @param subjectRegex Expresión regular a matchear con el asunto.
|
|
28
|
+
* @param timeoutMs Tiempo máximo a esperar (ms).
|
|
29
|
+
* @param pollMs Intervalo entre consultas (ms).
|
|
30
|
+
*/
|
|
31
|
+
readMailBySubject(subjectRegex: RegExp, timeoutMs?: number, pollMs?: number): Promise<GuerrillaEmail>;
|
|
32
|
+
/**
|
|
33
|
+
* Function 3: Elimina un correo específico del servidor.
|
|
34
|
+
* @param mailId ID retornado por la API (mail_id).
|
|
35
|
+
*/
|
|
36
|
+
deleteMail(mailId: string): Promise<void>;
|
|
37
|
+
/** Helper interno para descargar cuerpo completo */
|
|
38
|
+
private fetchEmail;
|
|
39
|
+
/**
|
|
40
|
+
* Function 4: Crea una nueva instancia de email client para aislamiento entre escenarios
|
|
41
|
+
*/
|
|
42
|
+
static createIsolatedClient(): GuerrillaMailClient;
|
|
43
|
+
/**
|
|
44
|
+
* Function 5: Elimina todos los emails del buzón actual
|
|
45
|
+
*/
|
|
46
|
+
clearInbox(): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
export declare const email: GuerrillaMailClient;
|
|
49
|
+
export { GuerrillaMailClient };
|
|
@@ -0,0 +1,147 @@
|
|
|
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.GuerrillaMailClient = exports.email = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const axios_cookiejar_support_1 = require("axios-cookiejar-support");
|
|
9
|
+
const tough_cookie_1 = require("tough-cookie");
|
|
10
|
+
class GuerrillaMailClient {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.API_URL = 'https://api.guerrillamail.com/ajax.php';
|
|
13
|
+
this.ip = '127.0.0.1';
|
|
14
|
+
this.agent = encodeURIComponent('qa-automation-utils/1.0');
|
|
15
|
+
this.seq = 0;
|
|
16
|
+
this.emailAddress = '';
|
|
17
|
+
const jar = new tough_cookie_1.CookieJar();
|
|
18
|
+
this.client = (0, axios_cookiejar_support_1.wrapper)(axios_1.default.create({
|
|
19
|
+
jar,
|
|
20
|
+
withCredentials: true,
|
|
21
|
+
headers: {
|
|
22
|
+
// Recommended to identify politely
|
|
23
|
+
'User-Agent': 'qa-automation-utils/1.0',
|
|
24
|
+
},
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Function 1: Crea un email temporal. Si se pasa un alias se fuerza ese usuario.
|
|
29
|
+
* @param alias Nombre de usuario deseado (sin dominio).
|
|
30
|
+
* @returns Dirección de email completa.
|
|
31
|
+
*/
|
|
32
|
+
async createMail(alias) {
|
|
33
|
+
if (alias) {
|
|
34
|
+
const { data } = await this.client.get(this.API_URL, {
|
|
35
|
+
params: {
|
|
36
|
+
f: 'set_email_user',
|
|
37
|
+
email_user: alias,
|
|
38
|
+
ip: this.ip,
|
|
39
|
+
agent: this.agent,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
this.emailAddress = data.email_addr;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const { data } = await this.client.get(this.API_URL, {
|
|
46
|
+
params: {
|
|
47
|
+
f: 'get_email_address',
|
|
48
|
+
ip: this.ip,
|
|
49
|
+
agent: this.agent,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
this.emailAddress = data.email_addr;
|
|
53
|
+
}
|
|
54
|
+
return this.emailAddress;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Function 2: Lee la bandeja y devuelve el email MÁS RECIENTE cuyo subject matchee con subjectRegex.
|
|
58
|
+
* Hace polling hasta timeout.
|
|
59
|
+
* @param subjectRegex Expresión regular a matchear con el asunto.
|
|
60
|
+
* @param timeoutMs Tiempo máximo a esperar (ms).
|
|
61
|
+
* @param pollMs Intervalo entre consultas (ms).
|
|
62
|
+
*/
|
|
63
|
+
async readMailBySubject(subjectRegex, timeoutMs = 60000, pollMs = 5000) {
|
|
64
|
+
const start = Date.now();
|
|
65
|
+
while (Date.now() - start < timeoutMs) {
|
|
66
|
+
const { data } = await this.client.get(this.API_URL, {
|
|
67
|
+
params: {
|
|
68
|
+
f: 'check_email',
|
|
69
|
+
seq: this.seq,
|
|
70
|
+
ip: this.ip,
|
|
71
|
+
agent: this.agent,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
this.seq = data.seq ?? this.seq;
|
|
75
|
+
const list = data.list ?? [];
|
|
76
|
+
// Filtrar emails que matchean el subject
|
|
77
|
+
const matches = list.filter(m => subjectRegex.test(m.mail_subject));
|
|
78
|
+
if (matches.length > 0) {
|
|
79
|
+
// Ordenar por timestamp descendente (más reciente primero)
|
|
80
|
+
matches.sort((a, b) => b.mail_timestamp - a.mail_timestamp);
|
|
81
|
+
// Tomar el más reciente
|
|
82
|
+
const mostRecent = matches[0];
|
|
83
|
+
const full = await this.fetchEmail(mostRecent.mail_id);
|
|
84
|
+
return full;
|
|
85
|
+
}
|
|
86
|
+
await new Promise(r => setTimeout(r, pollMs));
|
|
87
|
+
}
|
|
88
|
+
throw new Error(`Timeout (${timeoutMs} ms) esperando un email con asunto que matchee ${subjectRegex}`);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Function 3: Elimina un correo específico del servidor.
|
|
92
|
+
* @param mailId ID retornado por la API (mail_id).
|
|
93
|
+
*/
|
|
94
|
+
async deleteMail(mailId) {
|
|
95
|
+
await this.client.get(this.API_URL, {
|
|
96
|
+
params: {
|
|
97
|
+
f: 'del_email',
|
|
98
|
+
email_ids: `[${mailId}]`, // formato aceptado por la API
|
|
99
|
+
ip: this.ip,
|
|
100
|
+
agent: this.agent,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/** Helper interno para descargar cuerpo completo */
|
|
105
|
+
async fetchEmail(mailId) {
|
|
106
|
+
const { data } = await this.client.get(this.API_URL, {
|
|
107
|
+
params: {
|
|
108
|
+
f: 'fetch_email',
|
|
109
|
+
email_id: mailId,
|
|
110
|
+
ip: this.ip,
|
|
111
|
+
agent: this.agent,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
return data;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Function 4: Crea una nueva instancia de email client para aislamiento entre escenarios
|
|
118
|
+
*/
|
|
119
|
+
static createIsolatedClient() {
|
|
120
|
+
return new GuerrillaMailClient();
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Function 5: Elimina todos los emails del buzón actual
|
|
124
|
+
*/
|
|
125
|
+
async clearInbox() {
|
|
126
|
+
const { data } = await this.client.get(this.API_URL, {
|
|
127
|
+
params: {
|
|
128
|
+
f: 'check_email',
|
|
129
|
+
seq: this.seq,
|
|
130
|
+
ip: this.ip,
|
|
131
|
+
agent: this.agent,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
const list = data.list ?? [];
|
|
135
|
+
// Eliminar todos los emails uno por uno
|
|
136
|
+
for (const email of list) {
|
|
137
|
+
try {
|
|
138
|
+
await this.deleteMail(email.mail_id);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
console.warn(`Could not delete email ${email.mail_id}:`, error);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
exports.GuerrillaMailClient = GuerrillaMailClient;
|
|
147
|
+
exports.email = new GuerrillaMailClient();
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "external-services-automation",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "External services automation library for Playwright and Cucumber",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/",
|
|
9
|
+
"README.md",
|
|
10
|
+
"package.json"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"playwright",
|
|
14
|
+
"automation",
|
|
15
|
+
"testing",
|
|
16
|
+
"kinde",
|
|
17
|
+
"stripe",
|
|
18
|
+
"external-services"
|
|
19
|
+
],
|
|
20
|
+
"author": "Email Awesome Team",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://bitbucket.org/connectist/external-services-automation.git"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc",
|
|
28
|
+
"build:watch": "tsc --watch",
|
|
29
|
+
"clean": "rm -rf dist",
|
|
30
|
+
"prebuild": "npm run clean",
|
|
31
|
+
"test": "echo \"No tests specified\" && exit 0",
|
|
32
|
+
"prepublishOnly": "npm run build"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@playwright/test": "^1.40.0",
|
|
36
|
+
"axios-cookiejar-support": "^6.0.2",
|
|
37
|
+
"dotenv": "^16.4.5",
|
|
38
|
+
"tough-cookie": "^5.1.2"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@playwright/test": "^1.40.0",
|
|
42
|
+
"@types/node": "^22.5.4",
|
|
43
|
+
"typescript": "^5.5.4"
|
|
44
|
+
}
|
|
45
|
+
}
|