@anddone/paymentlinkportaltestautomation 1.1.1
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/.github/workflows/npm-release.yml +100 -0
- package/.github/workflows/playwright.yml +65 -0
- package/package.json +32 -0
- package/playwright.config.ts +79 -0
- package/src/index.ts +3 -0
- package/src/pages/PaymentLinkPortalLandingPage.ts +477 -0
- package/src/pages/receiptPage.ts +65 -0
- package/src/pages/summaryPage.ts +112 -0
- package/tests/landingPageTest.spec.ts +45 -0
- package/tests/paymentLinkPortal.spec.ts +38 -0
- package/tsconfig.json +40 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
name: Release PaymentLinkPortalTestAutomation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
release_type:
|
|
7
|
+
description: "Release type (semantic versioning)"
|
|
8
|
+
required: true
|
|
9
|
+
default: patch
|
|
10
|
+
type: choice
|
|
11
|
+
options:
|
|
12
|
+
- patch
|
|
13
|
+
- minor
|
|
14
|
+
- major
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
release:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
# 1. Checkout repo
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
with:
|
|
24
|
+
fetch-depth: 0
|
|
25
|
+
|
|
26
|
+
# 2. Setup Node
|
|
27
|
+
- uses: actions/setup-node@v4
|
|
28
|
+
with:
|
|
29
|
+
node-version: 18
|
|
30
|
+
registry-url: https://registry.npmjs.org/
|
|
31
|
+
|
|
32
|
+
# 3. Install dependencies
|
|
33
|
+
- run: npm install
|
|
34
|
+
|
|
35
|
+
# 4. Build project
|
|
36
|
+
- run: npm run build
|
|
37
|
+
|
|
38
|
+
# 5. Configure Git for tags/commits
|
|
39
|
+
- name: Configure Git
|
|
40
|
+
run: |
|
|
41
|
+
git config user.name "github-actions[bot]"
|
|
42
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
43
|
+
|
|
44
|
+
# 6. Bump version safely
|
|
45
|
+
- name: Bump version
|
|
46
|
+
run: |
|
|
47
|
+
# Get release type (patch/minor/major)
|
|
48
|
+
RELEASE_TYPE=${{ inputs.release_type }}
|
|
49
|
+
|
|
50
|
+
# Get current version
|
|
51
|
+
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
|
52
|
+
|
|
53
|
+
# Calculate new version without creating a tag yet
|
|
54
|
+
NEW_VERSION=$(npm version $RELEASE_TYPE --no-git-tag-version)
|
|
55
|
+
|
|
56
|
+
echo "Current version: $CURRENT_VERSION"
|
|
57
|
+
echo "New version: $NEW_VERSION"
|
|
58
|
+
|
|
59
|
+
# Commit package.json changes
|
|
60
|
+
git add package.json
|
|
61
|
+
git commit -m "chore(release): $NEW_VERSION"
|
|
62
|
+
|
|
63
|
+
# Delete tag if exists (avoid errors in CI)
|
|
64
|
+
if git rev-parse "v$NEW_VERSION" >/dev/null 2>&1; then
|
|
65
|
+
git tag -d "v$NEW_VERSION"
|
|
66
|
+
git push origin :refs/tags/v$NEW_VERSION
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Create new tag
|
|
70
|
+
git tag "v$NEW_VERSION"
|
|
71
|
+
|
|
72
|
+
# Push commit + tag
|
|
73
|
+
git push origin master --follow-tags
|
|
74
|
+
|
|
75
|
+
# 7. Publish to npm
|
|
76
|
+
- name: Publish to npm
|
|
77
|
+
run: npm publish --access public
|
|
78
|
+
env:
|
|
79
|
+
NODE_AUTH_TOKEN: ${{ secrets.TESTAUTOMATIONAUTH }}
|
|
80
|
+
|
|
81
|
+
# 8. Slack Success Notification
|
|
82
|
+
- name: Slack Success
|
|
83
|
+
if: success()
|
|
84
|
+
run: |
|
|
85
|
+
PACKAGE_NAME="@anddone/PaymentLinkPortalTestAutomation"
|
|
86
|
+
VERSION=$(node -p "require('./package.json').version")
|
|
87
|
+
curl -X POST -H 'Content-type: application/json' \
|
|
88
|
+
--data "{
|
|
89
|
+
\"username\": \"PaymentLinkPortalTestAutomation\",
|
|
90
|
+
\"text\": \":white_check_mark: npm package published!\n*Package:* $PACKAGE_NAME\n*Version:* $VERSION\n*Release Notes:*\n• Updated Admin page \n• Updated Admin page Names\"
|
|
91
|
+
}" \
|
|
92
|
+
${{ secrets.SLACK_WEBHOOK_URL }}
|
|
93
|
+
|
|
94
|
+
# 9. Slack Failure Notification
|
|
95
|
+
- name: Slack Failure
|
|
96
|
+
if: failure()
|
|
97
|
+
run: |
|
|
98
|
+
curl -X POST -H 'Content-type: application/json' \
|
|
99
|
+
--data '{"text":":x: npm publish failed. Check GitHub Actions logs."}' \
|
|
100
|
+
${{ secrets.SLACK_WEBHOOK_URL }}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: Playwright Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
env:
|
|
7
|
+
description: "Environment"
|
|
8
|
+
required: true
|
|
9
|
+
default: QAT
|
|
10
|
+
type: choice
|
|
11
|
+
options:
|
|
12
|
+
- QAT
|
|
13
|
+
- UAT
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
test:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
|
|
19
|
+
environment: ${{ inputs.env }}
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- name: Checkout repo
|
|
23
|
+
uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Setup Node
|
|
26
|
+
uses: actions/setup-node@v4
|
|
27
|
+
with:
|
|
28
|
+
node-version: 20
|
|
29
|
+
|
|
30
|
+
- name: Install deps
|
|
31
|
+
run: npm ci
|
|
32
|
+
|
|
33
|
+
- name: Map secrets based on ENV
|
|
34
|
+
run: |
|
|
35
|
+
echo "ENV=${{ inputs.env }}" >> $GITHUB_ENV
|
|
36
|
+
|
|
37
|
+
if [ "${{ inputs.env }}" = "UAT" ]; then
|
|
38
|
+
echo "BASE_URL=${{ secrets.BASE_URL_UAT }}" >> $GITHUB_ENV
|
|
39
|
+
echo "MERCHANT_PORTAL_URL_UAT=${{secrets.MERCHANT_PORTAL_URL_UAT}}" >> $GITHUB_ENV
|
|
40
|
+
|
|
41
|
+
else
|
|
42
|
+
echo "BASE_URL=${{ secrets.BASE_URL_QAT }}" >> $GITHUB_ENV
|
|
43
|
+
echo "MERCHANT_PORTAL_URL_QAT=${{secrets.MERCHANT_PORTAL_URL_QAT}}" >> $GITHUB_ENV
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
- name: Install Playwright Browsers
|
|
47
|
+
run: npx playwright install --with-deps
|
|
48
|
+
|
|
49
|
+
- name: Run Playwright tests
|
|
50
|
+
run: npx playwright test
|
|
51
|
+
env:
|
|
52
|
+
DOTENVX_QUIET: true
|
|
53
|
+
ACH_ACCOUNT_HOLDER: ${{ secrets.ACH_ACCOUNT_HOLDER }}
|
|
54
|
+
ACH_ROUTING_NUMBER: ${{ secrets.ACH_ROUTING_NUMBER }}
|
|
55
|
+
ACH_ACCOUNT_NUMBER: ${{ secrets.ACH_ACCOUNT_NUMBER }}
|
|
56
|
+
|
|
57
|
+
CC_NAME: ${{ secrets.CC_NAME }}
|
|
58
|
+
CC_NUMBER: ${{ secrets.CC_NUMBER }}
|
|
59
|
+
CC_EXPIRY: ${{ secrets.CC_EXPIRY }}
|
|
60
|
+
CC_CVV: ${{ secrets.CC_CVV }}
|
|
61
|
+
|
|
62
|
+
DC_NAME: ${{ secrets.DC_NAME }}
|
|
63
|
+
DC_NUMBER: ${{ secrets.DC_NUMBER }}
|
|
64
|
+
DC_EXPIRY: ${{ secrets.DC_EXPIRY }}
|
|
65
|
+
DC_CVV: ${{ secrets.DC_CVV }}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@anddone/paymentlinkportaltestautomation",
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"clean": "rimraf dist",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"prepublishOnly": "npm run build"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/AndDone-LLC/PaymentLinkPortalTestAutomation.git"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [],
|
|
16
|
+
"author": "",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"type": "commonjs",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/AndDone-LLC/PaymentLinkPortalTestAutomation/issues"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/AndDone-LLC/PaymentLinkPortalTestAutomation#readme",
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@playwright/test": "^1.58.1",
|
|
25
|
+
"@types/dotenv": "^6.1.1",
|
|
26
|
+
"@types/node": "^25.2.0"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@anddone/coretestautomation": "^1.0.4",
|
|
30
|
+
"dotenv": "^17.2.4"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Read environment variables from file.
|
|
5
|
+
* https://github.com/motdotla/dotenv
|
|
6
|
+
*/
|
|
7
|
+
import dotenv from 'dotenv';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
dotenv.config({ path: path.resolve(__dirname, '.env') });
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* See https://playwright.dev/docs/test-configuration.
|
|
13
|
+
*/
|
|
14
|
+
export default defineConfig({
|
|
15
|
+
testDir: './tests',
|
|
16
|
+
/* Run tests in files in parallel */
|
|
17
|
+
fullyParallel: true,
|
|
18
|
+
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
|
19
|
+
forbidOnly: !!process.env.CI,
|
|
20
|
+
/* Retry on CI only */
|
|
21
|
+
retries: process.env.CI ? 2 : 0,
|
|
22
|
+
/* Opt out of parallel tests on CI. */
|
|
23
|
+
workers: process.env.CI ? 1 : undefined,
|
|
24
|
+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
|
25
|
+
reporter: 'html',
|
|
26
|
+
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
|
27
|
+
use: {
|
|
28
|
+
/* Base URL to use in actions like `await page.goto('')`. */
|
|
29
|
+
// baseURL: 'http://localhost:3000',
|
|
30
|
+
|
|
31
|
+
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
|
32
|
+
trace: 'on-first-retry',
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
/* Configure projects for major browsers */
|
|
36
|
+
projects: [
|
|
37
|
+
{
|
|
38
|
+
name: 'chromium',
|
|
39
|
+
use: { ...devices['Desktop Chrome'] },
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
// {
|
|
43
|
+
// name: 'firefox',
|
|
44
|
+
// use: { ...devices['Desktop Firefox'] },
|
|
45
|
+
// },
|
|
46
|
+
|
|
47
|
+
// {
|
|
48
|
+
// name: 'webkit',
|
|
49
|
+
// use: { ...devices['Desktop Safari'] },
|
|
50
|
+
// },
|
|
51
|
+
|
|
52
|
+
/* Test against mobile viewports. */
|
|
53
|
+
// {
|
|
54
|
+
// name: 'Mobile Chrome',
|
|
55
|
+
// use: { ...devices['Pixel 5'] },
|
|
56
|
+
// },
|
|
57
|
+
// {
|
|
58
|
+
// name: 'Mobile Safari',
|
|
59
|
+
// use: { ...devices['iPhone 12'] },
|
|
60
|
+
// },
|
|
61
|
+
|
|
62
|
+
/* Test against branded browsers. */
|
|
63
|
+
// {
|
|
64
|
+
// name: 'Microsoft Edge',
|
|
65
|
+
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
|
66
|
+
// },
|
|
67
|
+
// {
|
|
68
|
+
// name: 'Google Chrome',
|
|
69
|
+
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
|
70
|
+
// },
|
|
71
|
+
],
|
|
72
|
+
|
|
73
|
+
/* Run your local dev server before starting the tests */
|
|
74
|
+
// webServer: {
|
|
75
|
+
// command: 'npm run start',
|
|
76
|
+
// url: 'http://localhost:3000',
|
|
77
|
+
// reuseExistingServer: !process.env.CI,
|
|
78
|
+
// },
|
|
79
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
import { Page, Locator, expect } from "@playwright/test";
|
|
2
|
+
import { CommonUtils } from '@anddone/coretestautomation/dist';
|
|
3
|
+
|
|
4
|
+
export class PaymentLinkPortalLandingPage {
|
|
5
|
+
readonly page: Page;
|
|
6
|
+
|
|
7
|
+
//-----LEFT SUMMARY PANEL----
|
|
8
|
+
readonly merchantName: Locator;
|
|
9
|
+
readonly shopperName: Locator;
|
|
10
|
+
readonly transactionAmount: Locator;
|
|
11
|
+
readonly merchantReference: Locator;
|
|
12
|
+
readonly accountNumber: Locator;
|
|
13
|
+
readonly purposeOfPayment: Locator;
|
|
14
|
+
|
|
15
|
+
//-----CONTACT INFO-----
|
|
16
|
+
readonly companyNameInput: Locator;
|
|
17
|
+
readonly firstNameInput: Locator;
|
|
18
|
+
readonly lastNameInput: Locator;
|
|
19
|
+
readonly emailInput: Locator;
|
|
20
|
+
readonly phoneInput: Locator;
|
|
21
|
+
|
|
22
|
+
//-----PAYMENT METHOD
|
|
23
|
+
readonly achOption: Locator;
|
|
24
|
+
readonly cardOption: Locator;
|
|
25
|
+
readonly achSameNameCheckbox: Locator;
|
|
26
|
+
readonly cardSameNameCheckbox: Locator;
|
|
27
|
+
|
|
28
|
+
//-----ACH FIELDS----
|
|
29
|
+
readonly routingNumberInput: Locator;
|
|
30
|
+
readonly accountNumberInput: Locator;
|
|
31
|
+
readonly verifyAccountNumberInput: Locator;
|
|
32
|
+
readonly bankNameInput: Locator;
|
|
33
|
+
readonly accountHolderName: Locator;
|
|
34
|
+
|
|
35
|
+
//------CARD FIELDS-----
|
|
36
|
+
readonly cardNumberInput: Locator;
|
|
37
|
+
readonly expiryInput: Locator;
|
|
38
|
+
readonly cvvInput: Locator;
|
|
39
|
+
readonly cardHolderName: Locator;
|
|
40
|
+
|
|
41
|
+
//-----=====BILLING ADDRESS
|
|
42
|
+
readonly billingAddressInput: Locator;
|
|
43
|
+
readonly billingSuiteInput: Locator;
|
|
44
|
+
readonly billingCityInput: Locator;
|
|
45
|
+
readonly billingCountrySelect: Locator;
|
|
46
|
+
readonly billingStateSelect: Locator;
|
|
47
|
+
readonly billingPostalCodeInput: Locator;
|
|
48
|
+
|
|
49
|
+
//-----PRICE SUMMARY-----
|
|
50
|
+
readonly totalAmountValue: Locator;
|
|
51
|
+
|
|
52
|
+
//-----SUBMIT BUTTON-----
|
|
53
|
+
readonly submitButton: Locator;
|
|
54
|
+
|
|
55
|
+
constructor(page: Page) {
|
|
56
|
+
this.page = page;
|
|
57
|
+
|
|
58
|
+
// ----- CONTACT INFO -----
|
|
59
|
+
this.companyNameInput = page.locator('#inputCompanyName');
|
|
60
|
+
this.firstNameInput = page.locator('#inputFirstName');
|
|
61
|
+
this.lastNameInput = page.locator('#inputLastName');
|
|
62
|
+
this.emailInput = page.locator('#inputEmail');
|
|
63
|
+
this.phoneInput = page.locator('#inputPhone');
|
|
64
|
+
|
|
65
|
+
// ----- LEFT PANEL -----
|
|
66
|
+
this.merchantName = page.locator('.pflite-business-name');
|
|
67
|
+
this.shopperName = page.locator('#named-insured-value');
|
|
68
|
+
this.transactionAmount = page.locator('[id*="transaction-total"]');
|
|
69
|
+
this.merchantReference = page.locator('#merchantRef');
|
|
70
|
+
this.accountNumber = page.locator('#referenceValue1');
|
|
71
|
+
this.purposeOfPayment = page.locator('#output-purpose');
|
|
72
|
+
|
|
73
|
+
// ----- PAYMENT METHOD -----
|
|
74
|
+
this.achOption = page.locator('#pay-ach');
|
|
75
|
+
this.cardOption = page.locator('#pay-card');
|
|
76
|
+
this.achSameNameCheckbox = page.locator('#ach-name-copy');
|
|
77
|
+
this.cardSameNameCheckbox = page.locator('#card-name-copy');
|
|
78
|
+
|
|
79
|
+
// ----- ACH DETAILS -----
|
|
80
|
+
this.routingNumberInput = page.locator('#inputRouting');
|
|
81
|
+
this.accountNumberInput = page.locator('#bankAccNum');
|
|
82
|
+
this.verifyAccountNumberInput = page.locator('#inputBankAcc2');
|
|
83
|
+
this.bankNameInput = page.locator('#bankName');
|
|
84
|
+
this.accountHolderName = page.locator(('#inputAccName'));
|
|
85
|
+
|
|
86
|
+
// -----CARD DETAILS -----
|
|
87
|
+
this.cardNumberInput = page.locator('#cardNumber');
|
|
88
|
+
this.expiryInput = page.locator('#inputExp');
|
|
89
|
+
this.cvvInput = page.locator('#inputSec');
|
|
90
|
+
this.cardHolderName = page.locator('#nameOnCard');
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
// ----- BILLING ADDRESS -----
|
|
94
|
+
this.billingAddressInput = page.locator('input[formcontrolname="StreetNumber"]');
|
|
95
|
+
this.billingSuiteInput = page.locator('input[formcontrolname="Street"]');
|
|
96
|
+
this.billingCityInput = page.locator('input[formcontrolname="City"]');
|
|
97
|
+
this.billingCountrySelect = page.locator('select[formcontrolname="Country"]');
|
|
98
|
+
this.billingStateSelect = page.locator('select[formcontrolname="State"]');
|
|
99
|
+
this.billingPostalCodeInput = page.locator('#inBillingZipCodePayLink');
|
|
100
|
+
|
|
101
|
+
// ----- PRICE -----
|
|
102
|
+
this.totalAmountValue = page.locator('#totalAmountValue');
|
|
103
|
+
|
|
104
|
+
// ----- SUBMIT -----
|
|
105
|
+
this.submitButton = page.locator('.confirm-button');
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ================= CONTACT INFO ACTIONS =================
|
|
110
|
+
async fillCompanyName(company: string) {
|
|
111
|
+
await CommonUtils.fill(this.companyNameInput, company, {
|
|
112
|
+
description: 'Company Name Input'
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async fillFirstName(first: string) {
|
|
117
|
+
await CommonUtils.fill(this.firstNameInput, first, {
|
|
118
|
+
description: 'First Name Input'
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async fillLastName(last: string) {
|
|
123
|
+
await CommonUtils.fill(this.lastNameInput, last, {
|
|
124
|
+
description: 'Last Name Input'
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async fillEmail(email: string) {
|
|
129
|
+
await CommonUtils.fill(this.emailInput, email, {
|
|
130
|
+
description: 'Email Input'
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async fillPhone(phone: string) {
|
|
135
|
+
await CommonUtils.fill(this.phoneInput, phone, {
|
|
136
|
+
description: 'Phone Input'
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
async fillContactInfo(
|
|
140
|
+
options: { first?: string; last?: string; email?: string; phone?: string; company?: string; }
|
|
141
|
+
): Promise<void> {
|
|
142
|
+
|
|
143
|
+
if (options.company) {
|
|
144
|
+
await this.fillCompanyName(options.company);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (options.first) {
|
|
148
|
+
await this.fillFirstName(options.first);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (options.last) {
|
|
152
|
+
await this.fillLastName(options.last);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (options.email) {
|
|
156
|
+
await this.fillEmail(options.email);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (options.phone) {
|
|
160
|
+
await this.fillPhone(options.phone);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ================= LEFT PANEL GETTERS =================
|
|
165
|
+
|
|
166
|
+
async getShopperName(): Promise<string> {
|
|
167
|
+
return await CommonUtils.getText(this.shopperName);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async getTransactionAmount(): Promise<string> {
|
|
171
|
+
return await CommonUtils.getText(this.transactionAmount);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async getMerchantReference(): Promise<string> {
|
|
175
|
+
return await CommonUtils.getText(this.merchantReference);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async getAccountNumber(): Promise<string> {
|
|
179
|
+
return await CommonUtils.getText(this.accountNumber);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async getPurposeOfPayment(): Promise<string> {
|
|
183
|
+
return await CommonUtils.getText(this.purposeOfPayment);
|
|
184
|
+
}
|
|
185
|
+
// ================= PAYMENT METHOD =================
|
|
186
|
+
private currentPaymentMethod: 'ACH' | 'CARD' = 'ACH';
|
|
187
|
+
|
|
188
|
+
async selectPaymentMethod(method: 'ACH' | 'CARD'): Promise<void> {
|
|
189
|
+
const option =
|
|
190
|
+
method === 'ACH'
|
|
191
|
+
? this.achOption
|
|
192
|
+
: this.cardOption;
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
await CommonUtils.waitSeconds(5);
|
|
196
|
+
await CommonUtils.waitForVisible(option);
|
|
197
|
+
await CommonUtils.click(option, {
|
|
198
|
+
description: `${method} Payment Option`
|
|
199
|
+
});
|
|
200
|
+
this.currentPaymentMethod = method;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ================= CHECKBOX METHOD =================
|
|
204
|
+
|
|
205
|
+
async waitForValueToStabilize(locator: Locator, checks = 3, gapMs = 200) {
|
|
206
|
+
let prev = await locator.inputValue();
|
|
207
|
+
|
|
208
|
+
for (let i = 0; i < checks; i++) {
|
|
209
|
+
await locator.page().waitForTimeout(gapMs);
|
|
210
|
+
const next = await locator.inputValue();
|
|
211
|
+
if (next !== prev) {
|
|
212
|
+
prev = next;
|
|
213
|
+
i = -1;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async checkOrUncheckCheckbox(shouldBeChecked: boolean) {
|
|
218
|
+
const checkbox =
|
|
219
|
+
this.currentPaymentMethod === 'ACH'
|
|
220
|
+
? this.achSameNameCheckbox
|
|
221
|
+
: this.cardSameNameCheckbox;
|
|
222
|
+
|
|
223
|
+
const targetField =
|
|
224
|
+
this.currentPaymentMethod === 'ACH'
|
|
225
|
+
? this.accountHolderName
|
|
226
|
+
: this.cardHolderName;
|
|
227
|
+
|
|
228
|
+
const isChecked = await CommonUtils.isChecked(checkbox);
|
|
229
|
+
|
|
230
|
+
if (isChecked !== shouldBeChecked) {
|
|
231
|
+
await CommonUtils.click(checkbox, {
|
|
232
|
+
description: `${this.currentPaymentMethod} Same Name Checkbox`
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
if (!shouldBeChecked) {
|
|
236
|
+
await this.page.waitForTimeout(500);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
await expect(checkbox).toHaveJSProperty('checked', shouldBeChecked);
|
|
240
|
+
await this.waitForValueToStabilize(targetField);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
async fillAccountHolderName(accountHolderName: string) {
|
|
246
|
+
await CommonUtils.fill(this.accountHolderName, accountHolderName, {
|
|
247
|
+
description: 'Account Holder Name Input'
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async fillCardHolderName(cardHolderName: string): Promise<void> {
|
|
252
|
+
await CommonUtils.waitSeconds(2);
|
|
253
|
+
await CommonUtils.waitForVisible(this.cardHolderName, {
|
|
254
|
+
description: 'Card Holder Name Input'
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
await CommonUtils.fill(this.cardHolderName, cardHolderName, {
|
|
258
|
+
description: 'Card Holder Name Input'
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
await this.cardHolderName.press('Tab');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
// ================= ACH DETAILS =================
|
|
266
|
+
|
|
267
|
+
async clickRoutingNumberInput(): Promise<void> {
|
|
268
|
+
await CommonUtils.click(this.routingNumberInput, {
|
|
269
|
+
description: 'Routing Number Input'
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async typeRoutingNumberInput(routingNumber: string) {
|
|
274
|
+
await CommonUtils.typeText(this.routingNumberInput, routingNumber, {
|
|
275
|
+
description: 'Bank Routing Number Input'
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async typeBankAccountNumberInput(account: string): Promise<void> {
|
|
280
|
+
await CommonUtils.typeText(this.accountNumberInput, account, {
|
|
281
|
+
description: 'Bank Account Number Input'
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async typeVerifyBankAccountNumberInput(account: string): Promise<void> {
|
|
286
|
+
await CommonUtils.typeText(this.verifyAccountNumberInput, account, {
|
|
287
|
+
description: 'Verify Bank Account Number Input'
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async fillACHDetails(
|
|
292
|
+
options: { routing?: string; account?: string; }
|
|
293
|
+
): Promise<void> {
|
|
294
|
+
|
|
295
|
+
if (options.routing) {
|
|
296
|
+
await this.clickRoutingNumberInput();
|
|
297
|
+
await this.typeRoutingNumberInput(options.routing);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (options.account) {
|
|
301
|
+
await this.typeBankAccountNumberInput(options.account);
|
|
302
|
+
await this.typeVerifyBankAccountNumberInput(options.account);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ================= CARD DETAILS =================
|
|
307
|
+
|
|
308
|
+
async typeCardNumber(cardNum: string): Promise<void> {
|
|
309
|
+
// await CommonUtils.typeText(this.cardNumberInput, cardNum, {
|
|
310
|
+
// description: 'Card Number Input'
|
|
311
|
+
// });
|
|
312
|
+
//await this.secureFill(this.cardNumberInput,cardNum);
|
|
313
|
+
await this.secureType(this.cardNumberInput,cardNum);
|
|
314
|
+
}
|
|
315
|
+
async typeExpiryDate(exp: string): Promise<void> {
|
|
316
|
+
// await CommonUtils.typeText(this.expiryInput, exp, {
|
|
317
|
+
// description: 'Card Expiry Input'
|
|
318
|
+
// });
|
|
319
|
+
// await this.secureFill(this.expiryInput,exp);
|
|
320
|
+
await this.secureType(this.expiryInput,exp);
|
|
321
|
+
}
|
|
322
|
+
async typeCVV(cvv: string): Promise<void> {
|
|
323
|
+
// await CommonUtils.typeText(this.cvvInput, cvv, {
|
|
324
|
+
// description: 'Card CVV Input'
|
|
325
|
+
// });
|
|
326
|
+
// await this.secureFill(this.cvvInput,cvv);
|
|
327
|
+
await this.secureType(this.cvvInput,cvv);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async fillCardDetails(
|
|
331
|
+
options: { cardNum?: string; exp?: string; cvv?: string; }
|
|
332
|
+
): Promise<void> {
|
|
333
|
+
|
|
334
|
+
if (options.cardNum) {
|
|
335
|
+
await this.typeCardNumber(options.cardNum);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
await CommonUtils.waitSeconds(4);
|
|
339
|
+
if (options.exp) {
|
|
340
|
+
await this.typeExpiryDate(options.exp);
|
|
341
|
+
}
|
|
342
|
+
await CommonUtils.waitSeconds(2);
|
|
343
|
+
if (options.cvv) {
|
|
344
|
+
await this.typeCVV(options.cvv);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async fillBillingAddressLine(address: string): Promise<void> {
|
|
349
|
+
await CommonUtils.fill(this.billingAddressInput, address, {
|
|
350
|
+
description: 'Billing Address'
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
async fillBillingSuite(suite: string): Promise<void> {
|
|
354
|
+
await CommonUtils.fill(this.billingSuiteInput, suite, {
|
|
355
|
+
description: 'Billing Suite'
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
async fillBillingCity(city: string): Promise<void> {
|
|
359
|
+
await CommonUtils.fill(this.billingCityInput, city, {
|
|
360
|
+
description: 'Billing City'
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
async selectBillingCountry(country: string): Promise<void> {
|
|
364
|
+
await CommonUtils.selectByText(this.billingCountrySelect, country, {
|
|
365
|
+
description: 'Billing Country'
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
async selectBillingState(state: string): Promise<void> {
|
|
369
|
+
await CommonUtils.selectByText(this.billingStateSelect, state, {
|
|
370
|
+
description: 'Billing State'
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
async fillBillingPostalCode(postalCode: string): Promise<void> {
|
|
374
|
+
await CommonUtils.fill(this.billingPostalCodeInput, postalCode, {
|
|
375
|
+
description: 'Billing Postal Code'
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
async fillBillingAddress(
|
|
380
|
+
options: {
|
|
381
|
+
address?: string; suite?: string; city?: string; country?: string; state?: string;
|
|
382
|
+
postalCode?: string;
|
|
383
|
+
}
|
|
384
|
+
): Promise<void> {
|
|
385
|
+
|
|
386
|
+
await CommonUtils.waitForVisible(this.billingAddressInput, {
|
|
387
|
+
description: 'Billing Address Section'
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
if (options.address) {
|
|
391
|
+
await this.fillBillingAddressLine(options.address);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (options.suite) {
|
|
395
|
+
await this.fillBillingSuite(options.suite);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (options.city) {
|
|
399
|
+
await this.fillBillingCity(options.city);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (options.country) {
|
|
403
|
+
await this.selectBillingCountry(options.country);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (options.state) {
|
|
407
|
+
await this.selectBillingState(options.state);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (options.postalCode) {
|
|
411
|
+
await this.fillBillingPostalCode(options.postalCode);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
// ================= PRICE =================
|
|
417
|
+
async getTotalAmount(): Promise<string> {
|
|
418
|
+
return await CommonUtils.getText(this.totalAmountValue, {
|
|
419
|
+
description: 'Total Amount Value'
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ================= SUBMIT =================
|
|
424
|
+
async submitPayment(): Promise<void> {
|
|
425
|
+
const spinner = this.page.locator('.loading-spinner');
|
|
426
|
+
|
|
427
|
+
await CommonUtils.waitForInvisible(spinner, {
|
|
428
|
+
description: 'Submit Button Spinner'
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
await CommonUtils.click(this.submitButton, {
|
|
432
|
+
description: 'Submit Payment Button'
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async clickOnCardMethod(): Promise<void> {
|
|
437
|
+
await CommonUtils.click(this.cardOption);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async secureType(locator: Locator, value: string): Promise<void> {
|
|
441
|
+
|
|
442
|
+
await locator.waitFor({ state: 'visible' });
|
|
443
|
+
|
|
444
|
+
await locator.evaluate(async (element, val) => {
|
|
445
|
+
const input = element as HTMLInputElement;
|
|
446
|
+
|
|
447
|
+
input.focus();
|
|
448
|
+
input.value = '';
|
|
449
|
+
|
|
450
|
+
for (const char of val) {
|
|
451
|
+
|
|
452
|
+
// Create keyboard events
|
|
453
|
+
const keyDown = new KeyboardEvent('keydown', { key: char, bubbles: true });
|
|
454
|
+
const keyPress = new KeyboardEvent('keypress', { key: char, bubbles: true });
|
|
455
|
+
const keyUp = new KeyboardEvent('keyup', { key: char, bubbles: true });
|
|
456
|
+
|
|
457
|
+
input.dispatchEvent(keyDown);
|
|
458
|
+
input.dispatchEvent(keyPress);
|
|
459
|
+
|
|
460
|
+
// Append character
|
|
461
|
+
input.value += char;
|
|
462
|
+
|
|
463
|
+
// Trigger input event
|
|
464
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
465
|
+
|
|
466
|
+
input.dispatchEvent(keyUp);
|
|
467
|
+
|
|
468
|
+
// Small delay to mimic typing speed
|
|
469
|
+
await new Promise(res => setTimeout(res, 50));
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
473
|
+
input.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
474
|
+
|
|
475
|
+
}, value);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { CommonUtils, TableUtils } from "@anddone/coretestautomation/dist";
|
|
2
|
+
import { Page, Locator } from "@playwright/test";
|
|
3
|
+
|
|
4
|
+
export class ReceiptPage {
|
|
5
|
+
readonly page: Page;
|
|
6
|
+
readonly transactionStatus: Locator;
|
|
7
|
+
readonly merchantDbaName: Locator;
|
|
8
|
+
readonly transactionAmount: Locator;
|
|
9
|
+
readonly receiptLabel_list: Locator;
|
|
10
|
+
readonly receiptValue_list: Locator;
|
|
11
|
+
readonly merchantAddress_value: Locator;
|
|
12
|
+
readonly merchantEmail_value: Locator;
|
|
13
|
+
readonly merchantPhone_value: Locator;
|
|
14
|
+
readonly shopperBehalf_value: Locator;
|
|
15
|
+
readonly accountNumber_value: Locator;
|
|
16
|
+
readonly transactionId_value: Locator;
|
|
17
|
+
readonly merchantRef_value: Locator;
|
|
18
|
+
readonly traceNumber_value: Locator;
|
|
19
|
+
|
|
20
|
+
constructor(page: Page) {
|
|
21
|
+
this.page = page;
|
|
22
|
+
this.transactionStatus = page.locator('span.transaction-status');
|
|
23
|
+
this.merchantDbaName = page.locator('#merchant-dba');
|
|
24
|
+
this.transactionAmount = page.locator('#transaction-amount');
|
|
25
|
+
this.receiptLabel_list = page.locator('td.label');
|
|
26
|
+
this.receiptValue_list = page.locator('tr td:not(.label)');
|
|
27
|
+
this.merchantAddress_value = page.locator('#merchant-address');
|
|
28
|
+
this.merchantEmail_value = page.locator('#merchant-email')
|
|
29
|
+
this.merchantPhone_value = page.locator('#merchant-phone');
|
|
30
|
+
this.shopperBehalf_value = page.locator('#shopper-behalf');
|
|
31
|
+
this.accountNumber_value = page.locator('#transaction-invoice');
|
|
32
|
+
this.transactionId_value = page.locator('#transaction-id');
|
|
33
|
+
this.merchantRef_value = page.locator('#transaction-ref');
|
|
34
|
+
this.traceNumber_value = page.locator('#transaction-trace');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async getReceiptLabels(): Promise<string[]> {
|
|
38
|
+
return CommonUtils.getAllInnerText(this.receiptLabel_list);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async getReceiptLabelValues(): Promise<string[]> {
|
|
42
|
+
return CommonUtils.getAllInnerText(this.receiptValue_list);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async getReceiptDataMap(): Promise<Record<string, string>> {
|
|
46
|
+
const labels = await this.getReceiptLabels();
|
|
47
|
+
const values = await this.getReceiptLabelValues();
|
|
48
|
+
|
|
49
|
+
const receiptMap: Record<string, string> = {};
|
|
50
|
+
if (!labels.length || !values.length) {
|
|
51
|
+
console.warn("⚠ Receipt labels or values are empty");
|
|
52
|
+
return receiptMap;
|
|
53
|
+
}
|
|
54
|
+
if (labels.length !== values.length) {
|
|
55
|
+
console.warn(
|
|
56
|
+
`⚠ Label/value count mismatch → labels: ${labels.length}, values: ${values.length}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
labels.forEach((label, index) => {
|
|
60
|
+
receiptMap[label.trim()] = values[index]?.trim() ?? "";
|
|
61
|
+
});
|
|
62
|
+
return receiptMap;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Page, Locator } from "@playwright/test";
|
|
2
|
+
import { CommonUtils } from '@anddone/coretestautomation/dist'
|
|
3
|
+
|
|
4
|
+
export class SummaryPage {
|
|
5
|
+
readonly page: Page;
|
|
6
|
+
readonly businessName: Locator;
|
|
7
|
+
readonly receiptHasBeenSent_txtLine: Locator;
|
|
8
|
+
readonly paymentSummary_label: Locator;
|
|
9
|
+
readonly viewReceipt_icon: Locator;
|
|
10
|
+
readonly transactionId: Locator;
|
|
11
|
+
readonly transactionSummary_label_list: Locator;
|
|
12
|
+
readonly contactInfo_name_value: Locator;
|
|
13
|
+
readonly contactInfo_email_value: Locator;
|
|
14
|
+
readonly contactInfo_phone_value: Locator;
|
|
15
|
+
readonly shopperCompanyName_value: Locator;
|
|
16
|
+
readonly purposeOfPayment_value: Locator;
|
|
17
|
+
readonly paymentMethod_value: Locator;
|
|
18
|
+
readonly lastFourDigitsOfCard:Locator;
|
|
19
|
+
readonly paymentWillAppearAs_value: Locator;
|
|
20
|
+
readonly amount_value: Locator;
|
|
21
|
+
readonly technologyFee_value: Locator;
|
|
22
|
+
readonly paymentTotal_value: Locator;
|
|
23
|
+
readonly footer: Locator;
|
|
24
|
+
readonly privacyPolicy_footer_link: Locator;
|
|
25
|
+
readonly termsOfUse_footer_link: Locator;
|
|
26
|
+
|
|
27
|
+
constructor(page: Page) {
|
|
28
|
+
this.page = page;
|
|
29
|
+
this.businessName = page.locator('h5.business-name')
|
|
30
|
+
this.paymentSummary_label = page.getByText("Payment Summary");
|
|
31
|
+
this.viewReceipt_icon = page.locator('.summary-column #receipt-icon');
|
|
32
|
+
this.transactionId = page.getByText(/Your Transaction ID is:/);
|
|
33
|
+
this.transactionSummary_label_list = page.locator('div.summary-column div.summary-row span.label');
|
|
34
|
+
this.contactInfo_name_value = page.locator('#insured-name');
|
|
35
|
+
this.contactInfo_email_value = page.locator('#insured-email');
|
|
36
|
+
this.contactInfo_phone_value = page.locator('#insured-phone');
|
|
37
|
+
this.shopperCompanyName_value = page.locator('#insured-behalf');
|
|
38
|
+
this.purposeOfPayment_value = page.locator('#insured-purpose');
|
|
39
|
+
this.paymentMethod_value = page.locator('#insured-payMethod');
|
|
40
|
+
this.lastFourDigitsOfCard=page.locator('#insured-lastFour');
|
|
41
|
+
this.paymentWillAppearAs_value = page.locator('#insured-merchant');
|
|
42
|
+
this.amount_value = this.page.locator('#insured-amount');
|
|
43
|
+
this.technologyFee_value = this.page.locator('#insured-fee');
|
|
44
|
+
this.paymentTotal_value = this.page.locator('#insured-total');
|
|
45
|
+
this.footer = page.locator('div.footer');
|
|
46
|
+
this.receiptHasBeenSent_txtLine = page.locator('div.summary-header span');
|
|
47
|
+
this.privacyPolicy_footer_link = page.locator('.links a[href*="/privacy/"]');
|
|
48
|
+
this.termsOfUse_footer_link = page.locator('.links a[href*="/terms/"]')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async isTransactionSummaryPageDisplayed(): Promise<boolean> {
|
|
52
|
+
return await CommonUtils.isVisible(this.paymentSummary_label);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async getTransactionId(): Promise<string> {
|
|
56
|
+
const text = await CommonUtils.getText(this.transactionId);
|
|
57
|
+
return text?.split("is:")?.[1]?.trim() ?? "";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getSummaryLabels():Promise<string[]>{
|
|
61
|
+
return await CommonUtils.getAllText(this.transactionSummary_label_list);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async getNameFromContactInfo():Promise<string>{
|
|
65
|
+
return await CommonUtils.getText(this.contactInfo_name_value,{description:'Name'})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async getEmailFromContactInfo():Promise<string>{
|
|
69
|
+
return await CommonUtils.getText(this.contactInfo_email_value,{description:'Email'})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async getPhoneFromContactInfo():Promise<string>{
|
|
73
|
+
return await CommonUtils.getText(this.contactInfo_phone_value,{description:'Phone'})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async getShopperCompanyName():Promise<string>{
|
|
77
|
+
return await CommonUtils.getText(this.shopperCompanyName_value,{description:'Phone'})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async getPurposeOfPayment():Promise<string>{
|
|
81
|
+
return await CommonUtils.getText(this.purposeOfPayment_value,{description:'Purpose of Payment'})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async getPaymentMethod():Promise<string>{
|
|
85
|
+
return await CommonUtils.getText(this.paymentMethod_value,{description:'Payment Method'})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async getLastFourDigitOfCard():Promise<string>{
|
|
89
|
+
return await CommonUtils.getText(this.lastFourDigitsOfCard)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async getPaymentWillAppearAs():Promise<string>{
|
|
93
|
+
return await CommonUtils.getText(this.paymentWillAppearAs_value)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async getAmount():Promise<string>{
|
|
97
|
+
return await CommonUtils.getText(this.amount_value)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async getTechnologyFee():Promise<string>{
|
|
101
|
+
return await CommonUtils.getText(this.technologyFee_value)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async getPaymentTotal():Promise<string>{
|
|
105
|
+
return await CommonUtils.getText(this.paymentTotal_value)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async clickOnViewReceiptIcon():Promise<void>{
|
|
109
|
+
await CommonUtils.click(this.viewReceipt_icon)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test';
|
|
2
|
+
import { PaymentLinkPortalLandingPage } from '../src/pages/PaymentLinkPortalLandingPage';
|
|
3
|
+
import { SummaryPage } from '../src/pages/summaryPage';
|
|
4
|
+
import { AssertionUtils, CommonUtils,PageUtils } from '@anddone/coretestautomation/dist';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
test('Complete ACH payment flow', async ({ page }) => {
|
|
8
|
+
await PageUtils.navigateTo(page,'https://link.qat.anddone.com/#/qx7KNjWx1')
|
|
9
|
+
|
|
10
|
+
const paymentPage = new PaymentLinkPortalLandingPage(page);
|
|
11
|
+
const summaryPage = new SummaryPage(page);
|
|
12
|
+
|
|
13
|
+
await paymentPage.fillContactInfo({
|
|
14
|
+
company: 'ABC PVT', first: 'Aditi', last: 'Kadam',
|
|
15
|
+
email: 'abc@gmail.com', phone: '9876543210'
|
|
16
|
+
});
|
|
17
|
+
const value = await paymentPage.getShopperName();
|
|
18
|
+
console.log(value);
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
await paymentPage.selectPaymentMethod('CARD');
|
|
22
|
+
await paymentPage.checkOrUncheckCheckbox(false);
|
|
23
|
+
await paymentPage.fillCardHolderName('Ron Dixit');
|
|
24
|
+
await paymentPage.fillCardDetails({cardNum: '5555555555554444',exp: '03/30',cvv: '737'
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
await paymentPage.fillBillingAddress({address: '123 Main Street',suite: 'Apt 10A',
|
|
29
|
+
city: 'New York',country: 'USA',state: 'NY-New York',
|
|
30
|
+
postalCode: '400708456'
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const totalAmount = await paymentPage.getTotalAmount();
|
|
34
|
+
console.log(totalAmount);
|
|
35
|
+
|
|
36
|
+
await paymentPage.submitPayment();
|
|
37
|
+
|
|
38
|
+
const transactionId=await summaryPage.getTransactionId();
|
|
39
|
+
console.log('Transactio Id: ',transactionId)
|
|
40
|
+
AssertionUtils.verifyNotNull(transactionId);
|
|
41
|
+
|
|
42
|
+
const amount = await summaryPage.getAmount();
|
|
43
|
+
console.log('Amount:',transactionId)
|
|
44
|
+
|
|
45
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test';
|
|
2
|
+
import { PaymentLinkPortalLandingPage } from '../src/pages/PaymentLinkPortalLandingPage';
|
|
3
|
+
import { SummaryPage } from '../src/pages/summaryPage';
|
|
4
|
+
import { AssertionUtils, GenerationUtils, PageUtils } from '@anddone/coretestautomation/dist';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
test('Complete Cad payment flow', async ({ page }) => {
|
|
8
|
+
await PageUtils.navigateTo(page, 'https://link.qat.anddone.com/#/jxbYaaLv')
|
|
9
|
+
|
|
10
|
+
const paymentPage = new PaymentLinkPortalLandingPage(page);
|
|
11
|
+
const summaryPage = new SummaryPage(page);
|
|
12
|
+
|
|
13
|
+
await paymentPage.selectPaymentMethod('CARD');
|
|
14
|
+
|
|
15
|
+
await paymentPage.fillCardHolderName(GenerationUtils.randomFullName());
|
|
16
|
+
await paymentPage.fillCardDetails({
|
|
17
|
+
cardNum: process.env.CC_NUMBER, exp: process.env.CC_EXPIRY, cvv: process.env.CC_CVV
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
await paymentPage.fillBillingAddress({
|
|
21
|
+
address: GenerationUtils.randomAddressLine(), suite: 'Apt 10A',
|
|
22
|
+
city: 'New York', country: 'USA', state: 'NY-New York',
|
|
23
|
+
postalCode: '12131'
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const totalAmount = await paymentPage.getTotalAmount();
|
|
27
|
+
console.log(totalAmount);
|
|
28
|
+
|
|
29
|
+
await paymentPage.submitPayment();
|
|
30
|
+
|
|
31
|
+
const transactionId = await summaryPage.getTransactionId();
|
|
32
|
+
console.log('Transactio Id: ', transactionId)
|
|
33
|
+
AssertionUtils.verifyNotNull(transactionId);
|
|
34
|
+
|
|
35
|
+
const amount = await summaryPage.getAmount();
|
|
36
|
+
console.log('Amount:', amount)
|
|
37
|
+
|
|
38
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// {
|
|
2
|
+
// "compilerOptions": {
|
|
3
|
+
// "target": "ESNext",
|
|
4
|
+
// "module": "NodeNext",
|
|
5
|
+
// "moduleResolution": "NodeNext",
|
|
6
|
+
|
|
7
|
+
// "strict": true,
|
|
8
|
+
// "skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
// "sourceMap": true,
|
|
11
|
+
|
|
12
|
+
// "esModuleInterop": true,
|
|
13
|
+
// "allowSyntheticDefaultImports": true,
|
|
14
|
+
|
|
15
|
+
// "types": ["node", "playwright"],
|
|
16
|
+
// "ignoreDeprecations": "6.0",
|
|
17
|
+
|
|
18
|
+
// "resolveJsonModule": true,
|
|
19
|
+
|
|
20
|
+
// "verbatimModuleSyntax": false,
|
|
21
|
+
// "isolatedModules": false
|
|
22
|
+
// }
|
|
23
|
+
// }
|
|
24
|
+
|
|
25
|
+
{
|
|
26
|
+
"compilerOptions": {
|
|
27
|
+
"target": "ES2020",
|
|
28
|
+
"module": "CommonJS",
|
|
29
|
+
"rootDir": "src",
|
|
30
|
+
"outDir": "dist",
|
|
31
|
+
"declaration": true,
|
|
32
|
+
"declarationMap": true,
|
|
33
|
+
"sourceMap": true,
|
|
34
|
+
"strict": true,
|
|
35
|
+
"esModuleInterop": true,
|
|
36
|
+
"skipLibCheck": true
|
|
37
|
+
},
|
|
38
|
+
"include": ["src/**/*.ts"],
|
|
39
|
+
"exclude": ["node_modules", "dist"]
|
|
40
|
+
}
|