diff --git a/.gitignore b/.gitignore index 915a81b449..a37a1f3163 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ sslkeylogfile.log .tox/ .python-version coverage.xml +web/coverage/ diff --git a/web/src/js/__tests__/ducks/flowsSpec.js b/web/src/js/__tests__/ducks/flowsSpec.js index 185e12bbd3..a23864d107 100644 --- a/web/src/js/__tests__/ducks/flowsSpec.js +++ b/web/src/js/__tests__/ducks/flowsSpec.js @@ -1,50 +1,19 @@ jest.unmock('../../ducks/flows'); jest.mock('../../utils') -import reduceFlows, * as flowActions from '../../ducks/flows' -import reduceStore 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', () => { +describe('flow reducer', () => { - let state = reduceFlows(undefined, {}) + let state = undefined for (let i of [1, 2, 3, 4]) { - state = reduceFlows(state, {type: flowActions.ADD, data: {id: i}, cmd: 'add'}) + 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 be possible to select relative',() => { - // already selected some flows - let newState = {}, - getState = () => { return { flows: {...state, selected: [2]}}}, - dispatch = (action) => { newState = reduceFlows(getState().flows, action) } - flowActions.selectRelative(1)(dispatch, getState) - expect(newState).toEqual({...state, selected: [3]}) - - // haven't selected any flow - getState = () => { return { flows: { ...state, selected: []}}} - flowActions.selectRelative(-1)(dispatch, getState) - expect(newState).toEqual({...state, selected: [1]}) - }) -}) - -describe('flows reducer', () => { it('should return initial state', () => { expect(reduceFlows(undefined, {})).toEqual({ highlight: null, @@ -55,6 +24,68 @@ describe('flows reducer', () => { }) }) + 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) @@ -65,29 +96,21 @@ describe('flows reducer', () => { expect(reduceFlows(undefined, flowActions.setHighlight(key)).highlight).toEqual(key) }) - it('should be possilbe to set sort', () => { + 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) }) - it('should update state.selected on remove', () => { - let state = reduceFlows(undefined, {}) - for (let i of [1, 2, 3, 4]) { - state = reduceFlows(state, {type: flowActions.ADD, data: {id: i}, cmd: 'add'}) - } - state = reduceFlows(state, flowActions.select(2)) - expect(reduceFlows(state, {type: flowActions.REMOVE, data: 2, cmd: 'remove'}).selected).toEqual([3]) - //last row - state = reduceFlows(state, flowActions.select(4)) - expect(reduceFlows(state, {type: flowActions.REMOVE, data: 4, cmd: 'remove'}).selected).toEqual([3]) - }) }) describe('flows actions', () => { + let store = createStore({reduceFlows}) + let tflow = { id: 1 } it('should handle resume action', () => { - flowActions.resume(tflow)() + store.dispatch(flowActions.resume(tflow)) + expect(fetchApi).toBeCalledWith('/flows/1/resume', { method: 'POST' }) }) it('should handle resumeAll action', () => { @@ -126,7 +149,7 @@ describe('flows actions', () => { flowActions.uploadContent(tflow, "foo", "foo")() }) - it('should hanlde clear action', () => { + it('should handle clear action', () => { flowActions.clear()() }) @@ -142,45 +165,45 @@ describe('flows actions', () => { 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'}} + 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: {}} + 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'}} + 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}} + 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}} + 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}} + 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 9240889129..d36bc247ec 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) } diff --git a/web/src/js/ducks/ui/keyboard.js b/web/src/js/ducks/ui/keyboard.js index 30fd76e1d6..0e3491faba 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: