import { is, fromJS } from 'immutable';

import { TABLE_VIEW_MODE } from '../../constants/naming';
import { constructEdgeId } from '../../utils/layouter-utils';
import { highlightedEdgeIdsSelector } from '../../selectors/graph-view/decorators';

// var apiRoot = process.env.API_ROOT;
var apiRoot = 'http://observe-front.cestong.com.cn'
// Root reducer test suite using Jasmine matchers
describe('RootReducer', () => {
  const ActionTypes = require('../../constants/action-types').default;
  const reducer = require('../root').default;
  const { initialState } = require('../root');
  const topologyUtils = require('../../utils/topology-utils');
  const topologySelectors = require('../../selectors/topology');
  // TODO maybe extract those to topology-utils tests?
  const { activeTopologyOptionsSelector } = topologySelectors;
  const { getAdjacentNodes, isNodesDisplayEmpty, isTopologyNodeCountZero } = topologyUtils;
  const { getUrlState } = require('../../utils/router-utils');

  // fixtures

  const NODE_SET = {
    n1: {
      adjacency: ['n1', 'n2'],
      filtered: false,
      id: 'n1',
    },
    n2: {
      filtered: false,
      id: 'n2',
    }
  };

  const topologies = [
    {
      fullName: 'Processes',
      hide_if_empty: true,
      id: 'processes',
      name: 'Processes',
      options: [
        {
          defaultValue: 'hide',
          id: 'unconnected',
          options: [
            {
              label: 'Unconnected nodes hidden',
              value: 'hide'
            }
          ],
          selectType: 'one'
        }
      ],
      rank: 1,
      stats: {
        edge_count: 379,
        filtered_nodes: 214,
        node_count: 320,
        nonpseudo_node_count: 320
      },
      sub_topologies: [],
      url: '/api/topology/processes'   //本地用
      // url:`${apiRoot}/api/topology/processes`  //线上用
    },
    {
      hide_if_empty: true,
      name: 'Pods',
      options: [
        {
          defaultValue: 'default',
          id: 'namespace',
          options: [
            {
              label: 'monitoring',
              value: 'monitoring'
            },
            {
              label: 'scope',
              value: 'scope'
            },
            {
              label: 'All Namespaces',
              value: 'all'
            }
          ],
          selectType: 'many'
        },
        {
          defaultValue: 'hide',
          id: 'pseudo',
          options: [
            {
              label: 'Show Unmanaged',
              value: 'show'
            },
            {
              label: 'Hide Unmanaged',
              value: 'hide'
            }
          ]
        }
      ],
      rank: 3,
      stats: {
        edge_count: 15,
        filtered_nodes: 16,
        node_count: 32,
        nonpseudo_node_count: 27
      },
      sub_topologies: [
        {
          hide_if_empty: true,
          name: 'services',
          options: [
            {
              defaultValue: 'default',
              id: 'namespace',
              options: [
                {
                  label: 'monitoring',
                  value: 'monitoring'
                },
                {
                  label: 'scope',
                  value: 'scope'
                },
                {
                  label: 'All Namespaces',
                  value: 'all'
                }
              ],
              selectType: 'many'
            }
          ],
          rank: 0,
          stats: {
            edge_count: 14,
            filtered_nodes: 16,
            node_count: 159,
            nonpseudo_node_count: 154
          },
          url: '/api/topology/services'
          // url:`${apiRoot}/api/topology/services`
        }
      ],
      url: '/api/topology/pods'
      // url:`${apiRoot}/api/topology/pods`
    }
  ];

  // actions

  const ChangeTopologyOptionAction = {
    option: 'option1',
    topologyId: 'topo1',
    type: ActionTypes.CHANGE_TOPOLOGY_OPTION,
    value: ['on']
  };

  const ChangeTopologyOptionAction2 = {
    option: 'option1',
    topologyId: 'topo1',
    type: ActionTypes.CHANGE_TOPOLOGY_OPTION,
    value: ['off']
  };

  const ClickNodeAction = {
    nodeId: 'n1',
    type: ActionTypes.CLICK_NODE
  };

  const ClickNode2Action = {
    nodeId: 'n2',
    type: ActionTypes.CLICK_NODE
  };

  const ClickRelativeAction = {
    nodeId: 'rel1',
    type: ActionTypes.CLICK_RELATIVE
  };

  const ClickShowTopologyForNodeAction = {
    nodeId: 'rel1',
    topologyId: 'topo2',
    type: ActionTypes.CLICK_SHOW_TOPOLOGY_FOR_NODE
  };

  const ClickSubTopologyAction = {
    topologyId: 'topo1-grouped',
    type: ActionTypes.CLICK_TOPOLOGY
  };

  const ClickTopologyAction = {
    topologyId: 'topo1',
    type: ActionTypes.CLICK_TOPOLOGY
  };

  const ClickTopology2Action = {
    topologyId: 'topo2',
    type: ActionTypes.CLICK_TOPOLOGY
  };

  const CloseWebsocketAction = {
    type: ActionTypes.CLOSE_WEBSOCKET
  };

  const deSelectNode = {
    type: ActionTypes.DESELECT_NODE
  };

  const OpenWebsocketAction = {
    type: ActionTypes.OPEN_WEBSOCKET
  };

  const ReceiveNodesDeltaAction = {
    delta: {
      add: [{
        adjacency: ['n1', 'n2'],
        id: 'n1'
      }, {
        id: 'n2'
      }]
    },
    type: ActionTypes.RECEIVE_NODES_DELTA
  };

  const ReceiveNodesDeltaUpdateAction = {
    delta: {
      remove: ['n2'],
      update: [{
        adjacency: ['n1'],
        id: 'n1'
      }]
    },
    type: ActionTypes.RECEIVE_NODES_DELTA
  };

  const ReceiveTopologiesAction = {
    topologies: [{
      name: 'Topo1',
      options: [{
        defaultValue: 'off',
        id: 'option1',
        options: [
          {value: 'on'},
          {value: 'off'}
        ]
      }],
      stats: {
        node_count: 1
      },
      sub_topologies: [{
        name: 'topo 1 grouped',
        url: '/topo1-grouped'
      }],
      url: '/topo1'
    }, {
      name: 'Topo2',
      stats: {
        node_count: 0
      },
      sub_topologies: [{
        name: 'topo 2 sub',
        url: '/topo2-sub'
      }],
      url: '/topo2'
    }],
    type: ActionTypes.RECEIVE_TOPOLOGIES
  };

  const ReceiveTopologiesHiddenAction = {
    topologies: [{
      name: 'Topo1',
      stats: {
        node_count: 1
      },
      url: '/topo1'
    }, {
      hide_if_empty: true,
      name: 'Topo2',
      stats: { filtered_nodes: 0, node_count: 0 },
      sub_topologies: [{
        hide_if_empty: true,
        name: 'topo 2 sub',
        stats: { filtered_nodes: 0, node_count: 0 },
        url: '/topo2-sub',
      }],
      url: '/topo2'
    }],
    type: ActionTypes.RECEIVE_TOPOLOGIES
  };

  const RouteAction = {
    state: {},
    type: ActionTypes.ROUTE_TOPOLOGY
  };

  const ChangeInstanceAction = {
    type: ActionTypes.CHANGE_INSTANCE
  };

  // Basic tests

  it('returns initial state', () => {
    const nextState = reducer(undefined, {});
    expect(is(nextState, initialState)).toBeTruthy();
  });

  // topology tests

  it('init with no topologies', () => {
    const nextState = reducer(undefined, {});
    expect(nextState.get('topologies').size).toBe(0);
    expect(nextState.get('currentTopology')).toBeFalsy();
  });

  it('get current topology', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ReceiveTopologiesAction);
    nextState = reducer(nextState, ClickTopologyAction);

    expect(nextState.get('topologies').size).toBe(2);
    expect(nextState.get('currentTopology').get('name')).toBe('Topo1');
    expect(nextState.get('currentTopology').get('url')).toBe('/topo1');
    expect(nextState.get('currentTopology').get('options').first().get('id')).toEqual('option1');
    expect(nextState.getIn(['currentTopology', 'options']).toJS()).toEqual([{
      defaultValue: 'off',
      id: 'option1',
      options: [
        { value: 'on'},
        { value: 'off'}
      ],
      selectType: 'one'
    }]);
  });

  it('get sub-topology', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ReceiveTopologiesAction);
    nextState = reducer(nextState, ClickSubTopologyAction);

    expect(nextState.get('topologies').size).toBe(2);
    expect(nextState.get('currentTopology').get('name')).toBe('topo 1 grouped');
    expect(nextState.get('currentTopology').get('url')).toBe('/topo1-grouped');
    expect(nextState.get('currentTopology').get('options')).toBeUndefined();
  });

  // topology options

  it('changes topology option', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ReceiveTopologiesAction);
    nextState = reducer(nextState, ClickTopologyAction);

    // default options
    expect(activeTopologyOptionsSelector(nextState).has('option1')).toBeTruthy();
    expect(activeTopologyOptionsSelector(nextState).get('option1')).toBeInstanceOf(Array);
    expect(activeTopologyOptionsSelector(nextState).get('option1')).toEqual(['off']);
    expect(getUrlState(nextState).topologyOptions).toBeUndefined();

    // turn on
    nextState = reducer(nextState, ChangeTopologyOptionAction);
    expect(activeTopologyOptionsSelector(nextState).get('option1')).toEqual(['on']);
    expect(getUrlState(nextState).topologyOptions.topo1.option1).toEqual(['on']);

    // turn off
    nextState = reducer(nextState, ChangeTopologyOptionAction2);
    expect(activeTopologyOptionsSelector(nextState).get('option1')).toEqual(['off']);
    expect(getUrlState(nextState).topologyOptions).toBeUndefined();

    // sub-topology should retain main topo options
    nextState = reducer(nextState, ClickSubTopologyAction);
    expect(activeTopologyOptionsSelector(nextState).get('option1')).toEqual(['off']);
    expect(getUrlState(nextState).topologyOptions).toBeUndefined();

    // other topology w/o options dont return options, but keep in app state
    nextState = reducer(nextState, ClickTopology2Action);
    expect(activeTopologyOptionsSelector(nextState).size).toEqual(0);
    expect(getUrlState(nextState).topologyOptions).toBeUndefined();
  });

  it('adds/removes a topology option', () => {
    const addAction = {
      option: 'namespace',
      topologyId: 'services',
      type: ActionTypes.CHANGE_TOPOLOGY_OPTION,
      value: ['default', 'scope'],
    };
    const removeAction = {
      option: 'namespace',
      topologyId: 'services',
      type: ActionTypes.CHANGE_TOPOLOGY_OPTION,
      value: ['default']
    };
    let nextState = initialState;
    nextState = reducer(nextState, { topologies, type: ActionTypes.RECEIVE_TOPOLOGIES});
    nextState = reducer(nextState, { topologyId: 'services', type: ActionTypes.CLICK_TOPOLOGY });
    nextState = reducer(nextState, addAction);
    expect(activeTopologyOptionsSelector(nextState).toJS()).toEqual({
      namespace: ['default', 'scope'],
      pseudo: ['hide']
    });
    nextState = reducer(nextState, removeAction);
    expect(activeTopologyOptionsSelector(nextState).toJS()).toEqual({
      namespace: ['default'],
      pseudo: ['hide']
    });
  });

  it('sets topology options from route', () => {
    RouteAction.state = {
      selectedNodeId: null,
      topologyId: 'topo1',
      topologyOptions: {topo1: {option1: 'on'}}
    };

    let nextState = initialState;
    nextState = reducer(nextState, RouteAction);
    expect(activeTopologyOptionsSelector(nextState).get('option1')).toBe('on');
    expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('on');

    // stay same after topos have been received
    nextState = reducer(nextState, ReceiveTopologiesAction);
    nextState = reducer(nextState, ClickTopologyAction);
    expect(activeTopologyOptionsSelector(nextState).get('option1')).toBe('on');
    expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('on');
  });

  it('uses default topology options from route', () => {
    RouteAction.state = {
      selectedNodeId: null,
      topologyId: 'topo1',
      topologyOptions: null
    };
    let nextState = initialState;
    nextState = reducer(nextState, RouteAction);
    nextState = reducer(nextState, ReceiveTopologiesAction);
    nextState = reducer(nextState, ClickTopologyAction);
    expect(activeTopologyOptionsSelector(nextState).get('option1')).toEqual(['off']);
    expect(getUrlState(nextState).topologyOptions).toBeUndefined();
  });

  // nodes delta

  it('replaces adjacency on update', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ReceiveNodesDeltaAction);
    expect(nextState.get('nodes').toJS().n1.adjacency).toEqual(['n1', 'n2']);
    nextState = reducer(nextState, ReceiveNodesDeltaUpdateAction);
    expect(nextState.get('nodes').toJS().n1.adjacency).toEqual(['n1']);
  });

  // browsing

  it('shows nodes that were received', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ReceiveNodesDeltaAction);
    expect(nextState.get('nodes').toJS()).toEqual(NODE_SET);
  });

  it('knows a route was set', () => {
    let nextState = initialState;
    expect(nextState.get('routeSet')).toBeFalsy();
    nextState = reducer(nextState, RouteAction);
    expect(nextState.get('routeSet')).toBeTruthy();
  });

  it('gets selected node after click', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ReceiveNodesDeltaAction);
    nextState = reducer(nextState, ClickNodeAction);

    expect(nextState.get('selectedNodeId')).toBe('n1');
    expect(nextState.get('nodes').toJS()).toEqual(NODE_SET);

    nextState = reducer(nextState, deSelectNode);
    expect(nextState.get('selectedNodeId')).toBe(null);
    expect(nextState.get('nodes').toJS()).toEqual(NODE_SET);
  });

  it('keeps showing nodes on navigating back after node click', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ReceiveTopologiesAction);
    nextState = reducer(nextState, ClickTopologyAction);
    nextState = reducer(nextState, ReceiveNodesDeltaAction);
    expect(getUrlState(nextState).selectedNodeId).toBeUndefined();

    nextState = reducer(nextState, ClickNodeAction);
    expect(getUrlState(nextState).selectedNodeId).toEqual('n1');

    // go back in browsing
    RouteAction.state = {selectedNodeId: null, topologyId: 'topo1'};
    nextState = reducer(nextState, RouteAction);
    expect(nextState.get('selectedNodeId')).toBeNull();
    expect(nextState.get('nodes').toJS()).toEqual(NODE_SET);
  });

  it('closes details when changing topologies', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ReceiveTopologiesAction);
    nextState = reducer(nextState, ClickTopologyAction);
    nextState = reducer(nextState, ReceiveNodesDeltaAction);

    expect(getUrlState(nextState).selectedNodeId).toBeUndefined();
    expect(getUrlState(nextState).topologyId).toEqual('topo1');

    nextState = reducer(nextState, ClickNodeAction);
    expect(getUrlState(nextState).selectedNodeId).toEqual('n1');
    expect(getUrlState(nextState).topologyId).toEqual('topo1');

    nextState = reducer(nextState, ClickSubTopologyAction);
    expect(getUrlState(nextState).selectedNodeId).toBeUndefined();
    expect(getUrlState(nextState).topologyId).toEqual('topo1-grouped');
  });

  // connection errors

  it('resets topology on websocket reconnect', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ReceiveNodesDeltaAction);
    expect(nextState.get('nodes').toJS()).toEqual(NODE_SET);

    nextState = reducer(nextState, CloseWebsocketAction);
    expect(nextState.get('websocketClosed')).toBeTruthy();
    // keep showing old nodes
    expect(nextState.get('nodes').toJS()).toEqual(NODE_SET);

    nextState = reducer(nextState, OpenWebsocketAction);
    expect(nextState.get('websocketClosed')).toBeFalsy();
  });

  // adjacency test

  it('returns the correct adjacency set for a node', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ReceiveNodesDeltaAction);
    expect(getAdjacentNodes(nextState).size).toEqual(0);

    nextState = reducer(nextState, ClickNodeAction);
    expect(getAdjacentNodes(nextState, 'n1').size).toEqual(2);
    expect(getAdjacentNodes(nextState, 'n1').has('n1')).toBeTruthy();
    expect(getAdjacentNodes(nextState, 'n1').has('n2')).toBeTruthy();

    nextState = reducer(nextState, deSelectNode);
    expect(getAdjacentNodes(nextState).size).toEqual(0);
  });

  // empty topology

  it('detects that the nodes display is empty', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ReceiveTopologiesAction);
    nextState = reducer(nextState, ClickTopologyAction);
    expect(isNodesDisplayEmpty(nextState)).toBeTruthy();

    nextState = reducer(nextState, ReceiveNodesDeltaAction);
    expect(isNodesDisplayEmpty(nextState)).toBeFalsy();

    nextState = reducer(nextState, ClickTopology2Action);
    expect(isNodesDisplayEmpty(nextState)).toBeTruthy();

    nextState = reducer(nextState, ReceiveNodesDeltaAction);
    expect(isNodesDisplayEmpty(nextState)).toBeFalsy();
  });

  it('detects that the topo stats are empty', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ReceiveTopologiesAction);
    nextState = reducer(nextState, ClickTopologyAction);
    expect(isTopologyNodeCountZero(nextState)).toBeFalsy();

    nextState = reducer(nextState, ReceiveNodesDeltaAction);
    expect(isTopologyNodeCountZero(nextState)).toBeFalsy();

    nextState = reducer(nextState, ClickTopology2Action);
    expect(isTopologyNodeCountZero(nextState)).toBeTruthy();

    nextState = reducer(nextState, ClickTopologyAction);
    expect(isTopologyNodeCountZero(nextState)).toBeFalsy();
  });

  it('keeps hidden topology visible if selected', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ReceiveTopologiesAction);
    nextState = reducer(nextState, ClickTopology2Action);
    nextState = reducer(nextState, ReceiveTopologiesHiddenAction);
    expect(nextState.get('currentTopologyId')).toEqual('topo2');
    expect(nextState.get('topologies').toJS().length).toEqual(2);
  });

  it('keeps hidden topology visible if sub_topology selected', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ReceiveTopologiesAction);
    nextState = reducer(nextState, { topologyId: 'topo2-sub', type: ActionTypes.CLICK_TOPOLOGY });
    nextState = reducer(nextState, ReceiveTopologiesHiddenAction);
    expect(nextState.get('currentTopologyId')).toEqual('topo2-sub');
    expect(nextState.get('topologies').toJS().length).toEqual(2);
  });

  it('hides hidden topology if not selected', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ClickTopologyAction);
    nextState = reducer(nextState, ReceiveTopologiesHiddenAction);
    expect(nextState.get('topologies').toJS().length).toEqual(1);
  });

  // selection of relatives

  it('keeps relatives as a stack', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ClickNodeAction);
    expect(nextState.get('selectedNodeId')).toBe('n1');
    expect(nextState.get('nodeDetails').size).toEqual(1);
    expect(nextState.get('nodeDetails').has('n1')).toBeTruthy();
    expect(nextState.get('nodeDetails').keySeq().last()).toEqual('n1');

    nextState = reducer(nextState, ClickRelativeAction);
    // stack relative, first node stays main node
    expect(nextState.get('selectedNodeId')).toBe('n1');
    expect(nextState.get('nodeDetails').keySeq().last()).toEqual('rel1');
    expect(nextState.get('nodeDetails').size).toEqual(2);
    expect(nextState.get('nodeDetails').has('rel1')).toBeTruthy();

    // click on first node should clear the stack
    nextState = reducer(nextState, ClickNodeAction);
    expect(nextState.get('selectedNodeId')).toBe('n1');
    expect(nextState.get('nodeDetails').keySeq().last()).toEqual('n1');
    expect(nextState.get('nodeDetails').size).toEqual(1);
    expect(nextState.get('nodeDetails').has('rel1')).toBeFalsy();
  });

  it('keeps clears stack when sibling is clicked', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ClickNodeAction);
    expect(nextState.get('selectedNodeId')).toBe('n1');
    expect(nextState.get('nodeDetails').size).toEqual(1);
    expect(nextState.get('nodeDetails').has('n1')).toBeTruthy();
    expect(nextState.get('nodeDetails').keySeq().last()).toEqual('n1');

    nextState = reducer(nextState, ClickRelativeAction);
    // stack relative, first node stays main node
    expect(nextState.get('selectedNodeId')).toBe('n1');
    expect(nextState.get('nodeDetails').keySeq().last()).toEqual('rel1');
    expect(nextState.get('nodeDetails').size).toEqual(2);
    expect(nextState.get('nodeDetails').has('rel1')).toBeTruthy();

    // click on sibling node should clear the stack
    nextState = reducer(nextState, ClickNode2Action);
    expect(nextState.get('selectedNodeId')).toBe('n2');
    expect(nextState.get('nodeDetails').keySeq().last()).toEqual('n2');
    expect(nextState.get('nodeDetails').size).toEqual(1);
    expect(nextState.get('nodeDetails').has('n1')).toBeFalsy();
    expect(nextState.get('nodeDetails').has('rel1')).toBeFalsy();
  });

  it('selectes relatives topology while keeping node selected', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ClickTopologyAction);
    nextState = reducer(nextState, ReceiveTopologiesAction);
    expect(nextState.get('currentTopology').get('name')).toBe('Topo1');

    nextState = reducer(nextState, ClickNodeAction);
    expect(nextState.get('selectedNodeId')).toBe('n1');
    expect(nextState.get('nodeDetails').size).toEqual(1);
    expect(nextState.get('nodeDetails').has('n1')).toBeTruthy();
    expect(nextState.get('nodeDetails').keySeq().last()).toEqual('n1');

    nextState = reducer(nextState, ClickRelativeAction);
    // stack relative, first node stays main node
    expect(nextState.get('selectedNodeId')).toBe('n1');
    expect(nextState.get('nodeDetails').keySeq().last()).toEqual('rel1');
    expect(nextState.get('nodeDetails').size).toEqual(2);
    expect(nextState.get('nodeDetails').has('rel1')).toBeTruthy();

    // click switches over to relative's topology and selectes relative
    nextState = reducer(nextState, ClickShowTopologyForNodeAction);
    expect(nextState.get('selectedNodeId')).toBe('rel1');
    expect(nextState.get('nodeDetails').keySeq().last()).toEqual('rel1');
    expect(nextState.get('nodeDetails').size).toEqual(1);
    expect(nextState.get('currentTopology').get('name')).toBe('Topo2');
  });
  it('closes the help dialog if the canvas is clicked', () => {
    let nextState = initialState.set('showingHelp', true);
    nextState = reducer(nextState, { type: ActionTypes.CLICK_BACKGROUND });
    expect(nextState.get('showingHelp')).toBe(false);
  });
  it('switches to table view when complexity is high', () => {
    let nextState = initialState.set('currentTopology', fromJS(topologies[0]));
    nextState = reducer(nextState, {type: ActionTypes.SET_RECEIVED_NODES_DELTA});
    expect(nextState.get('topologyViewMode')).toEqual(TABLE_VIEW_MODE);
    expect(nextState.get('initialNodesLoaded')).toBe(true);
  });
  it('cleans up old adjacencies', () => {
    // Add some nodes
    const action1 = {
      delta: { add: [{ id: 'n1' }, { id: 'n2' }] },
      type: ActionTypes.RECEIVE_NODES_DELTA
    };
    // Show nodes as connected
    const action2 = {
      delta: {
        update: [{ adjacency: ['n2'], id: 'n1' }]
      },
      type: ActionTypes.RECEIVE_NODES_DELTA
    };
    // Remove the connection
    const action3 = {
      delta: {
        update: [{ id: 'n1' }]
      },
      type: ActionTypes.RECEIVE_NODES_DELTA
    };
    let nextState = reducer(initialState, action1);
    nextState = reducer(nextState, action2);
    nextState = reducer(nextState, action3);
    expect(nextState.getIn(['nodes', 'n1', 'adjacency'])).toBeFalsy();
  });
  it('removes non-transferrable state values when changing instances', () => {
    let nextState = initialState;
    nextState = reducer(nextState, ClickNodeAction);
    expect(nextState.get('selectedNodeId')).toEqual('n1');
    expect(nextState.getIn(['nodeDetails', 'n1'])).toBeTruthy();
    nextState = reducer(nextState, ChangeInstanceAction);
    expect(nextState.get('selectedNodeId')).toBeFalsy();
    expect(nextState.getIn(['nodeDetails', 'n1'])).toBeFalsy();
  });
  it('highlights bidirectional edges', () => {
    const action = {
      edgeId: constructEdgeId('abc123', 'def456'),
      type: ActionTypes.ENTER_EDGE
    };
    const nextState = reducer(initialState, action);
    expect(highlightedEdgeIdsSelector(nextState).toJS()).toEqual([
      constructEdgeId('abc123', 'def456'),
      constructEdgeId('def456', 'abc123')
    ]);
  });
});
