From 59e022bd94428b02901bd3ebf732fedd395bcec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Sch=C3=A4chinger?= Date: Sun, 25 Feb 2018 00:42:08 +0100 Subject: [PATCH 1/3] extracted 1C4A service from main class - modified tests - added stubs and reset functions --- lib/Internetmarke.js | 172 +++++------------------ lib/Order/Order.js | 19 ++- lib/Service/Soap/OneClickForApp.js | 154 ++++++++++++++++++++ lib/Service/Soap/Soap.js | 63 +++++++++ lib/constants.js | 4 +- test/Internetmarke.test.js | 131 +++++++++-------- test/Order/Order.test.js | 14 +- test/Service/Soap/OneClickForApp.test.js | 116 +++++++++++++++ test/Service/Soap/Soap.test.js | 35 +++++ test/stub/partner/index.js | 14 ++ test/stub/reset.js | 8 ++ test/stub/soapClient/index.js | 13 ++ yarn.lock | 2 +- 13 files changed, 531 insertions(+), 214 deletions(-) create mode 100644 lib/Service/Soap/OneClickForApp.js create mode 100644 lib/Service/Soap/Soap.js create mode 100644 test/Service/Soap/OneClickForApp.test.js create mode 100644 test/Service/Soap/Soap.test.js create mode 100644 test/stub/partner/index.js create mode 100644 test/stub/reset.js diff --git a/lib/Internetmarke.js b/lib/Internetmarke.js index 4b9b322..92e15dc 100644 --- a/lib/Internetmarke.js +++ b/lib/Internetmarke.js @@ -14,12 +14,12 @@ const errors = require('./errors'), Order = require('./Order'), Partner = require('./Partner'), User = require('./User'), + OneClickForAppService = require('./Service/Soap/OneClickForApp'), AddressFactory = require('./Address/AddressFactory'), layoutZoneHelper = require('./helper/LayoutZones'), { LAYOUT_ZONES, OUTPUT_FORMATS, WSDL } = require('./constants'); const _PARTNER = Symbol('partner'), - _SOAP = Symbol('soap'), _USER = Symbol('user'); class Internetmarke { @@ -31,9 +31,8 @@ class Internetmarke { * every request. */ constructor(partner) { + /** @type {(Partner)} */ this[_PARTNER] = partner; - /** @type {(Client|null)} */ - this[_SOAP] = null; /** @type {(User|null)} */ this[_USER] = null; @@ -41,6 +40,8 @@ class Internetmarke { this._gallery = new Gallery(); /** @type {Order} */ this._order = new Order(); + /** @type {OneClickForAppService} */ + this._1C4AService = new OneClickForAppService({ partner }); /** @type {Object} */ this._config = { outputFormat: OUTPUT_FORMATS.PNG, @@ -49,31 +50,17 @@ class Internetmarke { } /** + * Authorize an user to the api for check it's validity. + * * @param {User} user - the user object that should be authenticated. * @returns {Promise.} */ authenticateUser(user) { this[_USER] = user; - return this._getSoapClient() - .then(client => { - return client.authenticateUserAsync(this[_USER].getCredentials()) - }) - .then(response => { - if (response) { - this[_USER].setToken(response.userToken) - .setBalance(response.walletBalance) - .setTerms(response.showTermAndCondition) - .setInfoMessage(response.infoMessage || null); - } - + return this._1C4AService.authenticateUser(this[_USER]) + .then(success => { return this; - }) - .catch(reason => { - // TODO: check error type and message - // throw error error.internetmarke.invalidUserCredentials - - // OperationalError {cause: Error: ns2:Server: Unknown user or invalid passwor…, isOperational: true, root: Object, response: IncomingMessage, body: " { - checkoutOrderId = orderId; - - return this._getSoapClient(); - }) - .then(client => { - let order = this._order.checkout({ orderId: checkoutOrderId }); - if (!order) { - throw new Error(errors.internetmarke.shoppingcartEmpty); - } - if (order.total > this[_USER].getBalance()) { - throw new Error(errors.internetmarke.walletEmpty); - } + let order = this._order.getCheckout({ orderId }); - order = Object.assign({ - userToken: this[_USER].getToken() - }, order); - - return client[method](order); - }) - .then(response => { - this[_USER].setBalance(response.walletBallance || response.walletBalance); - - const result = { - orderId: response.shoppingCart.shopOrderId, - link: response.link, - vouchers: [] - }; - - response.shoppingCart.voucherList.voucher.forEach(voucher => { - const data = { id: voucher.voucherId }; - if (voucher.trackId) { - data.trackingCode = voucher.trackId; - } - result.vouchers.push(data); - }); + if (order.total > this[_USER].getBalance()) { + throw new Error(errors.internetmarke.walletEmpty); + } - return result; - }); + return this._1C4AService.checkout({ + order, + user: this[_USER], + outputFormat: this._config.outputFormat + }); } /** @@ -166,36 +117,12 @@ class Internetmarke { * @returns {Promise.} - The url of the preview voucher. */ getVoucherPreview({ productCode, imageId = null }) { - const method = `retrievePreviewVoucher${this._config.outputFormat}Async`; - - return this._getSoapClient() - .then(client => { - return client[method]({ - productCode, - voucherLayout - }) - }) - .then(response => { - return { - link: response.link - }; - }); - } - - /** - * Specify the layout of the voucher from the LAYOUT_ZONES enum. - * - * @param {string} voucherLayout - The layout that should be used. - * @returns {boolean} - */ - setDefaultVoucherLayout(voucherLayout) { - const valid = layoutZoneHelper.validate(voucherLayout); - - if (valid) { - this._config.voucherLayout = voucherLayout; - } - - return valid; + return this._1C4AService.previewVoucher({ + productCode, + imageId, + voucherLayout: this._config.voucherLayout, + outputFormat: this._config.outputFormat + }); } /** @@ -204,57 +131,24 @@ class Internetmarke { * @returns {Promise.} */ updateGalleries() { - return this._gallery.updateGalleries(this._getSoapClient()); + return Promise.resolve(this); + //return this._gallery.updateGalleries(this._getSoapClient()); } /** - * Helper method to retrieve the soap client for every api call. + * Specify the layout of the voucher from the LAYOUT_ZONES enum. * - * @returns {Promise.} + * @param {string} voucherLayout - The layout that should be used. + * @returns {boolean} */ - _getSoapClient() { - let promise = null; - - if (this[_SOAP]) { - promise = Promise.resolve(this[_SOAP]); - } - else { - promise = new Promise(resolve => { - soap.createClientAsync(WSDL, { - disableCache: true - }) - .then(client => { - this[_SOAP] = client; - - if (this[_PARTNER]) { - client.addSoapHeader(this[_PARTNER].getSoapHeaders()); - } + setDefaultVoucherLayout(voucherLayout) { + const valid = layoutZoneHelper.validate(voucherLayout); - resolve(client); - }); - }); + if (valid) { + this._config.voucherLayout = voucherLayout; } - return promise; - } - - /** - * Create a globally unique order id from the api. - * - * @returns {Promise.number} - */ - _generateOrderId() { - return this._getSoapClient() - .then(client => { - return client.createShopOrderIdAsync({ - userToken: this[_USER].getToken() - }) - }) - .then(response => { - this[_USER].addOrderId(response.shopOrderId); - - return response.shopOrderId; - }); + return valid; } } diff --git a/lib/Order/Order.js b/lib/Order/Order.js index edecc15..1a543d9 100644 --- a/lib/Order/Order.js +++ b/lib/Order/Order.js @@ -6,7 +6,8 @@ 'use strict'; -const Position = require('./Position'); +const Position = require('./Position'), + errors = require('../errors'); class Order { /** @@ -55,11 +56,11 @@ class Order { * should be generated and attached after checkout. * @param {number} [metadata.createShippingList] - Determine whether a shipping * list should be created: 0: no, 1: without addresses, 2: with addresses - * @returns {(Object|boolean)} + * @returns {Object} */ checkout({ orderId = this.orderId, createManifest = true, createShippingList = 2 } = {}) { - if (!orderId || !this._positions.length) { - return false; + if (!this._positions.length) { + throw new Error(errors.internetmarke.shoppingcartEmpty); } const positions = []; @@ -67,18 +68,22 @@ class Order { this._positions.forEach(pos => { total += pos.getPrice(); - positions.push(pos.getPosition()); }); - return { - shopOrderId: orderId, + const order = { //ppl: 33, positions, total, createManifest, createShippingList }; + + if (orderId) { + order.shopOrderId = orderId; + } + + return order; } } diff --git a/lib/Service/Soap/OneClickForApp.js b/lib/Service/Soap/OneClickForApp.js new file mode 100644 index 0000000..5e5c6bc --- /dev/null +++ b/lib/Service/Soap/OneClickForApp.js @@ -0,0 +1,154 @@ +/** + * internetmarke + * Copyright (c) 2018 Manuel Schächinger + * MIT Lisenced + */ + +'use strict'; + +const SoapService = require('./Soap'), + User = require('../../User'), + { WSDL } = require('../../constants'); + +class OneClickForAppService extends SoapService { + /** + * Create an instance of the 1C4A soap webservice. + * + * @constructor + * @param {Object} config + * @param {Partner} config.partner - The partner object that identifies the + * application for use with the api. + * @param {User} config.user - The user object with the session token. + */ + constructor({ partner }) { + super({ wsdl: WSDL.OneClickForApp }); + + /** @type {Partner} */ + this._partner = partner; + } + + /** + * Authorize an user to the api for check it's validity. + * + * @param {User} user - the user object that should be authenticated. + * @returns {Promise.} + */ + authenticateUser(user) { + this._user = user; + + return this._getSoapClient() + .then(client => { + return client.authenticateUserAsync(this._user.getCredentials()) + }) + .then(response => { + if (response) { + this._user.setToken(response.userToken) + .setBalance(response.walletBalance) + .setTerms(response.showTermAndCondition) + .setInfoMessage(response.infoMessage || null); + } + + return !!response; + }); + } + + /** + * Generates a preview what the voucher will look like. + * + * @param {Object} config + * @param {number} config.productCode - The id of the product that should be + * previewed. + * @param {string} config.voucherLayout - The layout of the voucher. + * @param {string} config.outputFormat - The format the voucher should have. + * @returns {Promise.} + */ + previewVoucher({ productCode, voucherLayout, outputFormat }) { + const method = `retrievePreviewVoucher${outputFormat}Async`; + + return this._getSoapClient() + .then(client => { + return client[method]({ + productCode, + voucherLayout + }); + }) + .then(response => { + return { + link: response.link + }; + }); + } + + /** + * Performs a checkout and retrieves the ordered vouchers. + * + * @param {Object} data + * @param {Object} data.order - The order information that hold the data about + * the vouchers. + * @param {string} data.outputFormat - The format the voucher should have. + * @returns {Promise.} + */ + checkout({ order, outputFormat }) { + return this._getSoapClient() + .then(client => { + const method = `checkoutShoppingCart${outputFormat}Async`; + + order = Object.assign({ + userToken: this._user.getToken() + }, order); + + return client[method](order); + }) + .then(response => { + this._user.setBalance(response.walletBallance || response.walletBalance); + + const result = { + orderId: response.shoppingCart.shopOrderId, + link: response.link, + vouchers: [] + }; + + response.shoppingCart.voucherList.voucher.forEach(voucher => { + const data = { id: voucher.voucherId }; + if (voucher.trackId) { + data.trackingCode = voucher.trackId; + } + result.vouchers.push(data); + }); + + return result; + }); + } + + /** + * Create a globally unique order id from the api. + * + * @returns {Promise.} + */ + generateOrderId() { + return this._getSoapClient() + .then(client => { + return client.createShopOrderIdAsync({ + userToken: this._user.getToken() + }) + }) + .then(response => { + this._user.addOrderId(response.shopOrderId); + + return response.shopOrderId; + }); + } + + /** + * Initialize the soap client with the partner information headers. + * + * @param {Client} client - The soap client object. + */ + _initClient(client) { + if (this._partner) { + client.addSoapHeader(this._partner.getSoapHeaders()); + } + } +} + +module.exports = OneClickForAppService; diff --git a/lib/Service/Soap/Soap.js b/lib/Service/Soap/Soap.js new file mode 100644 index 0000000..fa2c24b --- /dev/null +++ b/lib/Service/Soap/Soap.js @@ -0,0 +1,63 @@ +/** + * internetmarke + * Copyright (c) 2018 Manuel Schächinger + * MIT Lisenced + */ + +'use strict'; + +const soap = require('soap'); + +class SoapService { + /** + * Creates a new soap service instance. + * + * @constructor + * @param {Object} config + * @param {string} config.wsdl - The wsdl url that desclares the service. + * @param {User} config.user - The user object to retrieve the session token. + */ + constructor({ wsdl, user }) { + /** @type {string} */ + this._wsdl = wsdl; + /** @type{(User|null)} */ + this._user = user; + + this._soapClient = null; + } + + /** + * Helper method to retrieve the soap client for every api call. + * + * @returns {Promise.} + */ + _getSoapClient() { + let promise = null; + + if (this._soapClient) { + promise = Promise.resolve(this._soapClient); + } + else { + promise = soap.createClientAsync(this._wsdl, { + disableCache: true + }) + .then(client => { + this._soapClient = client; + this._initClient(client); + + return client; + }); + } + + return promise; + } + + /** + * Retrieve the client object in every case. + * + * @param {Client} client - The client object that links to the web serice. + */ + _initClient(client) {} +} + +module.exports = SoapService; diff --git a/lib/constants.js b/lib/constants.js index 999d5cf..3aa098b 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -15,5 +15,7 @@ module.exports = { FRANKING: 'FrankingZone', ADDRESS: 'AddressZone' }, - WSDL: 'https://internetmarke.deutschepost.de/OneClickForAppV3/OneClickForAppServiceV3?wsdl' + WSDL: { + OneClickForApp: 'https://internetmarke.deutschepost.de/OneClickForAppV3/OneClickForAppServiceV3?wsdl' + } }; diff --git a/test/Internetmarke.test.js b/test/Internetmarke.test.js index d267c11..2406280 100644 --- a/test/Internetmarke.test.js +++ b/test/Internetmarke.test.js @@ -2,82 +2,99 @@ const Internetmarke = require('../'), errors = require('../lib/errors'), { LAYOUT_ZONES } = require('../lib/constants'); -const CLIENT_STUB = require('./stub/soapClient'), - USER_STUB = require('./stub/user'); +const PARTNER_STUB = require('./stub/partner'), + USER_STUB = require('./stub/user'), + reset = require('./stub/reset'); describe('Internetmarke', () => { - const PARTNER = { - getSoapHeaders: sinon.stub().returns({}) + const SERVICE_STUB = { + authenticateUser: sinon.stub().returns(Promise.resolve(true)), + checkout: sinon.stub().returns(Promise.resolve()), + previewVoucher: sinon.stub().returns(Promise.resolve()) + }; + const ORDER_STUB = { + addPosition: sinon.stub(), + getCheckout: sinon.stub().returns({ total: 145 }) }; - describe('API Auth', () => { - it('should connect to service', (done) => { - const internetmarke = new Internetmarke(PARTNER); - internetmarke._getSoapClient() - .then(client => { - client.should.be.an.Object(); - // TODO: switch to client stub properties - client.should.have.keys( - 'authenticateUserAsync', 'createShopOrderIdAsync', - 'retrievePreviewVoucherPNGAsync', 'retrievePreviewVoucherPDFAsync', - 'checkoutShoppingCartPNGAsync', 'checkoutShoppingCartPDFAsync' - ); + /** @type {Internetmarke} */ + let internetmarke = null; - done(); - }); - }).timeout(5000); + beforeEach(() => { + internetmarke = new Internetmarke(PARTNER_STUB.partner); + internetmarke._1C4AService = SERVICE_STUB; + internetmarke._order = ORDER_STUB; + }); - it('should save user data after authentication', (done) => { - const internetmarke = new Internetmarke(PARTNER); - internetmarke._getSoapClient = sinon.stub().returns( - Promise.resolve(CLIENT_STUB.client) - ); - internetmarke._generateOrderId = sinon.stub().returns(Promise.resolve(1234)); + afterEach(() => { + reset(PARTNER_STUB.partner); + reset(USER_STUB.user); + reset(SERVICE_STUB); + reset(ORDER_STUB); + }); + describe('1C4A', () => { + it('should call service for user authentication', done => { internetmarke.authenticateUser(USER_STUB.user) .then(() => { - const response = CLIENT_STUB.response.authenticateUserAsync; - USER_STUB.user.getCredentials.calledOnce.should.be.true(); - USER_STUB.user.getToken().should.equal(response.userToken); - USER_STUB.user.getBalance().should.equal(response.walletBalance); - USER_STUB.user.getTerms().should.equal(response.showTermAndCondition); - should.not.exist(USER_STUB.user.getInfoMessage()); + SERVICE_STUB.authenticateUser.calledOnce.should.be.true(); + done(); - }) - .catch(e => { - should.not.exist(e); }); }); - }); - describe('Order vouchers', () => { - it('should change to valid voucher layouts', () => { - const internetmarke = new Internetmarke(PARTNER); - - internetmarke.setDefaultVoucherLayout(LAYOUT_ZONES.FRANKING); - internetmarke._config.voucherLayout.should.equal(LAYOUT_ZONES.FRANKING); + it('should call service for voucher preview', done => { + internetmarke.getVoucherPreview({}) + .then(() => { + SERVICE_STUB.previewVoucher.calledOnce.should.be.true(); - internetmarke.setDefaultVoucherLayout('INVALID_ZONE'); - internetmarke._config.voucherLayout.should.equal(LAYOUT_ZONES.FRANKING); + done(); + }); }); - it ('should add a voucher to the order', () => { - const VOUCHER = { - productCode: 1, - price: 70, - voucherLayout: 'FrankingZone' - }; + describe('Order Management', () => { + it('should add a voucher to the order', () => { + const VOUCHER = { + productCode: 1, + price: 70, + voucherLayout: LAYOUT_ZONES.FRANKING + }; + + internetmarke.orderVoucher(VOUCHER); + ORDER_STUB.addPosition.calledOnce.should.be.true(); + ORDER_STUB.addPosition.args[0][0].should.containEql(VOUCHER); + }); + + it('should not checkout if wallet is empty', () => { + internetmarke.authenticateUser(USER_STUB.user); + (() => { + internetmarke.checkout(); + }).should.throw(errors.internetmarke.walletEmpty); + }); - const internetmarke = new Internetmarke(PARTNER); + it('should call service for checkout', done => { + const getBalance = USER_STUB.user.getBalance; + USER_STUB.user.getBalance = sinon.stub().returns(1000000); - const order = { - addPosition: sinon.stub() - }; - internetmarke._order = order; + internetmarke.authenticateUser(USER_STUB.user); + internetmarke.checkout() + .then(() => { + SERVICE_STUB.checkout.calledOnce.should.be.true(); - internetmarke.orderVoucher(VOUCHER); - order.addPosition.calledOnce.should.be.true(); - order.addPosition.args[0][0].should.containEql(VOUCHER); + USER_STUB.user.getBalance = getBalance; + done(); + }); + }); }); }); + + it('should validate voucher layouts', () => { + internetmarke.setDefaultVoucherLayout(LAYOUT_ZONES.FRANKING) + .should.be.true(); + internetmarke._config.voucherLayout.should.equal(LAYOUT_ZONES.FRANKING); + + internetmarke.setDefaultVoucherLayout('INVALID_ZONE') + .should.be.false(); + internetmarke._config.voucherLayout.should.equal(LAYOUT_ZONES.FRANKING); + }); }); diff --git a/test/Order/Order.test.js b/test/Order/Order.test.js index 8e9b5ee..1700cee 100644 --- a/test/Order/Order.test.js +++ b/test/Order/Order.test.js @@ -1,4 +1,5 @@ -const Order = require('../../lib/Order'); +const Order = require('../../lib/Order'), + errors = require('../../lib/errors'); const TEST_DATA = require('./Order.data.json'); @@ -22,19 +23,14 @@ describe('Order', () => { }); }); - it('should not checkout without an order id', () => { - const order = new Order(); - - order.checkout().should.be.false(); - }); - it('should not checkout without any positions', () => { const order = new Order(); - order.checkout({ orderId: ORDER_ID }).should.be.false(); + (() => { + order.checkout({ orderId: ORDER_ID }); + }).should.throw(errors.internetmarke.shoppingcartEmpty) }); - it('should checkout with a summary', () => { const order = new Order(); diff --git a/test/Service/Soap/OneClickForApp.test.js b/test/Service/Soap/OneClickForApp.test.js new file mode 100644 index 0000000..5e3957e --- /dev/null +++ b/test/Service/Soap/OneClickForApp.test.js @@ -0,0 +1,116 @@ +const OneClickForAppService = require('../../../lib/Service/Soap/OneClickForApp'), + CLIENT_STUB = require('../../stub/soapClient'), + PARTNER_STUB = require('../../stub/partner'), + USER_STUB = require('../../stub/user'), + { WSDL, LAYOUT_ZONES, OUTPUT_FORMATS } = require('../../../lib/constants'), + reset = require('../../stub/reset'); + + +/** @type {OneClickForAppService} */ +let service = null; + +describe('1C4A Service', () => { + beforeEach(() => { + service = new OneClickForAppService({ partner: PARTNER_STUB.partner }); + service._getSoapClient = sinon.stub().returns( + Promise.resolve(CLIENT_STUB.client) + ); + service._user = USER_STUB.user; + }); + + afterEach(() => { + reset(CLIENT_STUB.client); + reset(PARTNER_STUB.partner); + reset(USER_STUB.user); + }); + + describe('authenticateUser', () => { + it('should save user data after authentication', done => { + service.authenticateUser(USER_STUB.user) + .then(success => { + success.should.be.true(); + CLIENT_STUB.client.authenticateUserAsync.calledOnce.should.be.true(); + + const response = CLIENT_STUB.response.authenticateUserAsync; + USER_STUB.user.getCredentials.calledOnce.should.be.true(); + USER_STUB.user.getToken().should.equal(response.userToken); + USER_STUB.user.getBalance().should.equal(response.walletBalance); + USER_STUB.user.getTerms().should.equal(response.showTermAndCondition); + should.not.exist(USER_STUB.user.getInfoMessage()); + + done(); + }); + }); + }); + + describe('previewVoucher', () => { + it('should call the PNG method', done => { + service.previewVoucher({ + productCode: 1, + voucherLayout: LAYOUT_ZONES.ADDRESS, + outputFormat: OUTPUT_FORMATS.PNG + }) + .then(result => { + result.should.be.an.Object().and.have.property('link'); + result.should.containEql(CLIENT_STUB.response.retrievePreviewVoucherPNGAsync); + + done(); + }); + }); + }); + + describe('checkout', () => { + it('should call the PNG checkout', done => { + const params = { + order: { ORDER_FAKE_STUB: true }, + outputFormat: OUTPUT_FORMATS.PNG + }; + + service.checkout(params) + .then(result => { + CLIENT_STUB.client.checkoutShoppingCartPNGAsync.calledOnce.should.be.true(); + CLIENT_STUB.client.checkoutShoppingCartPNGAsync.args[0][0] + .should.containDeep(params.order); + + done(); + }); + }); + + it('should update user data', done => { + const order = { ORDER_FAKE_STUB: true }; + + service.checkout({ + order, + outputFormat: OUTPUT_FORMATS.PNG + }) + .then(result => { + const response = CLIENT_STUB.response.checkoutShoppingCartPNGAsync; + USER_STUB.user._balance.should.equal(response.walletBallance); + result.should.have.properties([ 'orderId', 'link', 'vouchers' ]); + + done(); + }); + }); + }); + + describe('generateOrderId', () => { + it('should generate an order id', done => { + service.generateOrderId() + .then(orderId => { + CLIENT_STUB.client.createShopOrderIdAsync.calledOnce.should.be.true(); + orderId.should.equal(CLIENT_STUB.response.createShopOrderIdAsync.shopOrderId); + + done(); + }); + }); + }); + + describe('_initClient', () => { + it('should set soap headers on init', () => { + service._initClient(CLIENT_STUB.client); + + PARTNER_STUB.partner.getSoapHeaders.calledOnce.should.be.true(); + CLIENT_STUB.client.addSoapHeader.calledOnce.should.be.true(); + }); + }); +}); diff --git a/test/Service/Soap/Soap.test.js b/test/Service/Soap/Soap.test.js new file mode 100644 index 0000000..c0db176 --- /dev/null +++ b/test/Service/Soap/Soap.test.js @@ -0,0 +1,35 @@ +const SoapService = require('../../../lib/Service/Soap/Soap'), + CLIENT_STUB = require('../../stub/soapClient'), + { WSDL } = require('../../../lib/constants'); + +describe('Soap Service', () => { + describe('_getSoapClient', () => { + it('should the existing client', done => { + const service = new SoapService({}); + + const clientStub = { SINON_FAKE_CLIENT: true }; + + service._soapClient = clientStub; + + service._getSoapClient() + .then(client => { + client.should.have.property('SINON_FAKE_CLIENT'); + + done(); + }); + }); + + it('should create a new client if not existing', done => { + const service = new SoapService({ wsdl: WSDL.OneClickForApp }); + + service._getSoapClient() + .then(client => { + client.should.be.an.Object(); + client.constructor.name.should.equal('Client'); + client.should.have.properties(Object.keys(CLIENT_STUB.client)); + + done(); + }); + }).timeout(5000); + }); +}); diff --git a/test/stub/partner/index.js b/test/stub/partner/index.js new file mode 100644 index 0000000..f631312 --- /dev/null +++ b/test/stub/partner/index.js @@ -0,0 +1,14 @@ +const response = { + getSoapHeaders: { + FAKE_STUB_HEADERS: '11' + } +}; + +const partner = { + getSoapHeaders: sinon.stub().returns(response.getSoapHeaders) +}; + +module.exports = { + partner, + response +}; diff --git a/test/stub/reset.js b/test/stub/reset.js new file mode 100644 index 0000000..a2f3ebb --- /dev/null +++ b/test/stub/reset.js @@ -0,0 +1,8 @@ +module.exports = (stubObj) => { + Object.keys(stubObj).forEach(prop => { + const val = stubObj[prop]; + if ('function' === typeof val && val.resetHistory) { + val.resetHistory(); + } + }); +}; diff --git a/test/stub/soapClient/index.js b/test/stub/soapClient/index.js index bd14de8..4607c41 100644 --- a/test/stub/soapClient/index.js +++ b/test/stub/soapClient/index.js @@ -21,13 +21,26 @@ const response = { ] } } + }, + createShopOrderIdAsync: { + shopOrderId: 1234567890 + }, + retrievePreviewVoucherPNGAsync: { + link: 'https://schaechinger.com' } }; const client = { + addSoapHeader: sinon.stub(), authenticateUserAsync: sinon.stub().returns(Promise.resolve(response.authenticateUserAsync)), checkoutShoppingCartPNGAsync: sinon.stub().returns( Promise.resolve(response.checkoutShoppingCartPNGAsync) + ), + createShopOrderIdAsync: sinon.stub().returns( + Promise.resolve(response.createShopOrderIdAsync) + ), + retrievePreviewVoucherPNGAsync: sinon.stub().returns( + Promise.resolve(response.retrievePreviewVoucherPNGAsync) ) }; diff --git a/yarn.lock b/yarn.lock index 5403d5a..85b26f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -943,7 +943,7 @@ performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" -pino@4.11.0: +pino@^4.11.0: version "4.11.0" resolved "https://registry.yarnpkg.com/pino/-/pino-4.11.0.tgz#29ba036d065b1bea17647c4a42a1f7b8b37f8be0" dependencies: From 9a613e5c1f9c8c49d086cff14d8cb09bc0dbd848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Sch=C3=A4chinger?= Date: Sun, 25 Feb 2018 00:55:39 +0100 Subject: [PATCH 2/3] fix: - bug if zip was a number - wrong method name in order after refactoring --- lib/Address/Address.js | 4 ++-- lib/Order/Order.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Address/Address.js b/lib/Address/Address.js index 0a4cc8d..762e25f 100644 --- a/lib/Address/Address.js +++ b/lib/Address/Address.js @@ -29,8 +29,6 @@ class Address { /** @type {string} */ this._street = street; /** @type {string} */ - this._zip = zip; - /** @type {string} */ this._city = city; /** @type {(string|null)} */ this._additional = additional; @@ -42,6 +40,8 @@ class Address { if ('number' === typeof zip) { zip += ''; } + /** @type {string} */ + this._zip = zip; country = country || COUNTRY_CODES.DEFAULT; if (!COUNTRY_CODES[country]) { diff --git a/lib/Order/Order.js b/lib/Order/Order.js index 1a543d9..bad7914 100644 --- a/lib/Order/Order.js +++ b/lib/Order/Order.js @@ -58,7 +58,7 @@ class Order { * list should be created: 0: no, 1: without addresses, 2: with addresses * @returns {Object} */ - checkout({ orderId = this.orderId, createManifest = true, createShippingList = 2 } = {}) { + getCheckout({ orderId = this.orderId, createManifest = true, createShippingList = 2 } = {}) { if (!this._positions.length) { throw new Error(errors.internetmarke.shoppingcartEmpty); } From 7f828cffd0a1262c5990e08e5b0b3c70707891cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Sch=C3=A4chinger?= Date: Sun, 25 Feb 2018 00:58:57 +0100 Subject: [PATCH 3/3] fixed bug in tests after refactoring --- test/Order/Order.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Order/Order.test.js b/test/Order/Order.test.js index 1700cee..8e1d068 100644 --- a/test/Order/Order.test.js +++ b/test/Order/Order.test.js @@ -27,7 +27,7 @@ describe('Order', () => { const order = new Order(); (() => { - order.checkout({ orderId: ORDER_ID }); + order.getCheckout({ orderId: ORDER_ID }); }).should.throw(errors.internetmarke.shoppingcartEmpty) }); @@ -38,7 +38,7 @@ describe('Order', () => { order.addPosition(position); }); - const summary = order.checkout({ + const summary = order.getCheckout({ orderId: ORDER_ID, createManifest: false, createShippingList: 1