<commit_before>import {Map} from 'immutable';
import {createReducer} from 'redux-immutablejs';

const LIST_FOLDERS = 'garchen-panel/folder/LIST_FOLDERS';
const LIST_FOLDERS_SUCCESS = 'garchen-panel/folder/LIST_FOLDERS_SUCCESS';
const LIST_FOLDERS_FAIL = 'garchen-panel/folder/LIST_FOLDERS_FAIL';

const ADD_FOLDER = 'garchen-panel/folder/ADD_FOLDER';
const ADD_FOLDER_SUCCESS = 'garchen-panel/folder/ADD_FOLDER_SUCCESS';
const ADD_FOLDER_FAIL = 'garchen-panel/folder/ADD_FOLDER_FAIL';

const GET_FOLDER = 'garchen-panel/folder/GET_FOLDER';
const GET_FOLDER_SUCCESS = 'garchen-panel/folder/GET_FOLDER_SUCCESS';
const GET_FOLDER_FAIL = 'garchen-panel/folder/GET_FOLDER_FAIL';

const UPDATE_FOLDER = 'garchen-panel/folder/UPDATE_FOLDER';
const UPDATE_FOLDER_SUCCESS = 'garchen-panel/folder/UPDATE_FOLDER_SUCCESS';
const UPDATE_FOLDER_FAIL = 'garchen-panel/folder/UPDATE_FOLDER_FAIL';

const CHECK_FOLDER_EXISTS = 'garchen-panel/folder/CHECK_FOLDER_EXISTS';
const CHECK_FOLDER_EXISTS_SUCCESS = 'garchen-panel/folder/CHECK_FOLDER_EXISTS_SUCCESS';
const CHECK_FOLDER_EXISTS_FAIL = 'garchen-panel/folder/CHECK_FOLDER_EXISTS_FAIL';

const DELETE_FOLDERS = 'garchen-panel/folder/DELETE_FOLDERS';
const DELETE_FOLDERS_SUCCESS = 'garchen-panel/folder/DELETE_FOLDERS_SUCCESS';
const DELETE_FOLDERS_FAIL = 'garchen-panel/folder/DELETE_FOLDERS_FAIL';

const ADD_FOLDER_BY_CSV = 'garchen-panel/folder/ADD_FOLDER_BY_CSV';
const ADD_FOLDER_BY_CSV_SUCCESS = 'garchen-panel/folder/ADD_FOLDER_BY_CSV_SUCCESS';
const ADD_FOLDER_BY_CSV_FAIL = 'garchen-panel/folder/ADD_FOLDER_BY_CSV_FAIL';

const CANCEL_IMPORTING_CSV = 'garchen-panel/folder/CANCEL_IMPORTING_CSV';
const CANCEL_IMPORTING_CSV_SUCCESS = 'garchen-panel/folder/CANCEL_IMPORTING_CSV_SUCCESS';
const CANCEL_IMPORTING_CSV_FAIL = 'garchen-panel/folder/CANCEL_IMPORTING_CSV_FAIL';

const SET_IMPORTING_FOLDER_ID = 'garchen-panel/folder/SET_IMPORTING_FOLDER_ID';

const SET_DISPLAY_FOLDER_PERPAGE = 'garchen-panel/folder/SET_DISPLAY_FOLDER_PERPAGE';

const EXPORT_FOLDER_TO_CSV = 'garchen-panel/folder/EXPORT_FOLDER_TO_CSV';
const EXPORT_FOLDER_TO_CSV_SUCCESS = 'garchen-panel/folder/EXPORT_FOLDER_TO_CSV_SUCCESS';
const EXPORT_FOLDER_TO_CSV_FAIL = 'garchen-panel/folder/EXPORT_FOLDER_TO_CSV_FAIL';

const SET_IS_PROCESSING_CSV = 'garchen-panel/folder/SET_IS_PROCESSING_CSV';
const SET_IS_OPENING_DIALOG = 'garchen-panel/folder/SET_IS_OPENING_DIALOG';

const FOLDER_PERPAGE = 20;

const initialState = Map({
  perpage: FOLDER_PERPAGE,
  folder: null,
  folders: [],
  folderCount: 0,
  isProcessingCsv: false,
  isOpeningDialog: false,
  errorCsvMessage: null,
  errorCsvMessageId: null,
  errorCsvFilename: null,
  importingFolderId: null
});

export default createReducer(initialState, {

  [GET_FOLDER_SUCCESS]: (state, action) => {
    return state.set('folder', action.result);
  },

  [LIST_FOLDERS_SUCCESS]: (state, action) => {
    return state.set('folders', action.result.data)
      .set('folderCount', action.result.total);
  },

  [ADD_FOLDER_BY_CSV]: (state) => {
    return state.set('errorCsvMessage', null)
      .set('isOpeningDialog', true)
      .set('errorCsvMessageId', null)
      .set('errorCsvFilename', null);
  },

  [ADD_FOLDER_BY_CSV_SUCCESS]: (state) => {
    return state.set('isProcessingCsv', false)
      .set('isOpeningDialog', false)
      .set('importingFolderId', null);
  },

  [ADD_FOLDER_BY_CSV_FAIL]: (state, action) => {
    const {messageId, filename, message} = action.error;
    return state.set('isProcessingCsv', false)
      .set('isOpeningDialog', false)
      .set('importingFolderId', null)
      .set('errorCsvMessage', message)
      .set('errorCsvMessageId', messageId)
      .set('errorCsvFilename', filename);
  },

  [CANCEL_IMPORTING_CSV_SUCCESS]: (state) => {
    return state.set('isProcessingCsv', false)
      .set('importingFolderId', null)
      .set('errorCsvMessage', null)
      .set('errorCsvMessageId', null)
      .set('errorCsvFilename', null);
  },

  [SET_IMPORTING_FOLDER_ID]: (state, action) => {
    return state.set('importingFolderId', action.folderId);
  },

  [SET_DISPLAY_FOLDER_PERPAGE]: (state, action) => {
    return state.set('perpage', action.perpage);
  },

  [SET_IS_PROCESSING_CSV]: (state, action) => {
    return state.set('isProcessingCsv', action.isProcessingCsv);
  },

  [SET_IS_OPENING_DIALOG]: (state, action) => {
    return state.set('isOpeningDialog', action.isOpeningDialog);
  }
});

export function setIsOpeningDialog(isOpeningDialog) {
  return {
    type: SET_IS_OPENING_DIALOG,
    isOpeningDialog
  };
}

export function setIsProcessingCsv(isProcessingCsv) {
  return {
    type: SET_IS_PROCESSING_CSV,
    isProcessingCsv
  };
}

export function setImportingFolderId(folderId) {
  return {
    type: SET_IMPORTING_FOLDER_ID,
    folderId
  };
}

export function getFolder(data) {
  return {
    types: [GET_FOLDER, GET_FOLDER_SUCCESS, GET_FOLDER_FAIL],
    promise: (client) => client.send('get-folder', data)
  };
}

export function listFolders(data) {
  return {
    types: [LIST_FOLDERS, LIST_FOLDERS_SUCCESS, LIST_FOLDERS_FAIL],
    promise: (client) => client.send('list-folders', data)
  };
}

export function addFolder(data) {
  return {
    types: [ADD_FOLDER, ADD_FOLDER_SUCCESS, ADD_FOLDER_FAIL],
    promise: (client) => {
      return client.send('add-folder', data);
    }
  };
}

export function updateFolder(data) {
  return {
    types: [UPDATE_FOLDER, UPDATE_FOLDER_SUCCESS, UPDATE_FOLDER_FAIL],
    promise: (client) => {
      return client.send('update-folder', data);
    }
  };
}

export function deleteFolders(folderIds) {
  return {
    types: [DELETE_FOLDERS, DELETE_FOLDERS_SUCCESS, DELETE_FOLDERS_FAIL],
    promise: (client) => {
      return client.send('delete-folders', {folderIds});
    }
  };
}

export function checkFolderExists(data) {
  return {
    types: [CHECK_FOLDER_EXISTS, CHECK_FOLDER_EXISTS_SUCCESS, CHECK_FOLDER_EXISTS_FAIL],
    promise: (client) => {
      return client.send('check-folder-exists', data);
    }
  };
}

export function cancelImportingCsv() {
  return {
    types: [CANCEL_IMPORTING_CSV, CANCEL_IMPORTING_CSV_SUCCESS, CANCEL_IMPORTING_CSV_FAIL],
    promise: (client) => {
      return client.send('cancel-importing-csv');
    }
  };
}

export function addFolderByCsv(writeDelay) {
  return {
    types: [ADD_FOLDER_BY_CSV, ADD_FOLDER_BY_CSV_SUCCESS, ADD_FOLDER_BY_CSV_FAIL],
    promise: (client) => {
      return client.send('add-folder-by-csv', {writeDelay});
    }
  };
}

export function setDisplayFolderPerPage(perpage) {
  return {
    type: SET_DISPLAY_FOLDER_PERPAGE,
    perpage
  };
}

export function exportFolderToCsv(folderId) {
  return {
    types: [EXPORT_FOLDER_TO_CSV, EXPORT_FOLDER_TO_CSV_SUCCESS, EXPORT_FOLDER_TO_CSV_FAIL],
    promise: (client) => {
      return client.send('export-folder-to-csv', {folderId});
    }
  };
}
<commit_msg>add function markDeletedAtToFolders to folder duck<commit_after>import {Map} from 'immutable';
import {createReducer} from 'redux-immutablejs';

const LIST_FOLDERS = 'garchen-panel/folder/LIST_FOLDERS';
const LIST_FOLDERS_SUCCESS = 'garchen-panel/folder/LIST_FOLDERS_SUCCESS';
const LIST_FOLDERS_FAIL = 'garchen-panel/folder/LIST_FOLDERS_FAIL';

const ADD_FOLDER = 'garchen-panel/folder/ADD_FOLDER';
const ADD_FOLDER_SUCCESS = 'garchen-panel/folder/ADD_FOLDER_SUCCESS';
const ADD_FOLDER_FAIL = 'garchen-panel/folder/ADD_FOLDER_FAIL';

