From 5df511b0ac94742761acab87addd83f3bddd543f Mon Sep 17 00:00:00 2001 From: Michel Bade Date: Tue, 10 Dec 2024 19:32:08 +0100 Subject: [PATCH] PPI-1030 - Added jest tests --- .../app/administration/eslint.config.mjs | 1 + .../app/administration/jest.setup.js | 6 + .../swag-paypal-onboarding-button.spec.ts | 100 +++++ .../swag-paypal-setting.spec.ts | 408 ++++++++++++++++++ .../app/store/merchant-information.fixture.ts | 157 +++++++ .../src/app/store/settings.fixture.ts | 83 ++++ ...-paypal-merchant-information.store.spec.ts | 120 ++++++ .../store/swag-paypal-settings.store.spec.ts | 122 ++++++ .../swag-paypal-settings-webhook.spec.ts | 153 +++++++ .../swag-paypal-settings-advanced.spec.ts | 77 ++++ .../swag-paypal-settings-general.spec.ts | 160 +++++++ .../swag-paypal-settings-storefront.spec.ts | 144 +++++++ 12 files changed, 1531 insertions(+) create mode 100644 src/Resources/app/administration/src/app/component/swag-paypal-onboarding-button/swag-paypal-onboarding-button.spec.ts create mode 100644 src/Resources/app/administration/src/app/component/swag-paypal-setting/swag-paypal-setting.spec.ts create mode 100644 src/Resources/app/administration/src/app/store/merchant-information.fixture.ts create mode 100644 src/Resources/app/administration/src/app/store/settings.fixture.ts create mode 100644 src/Resources/app/administration/src/app/store/swag-paypal-merchant-information.store.spec.ts create mode 100644 src/Resources/app/administration/src/app/store/swag-paypal-settings.store.spec.ts create mode 100644 src/Resources/app/administration/src/module/swag-paypal-settings/components/swag-paypal-settings-webhook/swag-paypal-settings-webhook.spec.ts create mode 100644 src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.spec.ts create mode 100644 src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-general/swag-paypal-settings-general.spec.ts create mode 100644 src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-storefront/swag-paypal-settings-storefront.spec.ts diff --git a/src/Resources/app/administration/eslint.config.mjs b/src/Resources/app/administration/eslint.config.mjs index 9f0bc3674..f163efc03 100644 --- a/src/Resources/app/administration/eslint.config.mjs +++ b/src/Resources/app/administration/eslint.config.mjs @@ -189,6 +189,7 @@ export default tseslint.config( 'jest/prefer-to-contain': 'error', 'jest/prefer-to-have-length': 'error', 'jest/consistent-test-it': ['error', { fn: 'it', withinDescribe: 'it' }], + '@typescript-eslint/no-unsafe-member-access': 'off', // Needed for any/VueComponent typed wrappers }, }, { diff --git a/src/Resources/app/administration/jest.setup.js b/src/Resources/app/administration/jest.setup.js index 25d9e18b2..09e59323f 100644 --- a/src/Resources/app/administration/jest.setup.js +++ b/src/Resources/app/administration/jest.setup.js @@ -2,3 +2,9 @@ import 'SwagPayPal/mixin/swag-paypal-credentials-loader.mixin'; import 'SwagPayPal/mixin/swag-paypal-notification.mixin'; import 'SwagPayPal/mixin/swag-paypal-pos-catch-error.mixin'; import 'SwagPayPal/mixin/swag-paypal-pos-log-label.mixin'; + +import 'SwagPayPal/mixin/swag-paypal-settings.mixin'; +import 'SwagPayPal/mixin/swag-paypal-merchant-information.mixin'; + +import 'SwagPayPal/app/store/swag-paypal-settings.store'; +import 'SwagPayPal/app/store/swag-paypal-merchant-information.store'; diff --git a/src/Resources/app/administration/src/app/component/swag-paypal-onboarding-button/swag-paypal-onboarding-button.spec.ts b/src/Resources/app/administration/src/app/component/swag-paypal-onboarding-button/swag-paypal-onboarding-button.spec.ts new file mode 100644 index 000000000..92dfaead0 --- /dev/null +++ b/src/Resources/app/administration/src/app/component/swag-paypal-onboarding-button/swag-paypal-onboarding-button.spec.ts @@ -0,0 +1,100 @@ +import { mount } from '@vue/test-utils'; +import SwagPayPalOnboardingButton from '.'; + +Shopware.Component.register('swag-paypal-onboarding-button', Promise.resolve(SwagPayPalOnboardingButton)); + +async function loadScript(script: Element) { + window.PAYPAL = { + apps: { + // @ts-expect-error - not fully implemented on purpose + Signup: { + setup: jest.fn(), + render: jest.fn(), + }, + }, + }; + + script.dispatchEvent(new Event('load')); + + await flushPromises(); +} + +async function createWrapper() { + return mount( + await Shopware.Component.build('swag-paypal-onboarding-button') as typeof SwagPayPalOnboardingButton, + { + global: { + mocks: { $t: (key: string) => key }, + provide: { + acl: { can: () => true }, + SwagPayPalSettingsService: { + getApiCredentials: jest.fn(), + }, + }, + }, + }, + ); +} + +describe('swag-paypal-onboarding-button', () => { + it('should be a Vue.js component', async () => { + const wrapper = await createWrapper(); + + expect(wrapper.vm).toBeTruthy(); + }); + + it('should initialize component', async () => { + const wrapper = await createWrapper(); + + const script = document.head.querySelector('#paypal-js'); + expect(script).toBeTruthy(); + await loadScript(script!); + + expect(typeof window.PAYPAL!.apps.Signup.render).toBe('function'); + expect(window.PAYPAL!.apps.Signup.setup).not.toHaveBeenCalled(); + + const renderSpy = jest.spyOn(wrapper.vm, 'renderPayPalButton'); + + wrapper.vm.loadPayPalScript(); + + expect(renderSpy).not.toHaveBeenCalled(); + expect(window.PAYPAL!.apps.Signup.setup).toHaveBeenCalled(); + }); + + it('should set config', async () => { + const wrapper = await createWrapper(); + const store = Shopware.Store.get('swagPayPalSettings'); + + store.setConfig(null, {}); + wrapper.setProps({ type: 'live' }); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.suffix).toBe(''); + wrapper.vm.setConfig('client-id', 'client-secret', 'payer-id'); + + expect(store.allConfigs).toStrictEqual({ + null: { + 'SwagPayPal.settings.clientId': 'client-id', + 'SwagPayPal.settings.clientSecret': 'client-secret', + 'SwagPayPal.settings.merchantPayerId': 'payer-id', + 'SwagPayPal.settings.sandbox': false, + }, + }); + + store.setConfig(null, {}); + wrapper.setProps({ type: 'sandbox' }); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.suffix).toBe('Sandbox'); + wrapper.vm.setConfig('client-id', 'client-secret', 'payer-id'); + + expect(store.allConfigs).toStrictEqual({ + null: { + 'SwagPayPal.settings.clientIdSandbox': 'client-id', + 'SwagPayPal.settings.clientSecretSandbox': 'client-secret', + 'SwagPayPal.settings.merchantPayerIdSandbox': 'payer-id', + 'SwagPayPal.settings.sandbox': true, + }, + }); + }); +}); diff --git a/src/Resources/app/administration/src/app/component/swag-paypal-setting/swag-paypal-setting.spec.ts b/src/Resources/app/administration/src/app/component/swag-paypal-setting/swag-paypal-setting.spec.ts new file mode 100644 index 000000000..8a6f73ea5 --- /dev/null +++ b/src/Resources/app/administration/src/app/component/swag-paypal-setting/swag-paypal-setting.spec.ts @@ -0,0 +1,408 @@ +import { mount } from '@vue/test-utils'; +import SettingsFixture from '../../store/settings.fixture'; +import { INTENTS } from '../../../constant/swag-paypal-settings.constant'; +import SwagPayPalSetting from '.'; + +Shopware.Component.register('swag-paypal-setting', Promise.resolve(SwagPayPalSetting)); + +async function createWrapper(props: $TSFixMe = { path: 'SwagPayPal.settings.clientId' }, translations: Record = {}) { + return mount( + await Shopware.Component.build('swag-paypal-setting') as typeof SwagPayPalSetting, + { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + props, + global: { + mocks: { $t: (key: string) => translations[key] ?? key }, + provide: { acl: { can: () => true } }, + stubs: { + 'sw-inherit-wrapper': await wrapTestComponent('sw-inherit-wrapper', { sync: true }), + 'sw-inheritance-switch': await wrapTestComponent('sw-inheritance-switch', { sync: true }), + 'sw-help-text': await wrapTestComponent('sw-help-text', { sync: true }), + 'sw-icon': await wrapTestComponent('sw-icon', { sync: true }), + 'sw-icon-deprecated': await wrapTestComponent('sw-icon-deprecated', { sync: true }), + // type === boolean + 'sw-switch-field': await wrapTestComponent('sw-switch-field', { sync: true }), + 'sw-switch-field-deprecated': await wrapTestComponent('sw-switch-field-deprecated', { sync: true }), + 'sw-checkbox-field': await wrapTestComponent('sw-checkbox-field', { sync: true }), + 'sw-checkbox-field-deprecated': await wrapTestComponent('sw-checkbox-field-deprecated', { sync: true }), + // type === string + 'sw-text-field': await wrapTestComponent('sw-text-field', { sync: true }), + 'sw-text-field-deprecated': await wrapTestComponent('sw-text-field-deprecated', { sync: true }), + // type === string + options + 'sw-single-select': await wrapTestComponent('sw-single-select', { sync: true }), + 'sw-select-base': await wrapTestComponent('sw-select-base', { sync: true }), + // field bases + 'sw-contextual-field': await wrapTestComponent('sw-contextual-field', { sync: true }), + 'sw-block-field': await wrapTestComponent('sw-block-field', { sync: true }), + 'sw-base-field': await wrapTestComponent('sw-base-field', { sync: true }), + }, + }, + }, + ); +} + +describe('swag-paypal-setting', () => { + const store = Shopware.Store.get('swagPayPalSettings'); + + afterEach(() => { + store.$reset(); + }); + + it('should be a Vue.js component', async () => { + const wrapper = await createWrapper(); + + expect(wrapper.vm).toBeTruthy(); + }); + + it('should have computed label, helpText and hintText', async () => { + const wrapper = await createWrapper( + { path: 'SwagPayPal.settings.clientId' }, + { + 'swag-paypal-setting.label.clientId': 'Label text', + 'swag-paypal-setting.helpText.clientId': 'Help text', + 'swag-paypal-setting.hintText.clientId': 'Hint text', + }, + ); + + expect(wrapper.vm.label).toBe('Label text'); + expect(wrapper.vm.helpText).toBe('Help text'); + expect(wrapper.vm.hintText).toBe('Hint text'); + expect(wrapper.vm.attrs.label).toBe(wrapper.vm.label); + expect(wrapper.vm.attrs.helpText).toBe(wrapper.vm.helpText); + expect(wrapper.vm.attrs.hintText).toBe(wrapper.vm.hintText); + }); + + it('should have not overridden label, helpText and hintText', async () => { + const wrapper = await createWrapper( + { + path: 'SwagPayPal.settings.clientId', + label: 'Overridden label', + helpText: undefined, + hintText: 'Overridden hint text', + }, + { + 'swag-paypal-setting.label.clientId': 'Label text', + 'swag-paypal-setting.helpText.clientId': 'Help text', + 'swag-paypal-setting.hintText.clientId': 'Hint text', + }, + ); + + expect(wrapper.vm.label).toBe('Label text'); + expect(wrapper.vm.helpText).toBe('Help text'); + expect(wrapper.vm.hintText).toBe('Hint text'); + expect(wrapper.vm.attrs.label).toBe('Overridden label'); + expect(wrapper.vm.attrs.helpText).toBeUndefined(); + expect(wrapper.vm.attrs.hintText).toBe('Overridden hint text'); + }); + + it('should have normalized attrs', async () => { + const wrapper = await createWrapper({ + path: 'SwagPayPal.settings.clientId', + 'not-camel-case': 'value', + 'help-text': 'Help text', + }); + + expect(wrapper.vm.attrs).toStrictEqual({ + notCamelCase: 'value', + helpText: 'Help text', + }); + }); + + it('should have attrs', async () => { + const attrs = { + bordered: true, + disabled: true, + error: { code: 'TEST', detail: 'Test error' }, + helpText: 'Help text', + label: 'Label text', + labelProperty: 'label', + options: [], + required: true, + valueProperty: 'value', + }; + + const wrapper = await createWrapper({ path: 'SwagPayPal.settings.clientId', ...attrs }); + + expect(wrapper.vm.type).toBe('string'); + expect(wrapper.vm.attrs).toStrictEqual(attrs); + expect(wrapper.vm.wrapperAttrs).toStrictEqual({ + disabled: true, + helpText: 'Help text', + label: 'Label text', + required: true, + }); + expect(wrapper.vm.formAttrs).toStrictEqual({ + disabled: true, + error: { code: 'TEST', detail: 'Test error' }, + labelProperty: 'label', + options: [], + valueProperty: 'value', + }); + }); + + it('should find translations', async () => { + const wrapper = await createWrapper( + { path: 'SwagPayPal.settings.clientId' }, + { transOne: 'Label text', transTwo: 'Help text' }, + ); + + expect(wrapper.vm.tif('non-existing')).toBeNull(); + expect(wrapper.vm.tif('transOne')).toBe('Label text'); + expect(wrapper.vm.tif('transOne', 'transTwo')).toBe('Label text'); + expect(wrapper.vm.tif('non-existing', 'transTwo')).toBe('Help text'); + }); + + it('should be a text field without inheritance', async () => { + store.setConfig(null, SettingsFixture.WithCredentials); + + const wrapper = await createWrapper( + { path: 'SwagPayPal.settings.clientId' }, + { 'swag-paypal-setting.label.clientId': 'Client ID' }, + ); + + // computed properties + expect(wrapper.vm.value).toBe('some-client-id'); + expect(wrapper.vm.inheritedValue).toBeUndefined(); + expect(wrapper.vm.hasParent).toBe(false); + expect(wrapper.vm.pathDomainless).toBe('clientId'); + expect(wrapper.vm.disabled).toBe(false); + expect(wrapper.vm.type).toBe('string'); + expect(wrapper.vm.label).toBe('Client ID'); + expect(wrapper.vm.helpText).toBeNull(); + expect(wrapper.vm.hintText).toBeNull(); + + const field = wrapper.findComponent('.sw-field--text'); + + expect(field.exists()).toBe(true); + expect(field.vm.value).toBe('some-client-id'); + expect(field.vm.$attrs.name).toBe('SwagPayPal.settings.clientId'); + expect(field.vm.$attrs.disabled).toBe(false); + expect(field.vm.$attrs['map-inheritance']).toBeUndefined(); + + Object.keys(wrapper.vm.formAttrs).forEach((key) => { + expect(field.vm.$attrs).toHaveProperty(key); + expect(field.vm.$attrs[key]).toBe(wrapper.vm.formAttrs[key]); + }); + }); + + it('should be a boolean field without inheritance', async () => { + store.setConfig(null, SettingsFixture.WithCredentials); + + const wrapper = await createWrapper( + { path: 'SwagPayPal.settings.sandbox' }, + { 'swag-paypal-setting.label.sandbox': 'Sandbox' }, + ); + + // computed properties + expect(wrapper.vm.value).toBe(false); + expect(wrapper.vm.inheritedValue).toBeUndefined(); + expect(wrapper.vm.hasParent).toBe(false); + expect(wrapper.vm.pathDomainless).toBe('sandbox'); + expect(wrapper.vm.disabled).toBe(false); + expect(wrapper.vm.type).toBe('boolean'); + expect(wrapper.vm.label).toBe('Sandbox'); + expect(wrapper.vm.helpText).toBeNull(); + expect(wrapper.vm.hintText).toBeNull(); + + const field = wrapper.findComponent('.sw-field--switch'); + + expect(field.exists()).toBe(true); + expect(field.vm.value).toBe(false); + expect(field.vm.$attrs.name).toBe('SwagPayPal.settings.sandbox'); + expect(field.vm.$attrs.disabled).toBe(false); + + Object.keys(wrapper.vm.formAttrs).forEach((key) => { + expect(field.vm.$attrs).toHaveProperty(key); + expect(field.vm.$attrs[key]).toBe(wrapper.vm.formAttrs[key]); + }); + }); + + it('should be a select field without inheritance', async () => { + store.setConfig(null, SettingsFixture.WithCredentials); + const options = INTENTS.map((intent) => ({ value: intent, label: intent })); + + const wrapper = await createWrapper( + { path: 'SwagPayPal.settings.intent', options }, + { 'swag-paypal-setting.label.intent': 'Intent' }, + ); + + // computed properties + expect(wrapper.vm.value).toBe('CAPTURE'); + expect(wrapper.vm.inheritedValue).toBeUndefined(); + expect(wrapper.vm.hasParent).toBe(false); + expect(wrapper.vm.pathDomainless).toBe('intent'); + expect(wrapper.vm.disabled).toBe(false); + expect(wrapper.vm.type).toBe('string'); + expect(wrapper.vm.label).toBe('Intent'); + expect(wrapper.vm.helpText).toBeNull(); + expect(wrapper.vm.hintText).toBeNull(); + + const field = wrapper.findComponent('.sw-single-select'); + + expect(field.exists()).toBe(true); + expect(field.vm.value).toBe('CAPTURE'); + expect(field.vm.$attrs.name).toBe('SwagPayPal.settings.intent'); + expect(field.vm.$attrs.disabled).toBe(false); + + Object.keys(wrapper.vm.formAttrs).forEach((key: string) => { + if (key === 'options') { + expect(field.vm).toHaveProperty(key); + expect(field.vm[key]).toStrictEqual(wrapper.vm.formAttrs[key]); + } else { + expect(field.vm.$attrs).toHaveProperty(key); + expect(field.vm.$attrs[key]).toBe(wrapper.vm.formAttrs[key]); + } + }); + }); + + it('should be a text field with inheritance', async () => { + store.setConfig(null, SettingsFixture.WithCredentials); + store.setConfig('some-sales-channel', SettingsFixture.All); + store.salesChannel = 'some-sales-channel'; + + const wrapper = await createWrapper( + { path: 'SwagPayPal.settings.clientId' }, + { 'swag-paypal-setting.label.clientId': 'Client ID' }, + ); + + // computed properties + expect(wrapper.vm.value).toBe(''); + expect(wrapper.vm.inheritedValue).toBe('some-client-id'); + expect(wrapper.vm.hasParent).toBe(true); + expect(wrapper.vm.pathDomainless).toBe('clientId'); + expect(wrapper.vm.disabled).toBe(false); + expect(wrapper.vm.type).toBe('string'); + expect(wrapper.vm.wrapperAttrs.label).toBe('Client ID'); + + // field shows actual value + const field = wrapper.findComponent('.sw-field--text'); + expect(field.exists()).toBe(true); + expect(field.vm.value).toBe(''); + expect(field.vm.$attrs.disabled).toBe(false); + + // inheritance switch exists and is not inherited + const inheritSwitch = wrapper.findComponent('.sw-inheritance-switch'); + expect(inheritSwitch.exists()).toBe(true); + expect(inheritSwitch.vm.isInherited).toBe(false); + + // Switch inheritance - value should be restored + const icon = inheritSwitch.findComponent('.sw-icon'); + expect(icon.exists()).toBe(true); + await icon.trigger('click'); + + expect(inheritSwitch.vm.isInherited).toBe(true); + expect(wrapper.vm.value).toBeUndefined(); + expect(wrapper.vm.inheritedValue).toBe('some-client-id'); + expect(field.vm.$attrs.disabled).toBe(true); + expect(field.vm.value).toBe('some-client-id'); + + // Switch to "All Sales Channels" - inheritance should be disabled + store.salesChannel = null; + + await wrapper.vm.$nextTick(); + + expect(inheritSwitch.exists()).toBe(false); + }); + + it('should be a select field with inheritance', async () => { + store.setConfig(null, SettingsFixture.Default); + store.setConfig('some-sales-channel', { + 'SwagPayPal.settings.intent': 'AUTHORIZE', + }); + store.salesChannel = 'some-sales-channel'; + + const wrapper = await createWrapper( + { + path: 'SwagPayPal.settings.intent', + options: INTENTS.map((intent) => ({ value: intent, label: intent })), + }, + { 'swag-paypal-setting.label.intent': 'Intent' }, + ); + + // computed properties + expect(wrapper.vm.value).toBe('AUTHORIZE'); + expect(wrapper.vm.inheritedValue).toBe('CAPTURE'); + expect(wrapper.vm.hasParent).toBe(true); + expect(wrapper.vm.pathDomainless).toBe('intent'); + expect(wrapper.vm.disabled).toBe(false); + expect(wrapper.vm.type).toBe('string'); + expect(wrapper.vm.wrapperAttrs.label).toBe('Intent'); + + // field shows actual value + const field = wrapper.findComponent('.sw-single-select'); + expect(field.exists()).toBe(true); + expect(field.vm.value).toBe('AUTHORIZE'); + expect(field.vm.$attrs.disabled).toBe(false); + + // inheritance switch exists and is not inherited + const inheritSwitch = wrapper.findComponent('.sw-inheritance-switch'); + expect(inheritSwitch.exists()).toBe(true); + expect(inheritSwitch.vm.isInherited).toBe(false); + + // Switch inheritance - value should be restored + const icon = inheritSwitch.findComponent('.sw-icon'); + expect(icon.exists()).toBe(true); + await icon.trigger('click'); + + expect(inheritSwitch.vm.isInherited).toBe(true); + expect(wrapper.vm.value).toBeUndefined(); + expect(wrapper.vm.inheritedValue).toBe('CAPTURE'); + expect(field.vm.$attrs.disabled).toBe(true); + expect(field.vm.value).toBe('CAPTURE'); + + // Switch to "All Sales Channels" - inheritance should be disabled + store.salesChannel = null; + + await wrapper.vm.$nextTick(); + + expect(inheritSwitch.exists()).toBe(false); + }); + + it('should be a boolean field with inheritance', async () => { + store.setConfig(null, SettingsFixture.Default); + store.setConfig('some-sales-channel', { 'SwagPayPal.settings.sandbox': true }); + store.salesChannel = 'some-sales-channel'; + + const wrapper = await createWrapper( + { path: 'SwagPayPal.settings.sandbox' }, + { 'swag-paypal-setting.label.sandbox': 'Sandbox' }, + ); + + // computed properties + expect(wrapper.vm.value).toBe(true); + expect(wrapper.vm.inheritedValue).toBe(false); + expect(wrapper.vm.hasParent).toBe(true); + expect(wrapper.vm.pathDomainless).toBe('sandbox'); + expect(wrapper.vm.disabled).toBe(false); + expect(wrapper.vm.type).toBe('boolean'); + + // field shows actual value + const field = wrapper.findComponent('.sw-field--switch'); + expect(field.exists()).toBe(true); + expect(field.vm.value).toBe(true); + expect(field.vm.$attrs.disabled).toBe(false); + + // inheritance switch exists and is not inherited + const inheritSwitch = wrapper.findComponent('.sw-inheritance-switch'); + expect(inheritSwitch.exists()).toBe(true); + expect(inheritSwitch.vm.isInherited).toBe(false); + + // Switch inheritance - value should be restored + const icon = inheritSwitch.findComponent('.sw-icon'); + expect(icon.exists()).toBe(true); + await icon.trigger('click'); + + expect(inheritSwitch.vm.isInherited).toBe(true); + expect(wrapper.vm.value).toBeUndefined(); + expect(wrapper.vm.inheritedValue).toBe(false); + expect(field.vm.$attrs.disabled).toBe(true); + expect(field.vm.value).toBe(false); + + // Switch to "All Sales Channels" - inheritance should be disabled + store.salesChannel = null; + + await wrapper.vm.$nextTick(); + + expect(inheritSwitch.exists()).toBe(false); + }); +}); diff --git a/src/Resources/app/administration/src/app/store/merchant-information.fixture.ts b/src/Resources/app/administration/src/app/store/merchant-information.fixture.ts new file mode 100644 index 000000000..ebf9c8ea6 --- /dev/null +++ b/src/Resources/app/administration/src/app/store/merchant-information.fixture.ts @@ -0,0 +1,157 @@ +import type * as PayPal from 'src/types'; + +const Default = { + merchantIntegrations: { + merchant_id: '2CWUTJMHUSECB', + tracking_id: 'test@example.com', + products: [ + { name: 'BASIC_PPPLUS_CORE' }, + { name: 'BASIC_PPPLUS_PUI' }, + { name: 'BASIC_PPPLUS_GUEST_CC' }, + { name: 'BASIC_PPPLUS_GUEST_ELV' }, + { + name: 'PPCP_STANDARD', + vetting_status: 'SUBSCRIBED', + capabilities: [ + 'ACCEPT_DONATIONS', + 'BANK_MANAGED_WITHDRAWAL', + 'GUEST_CHECKOUT', + 'INSTALLMENTS', + 'PAY_WITH_PAYPAL', + 'PAYPAL_CHECKOUT_ALTERNATIVE_PAYMENT_METHODS', + 'PAYPAL_CHECKOUT_PAY_WITH_PAYPAL_CREDIT', + 'PAYPAL_CHECKOUT', + 'QR_CODE', + 'SEND_INVOICE', + 'SUBSCRIPTIONS', + 'WITHDRAW_FUNDS_TO_DOMESTIC_BANK', + ], + }, + { + name: 'PAYMENT_METHODS', + vetting_status: 'SUBSCRIBED', + capabilities: [ + 'APPLE_PAY', + 'GOOGLE_PAY', + 'IDEAL', + 'PAY_UPON_INVOICE', + 'PAY_WITH_PAYPAL', + 'SEPA', + 'VAT_TAX', + ], + }, + { + name: 'ADVANCED_VAULTING', + vetting_status: 'SUBSCRIBED', + capabilities: ['PAYPAL_WALLET_VAULTING_ADVANCED'], + }, + { + name: 'PPCP_CUSTOM', + vetting_status: 'SUBSCRIBED', + capabilities: [ + 'AMEX_OPTBLUE', + 'APPLE_PAY', + 'CARD_PROCESSING_VIRTUAL_TERMINAL', + 'COMMERCIAL_ENTITY', + 'CUSTOM_CARD_PROCESSING', + 'DEBIT_CARD_SWITCH', + 'FRAUD_TOOL_ACCESS', + 'GOOGLE_PAY', + 'PAY_UPON_INVOICE', + 'PAYPAL_WALLET_VAULTING_ADVANCED', + ], + }, + ], + capabilities: [ + { status: 'ACTIVE', name: 'ACCEPT_DONATIONS' }, + { status: 'ACTIVE', name: 'AMEX_OPTBLUE' }, + { status: 'ACTIVE', name: 'APPLE_PAY' }, + { status: 'ACTIVE', name: 'BANK_MANAGED_WITHDRAWAL' }, + { status: 'ACTIVE', name: 'CARD_PROCESSING_VIRTUAL_TERMINAL' }, + { status: 'ACTIVE', name: 'COMMERCIAL_ENTITY' }, + { status: 'ACTIVE', name: 'CUSTOM_CARD_PROCESSING' }, + { status: 'ACTIVE', name: 'DEBIT_CARD_SWITCH' }, + { status: 'ACTIVE', name: 'FRAUD_TOOL_ACCESS' }, + { status: 'ACTIVE', name: 'GOOGLE_PAY' }, + { status: 'ACTIVE', name: 'GUEST_CHECKOUT' }, + { status: 'ACTIVE', name: 'IDEAL' }, + { status: 'ACTIVE', name: 'INSTALLMENTS' }, + { status: 'ACTIVE', name: 'PAY_UPON_INVOICE' }, + { status: 'ACTIVE', name: 'PAY_WITH_PAYPAL' }, + { status: 'ACTIVE', name: 'PAYPAL_CHECKOUT_ALTERNATIVE_PAYMENT_METHODS' }, + { status: 'ACTIVE', name: 'PAYPAL_CHECKOUT_PAY_WITH_PAYPAL_CREDIT' }, + { status: 'ACTIVE', name: 'PAYPAL_CHECKOUT' }, + { status: 'ACTIVE', name: 'PAYPAL_WALLET_VAULTING_ADVANCED' }, + { status: 'ACTIVE', name: 'QR_CODE' }, + { status: 'ACTIVE', name: 'SEND_INVOICE' }, + { status: 'ACTIVE', name: 'SEPA' }, + { status: 'ACTIVE', name: 'SUBSCRIPTIONS' }, + { status: 'ACTIVE', name: 'VAT_TAX' }, + { status: 'ACTIVE', name: 'WITHDRAW_FUNDS_TO_DOMESTIC_BANK' }, + ], + oauth_integrations: [ + { + integration_method: 'PAYPAL', + integration_type: 'OAUTH_THIRD_PARTY', + oauth_third_party: [{ + merchant_client_id: 'xxFgvoYN7vzQ5KsoB4J7T1-8ylwpdVxlXCT0v0bMOILlfa4zvV8CQk4GdRXRfkPx3n4Jer_RjOH7OBzJ', + partner_client_id: '2H9kWfD51juip11YX0IN7SCYMq_BxXpHUnZV9hS6jx0EBaAxvDWEzJGR6XhDAfoRgiMAQGalsW9UNmmB', + scopes: [ + 'https://uri.paypal.com/services/payments/delay-funds-disbursement', + 'https://uri.paypal.com/services/payments/realtimepayment', + 'https://uri.paypal.com/services/reporting/search/read', + 'https://uri.paypal.com/services/payments/refund', + 'https://uri.paypal.com/services/customer/merchant-integrations/read', + 'https://uri.paypal.com/services/disputes/update-seller', + 'https://uri.paypal.com/services/payments/payment/authcapture', + 'https://uri.paypal.com/services/billing-agreements', + 'https://uri.paypal.com/services/vault/payment-tokens/read', + 'https://uri.paypal.com/services/vault/payment-tokens/readwrite', + 'https://uri.paypal.com/services/disputes/read-seller', + 'https://uri.paypal.com/services/shipping/trackers/readwrite', + ], + }], + }, + ], + granted_permissions: [], + payments_receivable: true, + legal_name: "Example's Test Store", + primary_email: 'test@example.com', + primary_email_confirmed: true, + }, + capabilities: { + 'some-payment-method-id': 'active', + }, +} satisfies PayPal.Setting<'merchant_information'>; + +const NotLoggedIn = { + merchantIntegrations: null, + capabilities: { + 'some-payment-method-id': 'inactive', + }, +} satisfies PayPal.Setting<'merchant_information'>; + +const NonPPCP = { + ...Default, + merchantIntegrations: { + ...Default.merchantIntegrations, + products: Default.merchantIntegrations.products.filter(({ name }) => !name.includes('PPCP')), + capabilities: Default.merchantIntegrations.capabilities.filter(({ name }) => !name.includes('PAYPAL_CHECKOUT')), + }, +} satisfies PayPal.Setting<'merchant_information'>; + +const NonVault = { + ...Default, + merchantIntegrations: { + ...Default.merchantIntegrations, + products: Default.merchantIntegrations.products.filter(({ name }) => !name.includes('VAULTING')), // ! PPCP_CUSTOM.capabilities still includes VAULTING ! + capabilities: Default.merchantIntegrations.capabilities.filter(({ name }) => !name.includes('VAULTING')), + }, +} satisfies PayPal.Setting<'merchant_information'>; + +export default { + Default, + NotLoggedIn, + NonPPCP, + NonVault, +}; diff --git a/src/Resources/app/administration/src/app/store/settings.fixture.ts b/src/Resources/app/administration/src/app/store/settings.fixture.ts new file mode 100644 index 000000000..8d1ce44e8 --- /dev/null +++ b/src/Resources/app/administration/src/app/store/settings.fixture.ts @@ -0,0 +1,83 @@ +import type * as PayPal from 'src/types'; + +const Default = { + 'SwagPayPal.settings.sandbox': false, + 'SwagPayPal.settings.intent': 'CAPTURE', + 'SwagPayPal.settings.submitCart': true, + 'SwagPayPal.settings.landingPage': 'NO_PREFERENCE', + 'SwagPayPal.settings.sendOrderNumber': true, + 'SwagPayPal.settings.ecsDetailEnabled': true, + 'SwagPayPal.settings.ecsCartEnabled': true, + 'SwagPayPal.settings.ecsOffCanvasEnabled': true, + 'SwagPayPal.settings.ecsLoginEnabled': true, + 'SwagPayPal.settings.ecsListingEnabled': false, + 'SwagPayPal.settings.ecsButtonColor': 'gold', + 'SwagPayPal.settings.ecsButtonShape': 'sharp', + 'SwagPayPal.settings.ecsShowPayLater': true, + 'SwagPayPal.settings.ecsButtonLanguageIso': null, + + 'SwagPayPal.settings.spbButtonColor': 'gold', + 'SwagPayPal.settings.spbButtonShape': 'sharp', + 'SwagPayPal.settings.spbButtonLanguageIso': null, + 'SwagPayPal.settings.spbShowPayLater': true, + 'SwagPayPal.settings.spbCheckoutEnabled': true, + 'SwagPayPal.settings.spbAlternativePaymentMethodsEnabled': false, + + 'SwagPayPal.settings.installmentBannerDetailPageEnabled': true, + 'SwagPayPal.settings.installmentBannerCartEnabled': true, + 'SwagPayPal.settings.installmentBannerOffCanvasCartEnabled': true, + 'SwagPayPal.settings.installmentBannerLoginPageEnabled': true, + 'SwagPayPal.settings.installmentBannerFooterEnabled': true, + + 'SwagPayPal.settings.vaultingEnabledWallet': false, + 'SwagPayPal.settings.vaultingEnabledACDC': false, + 'SwagPayPal.settings.vaultingEnabledVenmo': false, + + 'SwagPayPal.settings.acdcForce3DS': false, + + 'SwagPayPal.settings.excludedProductIds': [], + 'SwagPayPal.settings.excludedProductStreamIds': [], + + 'SwagPayPal.settings.crossBorderMessagingEnabled': false, + 'SwagPayPal.settings.crossBorderBuyerCountry': null, + + /** + * @deprecated tag:v10.0.0 - Will be removed without replacement. + */ + 'SwagPayPal.settings.merchantLocation': 'other', + + /** + * @deprecated tag:v10.0.0 - Will be removed without replacement. + */ + 'SwagPayPal.settings.plusCheckoutEnabled': false, +} satisfies PayPal.SystemConfig; + +const All = { + ...Default, + 'SwagPayPal.settings.clientId': '', + 'SwagPayPal.settings.clientSecret': '', + 'SwagPayPal.settings.clientIdSandbox': '', + 'SwagPayPal.settings.clientSecretSandbox': '', + 'SwagPayPal.settings.merchantPayerId': '', + 'SwagPayPal.settings.merchantPayerIdSandbox': '', + + 'SwagPayPal.settings.webhookId': '', + 'SwagPayPal.settings.webhookExecuteToken': '', + 'SwagPayPal.settings.brandName': '', + 'SwagPayPal.settings.orderNumberPrefix': '', + 'SwagPayPal.settings.orderNumberSuffix': '', + + 'SwagPayPal.settings.puiCustomerServiceInstructions': '', + + 'SwagPayPal.settings.vaultingEnabled': false, + 'SwagPayPal.settings.vaultingEnableAlways': false, +} satisfies Required; + +const WithCredentials = { + ...Default, + 'SwagPayPal.settings.clientId': 'some-client-id', + 'SwagPayPal.settings.clientSecret': 'some-client-secret', + 'SwagPayPal.settings.merchantPayerId': 'some-merchant-payer-id', +}; + +export default { Default, All, WithCredentials }; diff --git a/src/Resources/app/administration/src/app/store/swag-paypal-merchant-information.store.spec.ts b/src/Resources/app/administration/src/app/store/swag-paypal-merchant-information.store.spec.ts new file mode 100644 index 000000000..17bbf318a --- /dev/null +++ b/src/Resources/app/administration/src/app/store/swag-paypal-merchant-information.store.spec.ts @@ -0,0 +1,120 @@ +import MIFixture from './merchant-information.fixture'; +import './swag-paypal-merchant-information.store'; + +describe('swag-paypal-merchant-information.store', () => { + const store = Shopware.Store.get('swagPayPalMerchantInformation'); + + afterEach(() => { + store.$reset(); + }); + + it('shoud be a pinia store', () => { + expect(store.$id).toBe('swagPayPalMerchantInformation'); + }); + + it('should have correct default state', () => { + // state + expect(store.salesChannel).toBeNull(); + expect(store.allMerchantInformations).toStrictEqual({}); + + // actions + expect(store.has(null)).toBe(false); + + // getters + expect(store.isLoading).toBe(true); + expect(store.actual).toStrictEqual({ + merchantIntegrations: null, + capabilities: {}, + }); + expect(store.products).toStrictEqual([]); + expect(store.capabilities).toStrictEqual({}); + expect(store.merchantCapabilities).toStrictEqual([]); + expect(store.canVault).toBe(false); + expect(store.canPPCP).toBe(false); + expect(store.needsReonboarding).toBe(false); + }); + + it('should have correct root state', () => { + store.set(null, MIFixture.Default); + + // state + expect(store.salesChannel).toBeNull(); + expect(store.allMerchantInformations).toStrictEqual({ null: MIFixture.Default }); + + // actions + expect(store.has(null)).toBe(true); + + // getters + expect(store.isLoading).toBe(false); + expect(store.actual).toStrictEqual(MIFixture.Default); + expect(store.products).toStrictEqual(MIFixture.Default.merchantIntegrations.products); + expect(store.capabilities).toStrictEqual(MIFixture.Default.capabilities); + expect(store.merchantCapabilities).toStrictEqual(MIFixture.Default.merchantIntegrations.capabilities); + expect(store.canVault).toBe(true); + expect(store.canPPCP).toBe(true); + expect(store.needsReonboarding).toBe(false); + }); + + it('should have correct non-vault state', () => { + store.set(null, MIFixture.NonVault); + + // state + expect(store.salesChannel).toBeNull(); + expect(store.allMerchantInformations).toStrictEqual({ null: MIFixture.NonVault }); + + // actions + expect(store.has(null)).toBe(true); + + // getters + expect(store.isLoading).toBe(false); + expect(store.actual).toStrictEqual(MIFixture.NonVault); + expect(store.products).toStrictEqual(MIFixture.NonVault.merchantIntegrations.products); + expect(store.capabilities).toStrictEqual(MIFixture.NonVault.capabilities); + expect(store.merchantCapabilities).toStrictEqual(MIFixture.NonVault.merchantIntegrations.capabilities); + expect(store.canVault).toBe(false); + expect(store.canPPCP).toBe(true); + expect(store.needsReonboarding).toBe(false); + }); + + it('should have correct non-ppcp state', () => { + store.set(null, MIFixture.NonPPCP); + + // state + expect(store.salesChannel).toBeNull(); + expect(store.allMerchantInformations).toStrictEqual({ null: MIFixture.NonPPCP }); + + // actions + expect(store.has(null)).toBe(true); + + // getters + expect(store.isLoading).toBe(false); + expect(store.actual).toStrictEqual(MIFixture.NonPPCP); + expect(store.products).toStrictEqual(MIFixture.NonPPCP.merchantIntegrations.products); + expect(store.capabilities).toStrictEqual(MIFixture.NonPPCP.capabilities); + expect(store.merchantCapabilities).toStrictEqual(MIFixture.NonPPCP.merchantIntegrations.capabilities); + expect(store.canVault).toBe(true); + expect(store.canPPCP).toBe(false); + expect(store.needsReonboarding).toBe(false); + }); + + it('should have correct not-logged-in state', () => { + store.set(null, MIFixture.NotLoggedIn); + + // state + expect(store.salesChannel).toBeNull(); + expect(store.allMerchantInformations).toStrictEqual({ null: MIFixture.NotLoggedIn }); + + // actions + expect(store.has(null)).toBe(true); + + // getters + expect(store.isLoading).toBe(false); + expect(store.actual).toStrictEqual(MIFixture.NotLoggedIn); + expect(store.products).toStrictEqual([]); + expect(store.capabilities).toStrictEqual(MIFixture.NotLoggedIn.capabilities); + expect(store.merchantCapabilities).toStrictEqual([]); + expect(store.canVault).toBe(false); + expect(store.canPPCP).toBe(false); + expect(store.needsReonboarding).toBe(true); + }); +}); diff --git a/src/Resources/app/administration/src/app/store/swag-paypal-settings.store.spec.ts b/src/Resources/app/administration/src/app/store/swag-paypal-settings.store.spec.ts new file mode 100644 index 000000000..3e411963a --- /dev/null +++ b/src/Resources/app/administration/src/app/store/swag-paypal-settings.store.spec.ts @@ -0,0 +1,122 @@ +import SettingsFixture from './settings.fixture'; +import './swag-paypal-settings.store'; + +describe('swag-paypal-settings.store', () => { + const store = Shopware.Store.get('swagPayPalSettings'); + + afterEach(() => { + store.$reset(); + }); + + it('shoud be a pinia store', () => { + expect(store.$id).toBe('swagPayPalSettings'); + }); + + it('should have correct default state', () => { + // state + expect(store.salesChannel).toBeNull(); + expect(store.allConfigs).toStrictEqual({}); + + // actions + expect(store.hasConfig(null)).toBe(false); + + // getters + expect(store.isLoading).toBe(true); + expect(store.isSandbox).toBe(false); + expect(store.root).toStrictEqual({}); + expect(store.actual).toStrictEqual({}); + }); + + it('should have correct root state', () => { + store.setConfig(null, SettingsFixture.Default); + + // state + expect(store.salesChannel).toBeNull(); + expect(store.allConfigs).toStrictEqual({ null: SettingsFixture.Default }); + + // actions + expect(store.hasConfig(null)).toBe(true); + + // getters + expect(store.isLoading).toBe(false); + expect(store.isSandbox).toBe(false); + expect(store.root).toStrictEqual(SettingsFixture.Default); + expect(store.actual).toStrictEqual(SettingsFixture.Default); + }); + + it('should have correct actual state', () => { + const actual = { 'SwagPayPal.settings.sandbox': true }; + + store.setConfig(null, SettingsFixture.Default); + store.salesChannel = 'some-other-id'; + store.setConfig('some-other-id', actual); + + // state + expect(store.salesChannel).toBe('some-other-id'); + expect(store.allConfigs).toStrictEqual({ + null: SettingsFixture.Default, + 'some-other-id': actual, + }); + + // actions + expect(store.hasConfig(null)).toBe(true); + expect(store.hasConfig('some-other-id')).toBe(true); + + // getters + expect(store.isLoading).toBe(false); + expect(store.isSandbox).toBe(true); + expect(store.root).toStrictEqual(SettingsFixture.Default); + expect(store.actual).toStrictEqual(actual); + }); + + it('should have inherit correctly with root value', () => { + store.setConfig(null, SettingsFixture.Default); + store.salesChannel = 'some-other-id'; + store.setConfig('some-other-id', {}); + + const key = 'SwagPayPal.settings.intent'; + + expect(store.get(key)).toBe('CAPTURE'); + expect(store.getRoot(key)).toBe('CAPTURE'); + expect(store.getActual(key)).toBeUndefined(); + + store.set(key, 'AUTORIZE'); + expect(store.get(key)).toBe('AUTORIZE'); + expect(store.getRoot(key)).toBe('CAPTURE'); + expect(store.getActual(key)).toBe('AUTORIZE'); + }); + + it('should have inherit correctly without root value', () => { + store.setConfig(null, SettingsFixture.Default); + store.salesChannel = 'some-other-id'; + store.setConfig('some-other-id', {}); + + const key = 'SwagPayPal.settings.clientId'; + + expect(store.get(key)).toBeUndefined(); + expect(store.getRoot(key)).toBeUndefined(); + expect(store.getActual(key)).toBeUndefined(); + + store.set(key, 'some-client-id'); + expect(store.get(key)).toBe('some-client-id'); + expect(store.getRoot(key)).toBeUndefined(); + expect(store.getActual(key)).toBe('some-client-id'); + }); + + it('should have inherit correctly with root NULL value', () => { + store.setConfig(null, SettingsFixture.Default); + store.salesChannel = 'some-other-id'; + store.setConfig('some-other-id', {}); + + const key = 'SwagPayPal.settings.crossBorderBuyerCountry'; + + expect(store.get(key)).toBeUndefined(); + expect(store.getRoot(key)).toBeUndefined(); + expect(store.getActual(key)).toBeUndefined(); + + store.set(key, 'de-DE'); + expect(store.get(key)).toBe('de-DE'); + expect(store.getRoot(key)).toBeUndefined(); + expect(store.getActual(key)).toBe('de-DE'); + }); +}); diff --git a/src/Resources/app/administration/src/module/swag-paypal-settings/components/swag-paypal-settings-webhook/swag-paypal-settings-webhook.spec.ts b/src/Resources/app/administration/src/module/swag-paypal-settings/components/swag-paypal-settings-webhook/swag-paypal-settings-webhook.spec.ts new file mode 100644 index 000000000..8358acef5 --- /dev/null +++ b/src/Resources/app/administration/src/module/swag-paypal-settings/components/swag-paypal-settings-webhook/swag-paypal-settings-webhook.spec.ts @@ -0,0 +1,153 @@ +import { mount } from '@vue/test-utils'; +import SwagPayPalSettingsWebhook from '.'; + +Shopware.Component.register('swag-paypal-settings-webhook', Promise.resolve(SwagPayPalSettingsWebhook)); + +async function createWrapper() { + return mount( + await Shopware.Component.build('swag-paypal-settings-webhook') as typeof SwagPayPalSettingsWebhook, + { + global: { + mocks: { $tc: (key: string) => key }, + provide: { + acl: { + can: () => true, + }, + SwagPayPalWebhookService: { + status: jest.fn(() => Promise.resolve({ result: null })), + register: jest.fn(() => Promise.resolve()), + }, + }, + stubs: { + 'sw-button': await wrapTestComponent('sw-button', { sync: true }), + 'sw-card': await wrapTestComponent('sw-card', { sync: true }), + 'sw-card-deprecated': await wrapTestComponent('sw-card-deprecated', { sync: true }), + 'sw-label': await wrapTestComponent('sw-label', { sync: true }), + }, + }, + }, + ); +} + +describe('swag-paypal-settings-webhook', () => { + it('should be a Vue.js component', async () => { + const wrapper = await createWrapper(); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm).toBeTruthy(); + }); + + it('should fetch status on creation', async () => { + const wrapper = await createWrapper(); + + expect(wrapper.vm.SwagPayPalWebhookService.status).toBeCalled(); + }); + + it('should pick correct status variant', async () => { + const wrapper = await createWrapper(); + + wrapper.vm.allWebhookStatus.null = 'valid'; + expect(wrapper.vm.webhookStatusVariant).toBe('success'); + + wrapper.vm.allWebhookStatus.null = 'missing'; + expect(wrapper.vm.webhookStatusVariant).toBe('danger'); + + wrapper.vm.allWebhookStatus.null = 'invalid'; + expect(wrapper.vm.webhookStatusVariant).toBe('warning'); + + wrapper.vm.allWebhookStatus.null = ''; + expect(wrapper.vm.webhookStatusVariant).toBe('neutral'); + + wrapper.vm.allWebhookStatus.null = undefined; + expect(wrapper.vm.webhookStatusVariant).toBe('neutral'); + }); + + it('should allow refresh', async () => { + const wrapper = await createWrapper(); + + wrapper.vm.allWebhookStatus.null = 'valid'; + expect(wrapper.vm.allowRefresh).toBe(false); + + wrapper.vm.allWebhookStatus.null = 'missing'; + expect(wrapper.vm.allowRefresh).toBe(true); + + wrapper.vm.allWebhookStatus.null = 'invalid'; + expect(wrapper.vm.allowRefresh).toBe(true); + + wrapper.vm.allWebhookStatus.null = ''; + expect(wrapper.vm.allowRefresh).toBe(false); + + wrapper.vm.allWebhookStatus.null = undefined; + expect(wrapper.vm.allowRefresh).toBe(false); + }); + + it('should have correct status label', async () => { + const wrapper = await createWrapper(); + + wrapper.vm.allWebhookStatus.null = 'valid'; + expect(wrapper.vm.webhookStatusLabel).toBe('swag-paypal-settings.webhook.status.valid'); + + wrapper.vm.allWebhookStatus.null = 'missing'; + expect(wrapper.vm.webhookStatusLabel).toBe('swag-paypal-settings.webhook.status.missing'); + + wrapper.vm.allWebhookStatus.null = 'invalid'; + expect(wrapper.vm.webhookStatusLabel).toBe('swag-paypal-settings.webhook.status.invalid'); + + wrapper.vm.allWebhookStatus.null = ''; + expect(wrapper.vm.webhookStatusLabel).toBe('swag-paypal-settings.webhook.status.unknown'); + + wrapper.vm.allWebhookStatus.null = undefined; + expect(wrapper.vm.webhookStatusLabel).toBe('swag-paypal-settings.webhook.status.unknown'); + }); + + it('should fetch webhook status', async () => { + const wrapper = await createWrapper(); + + const spyStatus = jest.spyOn(wrapper.vm.SwagPayPalWebhookService, 'status'); + + wrapper.vm.fetchWebhookStatus(null); + + expect(wrapper.vm.status).toBe('fetching'); + expect(spyStatus).toBeCalled(); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.status).toBe('none'); + }); + + it('should refresh webhook', async () => { + const wrapper = await createWrapper(); + + const spyStatus = jest.spyOn(wrapper.vm.SwagPayPalWebhookService, 'status'); + const spyRegister = jest.spyOn(wrapper.vm.SwagPayPalWebhookService, 'register'); + + wrapper.vm.onRefreshWebhook(); + + expect(wrapper.vm.status).toBe('refreshing'); + expect(spyRegister).toBeCalled(); + + await flushPromises(); + + expect(wrapper.vm.status).toBe('none'); + + await wrapper.vm.$nextTick(); + + expect(spyStatus).toBeCalled(); + }); + + it('should refresh webhook with error', async () => { + const wrapper = await createWrapper(); + + wrapper.vm.createNotificationError = jest.fn(); + + wrapper.vm.SwagPayPalWebhookService.register = jest.fn(() => Promise.reject({ response: {} })); + + wrapper.vm.onRefreshWebhook(); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.status).toBe('refreshing'); + expect(wrapper.vm.createNotificationError).toBeCalled(); + }); +}); diff --git a/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.spec.ts b/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.spec.ts new file mode 100644 index 000000000..1768a286b --- /dev/null +++ b/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.spec.ts @@ -0,0 +1,77 @@ +import { mount } from '@vue/test-utils'; +import SwagPayPalSettingsAdvanced from '.'; +import type SwagPayPalSetting from 'SwagPayPal/app/component/swag-paypal-setting'; + +Shopware.Component.register('swag-paypal-settings-advanced', Promise.resolve(SwagPayPalSettingsAdvanced)); +Shopware.Component.register('swag-paypal-setting', () => import('SwagPayPal/app/component/swag-paypal-setting')); + +async function createWrapper() { + return mount( + await Shopware.Component.build('swag-paypal-settings-advanced') as typeof SwagPayPalSettingsAdvanced, + { + global: { + stubs: { + 'sw-card': await wrapTestComponent('sw-card', { sync: true }), + 'sw-card-deprecated': await wrapTestComponent('sw-card-deprecated', { sync: true }), + 'sw-alert': await wrapTestComponent('sw-alert', { sync: true }), + 'sw-alert-deprecated': await wrapTestComponent('sw-alert-deprecated', { sync: true }), + 'swag-paypal-setting': await Shopware.Component.build('swag-paypal-setting'), + 'swag-paypal-settings-webhook': true, + }, + }, + }, + ); +} + +describe('swag-paypal-settings-advanced', () => { + it('should be a Vue.js component', async () => { + const wrapper = await createWrapper(); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm).toBeTruthy(); + }); + + it('should have settings cards', async () => { + const wrapper = await createWrapper(); + + const cardClasses = wrapper + .findAll('.sw-card') + .map((el) => el.classes()) + .flat() + .filter((cl) => cl.startsWith('swag-paypal')); + + expect(cardClasses).toEqual([ + 'swag-paypal-settings-cross-border', + ]); + }); + + it('should have settings', async () => { + const wrapper = await createWrapper(); + + const components = wrapper.findAllComponents({ name: 'swag-paypal-setting' }); + const settings = Object.fromEntries(components.map((el) => [el.props().path, el])); + + expect(Object.keys(settings)).toEqual([ + 'SwagPayPal.settings.crossBorderMessagingEnabled', + 'SwagPayPal.settings.crossBorderBuyerCountry', + ]); + + expect(settings['SwagPayPal.settings.crossBorderBuyerCountry'].vm.$attrs.options) + .toBe(wrapper.vm.countryOverrideOptions); + }); + + it('should have cross-border information', async () => { + const wrapper = await createWrapper(); + + const alert = wrapper.find('.sw-alert'); + + expect(alert.exists()).toBe(true); + expect(alert.classes()).toContain('swag-paypal-settings-cross-border__warning-text'); + + const info = wrapper.find('.swag-paypal-settings-cross-border__info-text'); + + expect(info.exists()).toBe(true); + expect(info.text()).toBe('swag-paypal-settings.crossBorder.info'); + }); +}); diff --git a/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-general/swag-paypal-settings-general.spec.ts b/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-general/swag-paypal-settings-general.spec.ts new file mode 100644 index 000000000..e1da8fd21 --- /dev/null +++ b/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-general/swag-paypal-settings-general.spec.ts @@ -0,0 +1,160 @@ +import { mount } from '@vue/test-utils'; +import SwagPayPalSettingsGeneral from '.'; +import SettingsFixture from '../../../../app/store/settings.fixture'; +import type SwagPayPalSetting from 'SwagPayPal/app/component/swag-paypal-setting'; + +Shopware.Component.register('swag-paypal-settings-general', Promise.resolve(SwagPayPalSettingsGeneral)); +Shopware.Component.register('swag-paypal-setting', () => import('SwagPayPal/app/component/swag-paypal-setting')); + +async function createWrapper() { + return mount( + await Shopware.Component.build('swag-paypal-settings-general') as typeof SwagPayPalSettingsGeneral, + { + global: { + provide: { acl: { can: () => true } }, + mocks: { $tc: (key: string) => key }, + stubs: { + 'swag-paypal-setting': await Shopware.Component.build('swag-paypal-setting'), + 'sw-inherit-wrapper': await wrapTestComponent('sw-inherit-wrapper', { sync: true }), + 'sw-switch-field': await wrapTestComponent('sw-switch-field', { sync: true }), + 'sw-switch-field-deprecated': await wrapTestComponent('sw-switch-field-deprecated', { sync: true }), + 'sw-checkbox-field': await wrapTestComponent('sw-checkbox-field', { sync: true }), + 'sw-checkbox-field-deprecated': await wrapTestComponent('sw-checkbox-field-deprecated', { sync: true }), + + 'sw-card': await wrapTestComponent('sw-card', { sync: true }), + 'sw-card-deprecated': await wrapTestComponent('sw-card-deprecated', { sync: true }), + 'sw-base-field': await wrapTestComponent('sw-base-field', { sync: true }), + }, + }, + }, + ); +} + +describe('swag-paypal-settings-general', () => { + const store = Shopware.Store.get('swagPayPalSettings'); + + beforeEach(() => { + store.setConfig(null, SettingsFixture.Default); + }); + + afterEach(() => { + store.$reset(); + }); + + it('should be a Vue.js component', async () => { + const wrapper = await createWrapper(); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm).toBeTruthy(); + }); + + it('should have settings cards', async () => { + const wrapper = await createWrapper(); + + const cardClasses = wrapper + .findAll('.sw-card') + .map((el) => el.classes()) + .flat() + .filter((cl) => cl.startsWith('swag-paypal')); + + expect(cardClasses).toEqual([ + 'swag-paypal-settings-live-credentials', + 'swag-paypal-settings-sandbox-credentials', + 'swag-paypal-settings-behavior', + 'swag-paypal-settings-vaulting', + 'swag-paypal-settings-acdc', + 'swag-paypal-settings-pui', + ]); + }); + + it('should have settings', async () => { + const wrapper = await createWrapper(); + + const components = wrapper.findAllComponents({ name: 'swag-paypal-setting' }); + const settings = Object.fromEntries(components.map((el) => [el.props().path, el])); + + expect(Object.keys(settings)).toEqual([ + 'SwagPayPal.settings.sandbox', + 'SwagPayPal.settings.clientId', + 'SwagPayPal.settings.clientSecret', + 'SwagPayPal.settings.merchantPayerId', + 'SwagPayPal.settings.clientIdSandbox', + 'SwagPayPal.settings.clientSecretSandbox', + 'SwagPayPal.settings.merchantPayerIdSandbox', + 'SwagPayPal.settings.intent', + 'SwagPayPal.settings.submitCart', + 'SwagPayPal.settings.brandName', + 'SwagPayPal.settings.landingPage', + 'SwagPayPal.settings.sendOrderNumber', + 'SwagPayPal.settings.orderNumberPrefix', + 'SwagPayPal.settings.orderNumberSuffix', + 'SwagPayPal.settings.excludedProductIds', + 'SwagPayPal.settings.excludedProductStreamIds', + 'SwagPayPal.settings.acdcForce3DS', + 'SwagPayPal.settings.puiCustomerServiceInstructions', + ]); + }); + + it('should invert sandbox toggle for live and sandbox', async () => { + const wrapper = await createWrapper(); + + const liveSwitch = wrapper.findComponent('.swag-paypal-settings-live-credentials .sw-field--switch'); + expect(liveSwitch.exists()).toBe(true); + + const sandboxSwitch = wrapper.findComponent('.swag-paypal-settings-sandbox-credentials .sw-field--switch'); + expect(sandboxSwitch.exists()).toBe(true); + + expect(store.isSandbox).toBe(false); + expect(liveSwitch.vm.value).toBe(true); + expect(sandboxSwitch.vm.value).toBe(false); + + // Switch trough store + store.set('SwagPayPal.settings.sandbox', true); + + await wrapper.vm.$nextTick(); + + expect(liveSwitch.vm.value).toBe(false); + expect(sandboxSwitch.vm.value).toBe(true); + + // Switch trough UI + await sandboxSwitch.find('input').setValue(false); + expect(liveSwitch.vm.value).toBe(true); + expect(sandboxSwitch.vm.value).toBe(false); + + await liveSwitch.find('input').setValue(false); + expect(liveSwitch.vm.value).toBe(false); + expect(sandboxSwitch.vm.value).toBe(true); + }); + + it('should disable credentials fields on sandbox toggle', async () => { + const wrapper = await createWrapper(); + + const components = wrapper.findAllComponents({ name: 'swag-paypal-setting' }); + const settings = Object.fromEntries(components.map((el) => [el.props().path, el])); + + const live = [ + 'SwagPayPal.settings.clientId', + 'SwagPayPal.settings.clientSecret', + 'SwagPayPal.settings.merchantPayerId', + ]; + + const sandbox = [ + 'SwagPayPal.settings.clientIdSandbox', + 'SwagPayPal.settings.clientSecretSandbox', + 'SwagPayPal.settings.merchantPayerIdSandbox', + ]; + + store.set('SwagPayPal.settings.sandbox', true); + await wrapper.vm.$nextTick(); + + expect(live.map((setting) => settings[setting]?.vm.formAttrs.disabled)).toContain(true); + expect(sandbox.map((setting) => settings[setting]?.vm.formAttrs.disabled)).toContain(false); + + store.set('SwagPayPal.settings.sandbox', false); + await wrapper.vm.$nextTick(); + + expect(live.map((setting) => settings[setting]?.vm.formAttrs.disabled)).toContain(false); + expect(sandbox.map((setting) => settings[setting]?.vm.formAttrs.disabled)).toContain(true); + }); +}); diff --git a/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-storefront/swag-paypal-settings-storefront.spec.ts b/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-storefront/swag-paypal-settings-storefront.spec.ts new file mode 100644 index 000000000..043705109 --- /dev/null +++ b/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-storefront/swag-paypal-settings-storefront.spec.ts @@ -0,0 +1,144 @@ +import { mount } from '@vue/test-utils'; +import SwagPayPalSettingsStorefront from '.'; +import SwagPayPalSetting from 'SwagPayPal/app/component/swag-paypal-setting'; +import { SYSTEM_CONFIGS } from '../../../../constant/swag-paypal-settings.constant'; +import SettingsFixture from '../../../../app/store/settings.fixture'; + +Shopware.Component.register('swag-paypal-settings-storefront', Promise.resolve(SwagPayPalSettingsStorefront)); +Shopware.Component.register('swag-paypal-setting', Promise.resolve(SwagPayPalSetting)); + +async function createWrapper(options = {}) { + return mount( + await Shopware.Component.build('swag-paypal-settings-storefront') as typeof SwagPayPalSettingsStorefront, + Shopware.Utils.object.merge({ + global: { + stubs: { + 'sw-card': await wrapTestComponent('sw-card', { sync: true }), + 'sw-card-deprecated': await wrapTestComponent('sw-card-deprecated', { sync: true }), + 'swag-paypal-setting': await Shopware.Component.build('swag-paypal-setting'), + }, + provide: { + acl: { can: () => true }, + systemConfigApiService: { + getValues: () => false, + }, + }, + }, + }, options), + ); +} + +describe('swag-paypal-settings-storefront', () => { + const store = Shopware.Store.get('swagPayPalSettings'); + + it('should be a Vue.js component', async () => { + const wrapper = await createWrapper(); + + await wrapper.vm.$nextTick(); + + expect(wrapper.vm).toBeTruthy(); + }); + + it('should have settings cards', async () => { + const wrapper = await createWrapper(); + + const cardClasses = wrapper + .findAll('.sw-card') + .map((el) => el.classes()) + .flat() + .filter((cl) => cl.startsWith('swag-paypal')); + + expect(cardClasses).toEqual([ + 'swag-paypal-settings-express', + 'swag-paypal-settings-installment', + 'swag-paypal-settings-spb', + ]); + }); + + it('should have settings', async () => { + const wrapper = await createWrapper(); + + const components = wrapper.findAllComponents({ name: 'swag-paypal-setting' }); + const settings = Object.fromEntries(components.map((el) => [el.props().path, el])); + + expect(Object.keys(settings)).toEqual([ + 'SwagPayPal.settings.ecsDetailEnabled', + 'SwagPayPal.settings.ecsCartEnabled', + 'SwagPayPal.settings.ecsOffCanvasEnabled', + 'SwagPayPal.settings.ecsLoginEnabled', + 'SwagPayPal.settings.ecsListingEnabled', + 'SwagPayPal.settings.ecsButtonColor', + 'SwagPayPal.settings.ecsButtonShape', + 'SwagPayPal.settings.ecsButtonLanguageIso', + 'SwagPayPal.settings.ecsShowPayLater', + 'SwagPayPal.settings.installmentBannerDetailPageEnabled', + 'SwagPayPal.settings.installmentBannerCartEnabled', + 'SwagPayPal.settings.installmentBannerOffCanvasCartEnabled', + 'SwagPayPal.settings.installmentBannerLoginPageEnabled', + 'SwagPayPal.settings.installmentBannerFooterEnabled', + 'SwagPayPal.settings.spbCheckoutEnabled', + 'SwagPayPal.settings.spbAlternativePaymentMethodsEnabled', + 'SwagPayPal.settings.spbShowPayLater', + 'SwagPayPal.settings.spbButtonColor', + 'SwagPayPal.settings.spbButtonShape', + 'SwagPayPal.settings.spbButtonLanguageIso', + ]); + }); + + it('should disable ecs fields based on ecsSettingsDisabled', async () => { + store.setConfig(null, SettingsFixture.Default); + const wrapper = await createWrapper(); + + const components = wrapper.findAllComponents({ name: 'swag-paypal-setting' }); + const settings = Object.fromEntries(components.map((el) => [el.props().path, el])); + + const disabledSettings = [ + 'SwagPayPal.settings.ecsButtonColor', + 'SwagPayPal.settings.ecsButtonShape', + 'SwagPayPal.settings.ecsButtonLanguageIso', + 'SwagPayPal.settings.ecsShowPayLater', + ]; + + // enable all + SYSTEM_CONFIGS.filter((setting) => setting.startsWith('SwagPayPal.settings.ecs')).forEach((setting) => store.set(setting, true)); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.ecsSettingsDisabled).toBe(false); + expect(disabledSettings.map((setting) => settings[setting]?.vm.formAttrs.disabled)).toContain(false); + + // disable all + SYSTEM_CONFIGS.filter((setting) => setting.startsWith('SwagPayPal.settings.ecs')).forEach((setting) => store.set(setting, false)); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.ecsSettingsDisabled).toBe(true); + expect(disabledSettings.map((setting) => settings[setting]?.vm.formAttrs.disabled)).toContain(true); + }); + + it('should disable spb fields based on spbCheckoutEnabled', async () => { + store.setConfig(null, SettingsFixture.Default); + const wrapper = await createWrapper(); + + const components = wrapper.findAllComponents({ name: 'swag-paypal-setting' }); + const settings = Object.fromEntries(components.map((el) => [el.props().path, el])); + + const disabledSettings = [ + 'SwagPayPal.settings.spbAlternativePaymentMethodsEnabled', + 'SwagPayPal.settings.spbShowPayLater', + 'SwagPayPal.settings.spbButtonColor', + 'SwagPayPal.settings.spbButtonShape', + 'SwagPayPal.settings.spbButtonLanguageIso', + ]; + + store.set('SwagPayPal.settings.spbCheckoutEnabled', true); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.sbpSettingsDisabled).toBe(false); + expect(disabledSettings.map((setting) => settings[setting]?.vm.formAttrs.disabled)).toContain(false); + + store.set('SwagPayPal.settings.spbCheckoutEnabled', false); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.sbpSettingsDisabled).toBe(true); + expect(disabledSettings.map((setting) => settings[setting]?.vm.formAttrs.disabled)).toContain(true); + }); +});