Merge pull request #924 from Safelite/sshaik/develop

Sshaik/develop
This commit is contained in:
Siraj 2025-02-18 15:26:36 -05:00 committed by GitHub
commit a421bf213e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 1571 additions and 147 deletions

11
package-lock.json generated
View file

@ -40,6 +40,7 @@
"@vue/cli-service": "~5.0.0",
"@vue/test-utils": "^2.4.1",
"@vue/vue3-jest": "^27.0.0-alpha.1",
"axe-core": "^4.10.2",
"axios": "^1.7.8",
"axios-mock-adapter": "^1.21.5",
"babel-jest": "^27.0.6",
@ -6039,6 +6040,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/axe-core": {
"version": "4.10.2",
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz",
"integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==",
"dev": true,
"license": "MPL-2.0",
"engines": {
"node": ">=4"
}
},
"node_modules/axios": {
"version": "1.7.9",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",

View file

@ -73,6 +73,7 @@
"vite": "^4.5.9",
"vitest": "^0.33.0",
"volar-service-vetur": "latest",
"wait-on": "^8.0.2"
"wait-on": "^8.0.2",
"axe-core": "^4.10.2"
}
}

View file

@ -2,9 +2,17 @@ CCIS_API_AUTH=
# DEV
BASE_URL="https://selfservice.dev.glassclaim.com"
CCIS_API_URL="https://api.dev.belronus.io"
CCIS_API_URL="https://api.test.belronus.io" # Test API connects to our DEV environments for some reason
ADMIN_SERVICE_API_URL="https://issadminapi.dev.sagaws.net/iss-admin/api/v1/"
DIGITAL_API_URL="https://digitalapi.dev.safelite.io"
ENABLE_ACCESSIBILITY_TESTING = true
ENABLE_MOCK_TESTING = false
# QA
# BASE_URL="https://selfservice.test.glassclaim.com"
# CCIS_API_URL="https://api.test.belronus.io"
# CCIS_API_URL="https://api.test.belronus.io"
# ADMIN_SERVICE_API_URL="https://issadminapi.test.sagaws.net/iss-admin/api/v1/"
# DIGITAL_API_URL="https://digitalapi.test.safelite.io"
# ENABLE_ACCESSIBILITY_TESTING = true
# ENABLE_MOCK_TESTING = false

File diff suppressed because one or more lines are too long

View file

@ -30,9 +30,9 @@ export interface IVehicleDetails {
make: string,
model: string,
style?: string,
vin?: string,
licensePlateNumber?: string,
licensePlateState?: string,
vin?: string|string[],
licensePlateNumber?: string|string[],
licensePlateState?: string|string[],
vehicleLookupType?: VehicleLookupType,
}

View file