const GET_FOLDER = 'garchen-panel/folder/GET_FOLDER';
const GET_FOLDER_SUCCESS = 'garchen-panel/folder/GET_FOLDER_SUCCESS';
const GET_FOLDER_FAIL = 'garchen-panel/folder/GET_FOLDER_FAIL';

const UPDATE_FOLDER = 'garchen-panel/folder/UPDATE_FOLDER';
const UPDATE_FOLDER_SUCCESS = 'garchen-panel/folder/UPDATE_FOLDER_SUCCESS';
const UPDATE_FOLDER_FAIL = 'garchen-panel/folder/UPDATE_FOLDER_FAIL';

const CHECK_FOLDER_EXISTS = 'garchen-panel/folder/CHECK_FOLDER_EXISTS';
const CHECK_FOLDER_EXISTS_SUCCESS = 'garchen-panel/folder/CHECK_FOLDER_EXISTS_SUCCESS';
const CHECK_FOLDER_EXISTS_FAIL = 'garchen-panel/folder/CHECK_FOLDER_EXISTS_FAIL';

const DELETE_FOLDERS = 'garchen-panel/folder/DELETE_FOLDERS';
const DELETE_FOLDERS_SUCCESS = 'garchen-panel/folder/DELETE_FOLDERS_SUCCESS';
const DELETE_FOLDERS_FAIL = 'garchen-panel/folder/DELETE_FOLDERS_FAIL';

const ADD_FOLDER_BY_CSV = 'garchen-panel/folder/ADD_FOLDER_BY_CSV';
const ADD_FOLDER_BY_CSV_SUCCESS = 'garchen-panel/folder/ADD_FOLDER_BY_CSV_SUCCESS';
const ADD_FOLDER_BY_CSV_FAIL = 'garchen-panel/folder/ADD_FOLDER_BY_CSV_FAIL';

const CANCEL_IMPORTING_CSV = 'garchen-panel/folder/CANCEL_IMPORTING_CSV';
const CANCEL_IMPORTING_CSV_SUCCESS = 'garchen-panel/folder/CANCEL_IMPORTING_CSV_SUCCESS';
const CANCEL_IMPORTING_CSV_FAIL = 'garchen-panel/folder/CANCEL_IMPORTING_CSV_FAIL';

const SET_IMPORTING_FOLDER_ID = 'garchen-panel/folder/SET_IMPORTING_FOLDER_ID';

const SET_DISPLAY_FOLDER_PERPAGE = 'garchen-panel/folder/SET_DISPLAY_FOLDER_PERPAGE';

const EXPORT_FOLDER_TO_CSV = 'garchen-panel/folder/EXPORT_FOLDER_TO_CSV';
const EXPORT_FOLDER_TO_CSV_SUCCESS = 'garchen-panel/folder/EXPORT_FOLDER_TO_CSV_SUCCESS';
const EXPORT_FOLDER_TO_CSV_FAIL = 'garchen-panel/folder/EXPORT_FOLDER_TO_CSV_FAIL';

const SET_IS_PROCESSING_CSV = 'garchen-panel/folder/SET_IS_PROCESSING_CSV';
const SET_IS_OPENING_DIALOG = 'garchen-panel/folder/SET_IS_OPENING_DIALOG';

const MARK_DELETED_AT_TO_FOLDERS = 'garchen-panel/folder/MARK_DELETED_AT_TO_FOLDERS';
const MARK_DELETED_AT_TO_FOLDERS_SUCCESS = 'garchen-panel/folder/MARK_DELETED_AT_TO_FOLDERS_SUCCESS';
const MARK_DELETED_AT_TO_FOLDERS_FAIL = 'garchen-panel/folder/MARK_DELETED_AT_TO_FOLDERS_FAIL';

const FOLDER_PERPAGE = 20;

const initialState = Map({
  perpage: FOLDER_PERPAGE,
  folder: null,
  folders: [],
  folderCount: 0,
  isProcessingCsv: false,
  isOpeningDialog: false,
  errorCsvMessage: null,
  errorCsvMessageId: null,
  errorCsvFilename: null,
  importingFolderId: null
});

export default createReducer(initialState, {

  [GET_FOLDER_SUCCESS]: (state, action) => {
    return state.set('folder', action.result);
  },

  [LIST_FOLDERS_SUCCESS]: (state, action) => {
    return state.set('folders', action.result.data)
      .set('folderCount', action.result.total);
  },

  [ADD_FOLDER_BY_CSV]: (state) => {
    return state.set('errorCsvMessage', null)
      .set('isOpeningDialog', true)
      .set('errorCsvMessageId', null)
      .set('errorCsvFilename', null);
  },

  [ADD_FOLDER_BY_CSV_SUCCESS]: (state) => {
    return state.set('isProcessingCsv', false)
      .set('isOpeningDialog', false)
      .set('importingFolderId', null);
  },

  [ADD_FOLDER_BY_CSV_FAIL]: (state, action) => {
    const {messageId, filename, message} = action.error;
    return state.set('isProcessingCsv', false)
      .set('isOpeningDialog', false)
      .set('importingFolderId', null)
      .set('errorCsvMessage', message)
      .set('errorCsvMessageId', messageId)
      .set('errorCsvFilename', filename);
  },

  [CANCEL_IMPORTING_CSV_SUCCESS]: (state) => {
    return state.set('isProcessingCsv', false)
      .set('importingFolderId', null)
      .set('errorCsvMessage', null)
      .set('errorCsvMessageId', null)
      .set('errorCsvFilename', null);
  },

  [SET_IMPORTING_FOLDER_ID]: (state, action) => {
    return state.set('importingFolderId', action.folderId);
  },

  [SET_DISPLAY_FOLDER_PERPAGE]: (state, action) => {
    return state.set('perpage', action.perpage);
  },

  [SET_IS_PROCESSING_CSV]: (state, action) => {
    return state.set('isProcessingCsv', action.isProcessingCsv);
  },

  [SET_IS_OPENING_DIALOG]: (state, action) => {
    return state.set('isOpeningDialog', action.isOpeningDialog);
  }
});

export function setIsOpeningDialog(isOpeningDialog) {
  return {
    type: SET_IS_OPENING_DIALOG,
    isOpeningDialog
  };
}

export function setIsProcessingCsv(isProcessingCsv) {
  return {
    type: SET_IS_PROCESSING_CSV,
    isProcessingCsv
  };
}

export function setImportingFolderId(folderId) {
  return {
    type: SET_IMPORTING_FOLDER_ID,
    folderId
  };
}

export function getFolder(data) {
  return {
    types: [GET_FOLDER, GET_FOLDER_SUCCESS, GET_FOLDER_FAIL],
    promise: (client) => client.send('get-folder', data)
  };
}

export function listFolders(data) {
  return {
    types: [LIST_FOLDERS, LIST_FOLDERS_SUCCESS, LIST_FOLDERS_FAIL],
    promise: (client) => client.send('list-folders', data)
  };
}

export function addFolder(data) {
  return {
    types: [ADD_FOLDER, ADD_FOLDER_SUCCESS, ADD_FOLDER_FAIL],
    promise: (client) => {
      return client.send('add-folder', data);
    }
  };
}

export function updateFolder(data) {
  return {
    types: [UPDATE_FOLDER, UPDATE_FOLDER_SUCCESS, UPDATE_FOLDER_FAIL],
    promise: (client) => {
      return client.send('update-folder', data);
    }
  };
}

export function markDeletedAtToFolders(folderIds) {
  return {
    types: [MARK_DELETED_AT_TO_FOLDERS, MARK_DELETED_AT_TO_FOLDERS_SUCCESS, MARK_DELETED_AT_TO_FOLDERS_FAIL],
    promise: (client) => {
      return client.send('mark-deleted-at-to-folders', {folderIds});
    }
  };
}

export function deleteFolders(folderIds) {
  return {
    types: [DELETE_FOLDERS, DELETE_FOLDERS_SUCCESS, DELETE_FOLDERS_FAIL],
    promise: (client) => {
      return client.send('delete-folders', {folderIds});
    }
  };
}

export function checkFolderExists(data) {
  return {
    types: [CHECK_FOLDER_EXISTS, CHECK_FOLDER_EXISTS_SUCCESS, CHECK_FOLDER_EXISTS_FAIL],
    promise: (client) => {
      return client.send('check-folder-exists', data);
    }
  };
}

export function cancelImportingCsv() {
  return {
    types: [CANCEL_IMPORTING_CSV, CANCEL_IMPORTING_CSV_SUCCESS, CANCEL_IMPORTING_CSV_FAIL],
    promise: (client) => {
      return client.send('cancel-importing-csv');
    }
  };
}

export function addFolderByCsv(writeDelay) {
  return {
    types: [ADD_FOLDER_BY_CSV, ADD_FOLDER_BY_CSV_SUCCESS, ADD_FOLDER_BY_CSV_FAIL],
    promise: (client) => {
      return client.send('add-folder-by-csv', {writeDelay});
    }
  };
}

export function setDisplayFolderPerPage(perpage) {
  return {
    type: SET_DISPLAY_FOLDER_PERPAGE,
    perpage
  };
}

export function exportFolderToCsv(folderId) {
  return {
    types: [EXPORT_FOLDER_TO_CSV, EXPORT_FOLDER_TO_CSV_SUCCESS, EXPORT_FOLDER_TO_CSV_FAIL],
    promise: (client) => {
      return client.send('export-folder-to-csv', {folderId});
    }
  };
}
<|endoftext|>


<commit_before>/* @flow */

import stringArgv from 'string-argv';
import * as neodoc from 'neodoc';

export const NEODOC_SET_SOURCE = 'NEODOC_SET_SOURCE';
export const NEODOC_SET_ARGV = 'NEODOC_SET_ARGV';
export const NEODOC_SET_OPTS_FIRST = 'NEODOC_SET_OPTS_FIRST';
export const NEODOC_SET_SMART_OPTS = 'NEODOC_SET_SMART_OPTS';

type State = {
  source: string,
  output: Object,
  error: Object,
  argv: Array,
  optionsFirst: boolean,
  smartOptions: boolean,
};

export function setSource (value: string): Action {
  return {
    type: NEODOC_SET_SOURCE,
    payload: value
  };
}

