@@ -608,4 +608,177 @@ describe('🛒 Core Shopping - Cart & Checkout', function() {
608608 }
609609 } ) ;
610610 } ) ;
611+
612+ describe ( '5NF API Network Dependency Failures' , function ( ) {
613+ it ( '5NF should handle cart operations when API returns malformed JSON' , async function ( ) {
614+ await loginUser ( ) ;
615+
616+ await commands . driver . executeScript ( `
617+ // Mock cart API to return malformed JSON
618+ if (window.fetch) {
619+ const originalFetch = window.fetch;
620+ window.fetch = function(url, options) {
621+ if (url.includes('/api/cart') && options && options.method === 'POST') {
622+ return Promise.resolve({
623+ ok: true,
624+ status: 200,
625+ json: () => Promise.resolve({
626+ // Malformed response - missing required cart structure
627+ message: "success",
628+ items: "invalid_structure",
629+ total: null
630+ })
631+ });
632+ }
633+ return originalFetch.apply(this, arguments);
634+ };
635+ }
636+ ` ) ;
637+
638+ await commands . visit ( '/products' ) ;
639+ await commands . wait ( 2000 ) ;
640+
641+ const addButtons = await commands . getAll ( '[data-testid="add-to-cart-button"]' ) ;
642+ if ( addButtons . length > 0 ) {
643+ await addButtons [ 0 ] . click ( ) ;
644+ await commands . wait ( 2000 ) ;
645+
646+ const cartBadge = await commands . getAll ( '[data-testid="cart-badge"], .cart-count' ) ;
647+ expect ( cartBadge . length ) . to . be . greaterThan ( 0 , 'Cart badge should still be present' ) ;
648+
649+ await commands . visit ( '/cart' ) ;
650+ await commands . wait ( 2000 ) ;
651+
652+ const cartItems = await commands . getAll ( '[data-testid="cart-item"], .cart-item' ) ;
653+ expect ( cartItems . length ) . to . be . greaterThan ( 0 , 'Should display cart items despite malformed API response' ) ;
654+
655+ const productNames = await commands . getAll ( 'h3, .product-name, [data-testid="product-name"]' ) ;
656+ expect ( productNames . length ) . to . be . greaterThan ( 0 , 'Product names should be visible' ) ;
657+
658+ const itemPrices = await commands . getAll ( '.price, [data-testid="price"]' ) ;
659+ expect ( itemPrices . length ) . to . be . greaterThan ( 0 , 'Item prices should be displayed' ) ;
660+ } else {
661+ this . skip ( 'No add to cart buttons available' ) ;
662+ }
663+ } ) ;
664+
665+ it ( '5NF should proceed with checkout when order creation API fails' , async function ( ) {
666+ await loginUser ( ) ;
667+
668+ await commands . visit ( '/products' ) ;
669+ const addButtons = await commands . getAll ( '[data-testid="add-to-cart-button"]' ) ;
670+ if ( addButtons . length > 0 ) {
671+ await addButtons [ 0 ] . click ( ) ;
672+ await commands . wait ( 1000 ) ;
673+ }
674+
675+ await commands . driver . executeScript ( `
676+ // Mock order API to return failure
677+ if (window.fetch) {
678+ const originalFetch = window.fetch;
679+ window.fetch = function(url, options) {
680+ if (url.includes('/api/orders') && options && options.method === 'POST') {
681+ return Promise.resolve({
682+ ok: false,
683+ status: 500,
684+ json: () => Promise.resolve({
685+ error: 'Order processing temporarily unavailable',
686+ code: 'ORDER_SERVICE_DOWN'
687+ })
688+ });
689+ }
690+ return originalFetch.apply(this, arguments);
691+ };
692+ }
693+ ` ) ;
694+
695+ await commands . visit ( '/checkout' ) ;
696+ await commands . wait ( 2000 ) ;
697+
698+ const nameFields = await commands . getAll ( 'input[name*="name"], input[name*="firstName"]' ) ;
699+ if ( nameFields . length > 0 ) {
700+ await nameFields [ 0 ] . sendKeys ( 'Test User' ) ;
701+ }
702+
703+ const addressFields = await commands . getAll ( 'input[name*="address"], input[name*="street"]' ) ;
704+ if ( addressFields . length > 0 ) {
705+ await addressFields [ 0 ] . sendKeys ( '123 Main Street' ) ;
706+ }
707+
708+ const cityFields = await commands . getAll ( 'input[name*="city"]' ) ;
709+ if ( cityFields . length > 0 ) {
710+ await cityFields [ 0 ] . sendKeys ( 'Test City' ) ;
711+ }
712+
713+ const zipFields = await commands . getAll ( 'input[name*="zip"], input[name*="postal"]' ) ;
714+ if ( zipFields . length > 0 ) {
715+ await zipFields [ 0 ] . sendKeys ( '12345' ) ;
716+ }
717+
718+ const submitButtons = await commands . getAll ( 'button[type="submit"], button:contains("Place Order")' ) ;
719+ if ( submitButtons . length > 0 ) {
720+ await submitButtons [ 0 ] . click ( ) ;
721+ await commands . wait ( 4000 ) ;
722+
723+ const confirmationPage = await commands . getAll ( '.order-confirmation, .success, .confirmation' ) ;
724+ expect ( confirmationPage . length ) . to . be . greaterThan ( 0 , 'Should show confirmation despite API failure' ) ;
725+
726+ const orderNumber = await commands . getAll ( '.order-number, .reference-number' ) ;
727+ expect ( orderNumber . length ) . to . be . greaterThan ( 0 , 'Should generate order number locally' ) ;
728+
729+ const retryButton = await commands . getAll ( 'button:contains("Retry"), .retry-order' ) ;
730+ expect ( retryButton . length ) . to . be . greaterThan ( 0 , 'Should offer retry option' ) ;
731+ } else {
732+ this . skip ( 'No submit button found in checkout form' ) ;
733+ }
734+ } ) ;
735+
736+ it ( '5NF should display cart total when calculation API is down' , async function ( ) {
737+ await loginUser ( ) ;
738+
739+ await commands . visit ( '/products' ) ;
740+ const addButtons = await commands . getAll ( '[data-testid="add-to-cart-button"]' ) ;
741+ if ( addButtons . length >= 2 ) {
742+ await addButtons [ 0 ] . click ( ) ;
743+ await commands . wait ( 500 ) ;
744+ await addButtons [ 1 ] . click ( ) ;
745+ await commands . wait ( 1000 ) ;
746+ }
747+
748+ await commands . driver . executeScript ( `
749+ // Mock cart total calculation API to fail
750+ if (window.fetch) {
751+ const originalFetch = window.fetch;
752+ window.fetch = function(url, options) {
753+ if (url.includes('/api/cart/total') || url.includes('/api/calculate-total')) {
754+ return Promise.reject(new Error('Calculation service unavailable'));
755+ }
756+ return originalFetch.apply(this, arguments);
757+ };
758+ }
759+ ` ) ;
760+
761+ await commands . visit ( '/cart' ) ;
762+ await commands . wait ( 2000 ) ;
763+
764+ const cartItems = await commands . getAll ( '[data-testid="cart-item"], .cart-item' ) ;
765+ expect ( cartItems . length ) . to . be . greaterThan ( 0 , 'Cart items should be visible' ) ;
766+
767+ const itemPrices = await commands . getAll ( '.item-price, [data-testid="item-price"]' ) ;
768+ expect ( itemPrices . length ) . to . be . greaterThan ( 0 , 'Individual item prices should display' ) ;
769+
770+ const cartTotal = await commands . getAll ( '[data-testid="cart-total"], .cart-total, .total' ) ;
771+ expect ( cartTotal . length ) . to . be . greaterThan ( 0 , 'Cart total should be calculated locally' ) ;
772+
773+ if ( cartTotal . length > 0 ) {
774+ const totalText = await cartTotal [ 0 ] . getText ( ) ;
775+ const totalAmount = parseFloat ( totalText . replace ( / [ ^ 0 - 9 . ] / g, '' ) ) ;
776+ expect ( totalAmount ) . to . be . greaterThan ( 0 , 'Total amount should be greater than zero' ) ;
777+ }
778+
779+ const checkoutButton = await commands . getAll ( 'button:contains("Checkout"), [data-testid="checkout-button"]' ) ;
780+ expect ( checkoutButton . length ) . to . be . greaterThan ( 0 , 'Checkout button should remain enabled' ) ;
781+ expect ( await checkoutButton [ 0 ] . isEnabled ( ) ) . to . be . true ;
782+ } ) ;
783+ } ) ;
611784} ) ;
0 commit comments