diff options
Diffstat (limited to 'web')
-rw-r--r-- | web/src/js/__tests__/ducks/flowsSpec.js | 240 | ||||
-rw-r--r-- | web/src/js/ducks/flows.js | 37 | ||||
-rw-r--r-- | web/src/js/ducks/ui/keyboard.js | 19 |
3 files changed, 244 insertions, 52 deletions
diff --git a/web/src/js/__tests__/ducks/flowsSpec.js b/web/src/js/__tests__/ducks/flowsSpec.js index acfa3083..2dfe046f 100644 --- a/web/src/js/__tests__/ducks/flowsSpec.js +++ b/web/src/js/__tests__/ducks/flowsSpec.js @@ -1,31 +1,225 @@ jest.unmock('../../ducks/flows'); +jest.mock('../../utils') -import reduceFlows, * as flowActions from '../../ducks/flows' -import * as storeActions from '../../ducks/utils/store' +import reduceFlows from "../../ducks/flows" +import * as flowActions from "../../ducks/flows" +import reduceStore from "../../ducks/utils/store" +import {fetchApi} from "../../utils" +import {createStore} from "./tutils" - -describe('select flow', () => { - - let state = reduceFlows(undefined, {}) +describe('flow reducer', () => { + let state = undefined for (let i of [1, 2, 3, 4]) { - state = reduceFlows(state, storeActions.add({ id: i })) + state = reduceFlows(state, { type: flowActions.ADD, data: { id: i }, cmd: 'add' }) } - it('should be possible to select a single flow', () => { - expect(reduceFlows(state, flowActions.select(2))).toEqual( - { - ...state, - selected: [2], - } - ) - }) - - it('should be possible to deselect a flow', () => { - expect(reduceFlows({ ...state, selected: [1] }, flowActions.select())).toEqual( - { - ...state, - selected: [], - } - ) + it('should return initial state', () => { + expect(reduceFlows(undefined, {})).toEqual({ + highlight: null, + filter: null, + sort: { column: null, desc: false }, + selected: [], + ...reduceStore(undefined, {}) + }) + }) + + describe('selections', () => { + it('should be possible to select a single flow', () => { + expect(reduceFlows(state, flowActions.select(2))).toEqual( + { + ...state, + selected: [2], + } + ) + }) + + it('should be possible to deselect a flow', () => { + expect(reduceFlows({ ...state, selected: [1] }, flowActions.select())).toEqual( + { + ...state, + selected: [], + } + ) + }) + + it('should be possible to select relative', () => { + // haven't selected any flow + expect( + flowActions.selectRelative(state, 1) + ).toEqual( + flowActions.select(4) + ) + + // already selected some flows + expect( + flowActions.selectRelative({ ...state, selected: [2] }, 1) + ).toEqual( + flowActions.select(3) + ) + }) + + it('should update state.selected on remove', () => { + let next + next = reduceFlows({ ...state, selected: [2] }, { + type: flowActions.REMOVE, + data: 2, + cmd: 'remove' + }) + expect(next.selected).toEqual([3]) + + //last row + next = reduceFlows({ ...state, selected: [4] }, { + type: flowActions.REMOVE, + data: 4, + cmd: 'remove' + }) + expect(next.selected).toEqual([3]) + + //multiple selection + next = reduceFlows({ ...state, selected: [2, 3, 4] }, { + type: flowActions.REMOVE, + data: 3, + cmd: 'remove' + }) + expect(next.selected).toEqual([2, 4]) + }) + }) + + it('should be possible to set filter', () => { + let filt = "~u 123" + expect(reduceFlows(undefined, flowActions.setFilter(filt)).filter).toEqual(filt) + }) + + it('should be possible to set highlight', () => { + let key = "foo" + expect(reduceFlows(undefined, flowActions.setHighlight(key)).highlight).toEqual(key) + }) + + it('should be possible to set sort', () => { + let sort = { column: "TLSColumn", desc: 1 } + expect(reduceFlows(undefined, flowActions.setSort(sort.column, sort.desc)).sort).toEqual(sort) + }) + +}) + +describe('flows actions', () => { + + let store = createStore({reduceFlows}) + + let tflow = { id: 1 } + it('should handle resume action', () => { + store.dispatch(flowActions.resume(tflow)) + expect(fetchApi).toBeCalledWith('/flows/1/resume', { method: 'POST' }) + }) + + it('should handle resumeAll action', () => { + store.dispatch(flowActions.resumeAll()) + expect(fetchApi).toBeCalledWith('/flows/resume', { method: 'POST' }) + }) + + it('should handle kill action', () => { + store.dispatch(flowActions.kill(tflow)) + expect(fetchApi).toBeCalledWith('/flows/1/kill', { method: 'POST' }) + + }) + + it('should handle killAll action', () => { + store.dispatch(flowActions.killAll()) + expect(fetchApi).toBeCalledWith('/flows/kill', { method: 'POST' }) + }) + + it('should handle remove action', () => { + store.dispatch(flowActions.remove(tflow)) + expect(fetchApi).toBeCalledWith('/flows/1', { method: 'DELETE' }) + }) + + it('should handle duplicate action', () => { + store.dispatch(flowActions.duplicate(tflow)) + expect(fetchApi).toBeCalledWith('/flows/1/duplicate', { method: 'POST' }) + }) + + it('should handle replay action', () => { + store.dispatch(flowActions.replay(tflow)) + expect(fetchApi).toBeCalledWith('/flows/1/replay', { method: 'POST' }) + }) + + it('should handle revert action', () => { + store.dispatch(flowActions.revert(tflow)) + expect(fetchApi).toBeCalledWith('/flows/1/revert', { method: 'POST' }) + }) + + it('should handle update action', () => { + store.dispatch(flowActions.update(tflow, 'foo')) + expect(fetchApi.put).toBeCalledWith('/flows/1', 'foo') + }) + + it('should handle uploadContent action', () => { + let body = new FormData(), + file = new window.Blob(['foo'], { type: 'plain/text' }) + body.append('file', file) + store.dispatch(flowActions.uploadContent(tflow, 'foo', 'foo')) + expect(fetchApi).toBeCalledWith('/flows/1/foo/content', { method: 'POST', body}) + }) + + it('should handle clear action', () => { + store.dispatch(flowActions.clear()) + expect(fetchApi).toBeCalledWith('/clear', { method: 'POST'} ) + }) + + it('should handle download action', () => { + let state = reduceFlows(undefined, {}) + expect(reduceFlows(state, flowActions.download())).toEqual(state) + }) + + it('should handle upload action', () => { + let body = new FormData() + body.append('file', 'foo') + store.dispatch(flowActions.upload('foo')) + expect(fetchApi).toBeCalledWith('/flows/dump', { method: 'POST', body }) + }) +}) + +describe('makeSort', () => { + it('should be possible to sort by TLSColumn', () => { + let sort = flowActions.makeSort({ column: 'TLSColumn', desc: true }), + a = { request: { scheme: 'http' } }, + b = { request: { scheme: 'https' } } + expect(sort(a, b)).toEqual(1) + }) + + it('should be possible to sort by PathColumn', () => { + let sort = flowActions.makeSort({ column: 'PathColumn', desc: true }), + a = { request: {} }, + b = { request: {} } + expect(sort(a, b)).toEqual(0) + + }) + + it('should be possible to sort by MethodColumn', () => { + let sort = flowActions.makeSort({ column: 'MethodColumn', desc: true }), + a = { request: { method: 'GET' } }, + b = { request: { method: 'POST' } } + expect(sort(b, a)).toEqual(-1) + }) + + it('should be possible to sort by StatusColumn', () => { + let sort = flowActions.makeSort({ column: 'StatusColumn', desc: false }), + a = { response: { status_code: 200 } }, + b = { response: { status_code: 404 } } + expect(sort(a, b)).toEqual(-1) + }) + + it('should be possible to sort by TimeColumn', () => { + let sort = flowActions.makeSort({ column: 'TimeColumn', desc: false }), + a = { response: { timestamp_end: 9 }, request: { timestamp_start: 8 } }, + b = { response: { timestamp_end: 10 }, request: { timestamp_start: 8 } } + expect(sort(b, a)).toEqual(1) + }) + + it('should be possible to sort by SizeColumn', () => { + let sort = flowActions.makeSort({ column: 'SizeColumn', desc: true }), + a = { request: { contentLength: 1 }, response: { contentLength: 1 } }, + b = { request: { contentLength: 1 } } + expect(sort(a, b)).toEqual(-1) }) }) diff --git a/web/src/js/ducks/flows.js b/web/src/js/ducks/flows.js index 92408891..523ec396 100644 --- a/web/src/js/ducks/flows.js +++ b/web/src/js/ducks/flows.js @@ -1,5 +1,6 @@ import { fetchApi } from "../utils" -import reduceStore, * as storeActions from "./utils/store" +import reduceStore from "./utils/store" +import * as storeActions from "./utils/store" import Filt from "../filt/filt" import { RequestUtils } from "../flow/utils" @@ -29,8 +30,6 @@ export default function reduce(state = defaultState, action) { case UPDATE: case REMOVE: case RECEIVE: - // FIXME: Update state.selected on REMOVE: - // The selected flow may have been removed, we need to select the next one in the view. let storeAction = storeActions[action.cmd]( action.data, makeFilter(state.filter), @@ -152,22 +151,20 @@ export function setSort(column, desc) { return { type: SET_SORT, sort: { column, desc } } } -export function selectRelative(shift) { - return (dispatch, getState) => { - let currentSelectionIndex = getState().flows.viewIndex[getState().flows.selected[0]] - let minIndex = 0 - let maxIndex = getState().flows.view.length - 1 - let newIndex - if (currentSelectionIndex === undefined) { - newIndex = (shift < 0) ? minIndex : maxIndex - } else { - newIndex = currentSelectionIndex + shift - newIndex = window.Math.max(newIndex, minIndex) - newIndex = window.Math.min(newIndex, maxIndex) - } - let flow = getState().flows.view[newIndex] - dispatch(select(flow ? flow.id : undefined)) +export function selectRelative(flows, shift) { + let currentSelectionIndex = flows.viewIndex[flows.selected[0]] + let minIndex = 0 + let maxIndex = flows.view.length - 1 + let newIndex + if (currentSelectionIndex === undefined) { + newIndex = (shift < 0) ? minIndex : maxIndex + } else { + newIndex = currentSelectionIndex + shift + newIndex = window.Math.max(newIndex, minIndex) + newIndex = window.Math.min(newIndex, maxIndex) } + let flow = flows.view[newIndex] + return select(flow ? flow.id : undefined) } @@ -212,7 +209,7 @@ export function uploadContent(flow, file, type) { const body = new FormData() file = new window.Blob([file], { type: 'plain/text' }) body.append('file', file) - return dispatch => fetchApi(`/flows/${flow.id}/${type}/content`, { method: 'post', body }) + return dispatch => fetchApi(`/flows/${flow.id}/${type}/content`, { method: 'POST', body }) } @@ -228,7 +225,7 @@ export function download() { export function upload(file) { const body = new FormData() body.append('file', file) - return dispatch => fetchApi('/flows/dump', { method: 'post', body }) + return dispatch => fetchApi('/flows/dump', { method: 'POST', body }) } diff --git a/web/src/js/ducks/ui/keyboard.js b/web/src/js/ducks/ui/keyboard.js index 30fd76e1..0e3491fa 100644 --- a/web/src/js/ducks/ui/keyboard.js +++ b/web/src/js/ducks/ui/keyboard.js @@ -9,39 +9,40 @@ export function onKeyDown(e) { return () => { } } - var key = e.keyCode - var shiftKey = e.shiftKey + let key = e.keyCode, + shiftKey = e.shiftKey e.preventDefault() return (dispatch, getState) => { - const flow = getState().flows.byId[getState().flows.selected[0]] + const flows = getState().flows, + flow = flows.byId[getState().flows.selected[0]] switch (key) { case Key.K: case Key.UP: - dispatch(flowsActions.selectRelative(-1)) + dispatch(flowsActions.selectRelative(flows, -1)) break case Key.J: case Key.DOWN: - dispatch(flowsActions.selectRelative(+1)) + dispatch(flowsActions.selectRelative(flows, +1)) break case Key.SPACE: case Key.PAGE_DOWN: - dispatch(flowsActions.selectRelative(+10)) + dispatch(flowsActions.selectRelative(flows, +10)) break case Key.PAGE_UP: - dispatch(flowsActions.selectRelative(-10)) + dispatch(flowsActions.selectRelative(flows, -10)) break case Key.END: - dispatch(flowsActions.selectRelative(+1e10)) + dispatch(flowsActions.selectRelative(flows, +1e10)) break case Key.HOME: - dispatch(flowsActions.selectRelative(-1e10)) + dispatch(flowsActions.selectRelative(flows, -1e10)) break case Key.ESC: |