export function setArgv (value: string): Action {
  return {
    type: NEODOC_SET_ARGV,
    payload: value
  };
}

export function setOptionsFirst (value: boolean): Action {
  return {
    type: NEODOC_SET_OPTS_FIRST,
    payload: value
  };
}

export function setSmartOptions (value: boolean): Action {
  return {
    type: NEODOC_SET_SMART_OPTS,
    payload: value
  };
}

export const actions = {
  setSource,
  setArgv,
  setOptionsFirst,
  setSmartOptions
};

function run (text, argv, optionsFirst, smartOptions) {
  let output = null;
  let error = null;
  console.log(text, argv, optionsFirst, smartOptions);
  try {
    output = neodoc.run(
      text,
      {
        argv: stringArgv(argv),
        dontExit: true,
        optionsFirst: optionsFirst,
        smartOptions: smartOptions
      }
    );
  } catch (e) {
    error = e;
  }

  return {
    source: text,
    output: output,
    argv: argv,
    error: error,
    optionsFirst: optionsFirst,
    smartOptions: smartOptions
  };
}

const ACTION_HANDLERS = {
  [NEODOC_SET_SOURCE]:
    (state: State, action: {payload: string}): string => {
      return run(
        action.payload,
        state.argv,
        state.optionsFirst,
        state.smartOptions
      );
    },
  [NEODOC_SET_ARGV]:
    (state: State, action: {payload: string}): string => {
      return run(
        state.source,
        action.payload,
        state.optionsFirst,
        state.smartOptions
      );
    },
  [NEODOC_SET_OPTS_FIRST]:
    (state: State, action: {payload: boolean}): boolean => {
      return run(
        state.source,
        state.argv,
        action.payload,
        state.smartOptions
      );
    },
  [NEODOC_SET_SMART_OPTS]:
    (state: State, action: {payload: boolean}): boolean => {
      return run(
        state.source,
        state.argv,
        state.optionsFirst,
        action.payload
      );
    }
};

const initialState = {
  source:
`\
Usage:
  git -f x <y>...
`,
  argv: '',
  output: null,
  error: null,
  optionsFirst: false,
  smartOptions: false
};

export default function counterReducer (
  state: State = initialState,
  action: Action
): number {
  const handler = ACTION_HANDLERS[action.type];
  return handler ? handler(state, action) : state;
}
<commit_msg>Use `git --help` as example<commit_after>/* @flow */

import stringArgv from 'string-argv';
import * as neodoc from 'neodoc';

export const NEODOC_SET_SOURCE = 'NEODOC_SET_SOURCE';
export const NEODOC_SET_ARGV = 'NEODOC_SET_ARGV';
export const NEODOC_SET_OPTS_FIRST = 'NEODOC_SET_OPTS_FIRST';
export const NEODOC_SET_SMART_OPTS = 'NEODOC_SET_SMART_OPTS';

type State = {
  source: string,
  output: Object,
  error: Object,
  argv: Array,
  optionsFirst: boolean,
  smartOptions: boolean,
};

export function setSource (value: string): Action {
  return {
    type: NEODOC_SET_SOURCE,
    payload: value
  };
}

export function setArgv (value: string): Action {
  return {
    type: NEODOC_SET_ARGV,
    payload: value
  };
}

export function setOptionsFirst (value: boolean): Action {
  return {
    type: NEODOC_SET_OPTS_FIRST,
    payload: value
  };
}

export function setSmartOptions (value: boolean): Action {
  return {
    type: NEODOC_SET_SMART_OPTS,
    payload: value
  };
}

export const actions = {
  setSource,
  setArgv,
  setOptionsFirst,
  setSmartOptions
};

function run (text, argv, optionsFirst, smartOptions) {
  let output = null;
  let error = null;
  console.log(text, argv, optionsFirst, smartOptions);
  try {
    output = neodoc.run(
      text,
      {
        argv: stringArgv(argv),
        dontExit: true,
        optionsFirst: optionsFirst,
        smartOptions: smartOptions
      }
    );
  } catch (e) {
    error = e;
  }

  return {
    source: text,
    output: output,
    argv: argv,
    error: error,
    optionsFirst: optionsFirst,
    smartOptions: smartOptions
  };
}

const ACTION_HANDLERS = {
  [NEODOC_SET_SOURCE]:
    (state: State, action: {payload: string}): string => {
      return run(
        action.payload,
        state.argv,
        state.optionsFirst,
        state.smartOptions
      );
    },
  [NEODOC_SET_ARGV]:
    (state: State, action: {payload: string}): string => {
      return run(
        state.source,
        action.payload,
        state.optionsFirst,
        state.smartOptions
      );
    },
  [NEODOC_SET_OPTS_FIRST]:
    (state: State, action: {payload: boolean}): boolean => {
      return run(
        state.source,
        state.argv,
        action.payload,
        state.smartOptions
      );
    },
  [NEODOC_SET_SMART_OPTS]:
    (state: State, action: {payload: boolean}): boolean => {
      return run(
        state.source,
        state.argv,
        state.optionsFirst,
        action.payload
      );
    }
};

const initialState = {
  source:
`\
usage: git [--version] [--help] [-C <path>] [-c <name=value>]
           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
           [<command> [<args>...]]

The most commonly used git commands are:
   add        Add file contents to the index
   bisect     Find by binary search the change that introduced a bug
   branch     List, create, or delete branches
   checkout   Checkout a branch or paths to the working tree
   clone      Clone a repository into a new directory
   commit     Record changes to the repository
   diff       Show changes between commits, commit and working tree, etc
   fetch      Download objects and refs from another repository
   grep       Print lines matching a pattern
   init       Create an empty Git repository or reinitialize an existing one
   log        Show commit logs
   merge      Join two or more development histories together
   mv         Move or rename a file, a directory, or a symlink
   pull       Fetch from and integrate with another repository or a local branch
   push       Update remote refs along with associated objects
   rebase     Forward-port local commits to the updated upstream head
   reset      Reset current HEAD to the specified state
   rm         Remove files from the working tree and from the index
   show       Show various types of objects
   status     Show the working tree status
   tag        Create, list, delete or verify a tag object signed with GPG

'git help -a' and 'git help -g' lists available subcommands and some
concept guides. See 'git help <command>' or 'git help <concept>'
to read about a specific subcommand or concept.
`,
  argv: '',
  output: null,
  error: null,
  optionsFirst: true,
  smartOptions: true
};

export default function counterReducer (
  state: State = initialState,
  action: Action
): number {
  const handler = ACTION_HANDLERS[action.type];
  return handler ? handler(state, action) : state;
}
<|endoftext|>


