#!/usr/bin/env node /** * Complete API test simulating frontend checkout flow * Tests every step the frontend takes */ const SALEOR_API_URL = 'https://api.manoonoils.com/graphql/'; async function saleorFetch(query, variables = {}) { const response = await fetch(SALEOR_API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: query.replace(/\n\s*/g, ' '), variables }), }); const result = await response.json(); if (result.errors) { console.error('GraphQL Error:', JSON.stringify(result.errors, null, 2)); throw new Error(result.errors[0].message); } return result.data; } async function runTest() { console.log('๐Ÿงช TESTING FRONTEND CHECKOUT FLOW\n'); console.log('=' .repeat(50)); let checkoutId = null; let checkoutToken = null; let shippingMethodId = null; try { // STEP 1: Create checkout (like frontend does on first cart add) console.log('\n๐Ÿ“ฆ STEP 1: Create Checkout'); console.log('-'.repeat(50)); const createResult = await saleorFetch(` mutation CheckoutCreate($input: CheckoutCreateInput!) { checkoutCreate(input: $input) { checkout { id token totalPrice { gross { amount currency } } subtotalPrice { gross { amount } } } errors { field message } } } `, { input: { channel: "default-channel", email: "test@test.com", lines: [], languageCode: "SR" } }); checkoutId = createResult.checkoutCreate.checkout.id; checkoutToken = createResult.checkoutCreate.checkout.token; console.log('โœ… Checkout created'); console.log(' ID:', checkoutId); console.log(' Token:', checkoutToken); console.log(' Initial Total:', createResult.checkoutCreate.checkout.totalPrice.gross.amount, 'RSD'); // STEP 2: Add product (like frontend does) console.log('\n๐Ÿ›’ STEP 2: Add Product to Cart'); console.log('-'.repeat(50)); // Get a valid variant first const productsResult = await saleorFetch(` query { products(channel: "default-channel", first: 1) { edges { node { variants { id name } } } } } `); const variantId = productsResult.products.edges[0].node.variants[0].id; const addLineResult = await saleorFetch(` mutation CheckoutLinesAdd($checkoutId: ID!, $lines: [CheckoutLineInput!]!) { checkoutLinesAdd(checkoutId: $checkoutId, lines: $lines) { checkout { id token totalPrice { gross { amount currency } } subtotalPrice { gross { amount } } } errors { field message } } } `, { checkoutId: checkoutId, lines: [{ variantId: variantId, quantity: 1 }] }); const afterAdd = addLineResult.checkoutLinesAdd.checkout; console.log('โœ… Product added'); console.log(' Product Total:', afterAdd.totalPrice.gross.amount, 'RSD'); console.log(' Subtotal:', afterAdd.subtotalPrice.gross.amount, 'RSD'); // STEP 3: Refresh checkout by token (what refreshCheckout() does) console.log('\n๐Ÿ”„ STEP 3: Refresh Checkout by Token'); console.log('-'.repeat(50)); console.log(' (This simulates what refreshCheckout() does in the store)'); const refreshResult = await saleorFetch(` query GetCheckout($token: UUID!) { checkout(token: $token) { id token totalPrice { gross { amount currency } } subtotalPrice { gross { amount } } } } `, { token: checkoutToken }); console.log('โœ… Refreshed checkout'); console.log(' Total from refresh:', refreshResult.checkout.totalPrice.gross.amount, 'RSD'); // STEP 4: Set shipping address console.log('\n๐Ÿ“ STEP 4: Set Shipping Address'); console.log('-'.repeat(50)); const addressResult = await saleorFetch(` mutation CheckoutShippingAddressUpdate($checkoutId: ID!, $shippingAddress: AddressInput!) { checkoutShippingAddressUpdate(checkoutId: $checkoutId, shippingAddress: $shippingAddress) { checkout { id shippingMethods { id name price { amount currency } } } errors { field message } } } `, { checkoutId: checkoutId, shippingAddress: { firstName: "Test", lastName: "User", streetAddress1: "123 Test Street", city: "Belgrade", postalCode: "11000", country: "RS", phone: "+38160123456" } }); const methods = addressResult.checkoutShippingAddressUpdate.checkout.shippingMethods; console.log('โœ… Address set'); console.log(' Available shipping methods:', methods.length); if (methods.length === 0) { console.log('โŒ No shipping methods available!'); return; } methods.forEach((m, i) => { console.log(` [${i+1}] ${m.name}: ${m.price.amount} ${m.price.currency}`); }); shippingMethodId = methods[0].id; const shippingPrice = methods[0].price.amount; // STEP 5: Select shipping method (what happens when user clicks radio button) console.log('\n๐Ÿšš STEP 5: Select Shipping Method'); console.log('-'.repeat(50)); console.log(` Selecting: ${methods[0].name} (${shippingPrice} RSD)`); const methodResult = await saleorFetch(` mutation CheckoutShippingMethodUpdate($checkoutId: ID!, $shippingMethodId: ID!) { checkoutShippingMethodUpdate(checkoutId: $checkoutId, shippingMethodId: $shippingMethodId) { checkout { id totalPrice { gross { amount currency } } subtotalPrice { gross { amount } } shippingPrice { gross { amount } } } errors { field message } } } `, { checkoutId: checkoutId, shippingMethodId: shippingMethodId }); const afterMethod = methodResult.checkoutShippingMethodUpdate.checkout; console.log('โœ… Shipping method set'); console.log(' Total:', afterMethod.totalPrice.gross.amount, 'RSD'); console.log(' Subtotal:', afterMethod.subtotalPrice.gross.amount, 'RSD'); console.log(' Shipping:', afterMethod.shippingPrice.gross.amount, 'RSD'); // STEP 6: Refresh checkout again (what refreshCheckout() does after setting method) console.log('\n๐Ÿ”„ STEP 6: Refresh Checkout Again'); console.log('-'.repeat(50)); console.log(' (Simulating refreshCheckout() call in handleShippingMethodSelect)'); const finalRefresh = await saleorFetch(` query GetCheckout($token: UUID!) { checkout(token: $token) { id token totalPrice { gross { amount currency } } subtotalPrice { gross { amount } } shippingPrice { gross { amount } } } } `, { token: checkoutToken }); const final = finalRefresh.checkout; console.log('โœ… Final checkout state after refresh:'); console.log(' Total:', final.totalPrice.gross.amount, 'RSD'); console.log(' Subtotal:', final.subtotalPrice.gross.amount, 'RSD'); console.log(' Shipping:', final.shippingPrice.gross.amount, 'RSD'); // VERIFICATION console.log('\n๐Ÿ“Š VERIFICATION'); console.log('=' .repeat(50)); const expectedTotal = final.subtotalPrice.gross.amount + final.shippingPrice.gross.amount; const actualTotal = final.totalPrice.gross.amount; if (actualTotal === expectedTotal) { console.log('โœ… PASS: API returns correct total with shipping'); console.log(` ${final.subtotalPrice.gross.amount} + ${final.shippingPrice.gross.amount} = ${actualTotal}`); } else { console.log('โŒ FAIL: API total does not include shipping'); console.log(` Expected: ${expectedTotal}, Got: ${actualTotal}`); } console.log('\n๐Ÿ” FRONTEND ISSUE ANALYSIS'); console.log('=' .repeat(50)); console.log('The API works correctly. The bug is in the frontend.'); console.log(''); console.log('What should happen:'); console.log(' 1. User selects shipping method โ†’ handleShippingMethodSelect()'); console.log(' 2. Calls checkoutService.updateShippingMethod() โ†’ API updates'); console.log(' 3. Calls refreshCheckout() โ†’ store updates with new checkout'); console.log(' 4. Component re-renders with new checkout.totalPrice'); console.log(''); console.log('Check browser console for:'); console.log(' - [Checkout Debug] logs showing totalPrice values'); console.log(' - Network tab showing the GraphQL mutation/refresh calls'); console.log(' - React DevTools showing if checkout object updates'); } catch (error) { console.error('\nโŒ Test failed:', error.message); process.exit(1); } } runTest();