Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Services and utils unit tests #83

Merged
merged 22 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 2 additions & 28 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,39 +13,13 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
uses: actions/checkout@v4

# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
# Override language selection by uncommenting this and choosing your languages
# with:
# languages: go, javascript, csharp, python, cpp, java

# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl

# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language

#- run: |
# make bootstrap
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v2
5 changes: 5 additions & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

cd packages/adyen-salesforce-pwa
npm run test
6 changes: 3 additions & 3 deletions packages/adyen-retail-react-app/build/loadable-stats.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "client",
"hash": "84e140465b7193b7b54a",
"hash": "31d19a4495175b725991",
"publicPath": "/mobify/bundle/development/",
"outputPath": "/Users/aleksandarm/Repositories/adyen-salesforce-headless-commerce-pwa/packages/adyen-retail-react-app/build",
"assetsByChunkName": {
Expand Down Expand Up @@ -54,7 +54,7 @@
{
"type": "asset",
"name": "vendor.js",
"size": 8676138,
"size": 8692636,
"emitted": false,
"comparedForEmit": false,
"cached": true,
Expand All @@ -81,7 +81,7 @@
{
"type": "asset",
"name": "main.js",
"size": 1221022,
"size": 1221139,
"emitted": false,
"comparedForEmit": false,
"cached": true,
Expand Down
12 changes: 6 additions & 6 deletions packages/adyen-retail-react-app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/adyen-retail-react-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"style-loader": "^3.3.3"
},
"dependencies": {
"@adyen/adyen-salesforce-pwa": "1.0.0-beta.3"
"@adyen/adyen-salesforce-pwa": "file:../adyen-salesforce-pwa"
},
"scripts": {
"analyze-build": "cross-env MOBIFY_ANALYZE=true npm run build",
Expand Down
3 changes: 2 additions & 1 deletion packages/adyen-salesforce-pwa/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ module.exports = {
},
collectCoverageFrom: [
'lib/**/*.{js,jsx,mjs,cjs}',
'!lib/scripts/**/*{js,jsx,mjs,cjs}',
'!**/node_modules/**',
'!**/dist/**',
'!**/mocks/**'
'!**/mocks/**',
],
clearMocks: true,
restoreMocks: true,
Expand Down
81 changes: 81 additions & 0 deletions packages/adyen-salesforce-pwa/lib/services/tests/api.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {ApiClient} from '../api'
import {ADYEN_API_BASEPATH} from '../../../mocks/adyenApi/constants'

describe('ApiClient', () => {
let apiClient

beforeEach(() => {
apiClient = new ApiClient(ADYEN_API_BASEPATH, 'mockToken')
Dismissed Show dismissed Hide dismissed
})

afterEach(() => {
jest.restoreAllMocks()
})

it('should construct ApiClient with url and token', () => {
expect(apiClient.url).toBe(ADYEN_API_BASEPATH)
expect(apiClient.token).toBe('mockToken')
})

it('should call fetch with correct parameters for GET request', async () => {
const mockResponse = {data: 'some data'}
const mockJsonPromise = Promise.resolve(mockResponse)
const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise,
ok: true
})

jest.spyOn(global, 'fetch').mockImplementation(() => mockFetchPromise)

const options = {
queryParams: {param: 'value'},
headers: {customHeader: 'customValue'}
}

await apiClient.get(options)

expect(global.fetch).toHaveBeenCalledWith(
`${ADYEN_API_BASEPATH}?param=value`,
expect.objectContaining({
method: 'get',
body: null,
headers: {
'Content-Type': 'application/json',
authorization: `Bearer mockToken`,
Dismissed Show dismissed Hide dismissed
customHeader: 'customValue'
}
})
)
})

it('should call fetch with correct parameters for POST request', async () => {
const mockResponse = {success: true}
const mockJsonPromise = Promise.resolve(mockResponse)
const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise,
ok: true
})

jest.spyOn(global, 'fetch').mockImplementation(() => mockFetchPromise)

const options = {
body: JSON.stringify({key: 'value'}),
headers: {customHeader: 'customValue'}
}

await apiClient.post(options)

expect(global.fetch).toHaveBeenCalledWith(
ADYEN_API_BASEPATH,
expect.objectContaining({
method: 'post',
body: JSON.stringify({key: 'value'}),
headers: {
'Content-Type': 'application/json',
authorization: `Bearer mockToken`,
Dismissed Show dismissed Hide dismissed
customHeader: 'customValue'
}
})
)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {AdyenEnvironmentService} from '../environment'
import {ApiClient} from '../api'

jest.mock('../api', () => {
return {
ApiClient: jest.fn().mockImplementation(() => ({
get: jest.fn()
}))
}
})

describe('AdyenEnvironmentService', () => {
let adyenService
let mockToken = 'mockToken'

beforeEach(() => {
adyenService = new AdyenEnvironmentService(mockToken)
})

afterEach(() => {
jest.clearAllMocks()
})

it('should create an instance of AdyenEnvironmentService with ApiClient', () => {
expect(ApiClient).toHaveBeenCalledWith('/api/adyen/environment', mockToken)
})

it('should fetch environment successfully', async () => {
const mockResponse = {environmentData: 'some data'}
const mockJsonPromise = Promise.resolve(mockResponse)
const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise,
status: 200
})

adyenService.apiClient.get.mockResolvedValueOnce(mockFetchPromise)

const environmentData = await adyenService.fetchEnvironment()

expect(adyenService.apiClient.get).toHaveBeenCalled()
expect(environmentData).toEqual(mockResponse)
})

it('should throw an error when fetchEnvironment gets a status >= 300', async () => {
const mockFetchPromise = Promise.resolve({
status: 400,
statusText: 'Bad Request'
})

adyenService.apiClient.get.mockResolvedValueOnce(mockFetchPromise)

await expect(adyenService.fetchEnvironment()).rejects.toThrow('[object Object]')
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {AdyenPaymentMethodsService} from '../payment-methods'
import {ApiClient} from '../api'
import {CUSTOMER_ID_MOCK, LOCALE_MOCK} from '../../../mocks/adyenApi/constants'

jest.mock('../api', () => {
return {
ApiClient: jest.fn().mockImplementation(() => ({
get: jest.fn()
}))
}
})

describe('AdyenPaymentMethodsService', () => {
let paymentMethodsService
let mockToken = 'mockToken'
let mockCustomerId = CUSTOMER_ID_MOCK
let mockLocale = {id: LOCALE_MOCK}

beforeEach(() => {
paymentMethodsService = new AdyenPaymentMethodsService(mockToken)
})

afterEach(() => {
jest.clearAllMocks()
})

it('should create an instance of AdyenPaymentMethodsService with ApiClient', () => {
expect(ApiClient).toHaveBeenCalledWith('/api/adyen/paymentMethods', mockToken)
})

it('should fetch payment methods successfully', async () => {
const mockResponse = {paymentMethods: ['visa', 'paypal']}
const mockJsonPromise = Promise.resolve(mockResponse)
const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise,
status: 200
})

paymentMethodsService.apiClient.get.mockResolvedValueOnce(mockFetchPromise)

const paymentMethods = await paymentMethodsService.fetchPaymentMethods(
mockCustomerId,
mockLocale
)

expect(paymentMethodsService.apiClient.get).toHaveBeenCalledWith({
queryParams: {locale: mockLocale.id},
headers: {customerid: mockCustomerId}
})
expect(paymentMethods).toEqual(mockResponse)
})

it('should throw an error when fetchPaymentMethods gets a status >= 300', async () => {
const mockFetchPromise = Promise.resolve({
status: 400,
statusText: 'Bad Request'
})

paymentMethodsService.apiClient.get.mockResolvedValueOnce(mockFetchPromise)

await expect(
paymentMethodsService.fetchPaymentMethods(mockCustomerId, mockLocale)
).rejects.toThrow('[object Object]')
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {AdyenPaymentsDetailsService} from '../payments-details'
import {ApiClient} from '../api'

jest.mock('../api', () => {
return {
ApiClient: jest.fn().mockImplementation(() => ({
post: jest.fn()
}))
}
})

describe('AdyenPaymentsDetailsService', () => {
let paymentsDetailsService
let mockToken = 'mockTokenHere'
let mockData = {someData: 'mockData'}
let mockCustomerId = 'mockCustomerId'

beforeEach(() => {
paymentsDetailsService = new AdyenPaymentsDetailsService(mockToken)
})

afterEach(() => {
jest.clearAllMocks()
})

it('should create an instance of AdyenPaymentsDetailsService with ApiClient', () => {
expect(paymentsDetailsService).toBeInstanceOf(AdyenPaymentsDetailsService)
expect(ApiClient).toHaveBeenCalledWith('/api/adyen/payments/details', mockToken)
})

it('should submit payment details successfully', async () => {
const mockResponse = {paymentDetailsResult: 'success'}
const mockJsonPromise = Promise.resolve(mockResponse)
const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise,
status: 200
})

paymentsDetailsService.apiClient.post.mockResolvedValueOnce(mockFetchPromise)

const paymentDetailsResult = await paymentsDetailsService.submitPaymentsDetails(
mockData,
mockCustomerId
)

expect(paymentsDetailsService.apiClient.post).toHaveBeenCalledWith({
body: JSON.stringify({data: mockData}),
headers: {
customerid: mockCustomerId
}
})
expect(paymentDetailsResult).toEqual(mockResponse)
})

it('should throw an error when submitPaymentsDetails gets a status >= 300', async () => {
const mockFetchPromise = Promise.resolve({
status: 400,
statusText: 'Bad Request'
})

paymentsDetailsService.apiClient.post.mockResolvedValueOnce(mockFetchPromise)

await expect(
paymentsDetailsService.submitPaymentsDetails(mockData, mockCustomerId)
).rejects.toThrow('[object Object]')
})
})
Loading