<commit_before>window.multigraph.util.namespace("window.multigraph.graphics.raphael", function (ns) {
    "use strict";

    ns.mixin.add(function (ns) {

        // cached settings object, for quick access during rendering, populated in begin() method:
        ns.BarRenderer.hasA("settings");

        ns.BarRenderer.respondsTo("begin", function (graphicsContext) {
            var settings = {
                "paper"              : graphicsContext.paper,
                "set"                : graphicsContext.set,
                "paths"              : [],
                "barpixelwidth"      : this.getOptionValue("barwidth").getRealValue() * this.plot().horizontalaxis().axisToDataRatio(),
                "baroffset"          : this.getOptionValue("baroffset"),
                "barpixelbase"       : (this.getOptionValue("barbase") !== null)?this.plot().verticalaxis().dataValueToAxisValue(this.getOptionValue("barbase")):0,
                "fillcolor"          : this.getOptionValue("fillcolor"),
                "linecolor"          : this.getOptionValue("linecolor"),
                "hidelines"          : this.getOptionValue("hidelines"),
                "barGroups"          : [],
                "currentBarGroup"    : null,
                "prevCorner"         : null,
                "pixelEdgeTolerance" : 1
            };

            this.settings(settings);
        });

        ns.BarRenderer.respondsTo("dataPoint", function (datap) {
            var settings = this.settings(),
                p,
                x0,
                x1,
                fillcolor = this.getOptionValue("fillcolor", datap[1]),
                flag = false,
                i;

            if (this.isMissing(datap)) {
                return;
            }

            p = this.transformPoint(datap);

            x0 = p[0] + settings.baroffset;
            x1 = p[0] + settings.baroffset + settings.barpixelwidth;

            for (i = 0; i < settings.paths.length; i++) {
                if (settings.paths[i].fillcolor === fillcolor) {
                    settings.paths[i].path += this.generateBar(x0, settings.barpixelbase, settings.barpixelwidth, p[1] - settings.barpixelbase);
                    flag = true;
                    break;
                }
            }

            if (flag === false) {
                i = settings.paths.length;
                settings.paths[i] = {};
                settings.paths[i].fillcolor = fillcolor;
                settings.paths[i].path = this.generateBar(x0, settings.barpixelbase, settings.barpixelwidth, p[1] - settings.barpixelbase);
            }

            if (settings.barpixelwidth > settings.hidelines) {
                if (settings.prevCorner === null) {
                    settings.currentBarGroup = [ [x0,p[1]] ];
                } else {
                    if (Math.abs(x0 - settings.prevCorner[0]) <= settings.pixelEdgeTolerance) {
                        settings.currentBarGroup.push( [x0,p[1]] );
                    } else {
                        settings.currentBarGroup.push( settings.prevCorner );
                        settings.barGroups.push( settings.currentBarGroup );
                        settings.currentBarGroup = [ [x0,p[1]] ];
                    }
                }
                settings.prevCorner = [x1,p[1]];
            }
        });

        ns.BarRenderer.respondsTo("end", function () {
            var settings = this.settings(),
                p,
                outlinePath = "",
                barGroup,
                i,
                j,
                n;

            if (settings.prevCorner !== null && settings.currentBarGroup !== null) {
                settings.currentBarGroup.push( settings.prevCorner );
                settings.barGroups.push( settings.currentBarGroup );
            }

            for (i = 0; i < settings.barGroups.length; i++) {
                barGroup = settings.barGroups[i];
                n = barGroup.length;
                if (n < 2) { return; } // this should never happen

                // For the first point, draw 3 lines:
                //
                //       y |------
                //         |
                //         |
                //    base |------
                //         ^     ^
                //         x     x(next)
                //

                //   horizontal line @ y from x(next) to x
                outlinePath += "M" + barGroup[1][0] + "," + barGroup[0][1];
                outlinePath += "L" + barGroup[0][0] + "," + barGroup[0][1];
                //   vertical line @ x from y to base
                outlinePath += "L" + barGroup[0][0] + "," + settings.barpixelbase;
                //   horizontal line @ base from x to x(next)
                outlinePath += "L" + barGroup[1][0] + "," + settings.barpixelbase;

                for (j = 1; j < n - 1; ++j) {
                    // For intermediate points, draw 3 lines:
                    //
                    //       y |
                    //         |
                    //         |
                    //         |------ y(next)
                    //         |
                    //         |
                    //         |------ base
                    //         ^     ^
                    //         x     x(next)
                    //
                    //   vertical line @ x from min to max of (y, y(next), base)
                    outlinePath += "M" + barGroup[j][0] + "," + Math.min(barGroup[j-1][1], barGroup[j][1], settings.barpixelbase);
                    outlinePath += "L" + barGroup[j][0] + "," + Math.max(barGroup[j-1][1], barGroup[j][1], settings.barpixelbase);
                    //   horizontal line @ y(next) from x to x(next)
                    outlinePath += "M" + barGroup[j][0] + "," +   barGroup[j][1];
                    outlinePath += "L" + barGroup[j+1][0] + "," + barGroup[j][1];
                    //   horizontal line @ base from x to x(next)
                    outlinePath += "M" + barGroup[j][0] + "," +   settings.barpixelbase;
                    outlinePath += "L" + barGroup[j+1][0] + "," + settings.barpixelbase;
                }
                // For last point, draw one line:
                //
                //       y |
                //         |
                //         |
                //    base |
                //         ^     ^
                //         x     x(next)
                //
                //   vertical line @ x from base to y
                outlinePath += "M" + barGroup[n-1][0] + "," + barGroup[n-1][1];
                outlinePath += "L" + barGroup[n-1][0] + "," + settings.barpixelbase;
            }

            for (i = 0; i < settings.paths.length; i++) {
                settings.set.push( settings.paper.path(settings.paths[i].path)
                                   .attr({
                                       "stroke-width": 1,
                                       "fill": settings.paths[i].fillcolor.getHexString("#"),
                                       "stroke": settings.fillcolor.getHexString("#")
                                   })
                                 );
            }

            settings.set.push( settings.paper.path(outlinePath)
                               .attr({
                                   "stroke-width": 1,
                                   "stroke": settings.linecolor.getHexString("#")
                               })
                             );

        });

        ns.BarRenderer.respondsTo("generateBar", function (x, y, width, height) {
            var path = "M" + x + "," + y;
            path    += "L" + x + "," + (y + height);
            path    += "L" + (x + width) + "," + (y + height);
            path    += "L" + (x + width) + "," + y;
            path    += "Z";
            return path;
        });

        ns.BarRenderer.respondsTo("renderLegendIcon", function (graphicsContext, x, y, icon, opacity) {
            var settings          = this.settings(),
                rendererFillColor = this.getOptionValue("fillcolor", 0),
                rendererOpacity   = this.getOptionValue("fillopacity", 0),
                iconAttrs,
                barwidth;

            // Draw icon background (with opacity)
            graphicsContext.set.push(
                graphicsContext.paper.rect(x, y, icon.width(), icon.height())
                    .attr({
                        "stroke" : "rgba(255, 255, 255, " + opacity + ")",
                        "fill"   : "rgba(255, 255, 255, " + opacity + ")"
                    })
            );

            iconAttrs = {
                "stroke-width" : 1,
                "fill"         : rendererFillColor.toRGBA(opacity * rendererOpacity)
            };

            if (settings.barpixelwidth < settings.hidelines) {
                iconAttrs.stroke = rendererFillColor.toRGBA(opacity * rendererOpacity);
            } else {
                iconAttrs.stroke = this.getOptionValue("linecolor", 0).toRGBA(opacity);
            }

            // Adjust the width of the icons bars based upon the width and height of the icon Ranges: {20, 10, 0}
            if (icon.width() > 20 || icon.height() > 20) {
                barwidth = icon.width() / 6;
            } else if (icon.width() > 10 || icon.height() > 10) {
                barwidth = icon.width() / 4;
            } else {
                barwidth = icon.width() / 4;
            }

            // If the icon is large enough draw extra bars
            if (icon.width() > 20 && icon.height() > 20) {
                graphicsContext.set.push(
                    graphicsContext.paper.rect((icon.width() / 4) - (barwidth / 2), 0, barwidth, icon.height() / 2)
                        .attr(iconAttrs)
                        .transform("t" + x + "," + y),

                    graphicsContext.paper.rect(icon.width() - (icon.width() / 4) - (barwidth / 2), 0, barwidth, icon.height() / 3)
                        .attr(iconAttrs)
                        .transform("t" + x + "," + y)
                );
            }

            graphicsContext.set.push(
                graphicsContext.paper.rect((icon.width() / 2) - (barwidth / 2), 0, barwidth, icon.height() - (icon.height() / 4))
                    .attr(iconAttrs)
                    .transform("t" + x + "," + y)
            );

        });

    });

});
<commit_msg>simplifies raphaels creation of the legend icon for the bar renderer<commit_after>window.multigraph.util.namespace("window.multigraph.graphics.raphael", function (ns) {
    "use strict";

    ns.mixin.add(function (ns) {

        // cached settings object, for quick access during rendering, populated in begin() method:
        ns.BarRenderer.hasA("settings");

        ns.BarRenderer.respondsTo("begin", function (graphicsContext) {
            var settings = {
                "paper"              : graphicsContext.paper,
                "set"                : graphicsContext.set,
                "paths"              : [],
                "barpixelwidth"      : this.getOptionValue("barwidth").getRealValue() * this.plot().horizontalaxis().axisToDataRatio(),
                "baroffset"          : this.getOptionValue("baroffset"),
                "barpixelbase"       : (this.getOptionValue("barbase") !== null)?this.plot().verticalaxis().dataValueToAxisValue(this.getOptionValue("barbase")):0,
                "fillcolor"          : this.getOptionValue("fillcolor"),
                "linecolor"          : this.getOptionValue("linecolor"),
                "hidelines"          : this.getOptionValue("hidelines"),
                "barGroups"          : [],
                "currentBarGroup"    : null,
                "prevCorner"         : null,
                "pixelEdgeTolerance" : 1
            };

            this.settings(settings);
        });

        ns.BarRenderer.respondsTo("dataPoint", function (datap) {
            var settings = this.settings(),
                p,
                x0,
                x1,
                fillcolor = this.getOptionValue("fillcolor", datap[1]),
                flag = false,
                i;

            if (this.isMissing(datap)) {
                return;
            }

            p = this.transformPoint(datap);

            x0 = p[0] + settings.baroffset;
            x1 = p[0] + settings.baroffset + settings.barpixelwidth;

            for (i = 0; i < settings.paths.length; i++) {
                if (settings.paths[i].fillcolor === fillcolor) {
                    settings.paths[i].path += this.generateBar(x0, settings.barpixelbase, settings.barpixelwidth, p[1] - settings.barpixelbase);
                    flag = true;
                    break;
                }
            }

            if (flag === false) {
                i = settings.paths.length;
                settings.paths[i] = {};
                settings.paths[i].fillcolor = fillcolor;
                settings.paths[i].path = this.generateBar(x0, settings.barpixelbase, settings.barpixelwidth, p[1] - settings.barpixelbase);
            }

            if (settings.barpixelwidth > settings.hidelines) {
                if (settings.prevCorner === null) {
                    settings.currentBarGroup = [ [x0,p[1]] ];
                } else {
                    if (Math.abs(x0 - settings.prevCorner[0]) <= settings.pixelEdgeTolerance) {
                        settings.currentBarGroup.push( [x0,p[1]] );
                    } else {
                        settings.currentBarGroup.push( settings.prevCorner );
                        settings.barGroups.push( settings.currentBarGroup );
                        settings.currentBarGroup = [ [x0,p[1]] ];
                    }
                }
                settings.prevCorner = [x1,p[1]];
            }
        });

        ns.BarRenderer.respondsTo("end", function () {
            var settings = this.settings(),
                p,
                outlinePath = "",
                barGroup,
                i,
                j,
                n;

            if (settings.prevCorner !== null && settings.currentBarGroup !== null) {
                settings.currentBarGroup.push( settings.prevCorner );
                settings.barGroups.push( settings.currentBarGroup );
            }

            for (i = 0; i < settings.barGroups.length; i++) {
                barGroup = settings.barGroups[i];
                n = barGroup.length;
                if (n < 2) { return; } // this should never happen

                // For the first point, draw 3 lines:
                //
                //       y |------
                //         |
                //         |
                //    base |------
                //         ^     ^
                //         x     x(next)
                //

                //   horizontal line @ y from x(next) to x
                outlinePath += "M" + barGroup[1][0] + "," + barGroup[0][1];
                outlinePath += "L" + barGroup[0][0] + "," + barGroup[0][1];
                //   vertical line @ x from y to base
                outlinePath += "L" + barGroup[0][0] + "," + settings.barpixelbase;
                //   horizontal line @ base from x to x(next)
                outlinePath += "L" + barGroup[1][0] + "," + settings.barpixelbase;

                for (j = 1; j < n - 1; ++j) {
                    // For intermediate points, draw 3 lines:
                    //
                    //       y |
                    //         |
                    //         |
                    //         |------ y(next)
                    //         |
                    //         |
                    //         |------ base
                    //         ^     ^
                    //         x     x(next)
                    //
                    //   vertical line @ x from min to max of (y, y(next), base)
                    outlinePath += "M" + barGroup[j][0] + "," + Math.min(barGroup[j-1][1], barGroup[j][1], settings.barpixelbase);
                    outlinePath += "L" + barGroup[j][0] + "," + Math.max(barGroup[j-1][1], barGroup[j][1], settings.barpixelbase);
                    //   horizontal line @ y(next) from x to x(next)
                    outlinePath += "M" + barGroup[j][0] + "," +   barGroup[j][1];
                    outlinePath += "L" + barGroup[j+1][0] + "," + barGroup[j][1];
                    //   horizontal line @ base from x to x(next)
                    outlinePath += "M" + barGroup[j][0] + "," +   settings.barpixelbase;
                    outlinePath += "L" + barGroup[j+1][0] + "," + settings.barpixelbase;
                }
                // For last point, draw one line:
                //
                //       y |
                //         |
                //         |
                //    base |
                //         ^     ^
                //         x     x(next)
                //
                //   vertical line @ x from base to y
                outlinePath += "M" + barGroup[n-1][0] + "," + barGroup[n-1][1];
                outlinePath += "L" + barGroup[n-1][0] + "," + settings.barpixelbase;
            }

            for (i = 0; i < settings.paths.length; i++) {
                settings.set.push( settings.paper.path(settings.paths[i].path)
                                   .attr({
                                       "stroke-width": 1,
                                       "fill": settings.paths[i].fillcolor.getHexString("#"),
                                       "stroke": settings.fillcolor.getHexString("#")
                                   })
                                 );
            }

            settings.set.push( settings.paper.path(outlinePath)
                               .attr({
                                   "stroke-width": 1,
                                   "stroke": settings.linecolor.getHexString("#")
                               })
                             );

        });

        ns.BarRenderer.respondsTo("generateBar", function (x, y, width, height) {
            var path = "M" + x + "," + y;
            path    += "L" + x + "," + (y + height);
            path    += "L" + (x + width) + "," + (y + height);
            path    += "L" + (x + width) + "," + y;
            path    += "Z";
            return path;
        });

        ns.BarRenderer.respondsTo("renderLegendIcon", function (graphicsContext, x, y, icon, opacity) {
            var settings          = this.settings(),
                rendererFillColor = this.getOptionValue("fillcolor", 0),
                rendererOpacity   = this.getOptionValue("fillopacity", 0),
                path = "",
                iconAttrs,
                barwidth;

            // Draw icon background (with opacity)
            graphicsContext.set.push(
                graphicsContext.paper.rect(x, y, icon.width(), icon.height())
                    .attr({
                        "stroke" : "rgba(255, 255, 255, " + opacity + ")",
                        "fill"   : "rgba(255, 255, 255, " + opacity + ")"
                    })
            );

            iconAttrs = {
                "stroke-width" : 1,
                "fill"         : rendererFillColor.toRGBA(opacity * rendererOpacity)
            };

            if (settings.barpixelwidth < settings.hidelines) {
                iconAttrs.stroke = rendererFillColor.toRGBA(opacity * rendererOpacity);
            } else {
                iconAttrs.stroke = this.getOptionValue("linecolor", 0).toRGBA(opacity);
            }

            // Adjust the width of the icons bars based upon the width and height of the icon Ranges: {20, 10, 0}
            if (icon.width() > 20 || icon.height() > 20) {
                barwidth = icon.width() / 6;
            } else if (icon.width() > 10 || icon.height() > 10) {
                barwidth = icon.width() / 4;
            } else {
                barwidth = icon.width() / 4;
            }

            // If the icon is large enough draw extra bars
            if (icon.width() > 20 && icon.height() > 20) {
                path += this.generateBar(x + (icon.width() / 4) - (barwidth / 2), y, barwidth, icon.height() / 2);
                path += this.generateBar(x + icon.width() - (icon.width() / 4) - (barwidth / 2), y, barwidth, icon.height() / 3);
            }

            path += this.generateBar(x + (icon.width() / 2) - (barwidth / 2), y, barwidth, icon.height() - (icon.height() / 4));

            graphicsContext.set.push(
                graphicsContext.paper.path(path)
                    .attr(iconAttrs)
            );

        });

    });

});
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';

