wip
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* Integration tests for Playwright adapter step 17 checkout flow with confirmation callback.
|
||||
* Tests the pause-for-confirmation mechanism before clicking checkout button.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeAll, afterAll, beforeEach, vi } from 'vitest';
|
||||
import { FixtureServer } from '../../../packages/infrastructure/adapters/automation/FixtureServer';
|
||||
import { PlaywrightAutomationAdapter } from '../../../packages/infrastructure/adapters/automation/PlaywrightAutomationAdapter';
|
||||
import { StepId } from '../../../packages/domain/value-objects/StepId';
|
||||
import { CheckoutConfirmation } from '../../../packages/domain/value-objects/CheckoutConfirmation';
|
||||
import { CheckoutPrice } from '../../../packages/domain/value-objects/CheckoutPrice';
|
||||
import { CheckoutState } from '../../../packages/domain/value-objects/CheckoutState';
|
||||
|
||||
describe('Playwright Step 17 Checkout Flow with Confirmation', () => {
|
||||
let server: FixtureServer;
|
||||
let adapter: PlaywrightAutomationAdapter;
|
||||
let baseUrl: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
server = new FixtureServer();
|
||||
const serverInfo = await server.start();
|
||||
baseUrl = serverInfo.url;
|
||||
|
||||
adapter = new PlaywrightAutomationAdapter({
|
||||
headless: true,
|
||||
timeout: 5000,
|
||||
baseUrl,
|
||||
mode: 'mock',
|
||||
});
|
||||
|
||||
const connectResult = await adapter.connect();
|
||||
expect(connectResult.success).toBe(true);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await adapter.disconnect();
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await adapter.navigateToPage(server.getFixtureUrl(17));
|
||||
// Clear any previous callback
|
||||
adapter.setCheckoutConfirmationCallback(undefined);
|
||||
});
|
||||
|
||||
describe('Checkout Confirmation Callback Injection', () => {
|
||||
it('should accept and store checkout confirmation callback', () => {
|
||||
const mockCallback = vi.fn();
|
||||
|
||||
// Should not throw
|
||||
expect(() => {
|
||||
adapter.setCheckoutConfirmationCallback(mockCallback);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should allow clearing the callback by passing undefined', () => {
|
||||
const mockCallback = vi.fn();
|
||||
adapter.setCheckoutConfirmationCallback(mockCallback);
|
||||
|
||||
// Should not throw when clearing
|
||||
expect(() => {
|
||||
adapter.setCheckoutConfirmationCallback(undefined);
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Step 17 Execution with Confirmation Flow', () => {
|
||||
it('should extract checkout info before requesting confirmation', async () => {
|
||||
const mockCallback = vi.fn().mockResolvedValue(
|
||||
CheckoutConfirmation.create('confirmed')
|
||||
);
|
||||
|
||||
adapter.setCheckoutConfirmationCallback(mockCallback);
|
||||
|
||||
const stepId = StepId.create(17);
|
||||
const result = await adapter.executeStep(stepId, {});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
|
||||
// Verify callback was called with price and state
|
||||
const callArgs = mockCallback.mock.calls[0];
|
||||
expect(callArgs).toHaveLength(2);
|
||||
|
||||
const [price, state] = callArgs;
|
||||
expect(price).toBeInstanceOf(CheckoutPrice);
|
||||
expect(state).toBeInstanceOf(CheckoutState);
|
||||
});
|
||||
|
||||
it('should show "Awaiting confirmation..." overlay before callback', async () => {
|
||||
const mockCallback = vi.fn().mockImplementation(async () => {
|
||||
// Check overlay message during callback execution
|
||||
const page = adapter.getPage()!;
|
||||
const overlayText = await page.locator('#gridpilot-action').textContent();
|
||||
expect(overlayText).toContain('Awaiting confirmation');
|
||||
|
||||
return CheckoutConfirmation.create('confirmed');
|
||||
});
|
||||
|
||||
adapter.setCheckoutConfirmationCallback(mockCallback);
|
||||
|
||||
const stepId = StepId.create(17);
|
||||
await adapter.executeStep(stepId, {});
|
||||
|
||||
expect(mockCallback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should click checkout button only if confirmation is "confirmed"', async () => {
|
||||
const mockCallback = vi.fn().mockResolvedValue(
|
||||
CheckoutConfirmation.create('confirmed')
|
||||
);
|
||||
|
||||
adapter.setCheckoutConfirmationCallback(mockCallback);
|
||||
|
||||
const stepId = StepId.create(17);
|
||||
const result = await adapter.executeStep(stepId, {});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
// Verify button was clicked by checking if navigation occurred
|
||||
const page = adapter.getPage()!;
|
||||
const currentUrl = page.url();
|
||||
// In mock mode, clicking checkout would navigate to a success page or different step
|
||||
expect(currentUrl).toBeDefined();
|
||||
});
|
||||
|
||||
it('should NOT click checkout button if confirmation is "cancelled"', async () => {
|
||||
const mockCallback = vi.fn().mockResolvedValue(
|
||||
CheckoutConfirmation.create('cancelled')
|
||||
);
|
||||
|
||||
adapter.setCheckoutConfirmationCallback(mockCallback);
|
||||
|
||||
const stepId = StepId.create(17);
|
||||
const result = await adapter.executeStep(stepId, {});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('cancelled');
|
||||
expect(mockCallback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should NOT click checkout button if confirmation is "timeout"', async () => {
|
||||
const mockCallback = vi.fn().mockResolvedValue(
|
||||
CheckoutConfirmation.create('timeout')
|
||||
);
|
||||
|
||||
adapter.setCheckoutConfirmationCallback(mockCallback);
|
||||
|
||||
const stepId = StepId.create(17);
|
||||
const result = await adapter.executeStep(stepId, {});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('timeout');
|
||||
expect(mockCallback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show success overlay after confirmed checkout', async () => {
|
||||
const mockCallback = vi.fn().mockResolvedValue(
|
||||
CheckoutConfirmation.create('confirmed')
|
||||
);
|
||||
|
||||
adapter.setCheckoutConfirmationCallback(mockCallback);
|
||||
|
||||
const stepId = StepId.create(17);
|
||||
await adapter.executeStep(stepId, {});
|
||||
|
||||
// Check for success overlay
|
||||
const page = adapter.getPage()!;
|
||||
const overlayExists = await page.locator('#gridpilot-overlay').count();
|
||||
expect(overlayExists).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should execute step normally if no callback is set', async () => {
|
||||
// No callback set - should execute without confirmation
|
||||
const stepId = StepId.create(17);
|
||||
const result = await adapter.executeStep(stepId, {});
|
||||
|
||||
// Should succeed without asking for confirmation
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle callback errors gracefully', async () => {
|
||||
const mockCallback = vi.fn().mockRejectedValue(
|
||||
new Error('Callback failed')
|
||||
);
|
||||
|
||||
adapter.setCheckoutConfirmationCallback(mockCallback);
|
||||
|
||||
const stepId = StepId.create(17);
|
||||
const result = await adapter.executeStep(stepId, {});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBeDefined();
|
||||
expect(mockCallback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should pass correct price from CheckoutPriceExtractor to callback', async () => {
|
||||
let capturedPrice: CheckoutPrice | null = null;
|
||||
|
||||
const mockCallback = vi.fn().mockImplementation(async (price: CheckoutPrice) => {
|
||||
capturedPrice = price;
|
||||
return CheckoutConfirmation.create('confirmed');
|
||||
});
|
||||
|
||||
adapter.setCheckoutConfirmationCallback(mockCallback);
|
||||
|
||||
const stepId = StepId.create(17);
|
||||
await adapter.executeStep(stepId, {});
|
||||
|
||||
expect(capturedPrice).not.toBeNull();
|
||||
expect(capturedPrice).toBeInstanceOf(CheckoutPrice);
|
||||
// The mock fixture should have a price formatted as $X.XX
|
||||
expect(capturedPrice!.toDisplayString()).toMatch(/^\$\d+\.\d{2}$/);
|
||||
});
|
||||
|
||||
it('should pass correct state from CheckoutState validation to callback', async () => {
|
||||
let capturedState: CheckoutState | null = null;
|
||||
|
||||
const mockCallback = vi.fn().mockImplementation(
|
||||
async (_price: CheckoutPrice, state: CheckoutState) => {
|
||||
capturedState = state;
|
||||
return CheckoutConfirmation.create('confirmed');
|
||||
}
|
||||
);
|
||||
|
||||
adapter.setCheckoutConfirmationCallback(mockCallback);
|
||||
|
||||
const stepId = StepId.create(17);
|
||||
await adapter.executeStep(stepId, {});
|
||||
|
||||
expect(capturedState).not.toBeNull();
|
||||
expect(capturedState).toBeInstanceOf(CheckoutState);
|
||||
// State should indicate whether checkout is ready (method, not property)
|
||||
expect(typeof capturedState!.isReady()).toBe('boolean');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Step 17 with Track State Configuration', () => {
|
||||
it('should set track state before requesting confirmation', async () => {
|
||||
const mockCallback = vi.fn().mockResolvedValue(
|
||||
CheckoutConfirmation.create('confirmed')
|
||||
);
|
||||
|
||||
adapter.setCheckoutConfirmationCallback(mockCallback);
|
||||
|
||||
const stepId = StepId.create(17);
|
||||
const result = await adapter.executeStep(stepId, {
|
||||
trackState: 'moderately-low',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mockCallback).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user