@ -9,6 +9,7 @@ export interface ITestData {
isDuplicateClaim: boolean,
isPolicyFound: boolean, // Effective difference between advanced and essential
isUseVehicleOnPolicy: boolean, // Should we use the vehicle on the policy?
isVehicleLookupValidations: boolean, // Should we validate vehicle lookup?
isNoComp: boolean, // Is this a NoComp policy?
isItac: boolean, // Is this an ITAC scenario?
hasStateLawPopup: boolean, // Are we expecting a state law pop-up on ProviderSelectionPage?

View file

@ -135,7 +135,9 @@ export default class TestCase extends DisposableBase implements ITestCase {
if (testInfo.testCase)
await testInfo.testCase.disposeAll();
if (!(testInfo.testCase.testData!.isMockTesting ?? false)) {
await testInfo.testCase.disposeAll();
}
const seconds: string = String(testInfo.duration / 1000);
const minutes: string = (testInfo.duration / 1000 / 60).toFixed(2);

View file

@ -1,11 +1,154 @@
import { Page } from '@playwright/test';
import { type AxiosInstance, type AxiosResponse } from 'axios';
import * as fs from 'fs';
import * as path from 'path';
import { Page } from 'playwright';
import { AxiosResponse, AxiosInstance } from 'axios';
import fs from 'fs';
import path from 'path';
interface MockResponse {
status: number;
body: any;
headers?: { [key: string]: string };
}
class MockApiManager {
private static instance: MockApiManager;
private mockResponses: { [url: string]: MockResponse } = {};
private constructor() { }
public static referralNumber: string;
public static referralSequenceNumber: string;
public static referralCorrelationId: string;
public static savedSessionId: string;
public static getInstance(): MockApiManager {
if (!MockApiManager.instance) {
MockApiManager.instance = new MockApiManager();
}
return MockApiManager.instance;
}
/**
* Loads mock responses from a configuration file.
* @param scenario - The scenario to load mock responses for.
*/
private loadMockResponses(scenario: string): void {
const mockResponsesDir = path.resolve(__dirname, '../../tests/mockResponses');
const configFilePath = path.join(mockResponsesDir, 'mockResponsesConfig.json');
if (fs.existsSync(configFilePath)) {
const config = JSON.parse(fs.readFileSync(configFilePath, 'utf-8'));
const scenarioConfig = config[scenario];
const commonConfig = config['common'];
for (const endpoint in scenarioConfig) {
this.loadMockResponse(endpoint, scenarioConfig[endpoint], mockResponsesDir);
}
for (const endpoint in commonConfig) {
if (!this.mockResponses[endpoint]) {
this.loadMockResponse(endpoint, commonConfig[endpoint], mockResponsesDir);
}
}
} else {
console.error(`Mock responses configuration file not found at path: ${configFilePath}`);
}
}
/**
* Loads a single mock response from a file.
* @param endpoint - The endpoint to load the mock response for.
* @param mockFilePath - The path to the mock response file.
* @param mockResponsesDir - The directory containing the mock response files.
*/
private loadMockResponse(endpoint: string, mockFilePath: string, mockResponsesDir: string): void {
const filePath = path.join(mockResponsesDir, mockFilePath);
if (fs.existsSync(filePath)) {
const mockResponse = fs.readFileSync(filePath, 'utf-8');
try {
this.mockResponses[endpoint] = JSON.parse(mockResponse);
} catch (error) {
console.error(`Error parsing mock response for endpoint: ${endpoint}`, error);
}
} else {
console.warn(`Mock response file${filePath} not found for endpoint: ${endpoint}`);
}
}
/**
* Gets the mocked API response for the given endpoint.
* @param endpoint - The endpoint to get the mocked response for.
* @param scenario - The scenario to load mock responses for.
* @returns The mocked response or null if not found.
*/
public getMockedApiResponse(endpoint: string, scenario: string): MockResponse | null {
if (!this.mockResponses[endpoint]) {
this.loadMockResponses(scenario);
}
return this.mockResponses[endpoint] || null;
}
/**
* Mocks the API response for the given endpoint.
* @param page - The Playwright page object.
* @param endpoint - The endpoint to mock.
* @param scenario - The scenario to load mock responses for.
* @param mockTestingFlag - Flag to enable or disable mocking.
*/
public async mockApiResponse(page: Page, scenario: string, mockTestingFlag: boolean): Promise<void> {
if (!mockTestingFlag) {
console.info("mockTestingFlag is false/undefined. so proceeding with original requests.")
return;
}
if (!scenario) {
console.error('Invalid scenario provided for mocking.');
return;
}
this.loadMockResponses(scenario);
for (const endpoint in this.mockResponses) {
await page.route(`**/${endpoint}`, async (route) => {
const response = this.mockResponses[endpoint];
if (response) {
route.fulfill({
status: response.status || 200,
body: JSON.stringify(response.body || response),
headers: response.headers || { 'Content-Type': 'application/json' },
});
}
console.info("Mocking Endpoint: " + endpoint)
});
}
}
public async updateReferralDetails(endpoint: string, response: MockResponse): Promise<MockResponse> {
try {
response.body.referralCorrelationId = MockApiManager.referralCorrelationId;
response.body.referralNumber = MockApiManager.referralNumber;
} catch (error) {
}
return response;
}
public async getReferralDetails(endpoint: string, response: MockResponse) {
if (endpoint.includes('order/save-session/iss')) {
MockApiManager.referralNumber = response.body.referralNumber;
MockApiManager.referralSequenceNumber = response.body.referralSequenceNumber;
MockApiManager.referralCorrelationId = response.body.referralCorrelationId;
MockApiManager.savedSessionId = response.body.sessionId;
}
}
}
// Export the instance
export const mockApiManager = MockApiManager.getInstance();
export async function httpGet<T>(client: AxiosInstance, url: string): Promise<T> {
const [isSuccess, response] = await handleHttp(client.get<T>(url));
if(isSuccess) {
if (isSuccess) {
return response;
}
@ -15,7 +158,7 @@ export async function httpGet<T>(client: AxiosInstance, url: string): Promise<T>
export async function httpPost<T, D>(client: AxiosInstance, url: string, data: D): Promise<T> {
const [isSuccess, response] = await handleHttp(client.post<T>(url, data));
if(isSuccess) {
if (isSuccess) {
return response;
}
@ -47,46 +190,3 @@ export function forceAPIError(page: Page, endpoint: string) {
: route.continue()
});
}
// Utility method to return mock response based on endpoint and scenario
export function getMockedApiResponse(endpoint: string, scenario: string): object | null {
const mockResponsesDir = path.resolve(__dirname, '../../tests/mockResponses');
const configFilePath = path.join(mockResponsesDir, 'mockResponsesConfig.json');
if (fs.existsSync(configFilePath)) {
const config = JSON.parse(fs.readFileSync(configFilePath, 'utf-8'));
const scenarioConfig = config[scenario];
const commonConfig = config['common'];
let mockFilePath = scenarioConfig ? scenarioConfig[endpoint] : null;
if (!mockFilePath && commonConfig) {
mockFilePath = commonConfig[endpoint];
}
if (mockFilePath) {
const filePath = path.join(mockResponsesDir, mockFilePath);
if (fs.existsSync(filePath)) {
const mockResponse = fs.readFileSync(filePath, 'utf-8');
return JSON.parse(mockResponse);
}
}
}
return null;
}
// Utility method for mocking API responses
export function mockApiResponse(page: Page, endpoint: string, scenario: string, mockTestingFlag: boolean) {
const mockResponse = getMockedApiResponse(endpoint, scenario);
page.route(`**/${endpoint}`, route => {
if (mockResponse && mockTestingFlag) {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(mockResponse)
});
} else {
route.continue();
}
});
}

View file

@ -0,0 +1,337 @@
import * as fs from 'fs';
import * as path from 'path';
import { jsonReportFilePath, reportFilePath } from '../../playwright.config';
interface PageSummary {
pageName: string;
critical: number;
serious: number;
moderate: number;
minor: number;
total: number;
}
export function updateAccessibilityReport(pageName: string, results: any) {
const reportDir = path.dirname(jsonReportFilePath);
if (!fs.existsSync(reportDir)) {
fs.mkdirSync(reportDir, { recursive: true });
}
let existingIssues: { [key: string]: any[] } = {};
if (fs.existsSync(jsonReportFilePath)) {
existingIssues = JSON.parse(fs.readFileSync(jsonReportFilePath, 'utf-8'));
}
if (!existingIssues[pageName]) {
existingIssues[pageName] = [];
}
results.violations.forEach(violation => {
const violationWithPage = { ...violation, pageName };
if (!isDuplicateIssue(existingIssues[pageName], violationWithPage)) {
existingIssues[pageName].push(violationWithPage);
}
});
fs.writeFileSync(jsonReportFilePath, JSON.stringify(existingIssues, null, 2));
}
export function consolidateJsonReport() {
if (!fs.existsSync(jsonReportFilePath)) {
console.error('JSON report file does not exist.');
return;
}
const existingIssues = JSON.parse(fs.readFileSync(jsonReportFilePath, 'utf-8'));
// Consolidate issues by page
const consolidatedIssues: { [key: string]: any[] } = {};
Object.keys(existingIssues).forEach(pageName => {
if (!consolidatedIssues[pageName]) {
consolidatedIssues[pageName] = [];
}
existingIssues[pageName].forEach(issue => {
if (!isDuplicateIssue(consolidatedIssues[pageName], issue)) {
consolidatedIssues[pageName].push(issue);
}
});
});
fs.writeFileSync(jsonReportFilePath, JSON.stringify(consolidatedIssues, null, 2));
}
function generateSummaryTable(existingIssues: { [key: string]: any[] }): string {
return `
<table class="summary-table" role="table">
<thead>
<tr>
<th>Page Name</th>
<th>Critical</th>
<th>Serious</th>
<th>Moderate</th>
<th>Minor</th>
<th>Total</th>
</tr>
</thead>
<tbody>
${Object.keys(existingIssues).map(pageName => {
const issues = existingIssues[pageName];
const critical = issues.filter(issue => issue.impact === 'critical').length;
const serious = issues.filter(issue => issue.impact === 'serious').length;
const moderate = issues.filter(issue => issue.impact === 'moderate').length;
const minor = issues.filter(issue => issue.impact === 'minor').length;
const total = issues.length;
return `
<tr>
<td><a href="#${pageName.replace(/\s+/g, '-')}">${pageName}</a></td>
<td><a href="#${pageName.replace(/\s+/g, '-')}-critical">${critical}</a></td>
<td><a href="#${pageName.replace(/\s+/g, '-')}-serious">${serious}</a></td>
<td><a href="#${pageName.replace(/\s+/g, '-')}-moderate">${moderate}</a></td>
<td><a href="#${pageName.replace(/\s+/g, '-')}-minor">${minor}</a></td>
<td>${total}</td>
</tr>
`;
}).join('')}
</tbody>
</table>
`;
}
function generateImpactSections(issues: any[], pageName: string): string {
return ['critical', 'serious', 'moderate', 'minor'].map(impactType => {
const violations = issues.filter(issue => issue.impact === impactType);
if (violations.length === 0) return '';
return `
<div class="impact-section" id="${pageName.replace(/\s+/g, '-')}-${impactType}">
<h3>${impactType.charAt(0).toUpperCase() + impactType.slice(1)} Impact</h3>
<button type="button" class="collapsible">${impactType.charAt(0).toUpperCase() + impactType.slice(1)} Impact</button>
<div class="content">
<table role="table">
<thead>
<tr>
<th>Impact</th>
<th>Description</th>
<th>Help URL</th>
<th>Tags</th>
<th>Nodes</th>
</tr>
</thead>
<tbody>
${violations.map(violation => `
<tr>
<td>${violation.impact}</td>
<td>${violation.description}</td>
<td><a href="${violation.helpUrl}" target="_blank">${violation.helpUrl}</a></td>
<td>${violation.tags.join(', ')}</td>
<td>
${violation.nodes.map(node => `
<div class="node">
<div class="html">${node.html}</div>
<div class="target">Target: ${node.target.join(', ')}</div>
<div class="failureSummary">${node.failureSummary}</div>
<div class="any">
<strong>Any:</strong>
${node.any.map(check => `
<div class="check">
<div class="message">${check.message}</div>
</div>
`).join('')}
</div>
<div class="all">
<strong>All:</strong>
${node.all.map(check => `
<div class="check">
<div class="message">${check.message}</div>
</div>
`).join('')}
</div>
<div class="none">
<strong>None:</strong>
${node.none.map(check => `
<div class="check">
<div class="message">${check.message}</div>
</div>
`).join('')}
</div>
</div>
`).join('')}
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
</div>
`;
}).join('');
}
function generateDetailedSections(existingIssues: { [key: string]: any[] }): string {
return Object.keys(existingIssues).map(pageName => {
const issues = existingIssues[pageName];
const impactSections = generateImpactSections(issues, pageName);
return `
<div class="page-section" id="${pageName.replace(/\s+/g, '-')}">
<h2>${pageName}</h2>
${impactSections}
</div>
`;
}).join('');
}
export function createAccessibilityHtmlReport() {
consolidateJsonReport();
if (!fs.existsSync(jsonReportFilePath)) {
console.error('JSON report file does not exist.');
return;
}
const existingIssues = JSON.parse(fs.readFileSync(jsonReportFilePath, 'utf-8'));
const inlineStyles = `
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
color: #333;
}
h1 {
text-align: center;
color: #4CAF50;
}
.summary-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.summary-table th, .summary-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.summary-table th {
background-color: #007BFF;
color: white;
}
.summary-table td a {
color: #007BFF;
text-decoration: none;
}
.summary-table td a:hover {
text-decoration: underline;
}
.page-section {
margin-bottom: 40px;
padding: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.impact-section {
margin-bottom: 20px;
}
.violation {
margin-bottom: 20px;
}
.violation .impact {
font-weight: bold;
color: #d9534f;
}
.violation .description {
margin-top: 5px;
}
.violation .helpUrl {
margin-top: 5px;
}
.violation .tags {
margin-top: 5px;
}
.violation .nodes {
margin-top: 10px;
}
.violation .node {
margin-top: 5px;
}
.collapsible {
background-color: #f9f9f9;
color: #333;
cursor: pointer;
padding: 10px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
}
.active, .collapsible:hover {
background-color: #ccc;
}
.content {
padding: 0 18px;
display: none;
overflow: hidden;
background-color: #f1f1f1;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th, td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
`;
let reportContent = `
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ISS Accessibility Report</title>
<style>${inlineStyles}</style>
</head>
<body>
<h1>Accessibility Report</h1>
`;
reportContent += generateSummaryTable(existingIssues);
reportContent += generateDetailedSections(existingIssues);
reportContent += `
<script>
var coll = document.getElementsByClassName("collapsible");
for (var i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.display === "block") {
content.style.display = "none";
} else {
content.style.display = "block";
}
});
}
</script>
</body>
</html>
`;
try {
fs.writeFileSync(reportFilePath, reportContent);
console.log(`Accessibility Report generated successfully at ${reportFilePath}`);
} catch (error) {
console.error('Error writing report file:', error);
}
}
function isDuplicateIssue(existingIssues: any[], newIssue: any): boolean {
return existingIssues.some(issue => {
return issue.id === newIssue.id && issue.pageName === newIssue.pageName;
});
}

View file

@ -1,4 +1,13 @@
import { expect, type Locator, type Page } from '@playwright/test';
import test, { expect, type Locator, type Page } from '@playwright/test';
import axe from 'axe-core';
import { updateAccessibilityReport } from '@utils/ReportUtils';
import { error } from 'console';
declare global {
interface Window {
axe: typeof axe;
}
}
export class BasePage {
readonly page: Page;
@ -6,15 +15,17 @@ export class BasePage {
readonly pageSpinner: Locator;
readonly buttonLoadSpin: Locator;
constructor(page: Page){
constructor(page: Page) {
this.page = page;
this.continueButton = page.locator('[id="infoBox"]').getByRole('button');
this.pageSpinner = page.getByRole('status');
this.buttonLoadSpin = page.getByRole('alert');
}
async nextPage() {
const startingUrl = this.page.url();
let pageName = startingUrl.split('Page=')[1] || startingUrl;
await this.validateAccessibility(pageName);
await expect(async () => {
const currentUrl = this.page.url();
if (currentUrl === startingUrl) {
@ -26,16 +37,82 @@ export class BasePage {
}).toPass({ timeout: 240_000 });
}
async validateURL(url:string){
await expect(this.pageSpinner).toHaveCount(0, {timeout: 60000});
async validateURL(url: string) {
await expect(this.pageSpinner).toHaveCount(0, { timeout: 60000 });
await this.page.waitForURL(url);
}
async fillAndValidate(element: Locator, value: string){
async fillAndValidate(element: Locator, value: string) {
await expect(async () => {
await element.clear();
await element.fill(value);
await expect(element).toHaveValue(value);
}).toPass();
}
async validateAccessibility(pageName: string) {
try {
if (process.env.ENABLE_ACCESSIBILITY_TESTING !== 'true') {
return;
}
// Inject axe-core script into the page
await this.page.addScriptTag({ content: axe.source });
// Run axe-core accessibility checks
const results = await this.page.evaluate(async () => {
return await window.axe.run();
});
// Attach the results to the test report
test.info().attach(`Accessibility results for ${pageName}`, {
body: JSON.stringify(results, null, 2),
contentType: 'application/json'
});
// Update the HTML report with the findings
updateAccessibilityReport(pageName, results);
} catch (error) {
console.warn(`Error running accessibility checks for ${pageName}: ${error}`);
}
}
async clickWithRetry(element, page) {
const timeout = 5000; // milli seconds
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
try {
if (await element.isEnabled()) {
await element.click();
await element.keyboard.press('Tab');
return; // Exit loop if click succeeds
}
} catch (error) {
// Ignore error and retry
}
await page.waitForTimeout(100); // Small delay before retrying
}
console.log(`Failed to click the the element within ${timeout/1000} seconds` + error);
}
async logReferralNumber() {
let mainLocalStorage = JSON.parse(await this.page.evaluate('localStorage.getItem(\'main\')'));
let referralNumber = mainLocalStorage.order.referralNumber as number;
let referralSequenceNumber = mainLocalStorage.order.referralSequenceNumber as number;
if (referralNumber == null) {
for (let i = 1; i <= 20; i++) {
if (!referralNumber == null) break;
await this.page.waitForTimeout(500);
mainLocalStorage = JSON.parse(await this.page.evaluate('localStorage.getItem(\'main\')'));
referralNumber = mainLocalStorage.order.referralNumber as number;
referralSequenceNumber = mainLocalStorage.order.referralSequenceNumber as number;
}
}
await test.step(`Referral Number:${referralNumber} Referral Sequence Number:${referralSequenceNumber}`, async () => {
console.log(`Referral Number:${referralNumber}`);
console.log(`Referral Sequence Number:${referralSequenceNumber}`);
});
}
}

View file

@ -35,7 +35,7 @@ export class OrderConfirmationPage extends BasePage {
async validateOrderConfirmationPage(testData: Partial<ITestData>) {
// Destructure data we use
const { vehicleDetails, customerDetails, servicePackage, isItac,
isNoComp, isPolicyFound, claimDetails, paymentDetails } = testData;
isNoComp, isPolicyFound, claimDetails, paymentDetails, isUseVehicleOnPolicy } = testData;
await this.serviceText.waitFor({ state: "visible" });
await this.logOrderNumber();
@ -73,7 +73,7 @@ export class OrderConfirmationPage extends BasePage {
expect.soft(servicePackageAmt).toBeGreaterThan(0);
}
if (isPolicyFound) {
if (isPolicyFound && (isUseVehicleOnPolicy ?? true)) {
// Extract numbers
const amountDueAmt = Number.parseFloat(amountDueValue!.split('$')[1].replaceAll(',', ''));
const deductibleAmt = deductibleTextValue? Number.parseFloat(deductibleTextValue.split('$')[1].replaceAll(',', '')): 0;

View file

@ -24,12 +24,13 @@ export class PartQuestionsPage extends BasePage {
async selectPartQuestionResponses(partQuestions: IPartQuestion[]) {
for (const pq of partQuestions) {
const partQuestionOptionButton = this.page.locator(`fieldset[aria-labelledby="${pq.partQuestionType}"]`).locator(`[buttonlabel="${pq.optionToSelect}"]`);
const parentobject=this.page.locator(`fieldset[aria-labelledby="${pq.partQuestionType}"]`);
const partQuestionOptionButton = parentobject.locator(`[buttonlabel="${pq.optionToSelect}"]`);
await partQuestionOptionButton.click();
if (pq.secondaryQuestionOptionToSelect != null) {
const secondaryQuestionButton = this.page.getByText(`${pq.secondaryQuestionOptionToSelect}`);
secondaryQuestionButton.click();
const secondaryQuestionButton = parentobject.getByText(`${pq.secondaryQuestionOptionToSelect}`);
await secondaryQuestionButton.click();
}
}
}
}
}

View file

@ -9,6 +9,7 @@ import { PaypalPage } from './PaypalPage';
export class PaymentMethodPage extends BasePage {
readonly page: Page;
readonly payAtServiceButton: Locator;
readonly havePaymentReadyMsg: Locator;
readonly payNowButton: Locator;
readonly payInFourButton: Locator;
readonly paypalButton: Locator;
@ -20,6 +21,7 @@ export class PaymentMethodPage extends BasePage {
super(page);
this.page = page;
this.payAtServiceButton = this.page.locator('[buttonlabel="Pay at my appointment"]'); //this.page.getByText('Pay at time of service');
this.havePaymentReadyMsg = this.page.getByText('Please have payment ready during your appointment.'); // it will be displayed when there is no payment option
this.payNowButton = this.page.locator('[buttonlabel="Pay now"]');
this.payInFourButton = this.page.locator('[buttonlabel="Pay in 4 installments"]');
this.paypalButton = this.page.frameLocator('iframe[name="card-frame"]').locator('div[id="paypalParentDiv"]');

View file

@ -10,7 +10,9 @@ export class TpaSearchPage extends BasePage {
constructor(page: Page) {
super(page);
this.page = page;
this.firstLocationButton = page.locator("fieldset[aria-labelledby='chooseShop']").first();
// this.firstLocationButton = page.locator("fieldset[aria-labelledby='chooseShop']/span").first();
this.firstLocationButton = page.locator("#buttonLabelSpan").first();
this.doNotSeeMyShopButton = page.getByRole('link', { name: 'I don\'t see my shop' });
}

View file

@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import { expect, type Locator, type Page } from '@playwright/test';
import { BasePage } from './BasePage';
import { IVehicleDetails } from '@business-logic/types/CustomerDetails';
@ -6,17 +6,34 @@ export class VehicleLookupLicensePage extends BasePage {
readonly page: Page;
readonly licensePlateNumTextBox: Locator;
readonly licensePlateStateDrpDwn: Locator;
readonly plateNoMatchError: Locator;
readonly plateMismatchAlert: Locator;
url = process.env['BASE_URL']! + '/?issPage='; // TODO: Input correct URL
constructor(page: Page) {
super(page);
this.page = page;
this.licensePlateNumTextBox = page.getByRole('textbox', { name: 'License plate number'});
this.licensePlateStateDrpDwn = page.getByRole('combobox', { name: 'License plate state'});
this.licensePlateNumTextBox = page.getByRole('textbox', { name: 'License plate number' });
this.licensePlateStateDrpDwn = page.getByRole('combobox', { name: 'License plate state' });
this.plateNoMatchError = page.getByText('Your license plate didnt return a VIN match.Please re-enter the information');
this.plateMismatchAlert = page.getByRole('alert').locator('div');
}
async enterPlateDetails(vehicleDetails: IVehicleDetails){
await this.licensePlateNumTextBox.fill(vehicleDetails.licensePlateNumber || '');
async enterPlateDetails(vehicleDetails: IVehicleDetails, isVehicleLookupValidations = false) {
if (Array.isArray(vehicleDetails.licensePlateNumber)) {
if (isVehicleLookupValidations) {
await this.licensePlateNumTextBox.fill(vehicleDetails.licensePlateNumber[0]);
await this.continueButton.click({ timeout: 1000 });
await expect(this.plateNoMatchError).toBeVisible();
await this.licensePlateNumTextBox.fill(vehicleDetails.licensePlateNumber[1]);
await this.continueButton.click({ timeout: 1000 });
await expect(this.plateMismatchAlert).toBeVisible();
await this.licensePlateNumTextBox.fill(vehicleDetails.licensePlateNumber[2]);
}
} else {
await this.licensePlateNumTextBox.fill(vehicleDetails.licensePlateNumber || '');
}
await this.licensePlateStateDrpDwn.selectOption(vehicleDetails.licensePlateState!);
}
}

View file

@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import { expect, type Locator, type Page } from '@playwright/test';
import { BasePage } from './BasePage';
import { IVehicleDetails } from '@business-logic/types/CustomerDetails';
@ -25,11 +25,18 @@ export class VehicleSelectionPage extends BasePage {
await this.yearDropdown.selectOption(vehicleDetails.year);
await this.yearDropdown.press('Tab');
await this.makeDropdown.selectOption(vehicleDetails.make);
await this.yearDropdown.press('Tab');
await expect(this.modelDropdown).toBeEditable({ timeout: 5000 });
await this.makeDropdown.press('Tab');
await this.modelDropdown.selectOption(vehicleDetails.model);
await this.yearDropdown.press('Tab');
if (vehicleDetails.style != undefined) {
await expect(this.styleDropdown).toBeEditable({ timeout: 2000 });
await this.modelDropdown.press('Tab');
if (vehicleDetails.style) {
await this.styleDropdown.selectOption(vehicleDetails.style);
} else {
if (await this.styleDropdown.inputValue() === 'Select an option') {
await this.styleDropdown.selectOption({ index: 1 })
}
}
}
}

View file

@ -1,10 +1,11 @@
import { type Locator, type Page } from '@playwright/test';
import test, { expect, type Locator, type Page } from '@playwright/test';
import { BasePage } from './BasePage';
export class VinLookupPage extends BasePage {
readonly page: Page;
readonly vinLookupTextBox: Locator;
readonly lookupVinForMe: Locator;
readonly vinMismatchAlert: Locator;
url = process.env['BASE_URL']! + '/?issPage=vin-lookup'; // TODO: Input correct URL
constructor(page: Page) {
@ -12,11 +13,26 @@ export class VinLookupPage extends BasePage {
this.page = page;
this.vinLookupTextBox = page.getByRole('textbox', { name: 'Enter your VIN' });
this.lookupVinForMe = page.getByRole('link', { name: 'look up your VIN' });
this.vinMismatchAlert = page.getByLabel('vehicle-not-matched-alert');
// this.validateURL(this.url);
}
async enterVin(vin: string) {
await this.vinLookupTextBox.fill(vin);
async enterVin(vin: string | string[], isVehicleLookupValidations = false) {
if (Array.isArray(vin)) {
if (vin.length > 1) {
if (isVehicleLookupValidations) {
await this.vinLookupTextBox.fill(vin[0]);
await this.continueButton.click({ timeout: 1000 });
await expect(this.vinMismatchAlert).toBeVisible();
await this.vinLookupTextBox.fill(vin[1]);
}
} else {
console.warn("VIN array is blank. Either provide VIN as string or string[]")
}
} else {
await this.vinLookupTextBox.fill(vin);
}
}
async triggerBailout() {

View file

@ -1,6 +1,7 @@
import { defineConfig, devices } from '@playwright/test';
import dotenv from 'dotenv-safe';
import { OrtoniReportConfig } from "ortoni-report";
import * as path from 'path';
if (!process.env.CI) {
// Environment variables are present in CI environment, no need to read from file
@ -29,7 +30,7 @@ const reportConfig: OrtoniReportConfig = {
open: "never",
folderPath: "test-results",
filename: "index.html",
logo: "../data/logo.png",
logo: "../business-logic/data/logo.png",
title: "Test Report",
showProject: false,
projectName: "ISS-Nextgen-Playwright-Report",
@ -38,6 +39,9 @@ const reportConfig: OrtoniReportConfig = {
base64Image: true,
};
export const reportFilePath = path.resolve(__dirname, './test-results/accessibility-report.html');
export const jsonReportFilePath = path.resolve(__dirname, './test-results/accessibility-report.json');
export default defineConfig({
testDir: './tests',
/* Run tests in files in parallel */
@ -51,9 +55,10 @@ export default defineConfig({
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: [
['ortoni-report', reportConfig],
['junit'],
['list']
],
timeout: 120_000,
timeout: 240_000,
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
@ -63,6 +68,7 @@ export default defineConfig({
trace: 'on-first-retry',
headless: process.env.CI ? true : false,
screenshot: "only-on-failure",
},
/* Configure projects for major browsers */

View file

@ -26,7 +26,8 @@ import essentialReplaceDynamicAdasTests from "./0005_EssentialReplaceDynamicAdas
import essentialReplaceStaticAdasTests from "./0001_EssentialReplaceStatisAdas";
import essentialVehicleLookupBailoutTests from "./0020_EssentialVehicleLookupBailout";
import essentialPriceServiceErrorBailoutTests from "./0021_EssentialPriceServiceErrorBailout";
import { forceAPIError, mockApiResponse } from "@impl/utils/HttpUtils";
import { forceAPIError, mockApiManager } from "@impl/utils/HttpUtils";
import advancedScenario0002TestCases from "./advanced/0002a_ReplaceOemEndorsement";
import ApiResponseInterceptUtil from "@impl/api/ApiResponseInterceptUtil";
import advancedScenario0004aTestCases from "./advanced/0004a_NoDeductibleAdas";
@ -46,6 +47,10 @@ import advancedScenario0020TestCases from "./advanced/0020a_NoCompChangeLoc";
import advancedScenario0005TestCases from "./advanced/0005a_CapabilityQuestions";
import advancedScenario0009TestCases from "./advanced/0009a_NoDeductibleFlorida";
import advancedScenario0010TestCases from "./advanced/0010a_RearGlass";
import advancedScenario0021TestCases from "./advanced/0021a_VehicleByPlate";
import advancedScenario0022TestCases from "./advanced/0022a_VehicleByVIN";
import { createAccessibilityHtmlReport } from "@impl/utils/ReportUtils";
test.describe.parallel('ISS QA Automation Regression', () => {
@ -153,7 +158,7 @@ test.describe.parallel('ISS QA Automation Regression', () => {
for (const testCase of essentialPriceServiceErrorBailoutTests) {
test(...prepareTest(testCase, run, options, ruleEngine));
}
// Advanced Scenarios
// Scenario 0001a
@ -165,7 +170,7 @@ test.describe.parallel('ISS QA Automation Regression', () => {
for (const testCase of advancedScenario0002TestCases) {
test(...prepareTest(testCase, run, options, ruleEngine));
}
// Scenario 0003a
// Note: payment will fail in dev. Payment (PIA) works fine in SYS
for (const testCase of advancedScenario0003TestCases) {
@ -232,7 +237,7 @@ test.describe.parallel('ISS QA Automation Regression', () => {
}
// Scenario 0016a
// FIXME:
// FIXME: Defect was created INSR-2138
for (const testCase of advancedScenario0016TestCases) {
test(...prepareTest(testCase, run, options, ruleEngine));
}
@ -252,10 +257,20 @@ test.describe.parallel('ISS QA Automation Regression', () => {
test(...prepareTest(testCase, run, options, ruleEngine));
}
// Scenario 0020a
// Scenario 0020a : There's a defect open INSR-2149
for (const testCase of advancedScenario0020TestCases) {
test(...prepareTest(testCase, run, options, ruleEngine));
}
// Scenario 0021a
for (const testCase of advancedScenario0021TestCases) {
test(...prepareTest(testCase, run, options, ruleEngine));
}
// Scenario 0022a
for (const testCase of advancedScenario0022TestCases) {
test(...prepareTest(testCase, run, options, ruleEngine));
}
});
@ -263,9 +278,14 @@ test.afterEach(async ({ page, testInfo }) => {
await TestCase.afterEachMethod(page, testInfo);
});
async function run(page: Page, testInfo: TestInfo): Promise<void> {
test.afterAll(() => {
createAccessibilityHtmlReport();
});
await testInfo.testCase.setup();
async function run(page: Page, testInfo: TestInfo): Promise<void> {
if (!(testInfo.testCase.testData?.isMockTesting ?? false)) {
await testInfo.testCase.setup();
}
testInfo.testCase.setupPages(page);
if (testInfo.testCase.testData.isAuthenticationRequired) {
await testInfo.testCase.pages.welcomePage.gotoWithAuthentication(testInfo.testCase.testData!.clientTag!);
@ -277,19 +297,25 @@ async function run(page: Page, testInfo: TestInfo): Promise<void> {
}
async function runWorkflow(page: Page, testCase: TestCase) {
// Intercept API Responses
const apiResponseInterceptUtil = new ApiResponseInterceptUtil(testCase.testData);
page.on('response', apiResponseInterceptUtil.handleInterceptResponse);
if (testCase.testData?.isMockTesting ?? false) {
// await mockApiManager.mockApiResponse(page, 'common', testCase.testData!.isMockTesting ?? false);
await mockApiManager.mockApiResponse(page, testCase.name.split(':')[0], testCase.testData!.isMockTesting ?? false);
} else {
// Intercept API Responses
const apiResponseInterceptUtil = new ApiResponseInterceptUtil(testCase.testData);
page.on('response', apiResponseInterceptUtil.handleInterceptResponse);
await mockApiManager.mockApiResponse(page, 'common', true); // making sure no experiments
}
// Destructure data for easy access
const { customerDetails, claimDetails, vehicleDetails, vehicleDamage,
appointmentDetails, isSafelite, endorsements,
partQuestions, paymentDetails, isNoComp, isItac, isRecalNotification,
partQuestions, paymentDetails, isNoComp, isItac, isRecalNotification,
isRecalWarning, servicePackage, hasStateLawPopup, otherVehiclesOnPolicy,
isSeparateApptsWarning, vehiclePartQuestions, editVehicleDetails,
hasMilitaryWarning, capabilityQuestions } = testCase.testData;
hasMilitaryWarning, capabilityQuestions, isUseVehicleOnPolicy, isVehicleLookupValidations } = testCase.testData;
let { isPolicyFound } = testCase.testData; // Allow isPolicyFound to be re-assigned
// Destructure pages for easy access
@ -304,8 +330,8 @@ async function runWorkflow(page: Page, testCase: TestCase) {
// Destructure bailout flags
const { isVehicleSelectBailout, isDoNotSeeMyShopBailout, isTpaNotEnabledBailout,
isRequestCallbackBailout, isHeavyTruckVehicleBailout, isPartsServiceErrorBailout,
isSafeliteNotTheProviderBailout, isVehicleLookupBailout, isPriceServiceErrorBailout } = testCase.testData.bailoutFlags || {};
isHeavyTruckVehicleBailout, isPartsServiceErrorBailout,
isVehicleLookupBailout, isPriceServiceErrorBailout } = testCase.testData.bailoutFlags || {};
const repairTypes: VehicleDamage[] = [
VehicleDamage.WindshieldOneChip,
@ -322,10 +348,6 @@ async function runWorkflow(page: Page, testCase: TestCase) {
await test.step('WelcomePage >> Populate Customer Details', async () => {
await welcomePage.populatePage(customerDetails!, claimDetails!, await welcomePage.hasCityInfo());
// mockApiResponse(page, 'location/api/v1/location/zip/43016', 'common', testCase.testData!.mockTesting || false);
mockApiResponse(page, 'location/api/v1/location/zip/36116', 'common',testCase.testData!.isMockTesting || false);
mockApiResponse(page, 'coverage/api/v1/coverage/policy-information', 'scenario1', testCase.testData!.isMockTesting || false);
await welcomePage.nextPage();
});
@ -341,16 +363,23 @@ async function runWorkflow(page: Page, testCase: TestCase) {
if (isPolicyFound) {
await test.step('PolicyVehiclesPage >> Select vehicle', async () => {
await policyVehiclesPage.logReferralNumber();
// Validate other vehicles on policy
if (otherVehiclesOnPolicy && otherVehiclesOnPolicy.length > 0) {
for (const vehicle of otherVehiclesOnPolicy) {
await policyVehiclesPage.validateVehicleIsOnPolicy(vehicle);
}
}
// Select vehicle
await policyVehiclesPage.selectVehicle(vehicleDetails!);
await policyVehiclesPage.nextPage();
if (!(isUseVehicleOnPolicy ?? true)) {
await policyVehiclesPage.selectVehicleNotListed();
await policyVehiclesPage.nextPage();
await vehicleSelectionPage.selectVehicle(vehicleDetails!);
await policyVehiclesPage.nextPage();
} else {
// Select vehicle
await policyVehiclesPage.selectVehicle(vehicleDetails!);
await policyVehiclesPage.nextPage();
}
});
if (isHeavyTruckVehicleBailout) {
@ -369,12 +398,13 @@ async function runWorkflow(page: Page, testCase: TestCase) {
}
} else {
await policyVehiclesPage.logReferralNumber();
await test.step('PolicyHolderDetailsPage >> Enter customer data', async () => {
await policyHolderDetailsPage.fillCustomerDetails(customerDetails!);
await policyHolderDetailsPage.nextPage();
});
if (isVehicleLookupBailout){
if (isVehicleLookupBailout) {
forceAPIError(page, '/vehicle/api/v1/vehicle/lookup')
}
@ -437,11 +467,14 @@ async function runWorkflow(page: Page, testCase: TestCase) {
return;
}
if (isReplace && !(isPolicyFound)) {
if (isReplace && (!(isPolicyFound) || !(isUseVehicleOnPolicy ?? true))) {
await test.step('VehicleLookupPage >> Select Lookup Type' + vehicleDetails?.vehicleLookupType, async () => {
await vehicleLookupPage.vehicleLookup(vehicleDetails!);
});
if (isVehicleSelectBailout) {
await test.step('BailoutPage >> Validate Bailout', async () => {
await vinLookupPage.enterVin(vehicleDetails!.vin!);
@ -458,14 +491,22 @@ async function runWorkflow(page: Page, testCase: TestCase) {
});
break;
case VehicleLookupType.LicensePlateNumber:
await test.step('VehicleLookupLicensePage >> Lookup by license plate: ' + vehicleDetails!.licensePlateNumber, async () => {
await vehicleLookupLicensePage.enterPlateDetails(vehicleDetails!);
await vehicleLookupLicensePage.nextPage();
});
if (isVehicleLookupValidations) {
await test.step('VehicleLookupLicensePage >> Lookup by license plate: ' + vehicleDetails!.licensePlateNumber, async () => {
await vehicleLookupLicensePage.enterPlateDetails(vehicleDetails!, isVehicleLookupValidations);
await vehicleLookupLicensePage.nextPage();
});
} else {
await test.step('VehicleLookupLicensePage >> Lookup by license plate: ' + vehicleDetails!.licensePlateNumber, async () => {
await vehicleLookupLicensePage.enterPlateDetails(vehicleDetails!);
await vehicleLookupLicensePage.nextPage();
});
}
break;
case VehicleLookupType.Vin:
await test.step('VinLookupPage >> Lookup by VIN: ' + vehicleDetails!.vin!, async () => {
await vinLookupPage.enterVin(vehicleDetails!.vin!);
await vinLookupPage.enterVin(vehicleDetails!.vin!, isVehicleLookupValidations);
await vinLookupPage.nextPage();
});
break;
@ -495,7 +536,7 @@ async function runWorkflow(page: Page, testCase: TestCase) {
if (isPolicyFound && (isItac || isNoComp)) {
await coverageStatementPage.continueToScheduleButton.click();
}
await coverageStatementPage.nextPage();
});
@ -511,7 +552,7 @@ async function runWorkflow(page: Page, testCase: TestCase) {
await providerPreferencePage.selectProvider(isSafelite);
});
}
// validations for Recal warning mesage
if (isRecalWarning) {
await serviceLocationPage.validateRecalWarning();
@ -611,7 +652,7 @@ async function runWorkflow(page: Page, testCase: TestCase) {
await servicePackagesPage.nextPage();
});
if (isPolicyFound && claimDetails!.policyDeductible > 0) {
if (isPolicyFound && (isUseVehicleOnPolicy ?? true) && claimDetails!.policyDeductible > 0) {
await test.step('PaymentMethodPage >> Execute Payment', async () => {
await paymentMethodPage.executePayment(paymentDetails!);
await paymentMethodPage.nextPage();

View file

@ -37,7 +37,7 @@ const essentialReplaceDynamicAdasData: Partial<ITestData> = {
damageCause: DamageType.Other
},
vehicleDetails: {
year: '2021',
year: '2020',
make: 'BMW',
model: '740',
style: '4 door sedan',

View file

@ -22,11 +22,19 @@ const customerDetails: ICustomerDetails = {
country: 'United States'
}
}
const policyNumber = `~AutomatedScenario0002a${faker.string.uuid().substring(0,6)}`;
var policyNumber = '';
if ((process.env.ENABLE_MOCK_TESTING ?? 'false') === 'true') {
policyNumber = '~AutomatedScenario0002aMockPolicy002';
customerDetails.firstName = 'Rosie';
customerDetails.lastName = 'Cormier';
customerDetails.address.street = '69825 W Pine Street';
} else {
policyNumber = `~AutomatedScenario0002a${faker.string.uuid().substring(0, 6)}`;
};
const policySoap = MockPolicyData.getPolicySoapByScenario('0002a', customerDetails, policyNumber);
const advancedScenario0002Data: Partial<ITestData> = {
isMockTesting: process.env.ENABLE_MOCK_TESTING === 'true' || false,
clientTag: '',
isDuplicateClaim: false,
isPolicyFound: true,
@ -41,6 +49,11 @@ const advancedScenario0002Data: Partial<ITestData> = {
// optionToSelect: 'Green Tint, Blue Shade'
// },
// {
// partQuestionType: PartQuestionType.WindshieldColor,
// isOnPage: true,
// optionToSelect: 'Green Tint'
// },
// {
// partQuestionType: PartQuestionType.DriverFrontColor,
// isOnPage: true,
// optionToSelect: 'Green Tint'
@ -75,7 +88,7 @@ const advancedScenario0002Data: Partial<ITestData> = {
year: '2006',
make: 'Chrysler',
model: '300',
style: ''
style: '4 door sedan'
},
vehicleDamage: [
// VehicleDamage.WindshieldThreeChips,
@ -102,10 +115,10 @@ const advancedScenario0002Data: Partial<ITestData> = {
const advancedClients = ClientData.getAdvancedClients();
const advancedScenario0002TestCases: TestCase[] = [];
for (const client of advancedClients) {
const data = {...advancedScenario0002Data};
const data = { ...advancedScenario0002Data };
data.clientTag = client.clientTag;
const tc = new TestCase({
name: `0002a Advanced Replace Deductible Client: "${client.accountName}"`,
name: `0002a_Advanced_Replace_Deductible_Client: "${client.accountName}"`,
tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'],
testData: data
}, undefined, '0002a');

View file

@ -54,7 +54,8 @@ const advancedScenario0016Data: Partial<ITestData> = {
{
partQuestionType: PartQuestionType.DriverVentColor,
isOnPage: true,
optionToSelect: 'Green Tint'
optionToSelect: 'Green Tint',
secondaryQuestionOptionToSelect: 'solar, driver side, rear'
},
{
partQuestionType: PartQuestionType.PassengerFrontColor,
@ -66,6 +67,11 @@ const advancedScenario0016Data: Partial<ITestData> = {
isOnPage: true,
optionToSelect: 'Green Tint'
},
{
partQuestionType: PartQuestionType.RearWindowColor,
isOnPage: true,
optionToSelect: 'Green Tint'
},
],
isSafelite: true,
servicePackage: ServicePackage.Standard,
@ -111,7 +117,7 @@ for (const client of advancedClients) {
data.clientTag = client.clientTag;
const tc = new TestCase({
name: `0016a Advanced Replace NoComp All Glass Client: "${client.accountName}"`,
tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced', '@Flaky'],
tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced', '@Defect-INSR-2138'],
testData: data
}, undefined, '0016a');
advancedScenario0016TestCases.push(tc);

View file

@ -83,7 +83,7 @@ for (const client of advancedClients) {
data.clientTag = client.clientTag;
const tc = new TestCase({
name: `0020a Advanced Repair Change Location Client: "${client.accountName}"`,
tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'],
tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced','@Defect-INSR-2149'],
testData: data
}, undefined, '0020a');
advancedScenario0020TestCases.push(tc);

View file

@ -0,0 +1,87 @@
import ClientData from "@business-logic/data/ClientData";
import TestCase from "@business-logic/types/TestCase";
import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums";
import { ITestData } from "@business-logic/types/ITestData"
import { faker } from "@faker-js/faker";
import { getNextWeekday } from "@impl/utils/DateUtils";
import MockPolicyData from "@business-logic/data/MockPolicyData";
import { ICustomerDetails } from "@business-logic/types/CustomerDetails";
const nextWeekday = getNextWeekday();
const customerDetails: ICustomerDetails = {
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
email: 'itqatest@safelite.com',
phoneNumber: '614-531-0031',
notes: 'Automated Test',
address: {
street: faker.location.streetAddress(),
city: 'San Jose',
state: 'CA',
postalCode: '97230-6373',
country: 'United States'
}
}
const policyNumber = `~AutomatedScenario0021a${faker.string.uuid().substring(0, 6)}`;
const policySoap = MockPolicyData.getPolicySoapByScenario('0021a', customerDetails, policyNumber);
const advancedScenario0021Data: Partial<ITestData> = {
clientTag: '',
isDuplicateClaim: false,
isPolicyFound: true,
isNoComp: false,
hasStateLawPopup: true,
endorsements: undefined,
isUseVehicleOnPolicy: false,
isVehicleLookupValidations: true,
vehiclePartQuestions: [
],
isSafelite: true,
servicePackage: faker.helpers.enumValue(ServicePackage),
customerDetails: customerDetails,
claimDetails: {
policyNumber: policyNumber,
policyDeductible: 500,
damageDate: '2017-06-02',
damageCause: DamageType.Vandalism
},
policySoap: policySoap,
vehicleDetails: {
year: '2003',
make: 'Lexus',
model: 'RX 300',
style: undefined,
vehicleLookupType: VehicleLookupType.LicensePlateNumber,
licensePlateNumber: ['NoPlate753', 'BQ40903','1111'],
licensePlateState: ['Illinois','Illinois','California'],
},
vehicleDamage: [
VehicleDamage.WindshieldCrack,
],
appointmentDetails: {
serviceLocation: ServiceLocation.InShop,
shopAddress: undefined,
appointmentDate: nextWeekday
},
paymentDetails: {
paymentType: PaymentType.PayAtService
}
}
// TODO: Add validation for deductible/covered amount
const advancedClients = ClientData.getAdvancedClients();
const advancedScenario0021TestCases: TestCase[] = [];
for (const client of advancedClients) {
const data = { ...advancedScenario0021Data };
data.clientTag = client.clientTag;
const tc = new TestCase({
name: `0021a Advanced client Vehicle By License Plate: "${client.accountName}"`,
tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'],
testData: data
}, undefined, '0021a');
advancedScenario0021TestCases.push(tc);
}
export default advancedScenario0021TestCases;

View file

@ -0,0 +1,86 @@
import ClientData from "@business-logic/data/ClientData";
import TestCase from "@business-logic/types/TestCase";
import { DamageType, PartQuestionType, PaymentType, ServiceLocation, ServicePackage, VehicleDamage, VehicleLookupType } from "@business-logic/types/Enums";
import { ITestData } from "@business-logic/types/ITestData"
import { faker } from "@faker-js/faker";
import { getNextWeekday } from "@impl/utils/DateUtils";
import MockPolicyData from "@business-logic/data/MockPolicyData";
import { ICustomerDetails } from "@business-logic/types/CustomerDetails";
const nextWeekday = getNextWeekday();
const customerDetails: ICustomerDetails = {
firstName: faker.person.firstName(),
lastName: faker.person.lastName(),
email: 'itqatest@safelite.com',
phoneNumber: '614-531-0031',
notes: 'Automated Test',
address: {
street: faker.location.streetAddress(),
city: 'San Jose',
state: 'CA',
postalCode: '97230-6373',
country: 'United States'
}
}
const policyNumber = `~AutomatedScenario0021a${faker.string.uuid().substring(0, 6)}`;
const policySoap = MockPolicyData.getPolicySoapByScenario('0021a', customerDetails, policyNumber);
const advancedScenario0021Data: Partial<ITestData> = {
clientTag: '',
isDuplicateClaim: false,
isPolicyFound: true,
isNoComp: false,
hasStateLawPopup: true,
endorsements: undefined,
isUseVehicleOnPolicy: false,
isVehicleLookupValidations: true,
vehiclePartQuestions: [
],
isSafelite: false,
servicePackage: faker.helpers.enumValue(ServicePackage),
customerDetails: customerDetails,
claimDetails: {
policyNumber: policyNumber,
policyDeductible: 500,
damageDate: '2017-06-02',
damageCause: DamageType.Vandalism
},
policySoap: policySoap,
vehicleDetails: {
year: '2003',
make: 'Lexus',
model: 'RX 300',
style: undefined,
vehicleLookupType: VehicleLookupType.Vin,
vin: ['1C4PJLCS6EW288461', '1HGCR2E30FA099831'],
},
vehicleDamage: [
VehicleDamage.WindshieldCrack,
],
appointmentDetails: {
serviceLocation: ServiceLocation.InShop,
shopAddress: undefined,
appointmentDate: nextWeekday
},
paymentDetails: {
paymentType: PaymentType.PayAtService
}
}
// TODO: Add validation for deductible/covered amount
const advancedClients = ClientData.getAdvancedClients();
const advancedScenario0022TestCases: TestCase[] = [];
for (const client of advancedClients) {
const data = { ...advancedScenario0021Data };
data.clientTag = client.clientTag;
const tc = new TestCase({
name: `0022a Advanced Client Vehicle By Vin: "${client.accountName}"`,
tags: [`@${client.clientTag}`, `@${client.accountName}`, '@Advanced'],
testData: data
}, undefined, '0022a');
advancedScenario0022TestCases.push(tc);
}
export default advancedScenario0022TestCases;

View file

@ -0,0 +1,10 @@
{
"userKey": 2004417,
"sessionKey": 2419636,
"sessionId": "8967dbc4-5081-47c4-a896-03050353d9e2",
"userId": "2ea43842-440b-4362-a72f-105d987f4bc1",
"deviceId": "2ea43842-440b-4362-a72f-105d987f4bc1",
"version": 1,
"status": 1,
"error": null
}

View file

@ -0,0 +1,18 @@
{
"accountNumber": "550036",
"detailedErrorMessage": null,
"notes": null,
"hasPolicies": false,
"deductible": 50,
"status": null,
"noCoverage": false,
"isOEMEndorsed": false,
"policyData": "AMjHAgrjFS8H8IiDcI9sC8DUxbkV/fm1HmxAnMOiIunuYyVWRdFu7IcgtzX6CCuZINR8ZapGM0Jume0ZhoISUQ/DHvocaVjtdjZEPbGktguUIQgCrthBdHKYxQyrleKaDlXp5Sit3Vgfkj+eKUJvhegia01DFcUqUOEp9nlLMf7RMbjUkHAGogx5CkK9hfGO7iRQ/SJ9YQ2BoLPZgAds9x+LasOS7Wy1+BVyFUswlvgestN6STUF7EzZ8uS+Rb6atNHYqM2ir3tEwxMDp2uSma2CSiS755MLIYKqZlB/UoXKgsc7U1hxIrP+c8LxXOk2",
"referralCorrelationId": "097de2c2-ab3c-455e-8e31-fb2951c68d0a",
"referralNumber": null,
"isSuccess": true,
"isError": false,
"errorCode": null,
"errorMessage": null,
"successMessage": null
}

View file

@ -0,0 +1,18 @@
{
"referralNumber": "108979",
"referralSequenceNumber": "10084346",
"referralDate": "2025-02-08T16:07:59.93",
"referralCorrelationId": "af625adc-87e5-47fc-8de6-0a003fe7003b",
"parentAccountNumber": 550036,
"billToAccountNumber": "214616",
"crmCustomerId": 855298,
"savedSessionId": "8f0bd26a-ef61-4929-a013-6f376a881052",
"eon": "S10080197",
"workOrderNumber": "",
"workOrderId": "",
"workOrderStatus": "",
"customerPortalLoginToken": "00000000-0000-0000-0000-000000000000",
"lockToken": null,
"settledTenderAmount": 0.0,
"isRecalAckOptIn": false
}

View file

@ -0,0 +1,82 @@
{
"policies": [
{
"policyEffectiveDate": "0001-01-01T00:00:00",
"expirationDate": "0001-01-01T00:00:00",
"type": null,
"lineOfBusiness": null,
"policyNumber": "~AutomatedScenario0002a126b81",
"status": null,
"source": null,
"insureds": [
{
"firstName": "Rosie",
"lastName": "Cormier",
"businessName": null,
"address": "69825 W Pine Street",
"city": "PLANO",
"state": "TX",
"zipCode": "75023",
"phones": null,
"email": null,
"driverLicenseState": null,
"relationToInsured": null,
"companyCode": "LIBERTY",
"customerId": null
},
{
"firstName": "Rosie",
"lastName": "Cormier",
"businessName": null,
"address": "69825 W Pine Street",
"city": "PLANO",
"state": "TX",
"zipCode": "75023",
"phones": null,
"email": null,
"driverLicenseState": null,
"relationToInsured": null,
"companyCode": "LIBERTY",
"customerId": null
}
],
"vehicles": [
{
"id": 0,
"vehicleYear": "2006",
"vehicleMake": "CHRY",
"vehicleModel": "300",
"vehicleStyle": null,
"licensePlate": "UNKNOWN",
"vin": "2C3KA53G06H407823",
"driver": null,
"owner": null,
"coverages": [
{
"code": "COMP",
"deductible": 50,
"individualLimit": 0,
"occurrenceLimit": 0,
"dayLimit": 0
}
],
"fleetNumber": null,
"fleetUnitNumber": "1",
"endorsements": [
"OEM Approved"
]
}
],
"taxExempt": "FALSE",
"policyData": "AMjHAgrjFS8H8IiDcI9sC8DUxbkV/fm1HmxAnMOiIunuYyVWRdFu7IcgtzX6CCuZINR8ZapGM0Jume0ZhoISUQ/DHvocaVjtdjZEPbGktguUIQgCrthBdHKYxQyrleKaDlXp5Sit3Vgfkj+eKUJvhegia01DFcUqUOEp9nlLMf7RMbjUkHAGogx5CkK9hfGO7zKZfyYsWKqUbh5UgaO/fzdKBhMHFKIWMsG5YydLN/4G9x3UDfZ//kNQ+vG3eN1RQ5kv9sMQXS0pOyG9Mqy0XvKf6SozAlPpBeX53PrrPrPeFpVRGalafXgrDDx9D2OaP72dhCnwEuRRQzQ7Gj4RsrtrVOvQjgHHMSlg8X7U6TW7Mni/Q61XrCkj3/rHySrL"
}
],
"referralCorrelationId": "097de2c2-ab3c-455e-8e31-fb2951c68d0a",
"referralNumber": null,
"accountNumber": "550036",
"isSuccess": true,
"isError": false,
"errorCode": null,
"errorMessage": null,
"successMessage": null
}

View file

@ -0,0 +1,15 @@
{
"claimantId": null,
"claimNumber": "0589139920001",
"notes": null,
"dispatchNumber": null,
"deductible": 0,
"referralCorrelationId": "097de2c2-ab3c-455e-8e31-fb2951c68d0a",
"referralNumber": null,
"accountNumber": "550036",
"isSuccess": true,
"isError": false,
"errorCode": null,
"errorMessage": null,
"successMessage": "registered"
}

View file

@ -0,0 +1,19 @@
{
"experiments": [
{
"universeName": "PIAInsurance",
"universeId": 640,
"testName": "PIAInsurance_V1",
"testId": 504,
"variationName": "YesShowPIAInsurance_TEST",
"variationId": 1430,
"isActive": true,
"isExposed": true,
"userPartitionNumber": 85,
"assignmentId": 14838407,
"settings": {
"DisplayPIAInsurance": "true"
}
}
]
}

View file

@ -0,0 +1,149 @@
{
"mobileProviderNumber": "001813",
"shopProviders": [
{
"address": {
"city": "PLANO",
"country": "US",
"state": "TX",
"streetAddress": "1601 E PLANO PKWY",
"streetAddress2": "STE 150",
"zipCode": "75074",
"zipCodeCtu": "01813"
},
"distanceInMiles": 4.187648412874978,
"providerNumber": "001813",
"companyName": "SAFELITE AUTOGLASS - DALLAS-FT.WO,TX SOR-CTU",
"phoneNumber": "9724237183",
"isSafeliteShop": true
},
{
"address": {
"city": "MCKINNEY",
"country": "US",
"state": "TX",
"streetAddress": "417 POWER HOUSE ST.",
"streetAddress2": "STE A",
"zipCode": "75071",
"zipCodeCtu": "01813"
},
"distanceInMiles": 13.220085913669747,
"providerNumber": "005318",
"companyName": "SAFELITE AUTOGLASS - MCKINNEY, TX",
"phoneNumber": "9725624492",
"isSafeliteShop": true
},
{
"address": {
"city": "LEWISVILLE",
"country": "US",
"state": "TX",
"streetAddress": "2129 S STEMMONS FWY",
"streetAddress2": "",
"zipCode": "75067",
"zipCodeCtu": "01813"
},
"distanceInMiles": 14.553951205638858,
"providerNumber": "004542",
"companyName": "SAFELITE AUTOGLASS - LEWISVILLE, TX",
"phoneNumber": "4694442057",
"isSafeliteShop": true
},
{
"address": {
"city": "MESQUITE",
"country": "US",
"state": "TX",
"streetAddress": "2131 N TOWN EAST BLVD",
"streetAddress2": "",
"zipCode": "75150",
"zipCodeCtu": "01813"
},
"distanceInMiles": 17.86005691117364,
"providerNumber": "004541",
"companyName": "SAFELITE AUTOGLASS - MESQUITE, TX",
"phoneNumber": "9722889891",
"isSafeliteShop": true
},
{
"address": {
"city": "N RICHLAND HILLS",
"country": "US",
"state": "TX",
"streetAddress": "5649 RUFE SNOW DR",
"streetAddress2": "",
"zipCode": "76180",
"zipCodeCtu": "04544"
},
"distanceInMiles": 32.558897054726366,
"providerNumber": "006504",
"companyName": "SAFELITE AUTOGLASS - FORT WORTH, TX RDU",
"phoneNumber": "8176569791",
"isSafeliteShop": true
},
{
"address": {
"city": "ARLINGTON",
"country": "US",
"state": "TX",
"streetAddress": "2411 S COOPER ST",
"streetAddress2": "",
"zipCode": "76015",
"zipCodeCtu": "04544"
},
"distanceInMiles": 33.065576901075474,
"providerNumber": "004540",
"companyName": "SAFELITE AUTOGLASS - ARLINGTON, TX",
"phoneNumber": "8175224300",
"isSafeliteShop": true
},
{
"address": {
"city": "GREENVILLE",
"country": "US",
"state": "TX",
"streetAddress": "7907 TRADERS CIR",
"streetAddress2": "",
"zipCode": "75402",
"zipCodeCtu": "01813"
},
"distanceInMiles": 36.369381330667089,
"providerNumber": "005317",
"companyName": "SAFELITE AUTOGLASS - GREENVILLE, TX",
"phoneNumber": "9034501771",
"isSafeliteShop": true
},
{
"address": {
"city": "FORT WORTH",
"country": "US",
"state": "TX",
"streetAddress": "2751 NORTHERN CROSS BLVD",
"streetAddress2": "",
"zipCode": "76137",
"zipCodeCtu": "04544"
},
"distanceInMiles": 36.676844951002508,
"providerNumber": "004544",
"companyName": "SAFELITE AUTOGLASS - FORT WORTH, TX",
"phoneNumber": "2143283111",
"isSafeliteShop": true
},
{
"address": {
"city": "TYLER",
"country": "US",
"state": "TX",
"streetAddress": "4043 S BROADWAY AVE",
"streetAddress2": "",
"zipCode": "75701",
"zipCodeCtu": "01813"
},
"distanceInMiles": 98.1186957553462,
"providerNumber": "000714",
"companyName": "SAFELITE AUTOGLASS - TYLER, TX",
"phoneNumber": "9035972059",
"isSafeliteShop": true
}
]
}

View file

@ -0,0 +1,8 @@
{
"containsMilitaryBase": false,
"isServiceable": true,
"isValid": true,
"state": "TX",
"providerNumber": "01813",
"zipCodeCtu": "01813"
}

View file

@ -0,0 +1,25 @@
{
"windshieldOptions": {
"availableReplacementOptions": [
"Single"
],
"isRepairAvailable": true
},
"backGlassOptions": {
"availableReplacementOptions": [
"Stationary"
]
},
"driverSideOptions": {
"availableReplacementOptions": [
"Back",
"Front"
]
},
"passengerSideOptions": {
"availableReplacementOptions": [
"Back",
"Front"
]
}
}

View file

@ -0,0 +1,32 @@
{
"partsOrQuestions": [
{
"glassPiece": {
"name": "Single",
"location": "Windshield"
},
"parts": [
{
"childPartQuestions": [],
"basePartNumber": "DW01571",
"safelitePartNumber": "DW01571 GTNOEM",
"color": "Green Tint",
"requiresRecalibration": false,
"recalibrationType": null,
"canSafeliteRecalibrate": false,
"requiresCapabilityQuestions": false,
"childParts": [
{
"partNumber": "GGG 1571",
"safelitePartNumber": "GGG 1571"
}
],
"partNumber": "DW01571GTNNOEM",
"description": "",
"partType": "WINDSHIELD"
}
],
"partQuestions": null
}
]
}

View file

@ -0,0 +1,32 @@
{
"partsOrQuestions": [
{
"glassPiece": {
"name": "Single",
"location": "Windshield"
},
"parts": [
{
"childPartQuestions": [],
"basePartNumber": "DW01571",
"safelitePartNumber": "DW01571 GTNOEM",
"color": "Green Tint",
"requiresRecalibration": false,
"recalibrationType": null,
"canSafeliteRecalibrate": false,
"requiresCapabilityQuestions": false,
"childParts": [
{
"partNumber": "GGG 1571",
"safelitePartNumber": "GGG 1571"
}
],
"partNumber": "DW01571GTNNOEM",
"description": "",
"partType": "WINDSHIELD"
}
],
"partQuestions": null
}
]
}

View file

@ -0,0 +1 @@
{"partNumber":"RAIN REPEL","description":null,"partType":"RAIN DEFENSE"}

View file

@ -0,0 +1,12 @@
[
{
"partNumber": "SBB22",
"description": "SAFELITE BEAM BLADE 22",
"partType": "FRONT WIPER"
},
{
"partNumber": "SBB22",
"description": "SAFELITE BEAM BLADE 22",
"partType": "FRONT WIPER"
}
]

View file

@ -0,0 +1,29 @@
{
"lineItems": [
{
"partNumber": "RAIN REPEL",
"promoCode": null,
"laborAmount": 0.0,
"sellingPrice": 44.99,
"kitPrice": 0,
"salesTax": null
},
{
"partNumber": "SBB22",
"promoCode": null,
"laborAmount": 0.0,
"sellingPrice": 34.99,
"kitPrice": 0,
"salesTax": null
},
{
"partNumber": "SBB22",
"promoCode": null,
"laborAmount": 0.0,
"sellingPrice": 34.99,
"kitPrice": 0,
"salesTax": null
}
],
"serverData": "KcYkmhhx3j2jfrKcPIai5c3vxIPezYw3algekZCIJ1nRzZHsoYWmD//QlA9cNAqo7E4vA4lcilhi/EbXZ5BfIvc9C7c7sl9TGGbrA2A3QUiVi9Q+eQQWUrGSlb5Te3PyfK8g5A+gvcXvn+YSjH0tiZuykzLPDmbZaUNkY09OTOynQ4jR2G6ZjxdvkFOecp6vocPznIZHxkT4ag0MWr73pMB3WTWP+yY6Ej6yheEvcCBO+TPGJ4FKsJ5tXm5Y4+pqAA0MbC5nB2qLFSeDuPA4aPe5x//6zjOavPfOpT1+RwOZWlPx5vwfv0dncNgn/itTlZwHj4B4YwZqc/y7JJJLh7nlT8IPB5tg5ISpK8tOyCHKmbUT3ABAL236U98EiydC45k9uSxRDklR+BVvq0aV7EieYbewrrvrbKK1Wsk0rcwBCrRimLJfecvP37qqm/vLzyRYppxSXXPkdn2i/Er81LQbaNKLAReRN9oncgO1k4Q="
}

View file

@ -0,0 +1,8 @@
{
"isItac": false,
"isItacOptimized": null,
"primaryBillToNumber": null,
"partsWerePriced": true,
"lineItems": [],
"serverData": "KcYkmhhx3j2jfrKcPIai5c3vxIPezYw3algekZCIJ1nRzZHsoYWmD//QlA9cNAqo7E4vA4lcilhi/EbXZ5BfIlUjQng0T1GMzltEwIcGbNw="
}

View file

@ -0,0 +1,29 @@
{
"taxedLineItems": [
{
"partNumber": "SBB22",
"promoCode": null,
"laborAmount": 0,
"sellingPrice": 34.99,
"kitPrice": 0,
"salesTax": 2.89
},
{
"partNumber": "SBB22",
"promoCode": null,
"laborAmount": 0,
"sellingPrice": 34.99,
"kitPrice": 0,
"salesTax": 2.89
},
{
"partNumber": "RAIN REPEL",
"promoCode": null,
"laborAmount": 0,
"sellingPrice": 44.99,
"kitPrice": 0,
"salesTax": 3.71
}
],
"serverData": "KcYkmhhx3j2jfrKcPIai5c3vxIPezYw3algekZCIJ1nRzZHsoYWmD//QlA9cNAqo7E4vA4lcilhi/EbXZ5BfIvc9C7c7sl9TGGbrA2A3QUiVi9Q+eQQWUrGSlb5Te3PyfK8g5A+gvcXvn+YSjH0tie6XYRgOnhi6ArHjHJ3GTgCmWIhMUosEpnBrUg3/W5ci/3UF4sCnDpihiJPPiMCnOMjzvWp1/xTg5jaoE8N8otARbxQ73ObbvZCJndil/EaSbqYMvL+ymPvDJHPoAfbXUJc90Drlef4gvSV1d6HEZGR/zwoKYipecNPSXcXzRGKrfxgmViszg1d1PyncoyVQrokAIl1KPRmDPJso5CmGrdDjEYsTV0SXUd0NcphMKd8wVrx7QUCLiyW8Vee7cVjWFnqdhkeJ9PYb14WoNb4j1cLJv39ofMcTmaJMI/maAcA8XKPOuhdWTTqWBXOPq3ax1g=="
}

View file

@ -0,0 +1,84 @@
{
"estimatedServiceMinutesMinimum": 60,
"estimatedServiceMinutesMaximum": 120,
"days": [
{
"date": "2025-02-22",
"timeSlots": [
{
"id": "01813-01813-S-B*20873*ALL DAY DROP OFF",
"startTime": "07:30",
"endTime": "17:00",
"offerPremium": false
}
]
},
{
"date": "2025-02-24",
"timeSlots": [
{
"id": "01813-01813-S-B*20875*ALL DAY DROP OFF",
"startTime": "07:30",
"endTime": "17:00",
"offerPremium": false
}
]
},
{
"date": "2025-02-25",
"timeSlots": [
{
"id": "01813-01813-S-B*20876*ALL DAY DROP OFF",
"startTime": "07:30",
"endTime": "17:00",
"offerPremium": false
}
]
},
{
"date": "2025-02-26",
"timeSlots": [
{
"id": "01813-01813-S-B*20877*ALL DAY DROP OFF",
"startTime": "07:30",
"endTime": "17:00",
"offerPremium": false
}
]
},
{
"date": "2025-02-27",
"timeSlots": [
{
"id": "01813-01813-S-B*20878*ALL DAY DROP OFF",
"startTime": "07:30",
"endTime": "17:00",
"offerPremium": false
}
]
},
{
"date": "2025-02-28",
"timeSlots": [
{
"id": "01813-01813-S-B*20879*ALL DAY DROP OFF",
"startTime": "07:30",
"endTime": "17:00",
"offerPremium": false
}
]
},
{
"date": "2025-03-01",
"timeSlots": [
{
"id": "01813-01813-S-B*20880*ALL DAY DROP OFF",
"startTime": "07:30",
"endTime": "17:00",
"offerPremium": false
}
]
}
],
"provisionalTriggers": []
}

View file

@ -0,0 +1,14 @@
{
"carId": "CR00056707",
"category": "CAR",
"year": 2006,
"make": "Chrysler",
"model": "300",
"style": "4 door sedan",
"imageUrl": "https://dbhdyzvm8lm25.cloudfront.net/color_0320_001/MY2006/3118/3118_cc0320_001_PWG.jpg",
"imageVifNumber": "3118",
"imageVifColor": "white",
"canSafeliteService": true,
"recalibrationServices": [],
"isMobileStaticRecalibrationApplicable": false
}

View file

@ -0,0 +1,19 @@
{
"experiments": [
{
"universeName": "PIAInsurance",
"universeId": 640,
"testName": "PIAInsurance_V1",
"testId": 504,
"variationName": "YesShowPIAInsurance_TEST",
"variationId": 1430,
"isActive": true,
"isExposed": true,
"userPartitionNumber": 12,
"assignmentId": 14863748,
"settings": {
"DisplayPIAInsurance": "true"
}
}
]
}

View file

@ -1,8 +0,0 @@
{
"containsMilitaryBase": false,
"isServiceable": true,
"isValid": false,
"state": "AL",
"providerNumber": "00795",
"zipCodeCtu": "01872"
}

View file

@ -1,8 +0,0 @@
{
"containsMilitaryBase": false,
"isServiceable": true,
"isValid": false,
"state": "OH",
"providerNumber": "03357",
"zipCodeCtu": "01820"
}

View file

@ -1,12 +1,25 @@
{
"common": {
"location/api/v1/location/zip/36116": "common/location/api/v1/location/zip/36116.json",
"location/api/v1/location/zip/43016": "common/location/api/v1/location/zip/43016.json"
"experiments/api/v1/experiments/run": "common/experiments/api/v1/experiments/run.json"
},
"scenario1": {
"coverage/api/v1/coverage/policy-information": "scenario1/coverage/api/v1/coverage/policy-information.json"
},
"scenario2": {
"location/api/v1/location/zip/36116": "scenario2/coverage/api/v1/coverage/policy-information.json"
"0002a_Advanced_Replace_Deductible_Client": {
"location/api/v1/location/zip/75023": "0002a_Advanced_Replace_Deductible_Client/location/api/v1/location/zip/75023.json",
"coverage/api/v1/coverage/policy-information": "0002a_Advanced_Replace_Deductible_Client/coverage/api/v1/coverage/policy-information.json",
"coverage/api/v1/coverage/order/api/v1/order/save-session/iss": "0002a_Advanced_Replace_Deductible_Client/coverage/api/v1/coverage/order/api/v1/order/save-session/iss.json",
"experiments/api/v1/experiments/run": "0002a_Advanced_Replace_Deductible_Client/experiments/api/v1/experiments/run.json",
"vehicle/api/v1/vehicle/lookup": "0002a_Advanced_Replace_Deductible_Client/vehicle/api/v1/vehicle/lookup.json",
"parts/api/v1/parts/damage-options/CR00056707": "0002a_Advanced_Replace_Deductible_Client/parts/api/v1/parts/damage-options/CR00056707.json",
"parts/api/v1/parts/parts-or-questions": "0002a_Advanced_Replace_Deductible_Client/parts/api/v1/parts/parts-or-questions.json",
"coverage/api/v1/coverage/final-deductible": "0002a_Advanced_Replace_Deductible_Client/coverage/api/v1/coverage/final-deductible.json",
"coverage/api/v1/coverage/register-claim": "0002a_Advanced_Replace_Deductible_Client/coverage/api/v1/coverage/register-claim.json",
"price/api/v1/price/order-items-with-itac-pricing": "0002a_Advanced_Replace_Deductible_Client/price/api/v1/price/order-items-with-itac-pricing.json",
"location/api/v1/location/providers/75023/Replace/100/550036/true/CR00056707/DW01571GTNNOEM": "0002a_Advanced_Replace_Deductible_Client/location/api/v1/location/providers/75023/Replace/100/550036/true/CR00056707/DW01571GTNNOEM.json",
"schedule/api/v1/schedule/shop-time-slots": "0002a_Advanced_Replace_Deductible_Client/schedule/api/v1/schedule/shop-time-slots.json",
"location/api/v1/location/alert-reasons/01813": "0002a_Advanced_Replace_Deductible_Client/location/api/v1/location/alert-reasons/01813.json",
"parts/api/v1/parts/rain-defense": "0002a_Advanced_Replace_Deductible_Client/parts/api/v1/parts/rain-defense.json",
"price/api/v1/price/combined-quote": "0002a_Advanced_Replace_Deductible_Client/price/api/v1/price/combined-quote.json"
}
}