import { makeCharacterApiUrl } from 'common/makeApiUrl';
import { captureException } from 'common/errorLogger';

const CHINESE_REGION = 'cn';

class CharacterProfileLoader extends React.PureComponent {
  static propTypes = {
    report: PropTypes.shape({
      exportedCharacters: PropTypes.arrayOf(PropTypes.shape({
        name: PropTypes.string.isRequired,
        region: PropTypes.string.isRequired,
        server: PropTypes.string.isRequired,
      })),
    }).isRequired,
    player: PropTypes.shape({
      name: PropTypes.string.isRequired,
      guid: PropTypes.number.isRequired,
    }).isRequired,
    children: PropTypes.func.isRequired,
  };

  constructor(props) {
    super(props);
    this.state = {
      isLoading: true,
      characterProfile: null,
    };
    this.load();
  }

  componentDidUpdate(prevProps, prevState, prevContext) {
    if (prevProps.report !== this.props.report || prevProps.player !== this.props.player) {
      this.setState({
        isLoading: true,
        characterProfile: null,
      });
      this.load();
    }
  }

  async load() {
    let characterProfile;
    try {
      characterProfile = await this.loadCharacterProfile();
    } catch (err) {
      // This only provides optional info, so it's no big deal if it fails.
      // We don't want to log it, as it will usually just be a 404 where the character moved or something
      console.error(err);
    }

    this.setState({
      isLoading: false,
      characterProfile,
    });
  }

  async loadCharacterProfile() {
    const { report, player } = this.props;
    // This is a Blizzard globally unique character id. If a character transfers, they get a new guid.
    // If we've seen the character before, this id will be stored in the database along with the player's name, realm and region which gives the API enough info to fetch their character profile.
    // If they're not stored in the database, we need the region, realm and name from the report to store the correct data in the database. For this reason the data from the expertedCharacters is optional and we may be able to get the correct profile without it.
    const id = player.guid;
    // TODO: Since the player selection loads this data now too, store it in Redux and use a cached version if available.
    let region;
    let realm;
    let name;
    const exportedCharacter = report.exportedCharacters ? report.exportedCharacters.find(char => char.name === player.name) : null;
    if (exportedCharacter) {
      region = exportedCharacter.region.toLowerCase();
      realm = exportedCharacter.server;
      name = exportedCharacter.name;
      if (region === CHINESE_REGION) {
        // China doesn't have an API
        return null;
      }
    }

    return fetch(makeCharacterApiUrl(id, region, realm, name)).then(data => data.json());
  }

  render() {
    return this.props.children(this.state.isLoading, this.state.characterProfile);
  }
}

export default CharacterProfileLoader;
<commit_msg>Fix eslint<commit_after>import React from 'react';
import PropTypes from 'prop-types';

import { makeCharacterApiUrl } from 'common/makeApiUrl';

const CHINESE_REGION = 'cn';

class CharacterProfileLoader extends React.PureComponent {
  static propTypes = {
    report: PropTypes.shape({
      exportedCharacters: PropTypes.arrayOf(PropTypes.shape({
        name: PropTypes.string.isRequired,
        region: PropTypes.string.isRequired,
        server: PropTypes.string.isRequired,
      })),
    }).isRequired,
    player: PropTypes.shape({
      name: PropTypes.string.isRequired,
      guid: PropTypes.number.isRequired,
    }).isRequired,
    children: PropTypes.func.isRequired,
  };

  constructor(props) {
    super(props);
    this.state = {
      isLoading: true,
      characterProfile: null,
    };
    this.load();
  }

  componentDidUpdate(prevProps, prevState, prevContext) {
    if (prevProps.report !== this.props.report || prevProps.player !== this.props.player) {
      this.setState({
        isLoading: true,
        characterProfile: null,
      });
      this.load();
    }
  }

  async load() {
    let characterProfile;
    try {
      characterProfile = await this.loadCharacterProfile();
    } catch (err) {
      // This only provides optional info, so it's no big deal if it fails.
      // We don't want to log it, as it will usually just be a 404 where the character moved or something
      console.error(err);
    }

    this.setState({
      isLoading: false,
      characterProfile,
    });
  }

  async loadCharacterProfile() {
    const { report, player } = this.props;
    // This is a Blizzard globally unique character id. If a character transfers, they get a new guid.
    // If we've seen the character before, this id will be stored in the database along with the player's name, realm and region which gives the API enough info to fetch their character profile.
    // If they're not stored in the database, we need the region, realm and name from the report to store the correct data in the database. For this reason the data from the expertedCharacters is optional and we may be able to get the correct profile without it.
    const id = player.guid;
    // TODO: Since the player selection loads this data now too, store it in Redux and use a cached version if available.
    let region;
    let realm;
    let name;
    const exportedCharacter = report.exportedCharacters ? report.exportedCharacters.find(char => char.name === player.name) : null;
    if (exportedCharacter) {
      region = exportedCharacter.region.toLowerCase();
      realm = exportedCharacter.server;
      name = exportedCharacter.name;
      if (region === CHINESE_REGION) {
        // China doesn't have an API
        return null;
      }
    }

    return fetch(makeCharacterApiUrl(id, region, realm, name)).then(data => data.json());
  }

  render() {
    return this.props.children(this.state.isLoading, this.state.characterProfile);
  }
}

export default CharacterProfileLoader;
<|endoftext|>


<commit_before>module.exports = {
  waitForConditionPollInterval: 2000,
  waitForConditionTimeout: 12000,
  pauseDuration: 7500,
  retryAssertionTimeout: 5000,
}
<commit_msg>Reset nighwatch timeouts<commit_after>module.exports = {
  waitForConditionPollInterval: 1000,
  waitForConditionTimeout: 6000,
  pauseDuration: 5000,
  retryAssertionTimeout: 2500,
}
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import {mount} from 'enzyme';
import {Provider} from 'react-redux';
import {genNotification, mockStore} from '../fixtures';
import {types, removeNotification} from '../../src/store/notifications';
import css from '../../src/components/Notification/Notification.scss';
import ConnectNotification, {Notification} from '../../src/components/Notification/Notification';

