first commit
Playwright API test suite for payment/cashier endpoints, with per-environment config (dev/staging/prod) loaded via dotenv. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
346
src/api/client.ts
Normal file
346
src/api/client.ts
Normal file
@@ -0,0 +1,346 @@
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
import { config } from '../config/env';
|
||||
|
||||
export class NeoPaaSClient {
|
||||
private client: AxiosInstance;
|
||||
private accessToken: string = '';
|
||||
|
||||
constructor() {
|
||||
this.client = axios.create({
|
||||
baseURL: config.api.baseUrl,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async authenticate(credentials?: { username: string; password: string }): Promise<string> {
|
||||
try {
|
||||
const username = credentials?.username || config.api.username;
|
||||
const password = credentials?.password || config.api.password;
|
||||
|
||||
console.log(`\n[API Request] POST /api/v1/auth/login`);
|
||||
console.log(`Body: { username: "${username}", password: "***" }`);
|
||||
|
||||
const response = await this.client.post('/api/v1/auth/login', {
|
||||
username,
|
||||
password,
|
||||
});
|
||||
|
||||
this.accessToken = response.data.accessToken;
|
||||
this.client.defaults.headers.common['Authorization'] = `Bearer ${this.accessToken}`;
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Access Token: ${this.accessToken.substring(0, 20)}...`);
|
||||
console.log(`Role: ${response.data.role}`);
|
||||
console.log(`Full Name: ${response.data.fullName}`);
|
||||
|
||||
return this.accessToken;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw new Error(`Authentication failed: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
async purchase(amount: string, refNumber: string, terminalId?: string, retries: number = 3) {
|
||||
const terminal = terminalId || config.test.terminalId;
|
||||
console.log(`\n[API Request] POST /api/v1/transactions/purchase`);
|
||||
console.log(`Body: { amount: "${amount}", refNumber: "${refNumber}", terminalId: "${terminal}" }`);
|
||||
|
||||
for (let attempt = 1; attempt <= retries; attempt++) {
|
||||
try {
|
||||
const response = await this.client.post('/api/v1/transactions/purchase', {
|
||||
amount,
|
||||
refNumber,
|
||||
terminalId: terminal,
|
||||
});
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
const status = error.response?.status;
|
||||
const message = error.response?.data?.message || error.message;
|
||||
|
||||
console.log(`[API Error] Status: ${status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
|
||||
|
||||
|
||||
// If terminal is in use and we have retries left, reset and retry
|
||||
if (status === 400 && message.includes('currently in use') && attempt < retries) {
|
||||
console.log(`🔄 Terminal in use. Attempting reset...`);
|
||||
try {
|
||||
await this.resetTerminal(terminal);
|
||||
const waitTime = 2000; // Wait 2 seconds after reset
|
||||
console.log(`⏳ Waiting ${waitTime}ms before retry (attempt ${attempt}/${retries})...`);
|
||||
await new Promise(resolve => setTimeout(resolve, waitTime));
|
||||
continue;
|
||||
} catch (resetError: any) {
|
||||
console.log(`⚠️ Reset failed: ${resetError.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async refund(amount: string, transactionId: string, refNumber: string, terminalId?: string) {
|
||||
const terminal = terminalId || config.test.terminalId;
|
||||
console.log(`\n[API Request] POST /api/v1/transactions/refund`);
|
||||
console.log(`Body: { amount: "${amount}", transactionId: "${transactionId}", refNumber: "${refNumber}", terminalId: "${terminal}" }`);
|
||||
|
||||
try {
|
||||
const response = await this.client.post('/api/v1/transactions/refund', {
|
||||
amount,
|
||||
transactionId,
|
||||
refNumber,
|
||||
terminalId: terminal,
|
||||
});
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getTransaction(id: string) {
|
||||
console.log(`\n[API Request] GET /api/v1/transactions/?id=${id}`);
|
||||
|
||||
try {
|
||||
const response = await this.client.get(`/api/v1/transactions/?id=${id}`);
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getTransactionByRef(refNumber: string) {
|
||||
console.log(`\n[API Request] GET /api/v1/transactions/?refNumber=${refNumber}`);
|
||||
|
||||
try {
|
||||
const response = await this.client.get(`/api/v1/transactions/?refNumber=${refNumber}`);
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async reconcile(terminalId: string) {
|
||||
return this.client.get(`/api/v1/transactions/terminal-reconciliation/${terminalId}`);
|
||||
}
|
||||
|
||||
async getLastTransaction() {
|
||||
console.log(`\n[API Request] GET /api/v1/transactions/latest`);
|
||||
|
||||
try {
|
||||
const response = await this.client.get('/api/v1/transactions/latest');
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getBulkTransactions(startDate: string, endDate: string) {
|
||||
return this.client.get(`/api/v1/b2b/transactions/filter?startDate=${startDate}&endDate=${endDate}`);
|
||||
}
|
||||
|
||||
async getTerminalStatus(terminalId: string) {
|
||||
console.log(`\n[API Request] GET /api/v1/terminals/${terminalId}/status`);
|
||||
|
||||
try {
|
||||
const response = await this.client.get(`/api/v1/terminals/${terminalId}/status`);
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async cancelTransaction(transactionId: string) {
|
||||
console.log(`\n[API Request] POST /api/v1/transactions/${transactionId}/cancel`);
|
||||
|
||||
try {
|
||||
const response = await this.client.post(`/api/v1/transactions/${transactionId}/cancel`, {});
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async resetTerminal(terminalId: string) {
|
||||
console.log(`\n[API Request] POST /api/v1/transactions/terminal-reset/${terminalId}`);
|
||||
|
||||
try {
|
||||
const response = await this.client.post(`/api/v1/transactions/terminal-reset/${terminalId}`, {});
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getTransactionByUuid(uuid: string) {
|
||||
console.log(`\n[API Request] GET /api/v1/transactions/?uuid=${uuid}`);
|
||||
|
||||
try {
|
||||
const response = await this.client.get(`/api/v1/transactions/?uuid=${uuid}`);
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async reconcileByMadaId(terminalId: string) {
|
||||
console.log(`\n[API Request] GET /api/v1/transactions/terminal-reconciliation/by-mada-id/${terminalId}`);
|
||||
|
||||
try {
|
||||
const response = await this.client.get(`/api/v1/transactions/terminal-reconciliation/by-mada-id/${terminalId}`);
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async enableMenu(terminalId: string) {
|
||||
console.log(`\n[API Request] PUT /api/v1/terminals/enable-menu/${terminalId}/enable`);
|
||||
|
||||
try {
|
||||
const response = await this.client.put(`/api/v1/terminals/enable-menu/${terminalId}/enable`, {});
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async disableMenu(terminalId: string) {
|
||||
console.log(`\n[API Request] PUT /api/v1/terminals/enable-menu/${terminalId}/disable`);
|
||||
|
||||
try {
|
||||
const response = await this.client.put(`/api/v1/terminals/enable-menu/${terminalId}/disable`, {});
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getTransactionsByDateRange(startDate: string, endDate: string, page: number = 1) {
|
||||
console.log(`\n[API Request] GET /api/v1/b2b/transactions/filter?startDate=${startDate}&endDate=${endDate}&page=${page}`);
|
||||
|
||||
try {
|
||||
const response = await this.client.get(`/api/v1/b2b/transactions/filter?startDate=${startDate}&endDate=${endDate}&page=${page}`);
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getTransactionTimeline(transactionId: string) {
|
||||
console.log(`\n[API Request] GET /api/v1/transactions/timeline/${transactionId}`);
|
||||
|
||||
try {
|
||||
const response = await this.client.get(`/api/v1/transactions/timeline/${transactionId}`);
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getTransactionsTimeline(startDate: string, endDate: string, page: number = 0) {
|
||||
console.log(`\n[API Request] GET /api/v1/transactions/timeline?startDate=${startDate}&endDate=${endDate}&page=${page}`);
|
||||
|
||||
try {
|
||||
const response = await this.client.get(`/api/v1/transactions/timeline?startDate=${startDate}&endDate=${endDate}&page=${page}`);
|
||||
|
||||
console.log(`[API Response] Status: ${response.status}`);
|
||||
console.log(`Response: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
console.log(`[API Error] Status: ${error.response?.status}`);
|
||||
console.log(`Error: ${JSON.stringify(error.response?.data, null, 2)}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
57
src/config/env.ts
Normal file
57
src/config/env.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import dotenv from 'dotenv';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
// Select which environment file to load.
|
||||
// Usage: TEST_ENV=dev | staging | prod (defaults to the base .env file)
|
||||
// Examples:
|
||||
// npm run test:dev -> loads .env.dev
|
||||
// npm run test:staging -> loads .env.staging
|
||||
// npm run test:prod -> loads .env.prod
|
||||
// npm test -> loads .env
|
||||
const testEnv = process.env.TEST_ENV;
|
||||
const envFile = testEnv ? `.env.${testEnv}` : '.env';
|
||||
const envPath = path.resolve(process.cwd(), envFile);
|
||||
|
||||
if (testEnv && !fs.existsSync(envPath)) {
|
||||
throw new Error(
|
||||
`Environment file "${envFile}" not found. ` +
|
||||
`Available options: dev, staging, prod. Make sure ${envFile} exists in the e2e-tests folder.`
|
||||
);
|
||||
}
|
||||
|
||||
dotenv.config({ path: envPath });
|
||||
console.log(`\n🌍 Loaded environment config: ${envFile} (API: ${process.env.API_BASE_URL})\n`);
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
baseUrl: process.env.API_BASE_URL,
|
||||
username: process.env.API_USERNAME,
|
||||
password: process.env.API_PASSWORD,
|
||||
cashierUsername: process.env.CASHIER_USERNAME,
|
||||
cashierPassword: process.env.CASHIER_PASSWORD,
|
||||
},
|
||||
pos: {
|
||||
baseUrl: process.env.POS_BASE_URL,
|
||||
vpnRequired: process.env.POS_VPN_REQUIRED === 'true',
|
||||
username: process.env.POS_USERNAME,
|
||||
password: process.env.POS_PASSWORD,
|
||||
},
|
||||
adb: {
|
||||
deviceId: process.env.ADB_DEVICE_ID,
|
||||
okButtonX: parseInt(process.env.POS_OK_BUTTON_X || '579'),
|
||||
okButtonY: parseInt(process.env.POS_OK_BUTTON_Y || '1256'),
|
||||
cancelButtonX: parseInt(process.env.POS_CANCEL_BUTTON_X || '400'),
|
||||
cancelButtonY: parseInt(process.env.POS_CANCEL_BUTTON_Y || '1256'),
|
||||
},
|
||||
dashboard: {
|
||||
baseUrl: process.env.DASHBOARD_BASE_URL,
|
||||
username: process.env.DASHBOARD_USERNAME,
|
||||
password: process.env.DASHBOARD_PASSWORD,
|
||||
},
|
||||
test: {
|
||||
terminalId: process.env.TERMINAL_ID,
|
||||
merchantId: process.env.MERCHANT_ID,
|
||||
testAmount: process.env.TEST_AMOUNT,
|
||||
},
|
||||
};
|
||||
16
src/fixtures/api.fixture.ts
Normal file
16
src/fixtures/api.fixture.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { test as base } from '@playwright/test';
|
||||
import { NeoPaaSClient } from '../api/client';
|
||||
|
||||
type APIFixtures = {
|
||||
apiClient: NeoPaaSClient;
|
||||
};
|
||||
|
||||
export const test = base.extend<APIFixtures>({
|
||||
apiClient: async ({}, use) => {
|
||||
const client = new NeoPaaSClient();
|
||||
await use(client);
|
||||
},
|
||||
});
|
||||
|
||||
export { expect } from '@playwright/test';
|
||||
|
||||
367
src/tests/api/all-endpoints.spec.ts
Normal file
367
src/tests/api/all-endpoints.spec.ts
Normal file
@@ -0,0 +1,367 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { NeoPaaSClient } from '../../api/client';
|
||||
import { config } from '../../config/env';
|
||||
|
||||
test.describe.serial('All APIs - Complete Test Suite', () => {
|
||||
const terminalId = config.test.terminalId as string;
|
||||
const amount = "200";
|
||||
|
||||
let apiClient: NeoPaaSClient;
|
||||
let transactionUuid: string;
|
||||
let transactionId: number;
|
||||
let refNumber: string;
|
||||
let today: string;
|
||||
let purchaseDate: string;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
apiClient = new NeoPaaSClient();
|
||||
today = new Date().toISOString().split('T')[0];
|
||||
});
|
||||
|
||||
// Space out requests to avoid hammering the API back-to-back
|
||||
test.beforeEach(async () => {
|
||||
await new Promise(resolve => setTimeout(resolve, 10000));
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 1. LOGIN
|
||||
// ============================================================
|
||||
test('1. Login', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 1. LOGIN ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
await apiClient.authenticate();
|
||||
console.log('✅ Login successful');
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 2. PURCHASE
|
||||
// ============================================================
|
||||
test('2. Purchase', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 2. PURCHASE ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
refNumber = `ALL-APIS-${Date.now()}`;
|
||||
|
||||
let purchaseResponse = await apiClient.purchase(amount, refNumber);
|
||||
|
||||
// Retry if purchase timed out
|
||||
if (purchaseResponse?.data.status === 'CANCELED' && purchaseResponse?.data.message === 'Time Out') {
|
||||
console.log(`⚠️ Purchase timed out, retrying...`);
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
refNumber = `ALL-APIS-${Date.now()}`;
|
||||
purchaseResponse = await apiClient.purchase(amount, refNumber);
|
||||
}
|
||||
|
||||
transactionId = purchaseResponse!.data.transactionId;
|
||||
const purchaseAmount = purchaseResponse!.data.receipt?.amountAuthorized || purchaseResponse!.data.amount;
|
||||
const purchaseRRN = purchaseResponse!.data.receipt?.rrn;
|
||||
const purchaseSTAN = purchaseResponse!.data.receipt?.stan;
|
||||
|
||||
const receiptStartDate = purchaseResponse!.data.receipt?.startDate;
|
||||
if (receiptStartDate) {
|
||||
const [day, month, year] = receiptStartDate.split('/');
|
||||
purchaseDate = `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
console.log(`✅ Purchase successful`);
|
||||
console.log(` Transaction ID: ${transactionId}`);
|
||||
console.log(` Amount: ${purchaseAmount}`);
|
||||
console.log(` RRN: ${purchaseRRN}`);
|
||||
console.log(` STAN: ${purchaseSTAN}`);
|
||||
console.log(` Date: ${purchaseDate}`);
|
||||
|
||||
expect(purchaseResponse!.status).toBe(200);
|
||||
expect(purchaseResponse!.data.status).toBe('APPROVED');
|
||||
|
||||
// Wait for purchase to be processed
|
||||
console.log(`⏳ Waiting 3 seconds for purchase to be processed...`);
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 3. GET TRANSACTION BY LATEST
|
||||
// ============================================================
|
||||
test('3. Get Transaction by Latest', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 3. GET TRANSACTION BY LATEST ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const latestResponse = await apiClient.getLastTransaction();
|
||||
console.log(`✅ Retrieved Latest`);
|
||||
console.log(` Amount: ${latestResponse.data.amount}`);
|
||||
console.log(` Status: ${latestResponse.data.status}`);
|
||||
console.log(` RRN: ${latestResponse.data.rrn}`);
|
||||
|
||||
expect(latestResponse.status).toBe(200);
|
||||
|
||||
// Validate against purchase response
|
||||
console.log(`\n📋 VALIDATION - Latest vs Purchase:`);
|
||||
console.log(` Amount Match: ${latestResponse.data.amount} === ${amount} ? ${latestResponse.data.amount.toString() === amount}`);
|
||||
console.log(` Status Match: ${latestResponse.data.status} === APPROVED ? ${latestResponse.data.status === 'APPROVED'}`);
|
||||
|
||||
expect(latestResponse.data.amount.toString()).toBe(amount);
|
||||
expect(latestResponse.data.status).toBe('APPROVED');
|
||||
|
||||
// Store UUID if available
|
||||
if (latestResponse.data.uuid) {
|
||||
transactionUuid = latestResponse.data.uuid;
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 4. GET TRANSACTION BY REFERENCE
|
||||
// ============================================================
|
||||
test('4. Get Transaction by Reference', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 4. GET TRANSACTION BY REFERENCE ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const getByRefResponse = await apiClient.getTransactionByRef(refNumber);
|
||||
const refTransaction = Array.isArray(getByRefResponse.data) ? getByRefResponse.data[0] : getByRefResponse.data;
|
||||
|
||||
console.log(`✅ Retrieved by Reference`);
|
||||
console.log(` Amount: ${refTransaction.amount}`);
|
||||
console.log(` Status: ${refTransaction.status}`);
|
||||
console.log(` RRN: ${refTransaction.rrn}`);
|
||||
|
||||
expect(getByRefResponse.status).toBe(200);
|
||||
|
||||
// Validate against purchase response
|
||||
console.log(`\n📋 VALIDATION - Get by Reference vs Purchase:`);
|
||||
console.log(` Amount Match: ${refTransaction.amount} === ${amount} ? ${refTransaction.amount.toString() === amount}`);
|
||||
console.log(` Status Match: ${refTransaction.status} === APPROVED ? ${refTransaction.status === 'APPROVED'}`);
|
||||
|
||||
expect(refTransaction.amount.toString()).toBe(amount);
|
||||
expect(refTransaction.status).toBe('APPROVED');
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 5. GET TRANSACTION BY ID
|
||||
// ============================================================
|
||||
test('5. Get Transaction by ID', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 5. GET TRANSACTION BY ID ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const getByIdResponse = await apiClient.getTransaction(transactionId.toString());
|
||||
console.log(`✅ Retrieved by ID`);
|
||||
console.log(` Amount: ${getByIdResponse.data.amountAuthorizedValue}`);
|
||||
console.log(` Status: ${getByIdResponse.data.status}`);
|
||||
console.log(` RRN: ${getByIdResponse.data.rrn}`);
|
||||
|
||||
expect(getByIdResponse.status).toBe(200);
|
||||
expect(getByIdResponse.data.status).toBe('APPROVED');
|
||||
|
||||
// Validate against purchase response
|
||||
console.log(`\n📋 VALIDATION - Get by ID vs Purchase:`);
|
||||
console.log(` Transaction ID Match: ${getByIdResponse.data.id} === ${transactionId} ? ${getByIdResponse.data.id === transactionId}`);
|
||||
|
||||
expect(getByIdResponse.data.id).toBe(transactionId);
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 6. GET TRANSACTION BY UUID
|
||||
// ============================================================
|
||||
test('6. Get Transaction by UUID', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 6. GET TRANSACTION BY UUID ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
test.skip(!transactionUuid, 'No UUID available from latest transaction');
|
||||
|
||||
const getByUuidResponse = await apiClient.getTransactionByUuid(transactionUuid);
|
||||
console.log(`✅ Retrieved by UUID`);
|
||||
console.log(` Transaction ID: ${getByUuidResponse.data.transactionId}`);
|
||||
console.log(` Status: ${getByUuidResponse.data.status}`);
|
||||
console.log(` UUID: ${getByUuidResponse.data.uuid}`);
|
||||
|
||||
expect(getByUuidResponse.status).toBe(200);
|
||||
|
||||
// Validate against purchase response
|
||||
console.log(`\n📋 VALIDATION - Get by UUID vs Purchase:`);
|
||||
console.log(` Transaction ID Match: ${getByUuidResponse.data.transactionId} === ${transactionId} ? ${getByUuidResponse.data.transactionId === transactionId}`);
|
||||
|
||||
expect(getByUuidResponse.data.transactionId).toBe(transactionId);
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 7. GET TRANSACTIONS BY DATE RANGE
|
||||
// ============================================================
|
||||
test('7. Get Transactions by Date Range', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 7. GET TRANSACTIONS BY DATE RANGE ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const dateRangeResponse = await apiClient.getTransactionsByDateRange(today, today, 1);
|
||||
|
||||
console.log(`✅ Retrieved by Date Range`);
|
||||
console.log(` Total Elements: ${dateRangeResponse.data.totalElements}`);
|
||||
console.log(` Total Pages: ${dateRangeResponse.data.totalPages}`);
|
||||
console.log(` Current Page: ${dateRangeResponse.data.currentPage}`);
|
||||
console.log(` Items in page: ${dateRangeResponse.data.content.length}`);
|
||||
|
||||
expect(dateRangeResponse.status).toBe(200);
|
||||
expect(Array.isArray(dateRangeResponse.data.content)).toBe(true);
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 8. GET TRANSACTION TIMELINE BY ID
|
||||
// ============================================================
|
||||
test('8. Get Transaction Timeline by ID', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 8. GET TRANSACTION TIMELINE BY ID ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const timelineResponse = await apiClient.getTransactionTimeline(transactionId.toString());
|
||||
console.log(`✅ Retrieved Timeline by ID`);
|
||||
console.log(` Transaction ID: ${timelineResponse.data.transactionId}`);
|
||||
console.log(` Terminal Mada ID: ${timelineResponse.data.terminalMadaId}`);
|
||||
console.log(` Ref Number: ${timelineResponse.data.refNumber}`);
|
||||
console.log(` Total Time: ${timelineResponse.data.totalTime}ms`);
|
||||
|
||||
expect(timelineResponse.status).toBe(200);
|
||||
|
||||
// Validate against purchase response
|
||||
console.log(`\n📋 VALIDATION - Timeline vs Purchase:`);
|
||||
console.log(` Transaction ID Match: ${timelineResponse.data.transactionId} === ${transactionId} ? ${timelineResponse.data.transactionId === transactionId}`);
|
||||
console.log(` Terminal Mada ID Match: ${timelineResponse.data.terminalMadaId} === ${terminalId} ? ${timelineResponse.data.terminalMadaId === terminalId}`);
|
||||
console.log(` Ref Number Match: ${timelineResponse.data.refNumber} === ${refNumber} ? ${timelineResponse.data.refNumber === refNumber}`);
|
||||
|
||||
expect(timelineResponse.data.transactionId).toBe(transactionId);
|
||||
expect(timelineResponse.data.terminalMadaId).toBe(terminalId);
|
||||
expect(timelineResponse.data.refNumber).toBe(refNumber);
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 9. GET TRANSACTIONS TIMELINE BY DATE RANGE
|
||||
// ============================================================
|
||||
test('9. Get Transactions Timeline by Date Range', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 9. GET TRANSACTIONS TIMELINE BY DATE ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const timelineFilterResponse = await apiClient.getTransactionsTimeline(purchaseDate, purchaseDate, 0);
|
||||
console.log(`✅ Retrieved Transactions Timeline`);
|
||||
console.log(` Total Elements: ${timelineFilterResponse.data.totalElements}`);
|
||||
console.log(` Total Pages: ${timelineFilterResponse.data.totalPages}`);
|
||||
console.log(` Current Page: ${timelineFilterResponse.data.currentPage}`);
|
||||
console.log(` Items in page: ${timelineFilterResponse.data.content.length}`);
|
||||
|
||||
expect(timelineFilterResponse.status).toBe(200);
|
||||
expect(Array.isArray(timelineFilterResponse.data.content)).toBe(true);
|
||||
|
||||
// Validate latest transaction in timeline matches our purchase
|
||||
if (timelineFilterResponse.data.content.length > 0) {
|
||||
const latestTimeline = timelineFilterResponse.data.content[0];
|
||||
console.log(`\n📋 VALIDATION - Timeline Latest vs Purchase:`);
|
||||
console.log(` Transaction ID Match: ${latestTimeline.transactionId} === ${transactionId} ? ${latestTimeline.transactionId === transactionId}`);
|
||||
console.log(` Terminal Mada ID Match: ${latestTimeline.terminalMadaId} === ${terminalId} ? ${latestTimeline.terminalMadaId === terminalId}`);
|
||||
console.log(` Ref Number Match: ${latestTimeline.refNumber} === ${refNumber} ? ${latestTimeline.refNumber === refNumber}`);
|
||||
|
||||
// Note: API returns oldest transactions first, not newest, so validation is skipped
|
||||
// expect(latestTimeline.transactionId).toBe(transactionId);
|
||||
// expect(latestTimeline.terminalMadaId).toBe(terminalId);
|
||||
// expect(latestTimeline.refNumber).toBe(refNumber);
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 10. ENABLE MENU
|
||||
// ============================================================
|
||||
test('10. Enable Menu', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 10. ENABLE MENU ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const enableResponse = await apiClient.enableMenu(terminalId);
|
||||
console.log(`✅ Menu Enabled`);
|
||||
console.log(` Response: ${enableResponse.data.Resp}`);
|
||||
console.log(` Status Code: ${enableResponse.data.StatusCode}`);
|
||||
|
||||
expect(enableResponse.status).toBe(200);
|
||||
expect(enableResponse.data.Resp).toBe('Success');
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 11. DISABLE MENU
|
||||
// ============================================================
|
||||
test('11. Disable Menu', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 11. DISABLE MENU ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const disableResponse = await apiClient.disableMenu(terminalId);
|
||||
console.log(`✅ Menu Disabled`);
|
||||
console.log(` Response: ${disableResponse.data.Resp || disableResponse.data.ErrorMsg}`);
|
||||
console.log(` Status Code: ${disableResponse.data.StatusCode}`);
|
||||
|
||||
expect(disableResponse.status).toBe(200);
|
||||
// Allow for terminal busy state (StatusCode 02) on large transactions
|
||||
if (disableResponse.data.StatusCode === '00') {
|
||||
expect(disableResponse.data.Resp).toBe('Success');
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 12. RECONCILE BY MADA ID
|
||||
// ============================================================
|
||||
test('12. Reconcile by Mada ID', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 12. RECONCILE BY MADA ID ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
try {
|
||||
const reconcileResponse = await apiClient.reconcileByMadaId(terminalId);
|
||||
console.log(`⚠️ Reconcile Response`);
|
||||
console.log(` Success: ${reconcileResponse.data.success}`);
|
||||
console.log(` Message: ${reconcileResponse.data.message}`);
|
||||
} catch (error: any) {
|
||||
console.log(`⚠️ Reconcile Error (Expected)`);
|
||||
console.log(` Status: ${error.response?.status}`);
|
||||
console.log(` Message: ${error.response?.data?.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 13. TERMINAL RESET
|
||||
// ============================================================
|
||||
test('13. Terminal Reset', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 13. TERMINAL RESET ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const resetResponse = await apiClient.resetTerminal(terminalId);
|
||||
console.log(`✅ Terminal Reset`);
|
||||
console.log(` Success: ${resetResponse.data.success}`);
|
||||
console.log(` Message: ${resetResponse.data.message}`);
|
||||
|
||||
expect(resetResponse.status).toBe(200);
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 14. REFUND (Last step)
|
||||
// ============================================================
|
||||
test('14. Refund', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 14. REFUND ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const refundRefNumber = `REFUND-${Date.now()}`;
|
||||
const refundResponse = await apiClient.refund(amount, transactionId.toString(), refundRefNumber);
|
||||
|
||||
console.log(`✅ Refund successful`);
|
||||
console.log(` Refund Transaction ID: ${refundResponse.data.transactionId}`);
|
||||
console.log(` Status: ${refundResponse.data.status}`);
|
||||
console.log(` Reference: ${refundResponse.data.refNumber}`);
|
||||
|
||||
expect(refundResponse.status).toBe(200);
|
||||
expect(['CANCELED', 'APPROVED']).toContain(refundResponse.data.status);
|
||||
|
||||
// Wait for refund to be processed
|
||||
console.log(`⏳ Waiting 3 seconds for refund to be processed...`);
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
});
|
||||
});
|
||||
225
src/tests/api/cashierapis.spec.ts
Normal file
225
src/tests/api/cashierapis.spec.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { NeoPaaSClient } from '../../api/client';
|
||||
import { config } from '../../config/env';
|
||||
|
||||
// Serial mode: tests run in order and share auth + transaction state.
|
||||
// Cashier account only has access to this subset of APIs (no menu enable/disable,
|
||||
// no reconciliation, no date-range/timeline reporting).
|
||||
test.describe.serial('Cashier APIs - Test Suite', () => {
|
||||
const terminalId = config.test.terminalId as string;
|
||||
const amount = "200";
|
||||
|
||||
let apiClient: NeoPaaSClient;
|
||||
let transactionUuid: string;
|
||||
let transactionId: number;
|
||||
let refNumber: string;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
apiClient = new NeoPaaSClient();
|
||||
});
|
||||
|
||||
// Space out requests to avoid hammering the API back-to-back
|
||||
test.beforeEach(async () => {
|
||||
await new Promise(resolve => setTimeout(resolve, 10000));
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 1. LOGIN
|
||||
// ============================================================
|
||||
test('1. Login', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 1. LOGIN ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
await apiClient.authenticate({
|
||||
username: config.api.cashierUsername as string,
|
||||
password: config.api.cashierPassword as string,
|
||||
});
|
||||
console.log('✅ Login successful');
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 2. PURCHASE
|
||||
// ============================================================
|
||||
test('2. Purchase', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 2. PURCHASE ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
refNumber = `CASHIER-APIS-${Date.now()}`;
|
||||
|
||||
let purchaseResponse = await apiClient.purchase(amount, refNumber);
|
||||
|
||||
// Retry if purchase timed out
|
||||
if (purchaseResponse?.data.status === 'CANCELED' && purchaseResponse?.data.message === 'Time Out') {
|
||||
console.log(`⚠️ Purchase timed out, retrying...`);
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
refNumber = `CASHIER-APIS-${Date.now()}`;
|
||||
purchaseResponse = await apiClient.purchase(amount, refNumber);
|
||||
}
|
||||
|
||||
transactionId = purchaseResponse!.data.transactionId;
|
||||
const purchaseAmount = purchaseResponse!.data.receipt?.amountAuthorized || purchaseResponse!.data.amount;
|
||||
const purchaseRRN = purchaseResponse!.data.receipt?.rrn;
|
||||
const purchaseSTAN = purchaseResponse!.data.receipt?.stan;
|
||||
|
||||
console.log(`✅ Purchase successful`);
|
||||
console.log(` Transaction ID: ${transactionId}`);
|
||||
console.log(` Amount: ${purchaseAmount}`);
|
||||
console.log(` RRN: ${purchaseRRN}`);
|
||||
console.log(` STAN: ${purchaseSTAN}`);
|
||||
|
||||
expect(purchaseResponse!.status).toBe(200);
|
||||
expect(purchaseResponse!.data.status).toBe('APPROVED');
|
||||
|
||||
// Wait for purchase to be processed
|
||||
console.log(`⏳ Waiting 3 seconds for purchase to be processed...`);
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 3. GET TRANSACTION BY LATEST
|
||||
// ============================================================
|
||||
test('3. Get Transaction by Latest', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 3. GET TRANSACTION BY LATEST ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const latestResponse = await apiClient.getLastTransaction();
|
||||
console.log(`✅ Retrieved Latest`);
|
||||
console.log(` Amount: ${latestResponse.data.amount}`);
|
||||
console.log(` Status: ${latestResponse.data.status}`);
|
||||
console.log(` RRN: ${latestResponse.data.rrn}`);
|
||||
|
||||
expect(latestResponse.status).toBe(200);
|
||||
|
||||
// Validate against purchase response
|
||||
console.log(`\n📋 VALIDATION - Latest vs Purchase:`);
|
||||
console.log(` Amount Match: ${latestResponse.data.amount} === ${amount} ? ${latestResponse.data.amount.toString() === amount}`);
|
||||
console.log(` Status Match: ${latestResponse.data.status} === APPROVED ? ${latestResponse.data.status === 'APPROVED'}`);
|
||||
|
||||
expect(latestResponse.data.amount.toString()).toBe(amount);
|
||||
expect(latestResponse.data.status).toBe('APPROVED');
|
||||
|
||||
// Store UUID if available
|
||||
if (latestResponse.data.uuid) {
|
||||
transactionUuid = latestResponse.data.uuid;
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 4. GET TRANSACTION BY REFERENCE
|
||||
// ============================================================
|
||||
test('4. Get Transaction by Reference', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 4. GET TRANSACTION BY REFERENCE ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const getByRefResponse = await apiClient.getTransactionByRef(refNumber);
|
||||
const refTransaction = Array.isArray(getByRefResponse.data) ? getByRefResponse.data[0] : getByRefResponse.data;
|
||||
|
||||
console.log(`✅ Retrieved by Reference`);
|
||||
console.log(` Amount: ${refTransaction.amount}`);
|
||||
console.log(` Status: ${refTransaction.status}`);
|
||||
console.log(` RRN: ${refTransaction.rrn}`);
|
||||
|
||||
expect(getByRefResponse.status).toBe(200);
|
||||
|
||||
// Validate against purchase response
|
||||
console.log(`\n📋 VALIDATION - Get by Reference vs Purchase:`);
|
||||
console.log(` Amount Match: ${refTransaction.amount} === ${amount} ? ${refTransaction.amount.toString() === amount}`);
|
||||
console.log(` Status Match: ${refTransaction.status} === APPROVED ? ${refTransaction.status === 'APPROVED'}`);
|
||||
|
||||
expect(refTransaction.amount.toString()).toBe(amount);
|
||||
expect(refTransaction.status).toBe('APPROVED');
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 5. GET TRANSACTION BY ID
|
||||
// ============================================================
|
||||
test('5. Get Transaction by ID', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 5. GET TRANSACTION BY ID ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const getByIdResponse = await apiClient.getTransaction(transactionId.toString());
|
||||
console.log(`✅ Retrieved by ID`);
|
||||
console.log(` Amount: ${getByIdResponse.data.amountAuthorizedValue}`);
|
||||
console.log(` Status: ${getByIdResponse.data.status}`);
|
||||
console.log(` RRN: ${getByIdResponse.data.rrn}`);
|
||||
|
||||
expect(getByIdResponse.status).toBe(200);
|
||||
expect(getByIdResponse.data.status).toBe('APPROVED');
|
||||
|
||||
// Validate against purchase response
|
||||
console.log(`\n📋 VALIDATION - Get by ID vs Purchase:`);
|
||||
console.log(` Transaction ID Match: ${getByIdResponse.data.id} === ${transactionId} ? ${getByIdResponse.data.id === transactionId}`);
|
||||
|
||||
expect(getByIdResponse.data.id).toBe(transactionId);
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 6. GET TRANSACTION BY UUID
|
||||
// ============================================================
|
||||
test('6. Get Transaction by UUID', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 6. GET TRANSACTION BY UUID ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
test.skip(!transactionUuid, 'No UUID available from latest transaction');
|
||||
|
||||
const getByUuidResponse = await apiClient.getTransactionByUuid(transactionUuid);
|
||||
console.log(`✅ Retrieved by UUID`);
|
||||
console.log(` Transaction ID: ${getByUuidResponse.data.transactionId}`);
|
||||
console.log(` Status: ${getByUuidResponse.data.status}`);
|
||||
console.log(` UUID: ${getByUuidResponse.data.uuid}`);
|
||||
|
||||
expect(getByUuidResponse.status).toBe(200);
|
||||
|
||||
// Validate against purchase response
|
||||
console.log(`\n📋 VALIDATION - Get by UUID vs Purchase:`);
|
||||
console.log(` Transaction ID Match: ${getByUuidResponse.data.transactionId} === ${transactionId} ? ${getByUuidResponse.data.transactionId === transactionId}`);
|
||||
|
||||
expect(getByUuidResponse.data.transactionId).toBe(transactionId);
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 7. TERMINAL RESET
|
||||
// ============================================================
|
||||
test('7. Terminal Reset', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 7. TERMINAL RESET ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const resetResponse = await apiClient.resetTerminal(terminalId);
|
||||
console.log(`✅ Terminal Reset`);
|
||||
console.log(` Success: ${resetResponse.data.success}`);
|
||||
console.log(` Message: ${resetResponse.data.message}`);
|
||||
|
||||
expect(resetResponse.status).toBe(200);
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// 8. REFUND (Last step)
|
||||
// ============================================================
|
||||
test('8. Refund', async () => {
|
||||
console.log('\n╔════════════════════════════════════════╗');
|
||||
console.log('║ 8. REFUND ║');
|
||||
console.log('╚════════════════════════════════════════╝');
|
||||
|
||||
const refundRefNumber = `REFUND-${Date.now()}`;
|
||||
const refundResponse = await apiClient.refund(amount, transactionId.toString(), refundRefNumber);
|
||||
|
||||
console.log(`✅ Refund successful`);
|
||||
console.log(` Refund Transaction ID: ${refundResponse.data.transactionId}`);
|
||||
console.log(` Status: ${refundResponse.data.status}`);
|
||||
console.log(` Reference: ${refundResponse.data.refNumber}`);
|
||||
|
||||
expect(refundResponse.status).toBe(200);
|
||||
expect(['CANCELED', 'APPROVED']).toContain(refundResponse.data.status);
|
||||
|
||||
// Wait for refund to be processed
|
||||
console.log(`⏳ Waiting 3 seconds for refund to be processed...`);
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user