| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- /* eslint-env jest */
- import Vue from 'vue'
- import ElementUI from 'element-ui'
- import path from 'path'
- import { load } from './loadGuestComponent'
- import * as shop from '@/api/shop'
- import { getGuestRegionList } from '@/api/config'
- import { toThousandFilter } from '@/filters'
- jest.mock('@/api/shop', () => ({ fetchGuestProducts: jest.fn(), fetchGuestCart: jest.fn(), createGuestOrder: jest.fn() }))
- jest.mock('@/api/config', () => ({ getGuestRegionList: jest.fn() }))
- jest.mock('@/router', () => ({ push: jest.fn(), replace: jest.fn() }))
- jest.mock('vue-paystack', () => ({
- name: 'Paystack', props: ['amount', 'currency', 'reference', 'email', 'metadata', 'callback', 'close', 'paystackkey', 'channels'],
- created() { this.scriptLoaded = Promise.resolve() },
- render: h => h('button')
- }))
- Vue.use(ElementUI)
- Vue.filter('toThousandFilter', toThousandFilter)
- const push = jest.fn()
- Vue.prototype.$router = { push, replace: jest.fn() }
- Vue.prototype.$route = { query: {}}
- Vue.prototype.$message = { error: jest.fn(), info: jest.fn() }
- const root = path.resolve('src/ui-refactor/views/guest-shop')
- const Flow = load(path.join(root, 'GuestPurchaseFlow.vue'))
- const MarketDialog = load(path.join(root, 'MarketDialog.vue'))
- const Order = load(path.join(root, 'GuestOrderPage.vue'))
- const errors = []
- const instances = []
- Vue.config.errorHandler = error => errors.push(error)
- async function settle() { for (let i = 0; i < 16; i++) { await Promise.resolve(); await Vue.nextTick() } }
- const descendants = vm => vm.$children.flatMap(child => [child, ...descendants(child)])
- function mount(component, propsData = {}) {
- const vm = new (Vue.extend(component))({ propsData }).$mount()
- document.body.appendChild(vm.$el)
- instances.push(vm)
- return vm
- }
- beforeEach(() => {
- jest.clearAllMocks()
- errors.length = 0
- shop.fetchGuestProducts.mockResolvedValue({ data: { list: [{ ID: 'P1', GOODS_NAME: 'Guest product', GOODS_NO: 'SKU', SELL_PRICE: 100, MARKET_PRICE: 150, PRICE_PV: 20, DISCOUNT: 80, TAX_RATE: 7.5, STORE_NUMS: 5, COIN: 'AED' }], totalCount: 1 }})
- shop.fetchGuestCart.mockResolvedValue({ data: { sellType: [{ id: 1, sell_type: [{ label: 'pay_stack', name: 'Paystack' }, { label: 'cash', name: 'Cash' }] }], freight: 10, freeShipping: 500, coin: 'AED' }})
- getGuestRegionList.mockResolvedValue({ data: [{ value: 'R1', label: 'Dubai' }] })
- })
- afterEach(() => {
- instances.splice(0).forEach(vm => { vm.$destroy(); vm.$el.remove() })
- delete window.PaystackPop
- expect(errors).toEqual([])
- })
- it('offers the two markets and navigates without asking for credentials', async() => {
- const vm = mount(MarketDialog, { visible: true })
- await settle()
- const options = vm.$el.querySelectorAll('.ng-market-option')
- expect(options).toHaveLength(2)
- expect(options[1].textContent).toContain('UAE')
- options[1].click()
- expect(push).toHaveBeenCalledWith({ path: '/guest-shop/ae' })
- })
- it('connects product, cart and checkout controls without guest BV or member fields', async() => {
- const vm = mount(Flow, { market: 'ae', stage: 'catalog' })
- await settle()
- expect(vm.$el.querySelector('.ng-signup-product-price').textContent).toContain('AED 80')
- expect(vm.$el.textContent).not.toContain('BV')
- vm.$el.querySelector('.ng-signup-product-add').click()
- await settle()
- vm.stage = 'cart'
- await settle()
- expect(vm.$el.querySelector('.ng-purchase-cart-item').textContent).toContain('AED 80 each')
- const cart = descendants(vm).find(child => child.$options.name === 'GuestCart')
- expect(cart.$children[0].$options.name).toBe('PurchaseCartItems')
- expect(vm.$el.querySelector('.ng-purchase-summary').textContent).not.toContain('BV')
- vm.stage = 'checkout'
- await settle()
- expect(getGuestRegionList).toHaveBeenCalledWith({ countryId: '620699181539139643' })
- const shippingForm = vm.$refs.shipping.$children[0]
- expect(shippingForm.$children.some(child => child.$options.name === 'PurchaseCheckoutFields')).toBe(true)
- expect(vm.$el.textContent).not.toContain('Member information')
- expect(vm.$el.textContent).not.toContain('Reporting Center')
- const recipient = vm.$el.querySelector('#ng-guest-consignee')
- recipient.value = 'Recipient'
- recipient.dispatchEvent(new Event('input', { bubbles: true }))
- await settle()
- expect(vm.form.consignee).toBe('Recipient')
- Object.assign(vm.form, { acceptMobile: '123', province: ['R1'], lgaName: 'District', cityName: 'Dubai', detailaddress: 'Street' })
- vm.$el.querySelector('.ng-purchase-place-order').click()
- await settle()
- const dialog = vm.$children.find(child => child.$options.name === 'PaystackPaymentDialog')
- expect(dialog.visible).toBe(true)
- expect(dialog.readonlyEmail).toBe(false)
- const input = dialog.$el.querySelector('#ng-paystack-payment-email')
- input.value = 'visitor@example.test'
- input.dispatchEvent(new Event('input', { bubbles: true }))
- await settle()
- expect(vm.form.email).toBe('visitor@example.test')
- expect(dialog.$el.querySelector('[type=password]')).toBeNull()
- })
- it('keeps Nigeria merchandise, cart, checkout and email amounts in the product currency', async() => {
- shop.fetchGuestProducts.mockResolvedValue({ data: { list: [{ ID: 'P1', GOODS_NAME: 'Nigeria product', SELL_PRICE: 100, DISCOUNT: 80, TAX_RATE: 7.5, STORE_NUMS: 5, COIN: 'NGN' }], totalCount: 1 }})
- const vm = mount(Flow, { market: 'ng', stage: 'catalog' })
- await settle()
- expect(vm.$el.querySelector('.ng-signup-product-price').textContent).toContain('NGN 80')
- vm.$el.querySelector('.ng-signup-product-add').click()
- vm.stage = 'cart'
- await settle()
- expect(vm.$el.querySelector('.ng-purchase-cart-item').textContent).toContain('NGN 80 each')
- expect(vm.$el.querySelector('.ng-purchase-summary').textContent).toContain('NGN 90')
- vm.stage = 'checkout'
- await settle()
- expect(vm.$el.querySelector('.ng-purchase-summary').textContent).toContain('NGN 90')
- Object.assign(vm.form, { consignee: 'Recipient', acceptMobile: '123', province: ['R1'], lgaName: 'District', cityName: 'Lagos', detailaddress: 'Street' })
- vm.openEmail()
- await settle()
- const dialog = descendants(vm).find(child => child.$options.name === 'PaystackPaymentDialog')
- expect(dialog.visible).toBe(true)
- expect(dialog.$el.querySelector('.ng-paystack-payment-amount').textContent).toContain('NGN 90')
- })
- it('opens Paystack with the order record amount, product currency and entered email', async() => {
- const openIframe = jest.fn()
- window.PaystackPop = { setup: jest.fn(() => ({ openIframe })) }
- shop.fetchGuestProducts.mockResolvedValue({ data: { list: [{ ID: 'P1', GOODS_NAME: 'Nigeria product', SELL_PRICE: 62500, DISCOUNT: 80, TAX_RATE: 7.5, STORE_NUMS: 5, COIN: 'NGN', LOCAL_CURRENCY_ID: '104' }], totalCount: 1 }})
- shop.fetchGuestCart.mockResolvedValue({ data: { sellType: [{ id: 1, sell_type: [{ label: 'pay_stack', name: 'Paystack' }] }], freight: 0, freeShipping: 0, coin: 'NGN' }})
- shop.createGuestOrder.mockResolvedValue({ data: { SN: 'SAMPLE-ORDER', DEC_SN: 'SAMPLE-DECLARATION', ORDER_AMOUNT: 50000, PAY_AMOUNT: 50000, PAY_FREIGHT: 0, COUNTRY_ID: '620699181539139625', CURRENCY_ID: '104', EMAIL: '', STATUS: 1 }})
- const vm = mount(Flow, { market: 'ng', stage: 'catalog' })
- vm.publicKey = 'sample-key'
- await settle()
- vm.toggleProduct(vm.products[0])
- vm.stage = 'checkout'
- await settle()
- Object.assign(vm.form, { consignee: 'Recipient', acceptMobile: '123', province: ['R1'], lgaName: 'District', cityName: 'Lagos', detailaddress: 'Street', email: 'visitor@example.test' })
- vm.$el.querySelector('.ng-purchase-place-order').click()
- await settle()
- const dialog = descendants(vm).find(child => child.$options.name === 'PaystackPaymentDialog')
- dialog.$el.querySelector('.ng-paystack-payment-continue').click()
- await settle()
- expect(openIframe).toHaveBeenCalledTimes(1)
- expect(window.PaystackPop.setup.mock.calls[0][0]).toMatchObject({ amount: 5000000, currency: 'NGN', email: 'visitor@example.test', metadata: { cart_id: 'SAMPLE-ORDER' }})
- expect(push).not.toHaveBeenCalled()
- window.PaystackPop.setup.mock.calls[0][0].onClose()
- await settle()
- vm.openEmail()
- await settle()
- dialog.$el.querySelector('.ng-paystack-payment-continue').click()
- await settle()
- expect(shop.createGuestOrder).toHaveBeenCalledTimes(1)
- expect(openIframe).toHaveBeenCalledTimes(2)
- window.PaystackPop.setup.mock.calls[1][0].callback({ reference: 'sample-reference' })
- await settle()
- expect(push).toHaveBeenCalledWith({ path: '/guest-order', query: { order: 'SAMPLE-ORDER' }})
- })
- it('does not turn a backend guest Cash option into a member balance', async() => {
- const vm = mount(Flow, { market: 'ae', stage: 'catalog' })
- await settle()
- vm.toggleProduct(vm.products[0])
- vm.stage = 'checkout'
- vm.payType = 'cash'
- await settle()
- expect(vm.$el.querySelector('.ng-payment-methods').textContent).not.toContain('Account Balance')
- expect(vm.$el.textContent).not.toContain('Available balance')
- })
- it('shows only the supplied order reference, without fabricating receipt or paid data', async() => {
- Vue.prototype.$route = { query: { order: 'REAL-ORDER' }}
- const vm = mount(Order)
- await settle()
- expect(vm.$el.textContent).toContain('Order REAL-ORDER')
- expect(vm.$el.textContent).toContain('Order details are not available yet')
- expect(vm.$el.textContent).not.toContain('PAID')
- expect(shop.fetchGuestProducts).not.toHaveBeenCalled()
- })
|