describe('Notification', () => {
  let notification = null;
  let store = null;

  // default className for Notification component
  const className = {
    main: css['notification'],
    meta: css['notification-meta'],
    title: css['notification-title'],
    message: css['notification-message'],
    icon: `fa ${css['notification-icon']}`,
    status: function(status) {
      return css[`notification--${status}`];
    },
    dismissible: css['notification--dismissible'],
    // `fa` corresponds to font-awesome's class name
    actions: function(count) {
      if (count === 1) {
        return css['notification--actions-1'];
      }
      else if (count === 2) {
        return css['notification--actions-2'];
      }
      return css['notification-actions'];
    },
    action: css['notification-action']
  };

  // Expected Notification component
  // used to test render method of Notification component
  class ExpectedNotification extends Component {
    static defaultProps = {
      className: className,
      actions: []
    };

    componentDidMount() {
      const {id, actions} = this.props;
      if (actions.length && this.refs[id]) {
        this.forceUpdate();
      }
    }

    renderActions() {
      const {actions, className} = this.props;
      return actions.map((action) => {
        return (
          <button key={action.name} className={className.action}
                  onClick={action.onClick}>
            {(action.primary
              ? {action.name}
              : action.name)}
          </button>
        );
      });
    }

    render() {
      const {id, title, message, status, dismissible, className, actions} = this.props;
      const isDismissible = (dismissible && actions.length === 0);
      let titleDiv = null;
      let actionDiv = null;
      let style = {};
      if (title) {
        titleDiv =
{title}
;
      }
      if (actions.length) {
        if (this.refs[id]) {
          style = {
            height: this.refs[id].clientHeight
          };
        }
        actionDiv = (

            {this.renderActions()}

        );
      }

      return (
        <div ref={id} className={
           `${className.main} ${className.status(status)} ${(isDismissible ? className.dismissible : '')} ${className.actions(actions.length)}`}
             onClick={isDismissible ? this._remove : ''}>

            {titleDiv}

              {message}

          {actionDiv}

      );
    }
  }

  beforeEach('generate a new notification and init store', () => {
    notification = genNotification();
    store = mockStore({notifications: []});
  });

  it('should mount with default props', () => {
    delete notification.onAdd;
    delete notification.onRemove;
    const wrapper = mount(
      <Notification key={notification.id} {...notification}
                    removeNotification={removeNotification}/>
    );
    expect(wrapper.props().className.main).toEqual(className.main);
    expect(wrapper.props().className.meta).toEqual(className.meta);
    expect(wrapper.props().className.title).toEqual(className.title);
    expect(wrapper.props().className.message).toEqual(className.message);
    expect(wrapper.props().className.icon).toEqual(className.icon);
    expect(wrapper.props().className.status()).toEqual(className.status());
    expect(wrapper.props().className.dismissible).toEqual(className.dismissible);
    expect(wrapper.props().className.actions()).toEqual(className.actions());
    expect(wrapper.props().className.action).toEqual(className.action);
    expect(wrapper.props().removeNotification).toEqual(removeNotification);
    expect(wrapper.props().onAdd()).toEqual((() => {})());
    expect(wrapper.props().onRemove()).toEqual((() => {})());
  });

  it('should render JSX and HTML correctly (with title)', () => {
    notification.dismissible = false;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (without title)', () => {
    notification.dismissible = false;
    notification.title = null;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should run onAdd() callback at componentDidMount() lifecycle', () => {
    const errorMessage = 'onAdd() callback';
    // we throw an error to capture where
    // the code has been executed before error was thrown
    notification.onAdd = () => {
      throw new Error(errorMessage);
    };
    try {
      mount(
        <Provider store={store}>
          <ConnectNotification key={notification.id} {...notification}/>
        </Provider>
      );
    }
    catch (error) {
      expect(error.stack).toMatch(/onAdd\ncomponentDidMount/);
      expect(error.message).toEqual(errorMessage);
    }
    // and we update function to check that
    // component mount without any error
    notification.onAdd = () => {
      return 0;
    };
    mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
  });

  it('should run onRemove() callback at componentWillUnmount() lifecycle', () => {
    const errorMessage = 'onRemove() callback';
    // we throw an error to capture where
    // the code has been executed before error was thrown
    notification.onRemove = () => {
      throw new Error(errorMessage);
    };
    let wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    try {
      wrapper.unmount();
    }
    catch (error) {
      expect(error.stack).toMatch(/onRemove\ncomponentWillUnmount/);
      expect(error.message).toEqual(errorMessage);
    }
    // and we update function without `throw` call to check that
    // component unmount without any error
    notification.onRemove = () => {
      return 0;
    };
    wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.unmount();
  });

  it('should create an action to remove the notification when it is clicked', () => {
    notification.dismissible = true;
    notification.actions = [];
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(ConnectNotification).simulate('click');
    const expectedAction = {
      type: types.REMOVE_NOTIFICATION,
      payload: notification.id
    };
    expect(store.getActions()).toEqual([expectedAction]);
  });

  it('should not create an action to remove the notification when it is clicked (dismissible : false)', () => {
    notification.dismissible = false;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(ConnectNotification).simulate('click');
    expect(store.getActions()).toEqual([]);
  });

  it('should create an action to remove the notification after `dismissAfter` duration', (done) => {
    notification.dismissAfter = 10;
    // remove actions otherwise `remove()` is not called after `dismissAfter` duration
    notification.actions = [];
    mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    const expectedAction = {
      type: types.REMOVE_NOTIFICATION,
      payload: notification.id
    };
    expect(store.getActions()).toEqual([]);
    setTimeout(() => {
      expect(store.getActions()).toEqual([expectedAction]);
      done();
    }, 15);
  });

  it('should not create an action to remove the notification after `dismissAfter` duration (dismissAfter = 0)', (done) => {
    notification.dismissAfter = 0;
    mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    setTimeout(() => {
      expect(store.getActions()).toEqual([]);
      done();
    }, 10);
  });
});
<commit_msg>add tests for action feature<commit_after>import React, {Component} from 'react';
import {mount} from 'enzyme';
import {Provider} from 'react-redux';
import {genNotification, mockStore} from '../fixtures';
import {types, removeNotification} from '../../src/store/notifications';
import css from '../../src/components/Notification/Notification.scss';
import ConnectNotification, {Notification} from '../../src/components/Notification/Notification';

describe('Notification', () => {
  let notification = null;
  let store = null;

  // default className for Notification component
  const className = {
    main: css['notification'],
    meta: css['notification-meta'],
    title: css['notification-title'],
    message: css['notification-message'],
    icon: `fa ${css['notification-icon']}`,
    status: function(status) {
      return css[`notification--${status}`];
    },
    dismissible: css['notification--dismissible'],
    // `fa` corresponds to font-awesome's class name
    actions: function(count) {
      if (count === 1) {
        return css['notification--actions-1'];
      }
      else if (count === 2) {
        return css['notification--actions-2'];
      }
      return css['notification-actions'];
    },
    action: css['notification-action']
  };

  // Expected Notification component
  // used to test render method of Notification component
  class ExpectedNotification extends Component {
    static defaultProps = {
      className: className,
      actions: []
    };

    componentDidMount() {
      const {id, actions} = this.props;
      if (actions.length && this.refs[id]) {
        this.forceUpdate();
      }
    }

    _renderActions() {
      const {actions, className} = this.props;
      return actions.map((action) => {
        return (
          <button key={action.name} className={className.action}
                  onClick={action.onClick}>
            {(action.primary
              ? {action.name}
              : action.name)}
          </button>
        );
      });
    }

    render() {
      const {id, title, message, status, dismissible, className, actions} = this.props;
      const isDismissible = (dismissible && actions.length === 0);
      let actionDiv = null;
      let style = {};

      if (actions.length) {
        if (this.refs[id]) {
          style = {
            height: this.refs[id].clientHeight
          };
        }
        actionDiv = (

            {this._renderActions()}

        );
      }

      return (
        <div ref={id} className={
           `${className.main} ${className.status(status)} ${(isDismissible ? className.dismissible : '')} ${className.actions(actions.length)}`}
             onClick={isDismissible ? this._remove : ''}>

            {(title
              ?
{title}

              : '')}
            {(message
              ?
{message}

              : '')}

          {actionDiv}

      );
    }
  }

  beforeEach('generate a new notification and init store', () => {
    notification = genNotification();
    store = mockStore({notifications: []});
  });

  it('should mount with default props', () => {
    delete notification.onAdd;
    delete notification.onRemove;
    const wrapper = mount(
      <Notification key={notification.id} {...notification}
                    removeNotification={removeNotification}/>
    );
    expect(wrapper.props().className.main).toEqual(className.main);
    expect(wrapper.props().className.meta).toEqual(className.meta);
    expect(wrapper.props().className.title).toEqual(className.title);
    expect(wrapper.props().className.message).toEqual(className.message);
    expect(wrapper.props().className.icon).toEqual(className.icon);
    expect(wrapper.props().className.status()).toEqual(className.status());
    expect(wrapper.props().className.dismissible).toEqual(className.dismissible);
    expect(wrapper.props().className.actions()).toEqual(className.actions());
    expect(wrapper.props().className.actions(1)).toEqual(className.actions(1));
    expect(wrapper.props().className.actions(2)).toEqual(className.actions(2));
    expect(wrapper.props().className.action).toEqual(className.action);
    expect(wrapper.props().removeNotification).toEqual(removeNotification);
    expect(wrapper.props().onAdd()).toEqual((() => {
    })());
    expect(wrapper.props().onRemove()).toEqual((() => {
    })());
  });

  it('should render JSX and HTML correctly (with title)', () => {
    // `dismissible` is set to `false` to be sure
    // that notification will not disappear
    notification.dismissible = false;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (without title)', () => {
    // `dismissible` is set to `false` to be sure
    // that notification will not disappear
    notification.dismissible = false;
    notification.title = null;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (with message)', () => {
    // `dismissible` is set to `false` to be sure
    // that notification will not disappear
    notification.dismissible = false;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (without message)', () => {
    // `dismissible` is set to `false` to be sure
    // that notification will not disappear
    notification.dismissible = false;
    notification.message = null;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (without actions)', () => {
    notification.actions = [];
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (with 1 action)', () => {
    notification.actions = notification.actions.slice(0, 1);
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (with 2 actions)', () => {
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should run onAdd() callback at componentDidMount() lifecycle', () => {
    const errorMessage = 'onAdd() callback';
    // we throw an error to capture where
    // the code has been executed before error was thrown
    notification.onAdd = () => {
      throw new Error(errorMessage);
    };
    try {
      mount(
        <Provider store={store}>
          <ConnectNotification key={notification.id} {...notification}/>
        </Provider>
      );
    }
    catch (error) {
      expect(error.stack).toMatch(/onAdd\ncomponentDidMount/);
      expect(error.message).toEqual(errorMessage);
    }
    // and we update function to check that
    // component mount without any error
    notification.onAdd = () => {
      return 0;
    };
    mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
  });

  it('should run onRemove() callback at componentWillUnmount() lifecycle', () => {
    const errorMessage = 'onRemove() callback';
    // we throw an error to capture where
    // the code has been executed before error was thrown
    notification.onRemove = () => {
      throw new Error(errorMessage);
    };
    let wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    try {
      wrapper.unmount();
    }
    catch (error) {
      expect(error.stack).toMatch(/onRemove\ncomponentWillUnmount/);
      expect(error.message).toEqual(errorMessage);
    }
    // and we update function without `throw` call to check that
    // component unmount without any error
    notification.onRemove = () => {
      return 0;
    };
    wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.unmount();
  });

  it('should create an action to remove the notification ' +
    'when it is clicked', () => {
    notification.dismissible = true;
    notification.actions = [];
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(ConnectNotification).simulate('click');
    const expectedAction = {
      type: types.REMOVE_NOTIFICATION,
      payload: notification.id
    };
    expect(store.getActions()).toEqual([expectedAction]);
  });

  it('should not create an action to remove the notification ' +
    'when it is clicked (dismissible : false)', () => {
    notification.dismissible = false;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(ConnectNotification).simulate('click');
    expect(store.getActions()).toEqual([]);
  });

  it('should create an action to remove the notification ' +
    'when a action button is clicked (dismissible : false)', () => {
    // we set `dismissible` to `false` to be sure
    // that dismissible is ignored in this case
    notification.dismissible = false;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(`.${css['notification-action']}`).first().simulate('click');
    const expectedAction = {
      type: types.REMOVE_NOTIFICATION,
      payload: notification.id
    };
    expect(store.getActions()).toEqual([expectedAction]);
  });

  it('should not create an action to remove the notification ' +
    'when it is clicked (actions.length > 0)', (done) => {
    // we set `dismissible` to `true` to be sure that
    // `actions.length` must equals `0` to allow removing
    // same thing for `dismissAfter`
    notification.dismissible = true;
    notification.dismissAfter = 5;
    let wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(ConnectNotification).simulate('click');
    expect(store.getActions()).toEqual([]);
    // we remove an action and test it again
    notification.actions = notification.actions.slice(0, 1);
    wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(ConnectNotification).simulate('click');
    expect(store.getActions()).toEqual([]);
    // check after `dismissAfter` duration
    setTimeout(() => {
      expect(store.getActions()).toEqual([]);
      done();
    }, 10);
  });

  it('should create an action to remove the notification after ' +
    '`dismissAfter` duration', (done) => {
    notification.dismissAfter = 10;
    // remove actions otherwise `remove()` is not called after `dismissAfter` duration
    notification.actions = [];
    mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    const expectedAction = {
      type: types.REMOVE_NOTIFICATION,
      payload: notification.id
    };
    expect(store.getActions()).toEqual([]);
    setTimeout(() => {
      expect(store.getActions()).toEqual([expectedAction]);
      done();
    }, 15);
  });

  it('should not create an action to remove the notification after ' +
    '`dismissAfter` duration (dismissAfter = 0)', (done) => {
    notification.dismissAfter = 0;
    mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    setTimeout(() => {
      expect(store.getActions()).toEqual([]);
      done();
    }, 10);
  });
});
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import {mount} from 'enzyme';
import {Provider} from 'react-redux';
import {genNotification, mockStore} from '../fixtures';
import {types, removeNotification} from '../../src/store/notifications';
import css from '../../src/components/Notification/Notification.scss';
import ConnectNotification, {Notification} from '../../src/components/Notification/Notification';

