зеркало из https://github.com/Azure/Sia-EventUI.git
Added more tests, attempted to add coverage yaml config for categoriz… (#93)
* Added more tests, attempted to add coverage yaml config for categorization of coverage * formatting improvements, cyclomatic complexity reduction on testableReduxBackedPromise * throw Error rather than string, some adjustment to validateActionSet
This commit is contained in:
Родитель
8504e3e396
Коммит
86437c9f65
|
@ -0,0 +1,34 @@
|
|||
coverage:
|
||||
precision: 2
|
||||
round: down
|
||||
range: "70...100"
|
||||
|
||||
status:
|
||||
project:
|
||||
default: on
|
||||
patch:
|
||||
default: on
|
||||
changes:
|
||||
default: off
|
||||
|
||||
comment:
|
||||
layout: "header, reach, diff, flags, files, footer"
|
||||
behavior: default
|
||||
require_changes: no
|
||||
require_base: no
|
||||
require_head: yes
|
||||
|
||||
flags:
|
||||
actions:
|
||||
paths:
|
||||
- src/actions/
|
||||
reducers:
|
||||
paths:
|
||||
- src/actions/
|
||||
services:
|
||||
paths:
|
||||
- src/services/
|
||||
components:
|
||||
paths:
|
||||
- src/components/
|
||||
- src/containers/
|
|
@ -1,27 +1,11 @@
|
|||
import { authenticatedFetch, authenticatedPost, authenticatedPut } from 'services/authenticatedFetch'
|
||||
|
||||
const needOnActionSet = (prop) => `Need "${prop}" function on actionSet!`
|
||||
export const testableReduxBackedPromise = (localAuthenticatedFetch, localAuthenticatedPost, localAuthenticatedPut) =>
|
||||
(promiseArgs, actionSet, operation = 'GET') =>
|
||||
(dispatch) => {
|
||||
validateActionSet(actionSet)
|
||||
|
||||
export const reduxBackedPromise = (promiseArgs, actionSet, operation = 'GET') => (dispatch) => {
|
||||
if (!actionSet.try) { throw needOnActionSet('try') }
|
||||
if (!actionSet.succeed) { throw needOnActionSet('succeed') }
|
||||
if (!actionSet.fail) { throw needOnActionSet('fail') }
|
||||
|
||||
let promiseGenerator
|
||||
switch (operation.toUpperCase()) {
|
||||
case 'PUT':
|
||||
promiseGenerator = authenticatedPut
|
||||
break
|
||||
case 'POST':
|
||||
promiseGenerator = authenticatedPost
|
||||
break
|
||||
default:
|
||||
promiseGenerator = authenticatedFetch
|
||||
break
|
||||
}
|
||||
if (!promiseGenerator) {
|
||||
throw new Error('promiseGenerator not initialized. This should not be possible. Consider rolling back.')
|
||||
}
|
||||
const promiseGenerator = getPromiseGenerator(localAuthenticatedFetch, localAuthenticatedPost, localAuthenticatedPut)(operation)
|
||||
|
||||
dispatch(actionSet.try())
|
||||
|
||||
|
@ -30,6 +14,44 @@ export const reduxBackedPromise = (promiseArgs, actionSet, operation = 'GET') =>
|
|||
error => dispatch(actionSet.fail(error)))
|
||||
}
|
||||
|
||||
export const reduxBackedPromise = testableReduxBackedPromise(authenticatedFetch, authenticatedPost, authenticatedPut)
|
||||
|
||||
const needOnActionSet = (prop) => `Need "${prop}" function on actionSet!`
|
||||
|
||||
/*
|
||||
Expect actionSet to have shape:
|
||||
{
|
||||
try: () => tryAction,
|
||||
succeed: (returnedObject) => succeedAction,
|
||||
fail: (failureReason) => failureAction
|
||||
}
|
||||
actions can be Thunk actions ((dispatch) => action) or plain action objects
|
||||
*/
|
||||
export const validateActionSet = (actionSet) => {
|
||||
['try', 'succeed', 'fail'].map(
|
||||
a => {
|
||||
if (!actionSet[a] || typeof actionSet[a] !== 'function') {
|
||||
throw new Error(needOnActionSet(a))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export const getPromiseGenerator = (localAuthenticatedFetch, localAuthenticatedPost, localAuthenticatedPut) =>
|
||||
(operation) => {
|
||||
switch (operation.toUpperCase()) {
|
||||
case 'TESTERROR':
|
||||
break // Never intended to happen in a deployed instance, just here for testing
|
||||
case 'PUT':
|
||||
return localAuthenticatedPut
|
||||
case 'POST':
|
||||
return localAuthenticatedPost
|
||||
default:
|
||||
return localAuthenticatedFetch
|
||||
}
|
||||
throw new Error('promiseGenerator not initialized. This should not be possible. Consider rolling back.')
|
||||
}
|
||||
|
||||
const goToPageActionType = (BASE_NAME) => 'GOTO_' + BASE_NAME + '_PAGE'
|
||||
const nextPageActionType = (BASE_NAME) => 'NEXT_' + BASE_NAME + '_PAGE'
|
||||
const prevPageActionType = (BASE_NAME) => 'PREV_' + BASE_NAME + '_PAGE'
|
||||
|
|
|
@ -3,8 +3,8 @@ These actions have no associated reducer, but they provide additional context fo
|
|||
without changing state directly. They're intended to be used with Redux dev tools.
|
||||
*/
|
||||
|
||||
const RAW_HTTP_RESPONSE = 'DEBUG_RAW_HTTP_RESPONSE'
|
||||
const JSON_RESULT = 'DEBUG_JSON_RESULT'
|
||||
export const RAW_HTTP_RESPONSE = 'DEBUG_RAW_HTTP_RESPONSE'
|
||||
export const JSON_RESULT = 'DEBUG_JSON_RESULT'
|
||||
|
||||
// response is not serializable
|
||||
export const rawHttpResponse = (response) => ({
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
'use strict'
|
||||
import { expect } from 'chai'
|
||||
import { expect, assert } from 'chai'
|
||||
|
||||
import * as actionHelpers from 'actions/actionHelpers'
|
||||
import { GetMockDispatch, GetDispatchRecorder } from 'test/helpers/mockDispatch'
|
||||
|
||||
describe('ActionHelpers', function () {
|
||||
describe('paginationActions', function () {
|
||||
|
@ -16,4 +18,46 @@ describe('ActionHelpers', function () {
|
|||
expect(result.types.FILTER).to.equal('FILTER_TEST')
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateActionSet', function () {
|
||||
it('Should throw "Need "try" function on actionSet!" when no try or try is not a function', function () {
|
||||
assert.throws(
|
||||
() => actionHelpers.validateActionSet({}),
|
||||
'Need "try" function on actionSet'
|
||||
)
|
||||
assert.throws(
|
||||
() => actionHelpers.validateActionSet({ try: true }),
|
||||
'Need "try" function on actionSet'
|
||||
)
|
||||
})
|
||||
it('Should throw "Need "succeed" function on actionSet!" when no succeed or succeed is not a function', function () {
|
||||
assert.throws(
|
||||
() => actionHelpers.validateActionSet({ try: () => null }),
|
||||
'Need "succeed" function on actionSet'
|
||||
)
|
||||
assert.throws(
|
||||
() => actionHelpers.validateActionSet({ try: () => null, succeed: true }),
|
||||
'Need "succeed" function on actionSet'
|
||||
)
|
||||
})
|
||||
it('Should throw "Need "fail" function on actionSet!" when no fail or fail is not a function', function () {
|
||||
assert.throws(
|
||||
() => actionHelpers.validateActionSet({ try: () => null, succeed: () => null }),
|
||||
'Need "fail" function on actionSet'
|
||||
)
|
||||
assert.throws(
|
||||
() => actionHelpers.validateActionSet({ try: () => null, succeed: () => null, fail: true }),
|
||||
'Need "fail" function on actionSet'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('testableReduxBackedPromise', function () {
|
||||
const mockSuccess = {
|
||||
json: 'successJson',
|
||||
response: 'successResponse'
|
||||
}
|
||||
const mockFailure = 'failureError'
|
||||
const successDummy = Promise.resolve(mockSuccess)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
'use strict'
|
||||
import { expect } from 'chai'
|
||||
import * as expandSectionActions from 'actions/expandSectionActions'
|
||||
|
||||
describe('Expand Section Actions', function () {
|
||||
describe('toggleCollapse', function () {
|
||||
context('when passed an elementName, the returned object', function () {
|
||||
const expectedElementName = 'testElement'
|
||||
const result = expandSectionActions.toggleCollapse(expectedElementName)
|
||||
|
||||
it('Should have type TOGGLE_COLLAPSE', function (){
|
||||
expect(result.type).to.equal(expandSectionActions.TOGGLE_COLLAPSE)
|
||||
})
|
||||
|
||||
it('Should have property elementName with value equal to the passed in elementName', function (){
|
||||
expect(result.elementName == expectedElementName).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -2,7 +2,6 @@
|
|||
import { expect } from 'chai'
|
||||
import queryString from 'query-string'
|
||||
import * as filterActions from 'actions/filterActions'
|
||||
import AddMockDispatch from 'test/helpers/mockDispatch'
|
||||
|
||||
// Istanbul for test coverage, provides us with a new command nyc-mocha, which runs the tests and gives a reporter
|
||||
describe('FilterActions', function () {
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
'use strict'
|
||||
import { expect } from 'chai'
|
||||
import * as debugActions from 'actions/debugActions'
|
||||
|
||||
describe('Debug Actions', function () {
|
||||
describe('rawHttpResponse', function() {
|
||||
const mockResponse = {
|
||||
bodyUsed: 'expectedBodyUsed',
|
||||
ok: 'expectedOk',
|
||||
redirected: 'expectedRedirected',
|
||||
status: 'expectedStatus',
|
||||
statusText: 'expectedStatusText',
|
||||
type: 'expectedResponseType',
|
||||
url: 'expectedUrl',
|
||||
mockUnusedField: '!!!expect undefined, not this!!!'
|
||||
}
|
||||
|
||||
const mockResult = debugActions.rawHttpResponse(mockResponse)
|
||||
const nullResult = debugActions.rawHttpResponse(null)
|
||||
const undefinedResult = debugActions.rawHttpResponse(undefined)
|
||||
|
||||
context('When passed a null or undefined response object', function () {
|
||||
it('Should return an object with a null response', function () {
|
||||
expect(nullResult.response).to.be.null
|
||||
expect(undefinedResult.response).to.be.null
|
||||
})
|
||||
})
|
||||
|
||||
context('When passed a valid response object', function () {
|
||||
it('Should copy a specific subset of the object property values to the result.response object', function () {
|
||||
expect(mockResult.response.bodyUsed).to.equal(mockResponse.bodyUsed)
|
||||
expect(mockResult.response.ok).to.equal(mockResponse.ok)
|
||||
expect(mockResult.response.redirected).to.equal(mockResponse.redirected)
|
||||
expect(mockResult.response.status).to.equal(mockResponse.status)
|
||||
expect(mockResult.response.statusText).to.equal(mockResponse.statusText)
|
||||
expect(mockResult.response.type).to.equal(mockResponse.type)
|
||||
expect(mockResult.response.url).to.equal(mockResponse.url)
|
||||
|
||||
expect(mockResult.response.mockUnusedField).to.be.undefined
|
||||
})
|
||||
|
||||
it('Should not return the passed in object as its response property', function () {
|
||||
expect(mockResult.response == mockResponse).to.be.false
|
||||
})
|
||||
})
|
||||
|
||||
context('Whenever the function is called', function () {
|
||||
it('Should return an object with a type property equal to RAW_HTTP_RESPONSE', function () {
|
||||
expect(nullResult.type).to.equal(debugActions.RAW_HTTP_RESPONSE)
|
||||
expect(undefinedResult.type).to.equal(debugActions.RAW_HTTP_RESPONSE)
|
||||
expect(mockResult.type).to.equal(debugActions.RAW_HTTP_RESPONSE)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('jsonResult', function () {
|
||||
const mock = {}
|
||||
const result = debugActions.jsonResult(mock)
|
||||
context('The returned object', function () {
|
||||
it('Should have type debugActions.JSON_RESULT', function () {
|
||||
expect(result.type).to.equal(debugActions.JSON_RESULT)
|
||||
})
|
||||
|
||||
it('Should have json with value the same as the passed in object', function () {
|
||||
expect(result.json == mock).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -2,7 +2,7 @@
|
|||
import { expect } from 'chai'
|
||||
import createComponent from 'test/helpers/shallowRenderHelper'
|
||||
import { IncidentRedirect, mapStateToProps, IncidentRedirectComponentDidMount } from 'components/Incident/IncidentRedirect'
|
||||
import AddMockDispatch from 'test/helpers/mockDispatch'
|
||||
import { GetMockDispatch, GetDispatchRecorder } from 'test/helpers/mockDispatch'
|
||||
import { Redirect } from 'react-router'
|
||||
|
||||
const setup = (props, children) => createComponent(IncidentRedirect, props, children)
|
||||
|
@ -11,16 +11,15 @@ describe('Incident Redirect', function () {
|
|||
describe('Component', function () {
|
||||
describe('ComponentDidMount', function () {
|
||||
it('Should attempt to fetch incident by incident id if ticketid is not known', function () {
|
||||
let mockDispatchRecorder = {
|
||||
action: null
|
||||
}
|
||||
let mockDispatchRecorder = GetDispatchRecorder()
|
||||
|
||||
const testProps = AddMockDispatch({
|
||||
const testProps = {
|
||||
ticketId: null,
|
||||
incidentId: 2
|
||||
})(mockDispatchRecorder)
|
||||
}
|
||||
const mockDispatch = GetMockDispatch(mockDispatchRecorder)
|
||||
|
||||
IncidentRedirectComponentDidMount(testProps)
|
||||
IncidentRedirectComponentDidMount({...testProps, dispatch: mockDispatch})
|
||||
|
||||
expect(mockDispatchRecorder.action.type).to.equal('REQUEST_INCIDENT')
|
||||
})
|
||||
|
@ -30,12 +29,14 @@ describe('Incident Redirect', function () {
|
|||
action: null
|
||||
}
|
||||
|
||||
const testProps = AddMockDispatch({
|
||||
const testProps = {
|
||||
ticketId: 1,
|
||||
incidentId: 2
|
||||
})(mockDispatchRecorder)
|
||||
}
|
||||
|
||||
IncidentRedirectComponentDidMount(testProps)
|
||||
const mockDispatch = GetMockDispatch(mockDispatchRecorder)
|
||||
|
||||
IncidentRedirectComponentDidMount({...testProps, dispatch: mockDispatch})
|
||||
|
||||
expect(mockDispatchRecorder.action).to.be.null
|
||||
})
|
||||
|
|
|
@ -9,7 +9,6 @@ import AutoComplete from 'material-ui/AutoComplete'
|
|||
|
||||
import createComponent from 'test/helpers/shallowRenderHelper'
|
||||
import AutoCompleteMenu, { onNewRequest } from 'components/elements/AutoCompleteMenu'
|
||||
import AddMockDispatch from 'test/helpers/mockDispatch'
|
||||
|
||||
|
||||
const mockProps = {
|
||||
|
|
|
@ -4,22 +4,20 @@ import React from 'react'
|
|||
import createComponent from 'test/helpers/shallowRenderHelper'
|
||||
import { DisplayRetryButton } from 'components/elements/Buttons'
|
||||
import FlatButtonStyled from 'components/elements/FlatButtonStyled'
|
||||
import AddMockDispatch from 'test/helpers/mockDispatch'
|
||||
import { GetMockDispatch, GetDispatchRecorder } from 'test/helpers/mockDispatch'
|
||||
|
||||
const setup = (props, children) => createComponent(DisplayRetryButton, props, children)
|
||||
|
||||
const dummyState = AddMockDispatch({
|
||||
const dummyState = {
|
||||
actionForRetry: {
|
||||
type: 'ActionForRetry'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
describe('DisplayRetryButton', function () {
|
||||
beforeEach(() => {
|
||||
this.mockDispatchRecorder = {
|
||||
action: null
|
||||
}
|
||||
this.singleState = setup(dummyState(this.mockDispatchRecorder), null)
|
||||
this.mockDispatchRecorder = GetDispatchRecorder()
|
||||
this.singleState = setup({ ...dummyState, dispatch: GetMockDispatch(this.mockDispatchRecorder)}, null)
|
||||
})
|
||||
|
||||
it('Should render a FlatButtonStyled with a Retry label', () => {
|
||||
|
|
|
@ -4,7 +4,6 @@ import React from 'react'
|
|||
import Chip from 'material-ui/Chip'
|
||||
import createComponent from 'test/helpers/shallowRenderHelper'
|
||||
import { FilterChips, mapStateToProps, renderChip, hydrateChip } from 'components/elements/FilterChips'
|
||||
import AddMockDispatch from 'test/helpers/mockDispatch'
|
||||
|
||||
describe('FilterChips', function () {
|
||||
describe('mapStateToProps', function () {
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { isObject } from 'util'
|
||||
|
||||
export const AddMockDispatch = (state) => (mockDispatchRecorder) => {
|
||||
/*
|
||||
Action could be:
|
||||
an object
|
||||
a function that takes dispatch as an argument (thunk middleware action)
|
||||
If the action is a function, call it passing in dispatch.
|
||||
If the action is an object record it; a single action sets the .action property,
|
||||
a second action turns .action into an array.
|
||||
Subsequent actions are added to the array.
|
||||
*/
|
||||
/*
|
||||
Action could be:
|
||||
an object
|
||||
a function that takes dispatch as an argument (thunk middleware action)
|
||||
If the action is a function, call it passing in dispatch.
|
||||
If the action is an object record it; a single action sets the .action property,
|
||||
a second action turns .action into an array.
|
||||
Subsequent actions are added to the array.
|
||||
*/
|
||||
export const GetMockDispatch = (mockDispatchRecorder) => {
|
||||
const dispatch = (action) => isObject(action)
|
||||
? mockDispatchRecorder.action
|
||||
? Array.isArray(mockDispatchRecorder.action)
|
||||
|
@ -17,10 +17,9 @@ export const AddMockDispatch = (state) => (mockDispatchRecorder) => {
|
|||
: mockDispatchRecorder.action = [mockDispatchRecorder.action, action]
|
||||
: mockDispatchRecorder.action = action
|
||||
: action(dispatch)
|
||||
return {
|
||||
...state,
|
||||
dispatch
|
||||
}
|
||||
return dispatch
|
||||
}
|
||||
|
||||
export default AddMockDispatch
|
||||
export const GetDispatchRecorder = () => ({ action: null })
|
||||
|
||||
export default GetMockDispatch
|
||||
|
|
Загрузка…
Ссылка в новой задаче