"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));

var _bluebird = _interopRequireDefault(require("bluebird"));

var _sfcc = require("./sfcc");

var _types = require("../types");

var _testUtils = require("../utils/test-utils");

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }

if (_testUtils.isIntegrationTest) {
  jest.setTimeout(45000);
}

const userFixture = key => {
  const time = _testUtils.isIntegrationTest ? new Date().getTime() : '';
  return {
    firstName: `test-${key}${time}-firstName`,
    lastName: `test-${key}${time}-lastName`,
    email: `test-${key}${time}@example-foo.com`,
    password: `test-${key}${time}-password`
  };
};

const validCredentials = {
  username: 'mobifyeng@gmail.com',
  password: 'P4sswordP4'
};
const invalidCredentials = {
  username: 'invaliduser@gmail.com',
  password: 'P4sswordP4'
};
const customerInformation = {
  email: 'engineer@mobify.com'
};
const orderAddress = {
  firstName: 'Donald',
  lastName: 'Trump',
  phone: '5555555555',
  addressLine1: '1600 Pennsylvania Ave NW',
  countryCode: 'US',
  stateCode: 'DC',
  city: 'Washington',
  postalCode: '20500'
};
const cartItem = {
  productId: '061492215594',
  quantity: 1
};
const payment = {
  amount: 179.98,
  methodId: 'CREDIT_CARD',
  details: {
    ccv: '123',
    type: 'Visa',
    holderName: 'Donald Trump',
    number: '411111111111112',
    expiryMonth: 1,
    expiryYear: 2028
  }
};
describe(`The SFCC Connector`, () => {
  const makeConnector = () => _sfcc.SalesforceConnector.fromConfig({
    basePath: `https://mobify-tech-prtnr-na03-dw.demandware.net/s/2017refresh/dw/shop/v17_8`,
    defaultHeaders: {
      'x-dw-client-id': '5640cc6b-f5e9-466e-9134-9853e9f9db93'
    }
  });

  describe('Checkout flow', () => {
    const spec0 = test('Registered User', () => {
      return (0, _testUtils.record)(spec0, () => {
        const {
          username,
          password
        } = validCredentials;
        const connector = makeConnector();
        let cart;
        return connector.login(username, password) // 1. Create a new cart
        .then(() => connector.createCart()).then(data => {
          expect((0, _testUtils.propTypeErrors)(_types.Cart, data)).toBeFalsy();
          cart = data;
        }) // 2. Set customer information
        .then(() => connector.setCustomerInformation(cart, customerInformation)).then(data => {
          expect(data.customerInfo.email).toEqual(customerInformation.email);
          cart = data; // Update local cart variable
        }) // 3. Add Cart Item
        .then(() => connector.addCartItem(cart, cartItem)).then(data => {
          expect(data.items).toHaveLength(1);
          expect(data.items[0].productId).toEqual(cartItem.productId);
          expect(data.items[0].quantity).toEqual(cartItem.quantity);
          cart = data; // Update local cart variable
        }) // 4. Update Cart Item
        .then(() => connector.updateCartItem(cart, _objectSpread({}, cart.items[0], {
          quantity: 2
        }))).then(data => {
          expect(data.items).toHaveLength(1);
          expect(data.items[0].productId).toEqual(cartItem.productId);
          expect(data.items[0].quantity).toEqual(2);
          cart = data;
        }) // 5. Remove Cart item
        .then(() => connector.removeCartItem(cart, cart.items[0].id)).then(data => {
          expect(data.items).toHaveLength(0);
          cart = data; // Update local cart variable
        }) // 5b. Re Add Cart Item so order creation doesn't fail
        .then(() => connector.addCartItem(cart, cartItem)).then(data => {
          cart = data; // Update local cart variable
        }) // 6. Set Billing Address
        .then(() => connector.setBillingAddress(cart, orderAddress)).then(data => {
          expect(data.billingAddress).toMatchObject(orderAddress);
          expect((0, _testUtils.propTypeErrors)(_types.OrderAddress, data.billingAddress)).toBeFalsy();
          cart = data; // Update local cart variable
        }) // 7. Set Shipping Address
        .then(() => connector.setShippingAddress(cart, orderAddress)).then(data => {
          expect(data.shippingAddress).toMatchObject(orderAddress);
          expect((0, _testUtils.propTypeErrors)(_types.OrderAddress, data.shippingAddress)).toBeFalsy();
          cart = data; // Update local cart variable
        }) // 8. Get Shipping Methods
        .then(() => connector.getShippingMethods(cart)).then(data => {
          cart.shippingMethods = data;
          expect(data.length).not.toBeLessThan(0);
          expect((0, _testUtils.propTypeErrors)(_types.Cart, cart)).toBeFalsy();
        }) // 9. Set Shipping Method
        .then(() => connector.setShippingMethod(cart, cart.shippingMethods[0])).then(data => {
          expect(data.selectedShippingMethodId).toEqual(cart.shippingMethods[0].id);
          expect((0, _testUtils.propTypeErrors)(_types.Cart, data)).toBeFalsy();
          cart = data; // Update local cart variable
        }) // 9b. Set Shipping Method
        .then(() => {
          return expect(connector.setShippingMethod(cart, 'INVALID_SHIPPING_METHOD_ID')).rejects.toThrow();
        }) // 10. Get Payment Methods
        .then(() => connector.getPaymentMethods(cart)).then(data => {
          expect(data.length).not.toBeLessThan(0);
          cart.paymentMethods = data;
          expect((0, _testUtils.propTypeErrors)(_types.Cart, cart)).toBeFalsy();
        }) // 11. Set Payment
        .then(() => connector.setPayment(cart, payment)).then(data => {
          expect(data.payments).toHaveLength(1);
          expect((0, _testUtils.propTypeErrors)(_types.Payment, data.payments[0])).toBeFalsy();
          expect(data.payments[0].methodId).toEqual(payment.methodId);
          cart = data; // Update local cart variable
        }) // 12. Set Payment (Updated Values)
        .then(() => connector.setPayment(cart, _objectSpread({}, cart.payments[0], {
          amount: 200
        }))).then(data => {
          expect(data.payments).toHaveLength(1);
          expect((0, _testUtils.propTypeErrors)(_types.Payment, data.payments[0])).toBeFalsy();
          expect(data.payments[0].amount).toEqual(200);
          cart = data; // Update local cart variable
        }) // 13. Create Order
        .then(() => connector.createOrder(cart)).then(data => {
          expect((0, _testUtils.propTypeErrors)(_types.Order, data)).toBeFalsy();
          return data;
        }) // 14. Verify order by getting it.
        .then(order => _bluebird.default.all([_bluebird.default.resolve(order), connector.getOrders([order.id])])).then(([order, orders]) => {
          expect(orders.count).toEqual(1);
          expect(orders.total).toEqual(1);
          expect(orders.data[0].id).toEqual(order.id);
          return null;
        }) // 15. Finally Delete the cart (also delete cart on error)
        .then(() => connector.deleteCart(cart.id)).catch(e => {
          // Ensure we delete the cart so our tests don't fail on cart creation next time.
          if (cart) {
            connector.deleteCart(cart.id);
          } // Rethrow the error so that the tests fail.


          throw e;
        });
      });
    });
  });
  describe('Searching for products', () => {
    const sorts = [undefined, {
      query: 'name-ascending',
      key: 'name',
      compare: (a, b) => a.localeCompare(b)
    }, {
      query: 'price-low-to-high',
      key: 'price',
      compare: (a, b) => a - b
    }];
    sorts.forEach(sortOpts => {
      const spec1 = test(`with a sort order [sort: ${sortOpts && sortOpts.query}]`, () => {
        return (0, _testUtils.record)(spec1, () => {
          const connector = makeConnector();

          const query = _objectSpread({
            filters: {
              categoryId: 'root'
            }
          }, sortOpts && {
            sort: sortOpts.query
          });

          return connector.searchProducts(query).then(data => {
            expect((0, _testUtils.propTypeErrors)(_types.ProductSearch, data)).toBeFalsy();

            if (sortOpts) {
              const {
                key,
                compare
              } = sortOpts; // eslint-disable-next-line max-nested-callbacks

              const values = data.results.map(item => item[key]);
              const expected = values.slice().sort(compare);
              expect(values).toEqual(expected);
            }
          });
        });
      });
    });
    const counts = [2, 8];
    counts.forEach(count => {
      const spec2 = test(`with a max count (count: ${count})`, () => {
        return (0, _testUtils.record)(spec2, () => {
          const connector = makeConnector();
          const query = {
            filters: {
              categoryId: 'root'
            },
            count
          };
          return connector.searchProducts(query).then(data => {
            expect((0, _testUtils.propTypeErrors)(_types.ProductSearch, data)).toBeFalsy();
            expect(data.results.length).toEqual(count);
          });
        });
      });
    });
  });
  describe('Getting an order', () => {
    const spec3 = test('with an invalid id should reject', () => {
      return (0, _testUtils.record)(spec3, () => {
        const connector = makeConnector();
        return expect(connector.getOrder('1')).rejects.toThrow();
      });
    });
  });
  describe('Getting orders', () => {
    const spec4 = test('with invalid ids should resolve with a OrderList instance with empty data', () => {
      return (0, _testUtils.record)(spec4, () => {
        const connector = makeConnector();
        const invalidIds = ['1', '2'];
        return connector.getOrders(invalidIds).then(result => {
          expect((0, _testUtils.propTypeErrors)(_types.OrderList, result)).toBeFalsy();
          expect(result.data.length).toEqual(0);
        });
      });
    });
  });
  describe('Getting a product', () => {
    const spec5 = test('with a valid id should resolve with a Product instance', () => {
      return (0, _testUtils.record)(spec5, () => {
        const connector = makeConnector();
        return connector.getProduct('25564777').then(data => {
          expect((0, _testUtils.propTypeErrors)(_types.Product, data)).toBeFalsy();
        });
      });
    });
    const spec6 = test('with an invalid id should reject', () => {
      return (0, _testUtils.record)(spec6, () => {
        const connector = makeConnector();
        return expect(connector.getProduct('1')).rejects.toThrow();
      });
    });
  });
  describe('Getting products', () => {
    const spec7 = test('with valid ids should resolve with a ProductList instance with populated data', () => {
      return (0, _testUtils.record)(spec7, () => {
        const connector = makeConnector();
        const validIds = ['25564777', '25518724'];
        return connector.getProducts(validIds).then(result => {
          expect((0, _testUtils.propTypeErrors)(_types.ProductList, result)).toBeFalsy();
          expect(result.data.length).toEqual(validIds.length);
        });
      });
    });
    const spec8 = test('with invalid ids should resolve with a ProductList instance with empty data', () => {
      return (0, _testUtils.record)(spec8, () => {
        const connector = makeConnector();
        const invalidIds = ['1', '2'];
        return connector.getProducts(invalidIds).then(result => {
          expect((0, _testUtils.propTypeErrors)(_types.ProductList, result)).toBeFalsy();
          expect(result.data.length).toEqual(0);
        });
      });
    });
    const spec9 = test('with a mix of valid and invalid ids should resolve with a ProductList instance with partial data', () => {
      return (0, _testUtils.record)(spec9, () => {
        const connector = makeConnector();
        const validIds = ['25564777', '25518724'];
        const invalidIds = ['1', '2'];
        return connector.getProducts([...validIds, ...invalidIds]).then(result => {
          expect((0, _testUtils.propTypeErrors)(_types.ProductList, result)).toBeFalsy();
          expect(result.data.length).toEqual(validIds.length);
          expect(result.total).toEqual(validIds.length);
          expect(result.count).toEqual(validIds.length);
        });
      });
    });
  });
  const spec10 = test('Getting a Store', () => {
    return (0, _testUtils.record)(spec10, () => {
      const connector = makeConnector();
      return connector.getStore('the_academy_of_magical_arts').then(data => {
        expect((0, _testUtils.propTypeErrors)(_types.Store, data)).toBeFalsy();
      });
    });
  });
  describe('Searching for Stores', () => {
    // searchStoreRequest is using default values for start, count if not provided
    const spec11 = test('Only longitude provided, missing latitude', () => {
      return (0, _testUtils.record)(spec11, () => {
        const connector = makeConnector();
        const searchStoreRequest = {
          latlon: {
            longitude: -118.225606
          }
        };
        expect(() => {
          connector.searchStores(searchStoreRequest);
        }).toThrowError('Provide Latitude and Longitude coordinates');
      });
    });
    const spec12 = test('Only latitude provided, missing longitude', () => {
      return (0, _testUtils.record)(spec12, () => {
        const connector = makeConnector();
        const searchStoreRequest = {
          latlon: {
            latitude: 34.014613
          }
        };
        expect(() => {
          connector.searchStores(searchStoreRequest);
        }).toThrowError('Provide Latitude and Longitude coordinates');
      });
    });
    const spec13 = test('Valid latitude and longitude provided', () => {
      return (0, _testUtils.record)(spec13, () => {
        const connector = makeConnector();
        const searchStoreRequest = {
          latlon: {
            latitude: 34.014613,
            longitude: -118.225606
          }
        };
        return connector.searchStores(searchStoreRequest).then(data => {
          expect(data.stores).not.toHaveLength(0);
          expect(data.stores).toHaveLength(data.count);
          expect((0, _testUtils.propTypeErrors)(_types.StoreSearchResult, data)).toBeFalsy();
        });
      });
    });
    const spec14 = test('0.0, 1.0 geo-coordinates allowed', () => {
      return (0, _testUtils.record)(spec14, () => {
        const connector = makeConnector();
        const searchStoreRequest = {
          latlon: {
            latitude: 0.0,
            longitude: 0.0
          }
        };
        return connector.searchStores(searchStoreRequest).then(data => {
          expect(data.stores).not.toHaveLength(0);
          expect(data.stores).toHaveLength(data.count);
          expect((0, _testUtils.propTypeErrors)(_types.StoreSearchResult, data)).toBeFalsy();
        });
      });
    });
  });
  describe('Get Product Categories', () => {
    const spec15 = test(`getCategory should resolve to a Category instance`, () => {
      return (0, _testUtils.record)(spec15, () => {
        const connector = makeConnector();
        const category = 'root';
        return connector.getCategory(category, {
          levels: 2
        }).then(data => {
          expect((0, _testUtils.propTypeErrors)(_types.Category, data)).toBeFalsy();
          expect(data.id).toEqual(category);
        });
      });
    });
    const spec16 = test(`getCategories should resolve to a CategoryList instance`, () => {
      return (0, _testUtils.record)(spec16, () => {
        const connector = makeConnector();
        const categories = ['mens', 'womens'];
        return connector.getCategories(categories, {
          levels: 2
        }).then(result => {
          expect((0, _testUtils.propTypeErrors)(_types.CategoryList, result)).toBeFalsy();
          expect(result.count).toEqual(2);
          expect(result.data[0].id).toEqual(categories[0]);
          expect(result.data[1].id).toEqual(categories[1]);
        });
      });
    });
  });
  describe('Customer Authorization', () => {
    const spec17 = test(`authorizing a valid customer should return said customer`, () => {
      return (0, _testUtils.record)(spec17, () => {
        const connector = makeConnector();
        const {
          username,
          password
        } = validCredentials;
        return connector.login(username, password).then(customer => {
          expect((0, _testUtils.propTypeErrors)(_types.Customer, customer)).toBeFalsy();
          expect(customer.email).toEqual(username);
        });
      });
    });
    const spec18 = test(`authorizing an invalid customer should throw`, () => {
      return (0, _testUtils.record)(spec18, () => {
        const connector = makeConnector();
        const {
          username,
          password
        } = invalidCredentials;
        return expect(connector.login(username, password)).rejects.toThrow();
      });
    });
    const spec19 = test(`authorizing as a guest should return a customer`, () => {
      return (0, _testUtils.record)(spec19, () => {
        const connector = makeConnector();
        return connector.login().then(customer => {
          expect((0, _testUtils.propTypeErrors)(_types.Customer, customer)).toBeFalsy();
        });
      });
    });
  });
  describe('Account management', () => {
    const spec20 = test('Should support creating a new user account', () => {
      return (0, _testUtils.record)(spec20, () => {
        const data = userFixture('creating-accounts');
        const connector = makeConnector();
        return _bluebird.default.resolve().then(() => connector.login()).then(() => connector.registerCustomer(data)).then(customer => {
          expect((0, _testUtils.propTypeErrors)(_types.Customer, customer)).toBeFalsy();
          return connector.getCustomer(customer.id); // Protected endpoint
        }).then(customer => {
          expect((0, _testUtils.propTypeErrors)(_types.Customer, customer)).toBeFalsy();
          Object.keys(data).filter(k => k !== 'password').forEach(k => expect(data[k]).toEqual(customer[k]));
          expect(customer).not.toHaveProperty('password');
        });
      });
    });
  });
  describe('Getting a customer', () => {
    const spec21 = test(`getting any customer without logging in first should throw`, () => {
      return (0, _testUtils.record)(spec21, () => {
        const connector = makeConnector();
        const customerId = 'bcyyeR5KK0gGKjf2uZHarcK93Z';
        const error = {
          arguments: undefined,
          message: "Unauthorized request: The 'Authorization' header is missing.",
          type: 'AuthorizationHeaderMissingException'
        };
        return expect(connector.getCustomer(customerId)).rejects.toEqual(error);
      });
    });
    const spec22 = test(`getting the logged in customer after logging in should return the customer`, () => {
      return (0, _testUtils.record)(spec22, () => {
        const connector = makeConnector();
        const {
          username,
          password
        } = validCredentials;
        return connector.login(username, password).then(customer => {
          return connector.getCustomer(customer.id);
        }).then(customer => {
          expect((0, _testUtils.propTypeErrors)(_types.Category, customer)).toBeFalsy();
          expect(customer.email).toEqual(username);
        });
      });
    });
    const spec23 = test(`can set default headers to maintain authentication tokens between connectors`, () => {
      return (0, _testUtils.record)(spec23, () => {
        const connector1 = makeConnector();
        const connector2 = makeConnector();
        const {
          username,
          password
        } = validCredentials;
        return connector1.login(username, password).then(customer => {
          connector2.setDefaultHeaders(connector1.getDefaultHeaders());
          return connector2.getCustomer(customer.id);
        }).then(customer => {
          expect((0, _testUtils.propTypeErrors)(_types.Category, customer)).toBeFalsy();
          expect(customer.email).toEqual(username);
        });
      });
    });
  });
  describe('Coupon support', () => {
    const testBody = connector => connector.createCart().then(cart => connector.addCartItem(cart, cartItem)).then(cart => connector.addCouponEntry(cart, 'MAGIC')).then(cart => {
      expect((0, _testUtils.propTypeErrors)(_types.Cart, cart)).toBeFalsy();
      expect(cart.items.length).toEqual(1);
      expect(cart.couponEntries[0].code).toEqual('MAGIC');
      expect(cart.couponEntries[0].id).toBeDefined();
      expect(cart.discounts).toBeLessThan(0);
      return cart;
    }).then(cart => connector.removeCouponEntry(cart, cart.couponEntries[0].id)).then(cart => {
      expect((0, _testUtils.propTypeErrors)(_types.Cart, cart)).toBeFalsy();
      expect(cart.items.length).toEqual(1);
      expect(cart.couponEntries).toEqual([]);
      expect(cart.discounts).toBe(0);
      return cart;
    }).then(cart => {
      return expect(connector.addCouponEntry(cart, 'INVALID_COUPON')).rejects.toThrow().then(() => cart);
    }).then(cart => {
      return expect(connector.removeCouponEntry(cart, 'INVALID_COUPONENTRY_ID')).rejects.toThrow().then(() => cart);
    });

    const spec1 = test('Should allow a registered user to add/remove a CouponEntry to/from their cart', () => {
      return (0, _testUtils.record)(spec1, () => {
        const userCreateFixture = userFixture('coupons-1');
        const connector = makeConnector();
        return connector.login().then(() => connector.registerCustomer(userCreateFixture)).then(() => testBody(connector));
      });
    });
    const spec2 = test('Should allow a guest user to add/remove a CouponEntry to/from their cart', () => {
      return (0, _testUtils.record)(spec2, () => {
        const connector = makeConnector();
        return connector.login().then(() => testBody(connector));
      });
    });
  });
});