describe('Notification', () => {
  let notification = null;
  let store = null;

  // default className for Notification component
  const className = {
    main: css['notification'],
    meta: css['notification-meta'],
    title: css['notification-title'],
    message: css['notification-message'],
    icon: `fa ${css['notification-icon']}`,
    status: function(status) {
      return css[`notification--${status}`];
    },
    dismissible: css['notification--dismissible'],
    // `fa` corresponds to font-awesome's class name
    actions: function(count) {
      if (count === 1) {
        return css['notification--actions-1'];
      }
      else if (count === 2) {
        return css['notification--actions-2'];
      }
      return css['notification-actions'];
    },
    action: css['notification-action']
  };

  // Expected Notification component
  // used to test render method of Notification component
  class ExpectedNotification extends Component {
    static defaultProps = {
      className: className,
      actions: []
    };

    componentDidMount() {
      const {id, actions} = this.props;
      if (actions.length && this.refs[id]) {
        this.forceUpdate();
      }
    }

    _renderActions() {
      const {actions, className} = this.props;
      return actions.map((action) => {
        return (
          <button key={action.name} className={className.action}
                  onClick={action.onClick}>
            {(action.primary
              ? {action.name}
              : action.name)}
          </button>
        );
      });
    }

    render() {
      const {id, title, message, status, dismissible, className, actions} = this.props;
      const isDismissible = (dismissible && actions.length === 0);
      let actionDiv = null;
      let style = {};

      if (actions.length) {
        if (this.refs[id]) {
          style = {
            height: this.refs[id].clientHeight
          };
        }
        actionDiv = (

            {this._renderActions()}

        );
      }

      return (
        <div ref={id} className={
           `${className.main} ${className.status(status)} ${(isDismissible ? className.dismissible : '')} ${className.actions(actions.length)}`}
             onClick={isDismissible ? this._remove : ''}>

            {(title
              ?
{title}

              : '')}
            {(message
              ?
{message}

              : '')}

          {actionDiv}

      );
    }
  }

  beforeEach('generate a new notification and init store', () => {
    notification = genNotification();
    store = mockStore({notifications: []});
  });

  it('should mount with default props', () => {
    delete notification.onAdd;
    delete notification.onRemove;
    const wrapper = mount(
      <Notification key={notification.id} {...notification}
                    removeNotification={removeNotification}/>
    );
    expect(wrapper.props().className.main).toEqual(className.main);
    expect(wrapper.props().className.meta).toEqual(className.meta);
    expect(wrapper.props().className.title).toEqual(className.title);
    expect(wrapper.props().className.message).toEqual(className.message);
    expect(wrapper.props().className.icon).toEqual(className.icon);
    expect(wrapper.props().className.status()).toEqual(className.status());
    expect(wrapper.props().className.dismissible).toEqual(className.dismissible);
    expect(wrapper.props().className.actions()).toEqual(className.actions());
    expect(wrapper.props().className.actions(1)).toEqual(className.actions(1));
    expect(wrapper.props().className.actions(2)).toEqual(className.actions(2));
    expect(wrapper.props().className.action).toEqual(className.action);
    expect(wrapper.props().removeNotification).toEqual(removeNotification);
    expect(wrapper.props().onAdd()).toEqual((() => {
    })());
    expect(wrapper.props().onRemove()).toEqual((() => {
    })());
  });

  it('should render JSX and HTML correctly (with title)', () => {
    // `dismissible` is set to `false` to be sure
    // that notification will not disappear
    notification.dismissible = false;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (without title)', () => {
    // `dismissible` is set to `false` to be sure
    // that notification will not disappear
    notification.dismissible = false;
    notification.title = null;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (with message)', () => {
    // `dismissible` is set to `false` to be sure
    // that notification will not disappear
    notification.dismissible = false;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (without message)', () => {
    // `dismissible` is set to `false` to be sure
    // that notification will not disappear
    notification.dismissible = false;
    notification.message = null;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (without actions)', () => {
    notification.actions = [];
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (with 1 action)', () => {
    notification.actions = notification.actions.slice(0, 1);
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (with 2 actions)', () => {
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should run onAdd() callback at componentDidMount() lifecycle', () => {
    const errorMessage = 'onAdd() callback';
    // we throw an error to capture where
    // the code has been executed before error was thrown
    notification.onAdd = () => {
      throw new Error(errorMessage);
    };
    try {
      mount(
        <Provider store={store}>
          <ConnectNotification key={notification.id} {...notification}/>
        </Provider>
      );
    }
    catch (error) {
      expect(error.stack).toMatch(/onAdd\ncomponentDidMount/);
      expect(error.message).toEqual(errorMessage);
    }
    // and we update function to check that
    // component mount without any error
    notification.onAdd = () => {
      return 0;
    };
    mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
  });

  it('should run onRemove() callback at componentWillUnmount() lifecycle', () => {
    const errorMessage = 'onRemove() callback';
    // we throw an error to capture where
    // the code has been executed before error was thrown
    notification.onRemove = () => {
      throw new Error(errorMessage);
    };
    let wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    try {
      wrapper.unmount();
    }
    catch (error) {
      expect(error.stack).toMatch(/onRemove\ncomponentWillUnmount/);
      expect(error.message).toEqual(errorMessage);
    }
    // and we update function without `throw` call to check that
    // component unmount without any error
    notification.onRemove = () => {
      return 0;
    };
    wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.unmount();
  });

  it('should create an action to remove the notification ' +
    'when it is clicked', () => {
    notification.dismissible = true;
    notification.actions = [];
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(ConnectNotification).simulate('click');
    const expectedAction = {
      type: types.REMOVE_NOTIFICATION,
      payload: notification.id
    };
    expect(store.getActions()).toEqual([expectedAction]);
  });

  it('should not create an action to remove the notification ' +
    'when it is clicked (dismissible : false)', () => {
    notification.dismissible = false;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(ConnectNotification).simulate('click');
    expect(store.getActions()).toEqual([]);
  });

  it('should create an action to remove the notification ' +
    'when a action button is clicked (dismissible : false)', () => {
    // we set `dismissible` to `false` to be sure
    // that dismissible is ignored in this case
    notification.dismissible = false;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(`.${css['notification-action']}`).first().simulate('click');
    const expectedAction = {
      type: types.REMOVE_NOTIFICATION,
      payload: notification.id
    };
    expect(store.getActions()).toEqual([expectedAction]);
  });

  it('should not create an action to remove the notification ' +
    'when it is clicked (actions.length > 0)', (done) => {
    // we set `dismissible` to `true` to be sure that
    // `actions.length` must equals `0` to allow removing
    // same thing for `dismissAfter`
    notification.dismissible = true;
    notification.dismissAfter = 5;
    let wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(ConnectNotification).simulate('click');
    expect(store.getActions()).toEqual([]);
    // we remove an action and test it again
    notification.actions = notification.actions.slice(0, 1);
    wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(ConnectNotification).simulate('click');
    expect(store.getActions()).toEqual([]);
    // check after `dismissAfter` duration
    setTimeout(() => {
      expect(store.getActions()).toEqual([]);
      done();
    }, 10);
  });

  it('should create an action to remove the notification after ' +
    '`dismissAfter` duration', (done) => {
    notification.dismissAfter = 10;
    // remove actions otherwise `remove()` is not called after `dismissAfter` duration
    notification.actions = [];
    mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    const expectedAction = {
      type: types.REMOVE_NOTIFICATION,
      payload: notification.id
    };
    expect(store.getActions()).toEqual([]);
    setTimeout(() => {
      expect(store.getActions()).toEqual([expectedAction]);
      done();
    }, 15);
  });

  it('should not create an action to remove the notification after ' +
    '`dismissAfter` duration (dismissAfter = 0)', (done) => {
    notification.dismissAfter = 0;
    mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    setTimeout(() => {
      expect(store.getActions()).toEqual([]);
      done();
    }, 10);
  });
});
<commit_msg>edit order of tests<commit_after>import React, {Component} from 'react';
import {mount} from 'enzyme';
import {Provider} from 'react-redux';
import {genNotification, mockStore} from '../fixtures';
import {types, removeNotification} from '../../src/store/notifications';
import css from '../../src/components/Notification/Notification.scss';
import ConnectNotification, {Notification} from '../../src/components/Notification/Notification';

