Update self-serve wash flow to dynamically manage allowed services per lane
- Refactored `handleConfirmNext` in `MyWashStart.vue` to fetch and update allowed services before lane selection. - Modified `isMachineAvailable` logic to use server-defined allowed services. - Added watcher for `radioLaneOption` to fetch services dynamically on lane selection. - Incorporated `isLoadingSelfServeData` check in `shouldDisableNext` to ensure data readiness. - Introduced new tests for service logic (`updateLaneAllowedServices`, `isServiceAllowed`, `isMachineAvailable`) and added E2E coverage for guided self-serve wash flow.
This commit is contained in:
@@ -0,0 +1,920 @@
|
||||
/**
|
||||
* Tests for useSelfServeLogic composable
|
||||
* Covers: activeTaskServices, isServiceAllowed, isMachineAvailable logic,
|
||||
* updateLaneAllowedServices, and enableMachineRelay functionality.
|
||||
*/
|
||||
|
||||
describe('useSelfServeLogic Composable', () => {
|
||||
describe('activeTaskServices computation', () => {
|
||||
it('should return empty array when no tasks have services defined', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const module = await import('/src/composables/useSelfServeLogic.js');
|
||||
const { tasks, activeTasks, activeTaskServices } = module.useSelfServeLogic();
|
||||
|
||||
// Set up tasks with no services
|
||||
tasks.value = [
|
||||
{ id: 1, condition_id: null },
|
||||
{ id: 2, condition_id: null }
|
||||
];
|
||||
|
||||
done({
|
||||
success: true,
|
||||
activeTaskServicesLength: activeTaskServices.value.length,
|
||||
activeTaskServices: activeTaskServices.value
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('activeTaskServices empty result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.activeTaskServicesLength).toBe(0);
|
||||
});
|
||||
|
||||
it('should collect unique services from active tasks', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const module = await import('/src/composables/useSelfServeLogic.js');
|
||||
const { tasks, activeTaskServices } = module.useSelfServeLogic();
|
||||
|
||||
// Set up tasks with services (no condition_id means always active)
|
||||
tasks.value = [
|
||||
{ id: 1, condition_id: null, services: ['MACHINE'] },
|
||||
{ id: 2, condition_id: null, services: ['MACHINE'] }, // Duplicate
|
||||
{ id: 3, condition_id: null, services: [] }
|
||||
];
|
||||
|
||||
done({
|
||||
success: true,
|
||||
activeTaskServices: activeTaskServices.value,
|
||||
hasMachine: activeTaskServices.value.includes('MACHINE')
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('activeTaskServices unique result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.hasMachine).toBe(true);
|
||||
// Should only have one MACHINE (unique)
|
||||
expect(result.activeTaskServices.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isServiceAllowed function', () => {
|
||||
it('should return true when service is in activeTaskServices', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const module = await import('/src/composables/useSelfServeLogic.js');
|
||||
const { tasks, isServiceAllowed } = module.useSelfServeLogic();
|
||||
|
||||
// Set up task with MACHINE service
|
||||
tasks.value = [
|
||||
{ id: 1, condition_id: null, services: ['MACHINE'] }
|
||||
];
|
||||
|
||||
done({
|
||||
success: true,
|
||||
machineAllowed: isServiceAllowed('MACHINE'),
|
||||
otherAllowed: isServiceAllowed('OTHER')
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('isServiceAllowed result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.machineAllowed).toBe(true);
|
||||
expect(result.otherAllowed).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when no active tasks have the service', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const module = await import('/src/composables/useSelfServeLogic.js');
|
||||
const { tasks, isServiceAllowed } = module.useSelfServeLogic();
|
||||
|
||||
// Set up tasks without services
|
||||
tasks.value = [
|
||||
{ id: 1, condition_id: null, services: [] },
|
||||
{ id: 2, condition_id: null }
|
||||
];
|
||||
|
||||
done({
|
||||
success: true,
|
||||
machineAllowed: isServiceAllowed('MACHINE')
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('isServiceAllowed no services result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.machineAllowed).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateLaneAllowedServices function', () => {
|
||||
it('should be a function that accepts laneId', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const module = await import('/src/composables/useSelfServeLogic.js');
|
||||
const { updateLaneAllowedServices } = module.useSelfServeLogic();
|
||||
|
||||
done({
|
||||
success: true,
|
||||
isFunction: typeof updateLaneAllowedServices === 'function'
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('updateLaneAllowedServices function check:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.isFunction).toBe(true);
|
||||
});
|
||||
|
||||
it('should return early if no laneId provided', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const module = await import('/src/composables/useSelfServeLogic.js');
|
||||
const { updateLaneAllowedServices, allowedServices } = module.useSelfServeLogic();
|
||||
|
||||
// Call without laneId
|
||||
const response = await updateLaneAllowedServices(null);
|
||||
|
||||
done({
|
||||
success: true,
|
||||
response: response,
|
||||
allowedServicesLength: allowedServices.value.length
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('updateLaneAllowedServices no laneId result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.response).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('enableMachineRelay function', () => {
|
||||
it('should be a function that accepts laneId and optional duration', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const module = await import('/src/composables/useSelfServeLogic.js');
|
||||
const { enableMachineRelay } = module.useSelfServeLogic();
|
||||
|
||||
done({
|
||||
success: true,
|
||||
isFunction: typeof enableMachineRelay === 'function'
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('enableMachineRelay function check:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.isFunction).toBe(true);
|
||||
});
|
||||
|
||||
it('should return early if no laneId provided', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const module = await import('/src/composables/useSelfServeLogic.js');
|
||||
const { enableMachineRelay } = module.useSelfServeLogic();
|
||||
|
||||
// Call without laneId
|
||||
const response = await enableMachineRelay(null);
|
||||
|
||||
done({
|
||||
success: true,
|
||||
response: response
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('enableMachineRelay no laneId result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.response).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('allowedServices ref', () => {
|
||||
it('should be exported and initialized as empty array', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const module = await import('/src/composables/useSelfServeLogic.js');
|
||||
const { allowedServices } = module.useSelfServeLogic();
|
||||
|
||||
done({
|
||||
success: true,
|
||||
isArray: Array.isArray(allowedServices.value),
|
||||
length: allowedServices.value.length
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('allowedServices ref result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.isArray).toBe(true);
|
||||
expect(result.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Task condition evaluation', () => {
|
||||
it('should include task in activeTasks when condition_id is null', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const module = await import('/src/composables/useSelfServeLogic.js');
|
||||
const { tasks, activeTasks } = module.useSelfServeLogic();
|
||||
|
||||
tasks.value = [
|
||||
{ id: 1, condition_id: null, services: ['MACHINE'] },
|
||||
{ id: 2, condition_id: 0, services: [] }
|
||||
];
|
||||
|
||||
done({
|
||||
success: true,
|
||||
activeTasksCount: activeTasks.value.length,
|
||||
activeTaskIds: activeTasks.value.map(t => t.id)
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('activeTasks condition evaluation result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.activeTasksCount).toBe(2);
|
||||
});
|
||||
|
||||
it('should filter out tasks with unmet conditions', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const module = await import('/src/composables/useSelfServeLogic.js');
|
||||
const { tasks, conditions, rules, activeTasks, answers } = module.useSelfServeLogic();
|
||||
|
||||
// Set up a condition that requires question 1 to be true
|
||||
conditions.value = [
|
||||
{ id: 100, name: 'Test Condition' }
|
||||
];
|
||||
|
||||
rules.value = [
|
||||
{ id: 1, condition_id: 100, object_type: 'question', object_id: 1, type: 'IS_TRUE' }
|
||||
];
|
||||
|
||||
tasks.value = [
|
||||
{ id: 1, condition_id: 100, services: ['MACHINE'] },
|
||||
{ id: 2, condition_id: null, services: [] }
|
||||
];
|
||||
|
||||
// Answer question 1 as false
|
||||
answers.value = { 1: false };
|
||||
|
||||
done({
|
||||
success: true,
|
||||
activeTasksCount: activeTasks.value.length,
|
||||
activeTaskIds: activeTasks.value.map(t => t.id)
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('activeTasks condition filtering result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
// Only task 2 should be active (task 1's condition is not met)
|
||||
expect(result.activeTasksCount).toBe(1);
|
||||
expect(result.activeTaskIds).toContain(2);
|
||||
});
|
||||
|
||||
it('should include task when condition is met', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const module = await import('/src/composables/useSelfServeLogic.js');
|
||||
const { tasks, conditions, rules, activeTasks, answers, activeTaskServices, isServiceAllowed } = module.useSelfServeLogic();
|
||||
|
||||
// Set up a condition that requires question 1 to be true
|
||||
conditions.value = [
|
||||
{ id: 100, name: 'Test Condition' }
|
||||
];
|
||||
|
||||
rules.value = [
|
||||
{ id: 1, condition_id: 100, object_type: 'question', object_id: 1, type: 'IS_TRUE' }
|
||||
];
|
||||
|
||||
tasks.value = [
|
||||
{ id: 1, condition_id: 100, services: ['MACHINE'] }
|
||||
];
|
||||
|
||||
// Answer question 1 as true
|
||||
answers.value = { 1: true };
|
||||
|
||||
done({
|
||||
success: true,
|
||||
activeTasksCount: activeTasks.value.length,
|
||||
activeTaskIds: activeTasks.value.map(t => t.id),
|
||||
activeServices: activeTaskServices.value,
|
||||
machineAllowed: isServiceAllowed('MACHINE')
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('activeTasks condition met result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.activeTasksCount).toBe(1);
|
||||
expect(result.activeTaskIds).toContain(1);
|
||||
expect(result.activeServices).toContain('MACHINE');
|
||||
expect(result.machineAllowed).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Integration: Services flow end-to-end', () => {
|
||||
it('should correctly determine MACHINE availability based on question answers', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const module = await import('/src/composables/useSelfServeLogic.js');
|
||||
const {
|
||||
tasks, conditions, rules, questions, answers,
|
||||
activeTasks, activeTaskServices, isServiceAllowed
|
||||
} = module.useSelfServeLogic();
|
||||
|
||||
// Setup: Question about machine wash, condition, and task
|
||||
questions.value = [
|
||||
{ id: 1, question: 'Do you need machine wash?', condition_id: null }
|
||||
];
|
||||
|
||||
conditions.value = [
|
||||
{ id: 100, name: 'Machine Requested' }
|
||||
];
|
||||
|
||||
rules.value = [
|
||||
{ id: 1, condition_id: 100, object_type: 'question', object_id: 1, type: 'IS_TRUE' }
|
||||
];
|
||||
|
||||
tasks.value = [
|
||||
{ id: 1, task: 'Enable Machine', condition_id: 100, services: ['MACHINE'] }
|
||||
];
|
||||
|
||||
// Initially no answer
|
||||
answers.value = {};
|
||||
const initialMachineAllowed = isServiceAllowed('MACHINE');
|
||||
const initialActiveTasks = activeTasks.value.length;
|
||||
|
||||
// Answer YES to machine question
|
||||
answers.value = { 1: true };
|
||||
const afterYesMachineAllowed = isServiceAllowed('MACHINE');
|
||||
const afterYesActiveTasks = activeTasks.value.length;
|
||||
|
||||
// Answer NO to machine question
|
||||
answers.value = { 1: false };
|
||||
const afterNoMachineAllowed = isServiceAllowed('MACHINE');
|
||||
const afterNoActiveTasks = activeTasks.value.length;
|
||||
|
||||
done({
|
||||
success: true,
|
||||
initialMachineAllowed,
|
||||
initialActiveTasks,
|
||||
afterYesMachineAllowed,
|
||||
afterYesActiveTasks,
|
||||
afterNoMachineAllowed,
|
||||
afterNoActiveTasks
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Integration test result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
// Initially: no answer means condition not met
|
||||
expect(result.initialMachineAllowed).toBe(false);
|
||||
expect(result.initialActiveTasks).toBe(0);
|
||||
// After YES: condition met, MACHINE allowed
|
||||
expect(result.afterYesMachineAllowed).toBe(true);
|
||||
expect(result.afterYesActiveTasks).toBe(1);
|
||||
// After NO: condition not met, MACHINE not allowed
|
||||
expect(result.afterNoMachineAllowed).toBe(false);
|
||||
expect(result.afterNoActiveTasks).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests for MyWashStart step navigation and validation
|
||||
* Covers: steps definition, clickableSteps validation, and step transitions
|
||||
*/
|
||||
describe('MyWashStart Step Navigation', () => {
|
||||
describe('Steps definition', () => {
|
||||
it('should define all required steps with correct indices', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
// Define expected steps matching MyWashStart.vue
|
||||
const expectedSteps = {
|
||||
VEHICLE: 0,
|
||||
QUESTIONS: 1,
|
||||
SELECT_LANE: 2,
|
||||
TASKS: 3,
|
||||
WASH_IN_PROGRESS: 4,
|
||||
COMPLETED: 5
|
||||
};
|
||||
|
||||
done({
|
||||
success: true,
|
||||
steps: expectedSteps,
|
||||
stepCount: Object.keys(expectedSteps).length
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Steps definition result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.stepCount).toBe(6);
|
||||
expect(result.steps.VEHICLE).toBe(0);
|
||||
expect(result.steps.QUESTIONS).toBe(1);
|
||||
expect(result.steps.SELECT_LANE).toBe(2);
|
||||
expect(result.steps.TASKS).toBe(3);
|
||||
expect(result.steps.WASH_IN_PROGRESS).toBe(4);
|
||||
expect(result.steps.COMPLETED).toBe(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('VEHICLE step validation', () => {
|
||||
it('should require customer number, license plate, and valid vehicle type', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
// Simulate validation logic from clickableSteps[steps.VEHICLE]
|
||||
const testCases = [
|
||||
{
|
||||
name: 'all empty',
|
||||
customerNumber: '',
|
||||
licensePlate: '',
|
||||
vehicleType: null,
|
||||
availableProducts: [1, 2],
|
||||
washInProgress: false,
|
||||
expected: false
|
||||
},
|
||||
{
|
||||
name: 'only customer number',
|
||||
customerNumber: '12345',
|
||||
licensePlate: '',
|
||||
vehicleType: null,
|
||||
availableProducts: [1, 2],
|
||||
washInProgress: false,
|
||||
expected: false
|
||||
},
|
||||
{
|
||||
name: 'customer and plate, no vehicle type',
|
||||
customerNumber: '12345',
|
||||
licensePlate: 'ABC123',
|
||||
vehicleType: null,
|
||||
availableProducts: [1, 2],
|
||||
washInProgress: false,
|
||||
expected: false
|
||||
},
|
||||
{
|
||||
name: 'all valid, vehicle type in available products',
|
||||
customerNumber: '12345',
|
||||
licensePlate: 'ABC123',
|
||||
vehicleType: '1',
|
||||
availableProducts: [1, 2],
|
||||
washInProgress: false,
|
||||
expected: true
|
||||
},
|
||||
{
|
||||
name: 'vehicle type not in available products',
|
||||
customerNumber: '12345',
|
||||
licensePlate: 'ABC123',
|
||||
vehicleType: '3',
|
||||
availableProducts: [1, 2],
|
||||
washInProgress: false,
|
||||
expected: false
|
||||
}
|
||||
];
|
||||
|
||||
const results = testCases.map(tc => {
|
||||
// Simulate the validation logic
|
||||
const customerNumberValid = !!(tc.customerNumber && parseInt(tc.customerNumber));
|
||||
const licensePlateValid = !!(tc.licensePlate && tc.licensePlate.trim() !== '');
|
||||
const vehicleTypeValid = !!tc.vehicleType && tc.availableProducts.includes(parseInt(tc.vehicleType));
|
||||
const isValid = customerNumberValid && licensePlateValid && vehicleTypeValid;
|
||||
|
||||
return {
|
||||
name: tc.name,
|
||||
expected: tc.expected,
|
||||
actual: isValid,
|
||||
passed: isValid === tc.expected
|
||||
};
|
||||
});
|
||||
|
||||
done({
|
||||
success: results.every(r => r.passed),
|
||||
results
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('VEHICLE step validation result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('QUESTIONS step validation', () => {
|
||||
it('should require license plate and vehicle type to be clickable', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const testCases = [
|
||||
{
|
||||
name: 'no license plate or vehicle type',
|
||||
licensePlate: '',
|
||||
vehicleType: null,
|
||||
washInProgress: false,
|
||||
currentStep: 0,
|
||||
expected: false
|
||||
},
|
||||
{
|
||||
name: 'only license plate',
|
||||
licensePlate: 'ABC123',
|
||||
vehicleType: null,
|
||||
washInProgress: false,
|
||||
currentStep: 0,
|
||||
expected: false
|
||||
},
|
||||
{
|
||||
name: 'license plate and vehicle type set',
|
||||
licensePlate: 'ABC123',
|
||||
vehicleType: '1',
|
||||
washInProgress: false,
|
||||
currentStep: 0,
|
||||
expected: true
|
||||
},
|
||||
{
|
||||
name: 'whitespace only license plate',
|
||||
licensePlate: ' ',
|
||||
vehicleType: '1',
|
||||
washInProgress: false,
|
||||
currentStep: 0,
|
||||
expected: false
|
||||
}
|
||||
];
|
||||
|
||||
const results = testCases.map(tc => {
|
||||
// Simulate validation: !!(licensePlate && licensePlate.trim() !== '') && !!vehicleType
|
||||
const isValid = !!(tc.licensePlate && tc.licensePlate.trim() !== '') && !!tc.vehicleType;
|
||||
|
||||
return {
|
||||
name: tc.name,
|
||||
expected: tc.expected,
|
||||
actual: isValid,
|
||||
passed: isValid === tc.expected
|
||||
};
|
||||
});
|
||||
|
||||
done({
|
||||
success: results.every(r => r.passed),
|
||||
results
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('QUESTIONS step validation result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow clicking back from tasks when wash in progress', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
// When washInProgress && currentStep >= steps.TASKS, should return true
|
||||
const steps = { VEHICLE: 0, QUESTIONS: 1, SELECT_LANE: 2, TASKS: 3, WASH_IN_PROGRESS: 4 };
|
||||
|
||||
const testCases = [
|
||||
{ washInProgress: true, currentStep: steps.TASKS, expected: true },
|
||||
{ washInProgress: true, currentStep: steps.WASH_IN_PROGRESS, expected: true },
|
||||
{ washInProgress: true, currentStep: steps.QUESTIONS, expected: true },
|
||||
{ washInProgress: false, currentStep: steps.TASKS, expected: false } // Will use regular validation
|
||||
];
|
||||
|
||||
const results = testCases.map(tc => {
|
||||
let isValid;
|
||||
if (tc.washInProgress && tc.currentStep >= steps.TASKS) {
|
||||
isValid = true;
|
||||
} else if (tc.washInProgress) {
|
||||
isValid = tc.currentStep === steps.QUESTIONS;
|
||||
} else {
|
||||
isValid = false; // Simplified; actual needs license plate check
|
||||
}
|
||||
|
||||
return {
|
||||
currentStep: tc.currentStep,
|
||||
washInProgress: tc.washInProgress,
|
||||
expected: tc.expected,
|
||||
actual: isValid,
|
||||
passed: isValid === tc.expected
|
||||
};
|
||||
});
|
||||
|
||||
done({
|
||||
success: results.every(r => r.passed),
|
||||
results
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('QUESTIONS step wash in progress result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SELECT_LANE step validation', () => {
|
||||
it('should require license plate, vehicle type, questions answered, and currentStep > QUESTIONS', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const steps = { QUESTIONS: 1, SELECT_LANE: 2 };
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: 'all requirements met',
|
||||
licensePlate: 'ABC123',
|
||||
vehicleType: '1',
|
||||
allQuestionsAnswered: true,
|
||||
currentStep: steps.SELECT_LANE,
|
||||
washInProgress: false,
|
||||
expected: true
|
||||
},
|
||||
{
|
||||
name: 'questions not answered',
|
||||
licensePlate: 'ABC123',
|
||||
vehicleType: '1',
|
||||
allQuestionsAnswered: false,
|
||||
currentStep: steps.SELECT_LANE,
|
||||
washInProgress: false,
|
||||
expected: false
|
||||
},
|
||||
{
|
||||
name: 'still on questions step',
|
||||
licensePlate: 'ABC123',
|
||||
vehicleType: '1',
|
||||
allQuestionsAnswered: true,
|
||||
currentStep: steps.QUESTIONS,
|
||||
washInProgress: false,
|
||||
expected: false
|
||||
}
|
||||
];
|
||||
|
||||
const results = testCases.map(tc => {
|
||||
// Simulate validation logic
|
||||
const isValid = !!(tc.licensePlate && tc.licensePlate.trim() !== '')
|
||||
&& !!tc.vehicleType
|
||||
&& tc.allQuestionsAnswered
|
||||
&& tc.currentStep > steps.QUESTIONS;
|
||||
|
||||
return {
|
||||
name: tc.name,
|
||||
expected: tc.expected,
|
||||
actual: isValid,
|
||||
passed: isValid === tc.expected
|
||||
};
|
||||
});
|
||||
|
||||
done({
|
||||
success: results.every(r => r.passed),
|
||||
results
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('SELECT_LANE step validation result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TASKS step validation', () => {
|
||||
it('should require license plate, vehicle type, lane selected, and questions answered', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const steps = { QUESTIONS: 1, SELECT_LANE: 2, TASKS: 3 };
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: 'all requirements met',
|
||||
licensePlate: 'ABC123',
|
||||
vehicleType: '1',
|
||||
laneOption: 'Lane1',
|
||||
allQuestionsAnswered: true,
|
||||
currentStep: steps.TASKS,
|
||||
washInProgress: false,
|
||||
expected: true
|
||||
},
|
||||
{
|
||||
name: 'no lane selected',
|
||||
licensePlate: 'ABC123',
|
||||
vehicleType: '1',
|
||||
laneOption: null,
|
||||
allQuestionsAnswered: true,
|
||||
currentStep: steps.SELECT_LANE,
|
||||
washInProgress: false,
|
||||
expected: false
|
||||
},
|
||||
{
|
||||
name: 'wash in progress at WASH_IN_PROGRESS step',
|
||||
licensePlate: 'ABC123',
|
||||
vehicleType: '1',
|
||||
laneOption: 'Lane1',
|
||||
allQuestionsAnswered: true,
|
||||
currentStep: 4, // WASH_IN_PROGRESS
|
||||
washInProgress: true,
|
||||
expected: true
|
||||
}
|
||||
];
|
||||
|
||||
const results = testCases.map(tc => {
|
||||
let isValid;
|
||||
if (tc.washInProgress && tc.currentStep === 4) { // WASH_IN_PROGRESS
|
||||
isValid = true;
|
||||
} else if (tc.washInProgress) {
|
||||
isValid = tc.currentStep === steps.TASKS;
|
||||
} else {
|
||||
isValid = !!(tc.licensePlate && tc.licensePlate.trim() !== '')
|
||||
&& !!tc.vehicleType
|
||||
&& !!tc.laneOption
|
||||
&& tc.allQuestionsAnswered
|
||||
&& tc.currentStep >= steps.SELECT_LANE;
|
||||
}
|
||||
|
||||
return {
|
||||
name: tc.name,
|
||||
expected: tc.expected,
|
||||
actual: isValid,
|
||||
passed: isValid === tc.expected
|
||||
};
|
||||
});
|
||||
|
||||
done({
|
||||
success: results.every(r => r.passed),
|
||||
results
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('TASKS step validation result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('WASH_IN_PROGRESS step validation', () => {
|
||||
it('should only be clickable when wash is in progress', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const testCases = [
|
||||
{ washInProgress: true, expected: true },
|
||||
{ washInProgress: false, expected: false }
|
||||
];
|
||||
|
||||
const results = testCases.map(tc => {
|
||||
// Validation is simply: return washInProgress.value
|
||||
const isValid = tc.washInProgress;
|
||||
|
||||
return {
|
||||
washInProgress: tc.washInProgress,
|
||||
expected: tc.expected,
|
||||
actual: isValid,
|
||||
passed: isValid === tc.expected
|
||||
};
|
||||
});
|
||||
|
||||
done({
|
||||
success: results.every(r => r.passed),
|
||||
results
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('WASH_IN_PROGRESS step validation result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Step transition flow', () => {
|
||||
it('should follow correct step progression', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (done) => {
|
||||
try {
|
||||
const steps = {
|
||||
VEHICLE: 0,
|
||||
QUESTIONS: 1,
|
||||
SELECT_LANE: 2,
|
||||
TASKS: 3,
|
||||
WASH_IN_PROGRESS: 4,
|
||||
COMPLETED: 5
|
||||
};
|
||||
|
||||
// Test the handleConfirmNext logic
|
||||
const transitions = [
|
||||
{ from: steps.QUESTIONS, to: steps.SELECT_LANE, description: 'QUESTIONS -> SELECT_LANE' },
|
||||
{ from: steps.SELECT_LANE, to: steps.TASKS, hasTasks: true, description: 'SELECT_LANE -> TASKS (with tasks)' },
|
||||
{ from: steps.SELECT_LANE, to: steps.WASH_IN_PROGRESS, hasTasks: false, description: 'SELECT_LANE -> WASH_IN_PROGRESS (no tasks)' },
|
||||
{ from: steps.TASKS, to: steps.WASH_IN_PROGRESS, description: 'TASKS -> WASH_IN_PROGRESS' }
|
||||
];
|
||||
|
||||
const results = transitions.map(t => {
|
||||
let nextStep;
|
||||
if (t.from === steps.QUESTIONS) {
|
||||
nextStep = steps.SELECT_LANE;
|
||||
} else if (t.from === steps.SELECT_LANE) {
|
||||
nextStep = t.hasTasks ? steps.TASKS : steps.WASH_IN_PROGRESS;
|
||||
} else if (t.from === steps.TASKS) {
|
||||
nextStep = steps.WASH_IN_PROGRESS;
|
||||
}
|
||||
|
||||
return {
|
||||
description: t.description,
|
||||
expected: t.to,
|
||||
actual: nextStep,
|
||||
passed: nextStep === t.to
|
||||
};
|
||||
});
|
||||
|
||||
done({
|
||||
success: results.every(r => r.passed),
|
||||
results
|
||||
});
|
||||
} catch (e) {
|
||||
done({ success: false, error: 'Exception: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Step transition flow result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user