describe('Notification', () => {
  let notification = null;
  let store = null;

  // default className for Notification component
  const className = {
    main: css['notification'],
    meta: css['notification-meta'],
    title: css['notification-title'],
    message: css['notification-message'],
    icon: `fa ${css['notification-icon']}`,
    status: function(status) {
      return css[`notification--${status}`];
    },
    dismissible: css['notification--dismissible'],
    // `fa` corresponds to font-awesome's class name
    actions: function(count) {
      if (count === 1) {
        return css['notification--actions-1'];
      }
      else if (count === 2) {
        return css['notification--actions-2'];
      }
      return css['notification-actions'];
    },
    action: css['notification-action']
  };

  // Expected Notification component
  // used to test render method of Notification component
  class ExpectedNotification extends Component {
    static defaultProps = {
      className: className,
      actions: []
    };

    componentDidMount() {
      const {id, actions} = this.props;
      if (actions.length && this.refs[id]) {
        this.forceUpdate();
      }
    }

    _renderActions() {
      const {actions, className} = this.props;
      return actions.map((action) => {
        return (
          <button key={action.name} className={className.action}
                  onClick={action.onClick}>
            {(action.primary
              ? {action.name}
              : action.name)}
          </button>
        );
      });
    }

    render() {
      const {id, title, message, status, dismissible, className, actions} = this.props;
      const isDismissible = (dismissible && actions.length === 0);
      let actionDiv = null;
      let style = {};

      if (actions.length) {
        if (this.refs[id]) {
          style = {
            height: this.refs[id].clientHeight
          };
        }
        actionDiv = (

            {this._renderActions()}

        );
      }

      return (
        <div ref={id} className={
           `${className.main} ${className.status(status)} ${(isDismissible ? className.dismissible : '')} ${className.actions(actions.length)}`}
             onClick={isDismissible ? this._remove : ''}>

            {(title
              ?
{title}

              : '')}
            {(message
              ?
{message}

              : '')}

          {actionDiv}

      );
    }
  }

  beforeEach('generate a new notification and init store', () => {
    notification = genNotification();
    store = mockStore({notifications: []});
  });

  it('should mount with default props', () => {
    delete notification.onAdd;
    delete notification.onRemove;
    const wrapper = mount(
      <Notification key={notification.id} {...notification}
                    removeNotification={removeNotification}/>
    );
    expect(wrapper.props().className.main).toEqual(className.main);
    expect(wrapper.props().className.meta).toEqual(className.meta);
    expect(wrapper.props().className.title).toEqual(className.title);
    expect(wrapper.props().className.message).toEqual(className.message);
    expect(wrapper.props().className.icon).toEqual(className.icon);
    expect(wrapper.props().className.status()).toEqual(className.status());
    expect(wrapper.props().className.dismissible).toEqual(className.dismissible);
    expect(wrapper.props().className.actions()).toEqual(className.actions());
    expect(wrapper.props().className.actions(1)).toEqual(className.actions(1));
    expect(wrapper.props().className.actions(2)).toEqual(className.actions(2));
    expect(wrapper.props().className.action).toEqual(className.action);
    expect(wrapper.props().removeNotification).toEqual(removeNotification);
    expect(wrapper.props().onAdd()).toEqual((() => {
    })());
    expect(wrapper.props().onRemove()).toEqual((() => {
    })());
  });

  it('should render JSX and HTML correctly (with title)', () => {
    // `dismissible` is set to `false` to be sure
    // that notification will not disappear
    notification.dismissible = false;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (without title)', () => {
    // `dismissible` is set to `false` to be sure
    // that notification will not disappear
    notification.dismissible = false;
    notification.title = null;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (with message)', () => {
    // `dismissible` is set to `false` to be sure
    // that notification will not disappear
    notification.dismissible = false;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (without message)', () => {
    // `dismissible` is set to `false` to be sure
    // that notification will not disappear
    notification.dismissible = false;
    notification.message = null;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (without actions)', () => {
    notification.actions = [];
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (with 1 action)', () => {
    notification.actions = notification.actions.slice(0, 1);
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should render JSX and HTML correctly (with 2 actions)', () => {
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );

    const expectedComponent = mount(
      <ExpectedNotification key={notification.id} {...notification}/>
    );
    expect(wrapper.html()).toEqual(expectedComponent.html());
  });

  it('should run onAdd() callback at componentDidMount() lifecycle', () => {
    const errorMessage = 'onAdd() callback';
    // we throw an error to capture where
    // the code has been executed before error was thrown
    notification.onAdd = () => {
      throw new Error(errorMessage);
    };
    try {
      mount(
        <Provider store={store}>
          <ConnectNotification key={notification.id} {...notification}/>
        </Provider>
      );
    }
    catch (error) {
      expect(error.stack).toMatch(/onAdd\ncomponentDidMount/);
      expect(error.message).toEqual(errorMessage);
    }
    // and we update function to check that
    // component mount without any error
    notification.onAdd = () => {
      return 0;
    };
    mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
  });

  it('should run onRemove() callback at componentWillUnmount() lifecycle', () => {
    const errorMessage = 'onRemove() callback';
    // we throw an error to capture where
    // the code has been executed before error was thrown
    notification.onRemove = () => {
      throw new Error(errorMessage);
    };
    let wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    try {
      wrapper.unmount();
    }
    catch (error) {
      expect(error.stack).toMatch(/onRemove\ncomponentWillUnmount/);
      expect(error.message).toEqual(errorMessage);
    }
    // and we update function without `throw` call to check that
    // component unmount without any error
    notification.onRemove = () => {
      return 0;
    };
    wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.unmount();
  });

  it('should create an action to remove the notification ' +
    'when it is clicked', () => {
    notification.dismissible = true;
    notification.actions = [];
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(ConnectNotification).simulate('click');
    const expectedAction = {
      type: types.REMOVE_NOTIFICATION,
      payload: notification.id
    };
    expect(store.getActions()).toEqual([expectedAction]);
  });

  it('should create an action to remove the notification ' +
    'when a action button is clicked (dismissible : false)', () => {
    // we set `dismissible` to `false` to be sure
    // that dismissible is ignored in this case
    notification.dismissible = false;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(`.${css['notification-action']}`).first().simulate('click');
    const expectedAction = {
      type: types.REMOVE_NOTIFICATION,
      payload: notification.id
    };
    expect(store.getActions()).toEqual([expectedAction]);
  });

  it('should create an action to remove the notification after ' +
    '`dismissAfter` duration', (done) => {
    notification.dismissAfter = 10;
    // remove actions otherwise `remove()` is not called after `dismissAfter` duration
    notification.actions = [];
    mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    const expectedAction = {
      type: types.REMOVE_NOTIFICATION,
      payload: notification.id
    };
    expect(store.getActions()).toEqual([]);
    setTimeout(() => {
      expect(store.getActions()).toEqual([expectedAction]);
      done();
    }, 15);
  });

  it('should not create an action to remove the notification ' +
    'when it is clicked (dismissible : false)', () => {
    notification.dismissible = false;
    const wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(ConnectNotification).simulate('click');
    expect(store.getActions()).toEqual([]);
  });

  it('should not create an action to remove the notification ' +
    'when it is clicked (actions.length > 0)', (done) => {
    // we set `dismissible` to `true` to be sure that
    // `actions.length` must equals `0` to allow removing
    // same thing for `dismissAfter`
    notification.dismissible = true;
    notification.dismissAfter = 5;
    let wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(ConnectNotification).simulate('click');
    expect(store.getActions()).toEqual([]);
    // we remove an action and test it again
    notification.actions = notification.actions.slice(0, 1);
    wrapper = mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    wrapper.find(ConnectNotification).simulate('click');
    expect(store.getActions()).toEqual([]);
    // check after `dismissAfter` duration
    setTimeout(() => {
      expect(store.getActions()).toEqual([]);
      done();
    }, 10);
  });

  it('should not create an action to remove the notification after ' +
    '`dismissAfter` duration (dismissAfter = 0)', (done) => {
    notification.dismissAfter = 0;
    mount(
      <Provider store={store}>
        <ConnectNotification key={notification.id} {...notification}/>
      </Provider>
    );
    setTimeout(() => {
      expect(store.getActions()).toEqual([]);
      done();
    }, 10);
  });
});
<|endoftext|>