diff --git a/package.json b/package.json index 200dc4d22..b1a5c7e89 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,6 @@ "nock": "^12.0.3", "prettier": "=2.0.2", "react-test-renderer": "=16.13.1", - "redux-logger": "=3.0.6", "redux-mock-store": "=1.5.4", "ts-jest": "^25.3.1", "webpack": "^4.42.1", diff --git a/src/js/__mocks__/mockedData.ts b/src/js/__mocks__/mockedData.ts index 79cbfc4d0..8a4391ab4 100644 --- a/src/js/__mocks__/mockedData.ts +++ b/src/js/__mocks__/mockedData.ts @@ -251,7 +251,7 @@ export const mockedEnterpriseNotifications = [ } as Notification, ]; -export const mockedNotificationsRecuderData = [ +export const mockedNotificationsReducerData = [ { hostname: 'github.com', notifications: mockedGithubNotifications, diff --git a/src/js/__mocks__/redux-logger.js b/src/js/__mocks__/redux-logger.js deleted file mode 100644 index 19f22a579..000000000 --- a/src/js/__mocks__/redux-logger.js +++ /dev/null @@ -1,5 +0,0 @@ -export function createLogger() { - return () => (next) => (action) => { - return next(action); - }; -} diff --git a/src/js/components/sidebar.test.tsx b/src/js/components/sidebar.test.tsx index 3eb132432..eec589100 100644 --- a/src/js/components/sidebar.test.tsx +++ b/src/js/components/sidebar.test.tsx @@ -42,20 +42,39 @@ describe('components/Sidebar.tsx', () => { clock.clearAllTimers(); }); - it('should test the mapStateToProps method', () => { + describe('mapStateToProps', () => { const state = { auth: { token: '12345', enterpriseAccounts: mockedEnterpriseAccounts, } as AuthState, notifications: { - response: [], + response: [{ hostname: 'Dolores', notifications: [{}, {}] }], }, } as AppState; - const mappedProps = mapStateToProps(state); - - expect(mappedProps.isEitherLoggedIn).toBeTruthy(); + it('should accept a provided token', () => { + const mappedProps = mapStateToProps(state); + expect(mappedProps.isEitherLoggedIn).toBeTruthy(); + expect(mappedProps.connectedAccounts).toBe(2); + }); + + it('should count notification lengths', () => { + const mappedProps = mapStateToProps(state); + expect(mappedProps.notificationsCount).toBe(2); + }); + + it('should accept a null token', () => { + const mappedProps = mapStateToProps({ + ...state, + auth: { + ...state.auth, + token: null, + }, + }); + expect(mappedProps.isEitherLoggedIn).toBeTruthy(); + expect(mappedProps.connectedAccounts).toBe(1); + }); }); it('should render itself & its children (logged in)', () => { diff --git a/src/js/middleware/notifications.test.ts b/src/js/middleware/notifications.test.ts index 5ba1e3a4a..751eb6884 100644 --- a/src/js/middleware/notifications.test.ts +++ b/src/js/middleware/notifications.test.ts @@ -2,14 +2,14 @@ import * as actions from '../actions'; import * as comms from '../utils/comms'; import { mockedGithubNotifications, - mockedNotificationsRecuderData, + mockedNotificationsReducerData, } from '../__mocks__/mockedData'; import notificationsMiddleware from './notifications'; import NativeNotifications from '../utils/notifications'; // Keep 3 notifications // Ps. To receive 4 on actions.NOTIFICATIONS.SUCCESS, -const mockedNotifications = mockedNotificationsRecuderData.map( +const mockedNotifications = mockedNotificationsReducerData.map( (account, accountIndex) => { if (accountIndex === 0) { return { @@ -22,25 +22,27 @@ const mockedNotifications = mockedNotificationsRecuderData.map( } ); -const createFakeStore = () => ({ +const DEFAULT_STORE = { + notifications: { + response: mockedNotifications, + }, + settings: { + playSound: false, + showNotifications: false, + }, +}; + +const createFakeStore = (storeData) => ({ getState() { - return { - notifications: { - response: mockedNotifications, - }, - settings: { - playSound: false, - showNotifications: false, - }, - }; + return storeData; }, }); -const dispatchWithStoreOf = (_, action) => { +const dispatchWithStoreOf = (storeData, action) => { let dispatched = null; - const dispatch = notificationsMiddleware(createFakeStore())( - (actionAttempt) => (dispatched = actionAttempt) - ); + const dispatch = notificationsMiddleware( + createFakeStore({ ...DEFAULT_STORE, ...storeData }) + )((actionAttempt) => (dispatched = actionAttempt)); dispatch(action); return dispatched; }; @@ -54,7 +56,7 @@ describe('middleware/notifications.js', () => { it('should raise notifications (native & sound, update tray icon, set badge)', () => { const action = { type: actions.NOTIFICATIONS.SUCCESS, - payload: mockedNotificationsRecuderData, + payload: mockedNotificationsReducerData, }; expect(dispatchWithStoreOf({}, action)).toEqual(action); @@ -93,4 +95,44 @@ describe('middleware/notifications.js', () => { expect(comms.updateTrayIcon).toHaveBeenCalledTimes(1); expect(comms.updateTrayIcon).toHaveBeenCalledWith(2); }); + + it('should update tray icon with no notifications', () => { + const noNewNotifications = mockedNotificationsReducerData.map((host) => ({ + ...host, + notifications: [], + })); + const action = { + type: actions.NOTIFICATIONS.SUCCESS, + payload: noNewNotifications, + }; + dispatchWithStoreOf( + { + ...DEFAULT_STORE, + notifications: { + response: noNewNotifications, + }, + }, + action + ); + expect(comms.updateTrayIcon).toHaveBeenCalledTimes(1); + expect(comms.updateTrayIcon).toHaveBeenCalledWith(0); + }); + + it('should show 0 notifications if no accounts logged in', () => { + const action = { + type: actions.NOTIFICATIONS.SUCCESS, + payload: mockedNotificationsReducerData, + }; + dispatchWithStoreOf( + { + ...DEFAULT_STORE, + notifications: { + response: [], + }, + }, + action + ); + expect(comms.updateTrayIcon).toHaveBeenCalledTimes(1); + expect(comms.updateTrayIcon).toHaveBeenCalledWith(4); + }); }); diff --git a/src/js/routes/enterprise-login.test.tsx b/src/js/routes/enterprise-login.test.tsx index 344a94727..af0207624 100644 --- a/src/js/routes/enterprise-login.test.tsx +++ b/src/js/routes/enterprise-login.test.tsx @@ -29,6 +29,7 @@ describe('routes/enterprise-login.js', () => { new BrowserWindow().loadURL.mockReset(); spyOn(ipcRenderer, 'send'); props.dispatch.mockReset(); + props.history.goBack = jest.fn(); }); it('should test the mapStateToProps method', () => { @@ -57,6 +58,19 @@ describe('routes/enterprise-login.js', () => { expect(tree).toMatchSnapshot(); }); + it('let us go back', () => { + props.history.goBack = jest.fn(); + const { getByLabelText } = render( + {})}> + + + + + ); + fireEvent.click(getByLabelText('Go Back')); + expect(props.history.goBack).toHaveBeenCalledTimes(1); + }); + it('should validate the form values', () => { let values; const emptyValues = { diff --git a/src/js/routes/notifications.test.tsx b/src/js/routes/notifications.test.tsx index 4f580b6f1..2f5464990 100644 --- a/src/js/routes/notifications.test.tsx +++ b/src/js/routes/notifications.test.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import * as TestRenderer from 'react-test-renderer'; import { AppState, NotificationsState } from '../../types/reducers'; -import { mockedNotificationsRecuderData } from '../__mocks__/mockedData'; +import { mockedNotificationsReducerData } from '../__mocks__/mockedData'; import { NotificationsRoute, mapStateToProps } from './notifications'; jest.mock('../components/account-notifications', () => ({ @@ -20,7 +20,7 @@ jest.mock('../components/oops', () => ({ describe('routes/notifications.ts', () => { const props = { failed: false, - accountNotifications: mockedNotificationsRecuderData, + accountNotifications: mockedNotificationsReducerData, notificationsCount: 4, hasMultipleAccounts: true, hasNotifications: true, @@ -29,7 +29,7 @@ describe('routes/notifications.ts', () => { it('should test the mapStateToProps method', () => { const state = { notifications: { - response: mockedNotificationsRecuderData, + response: mockedNotificationsReducerData, failed: false, } as NotificationsState, } as AppState; @@ -38,7 +38,7 @@ describe('routes/notifications.ts', () => { expect(mappedProps.failed).toBeFalsy(); expect(mappedProps.accountNotifications).toEqual( - mockedNotificationsRecuderData + mockedNotificationsReducerData ); expect(mappedProps.hasNotifications).toBeTruthy(); expect(mappedProps.notificationsCount).toBe(4); diff --git a/src/js/routes/settings.test.tsx b/src/js/routes/settings.test.tsx index 58f0acc4b..79479fdc2 100644 --- a/src/js/routes/settings.test.tsx +++ b/src/js/routes/settings.test.tsx @@ -42,7 +42,7 @@ describe('routes/settings.tsx', () => { props.history.replace.mockReset(); }); - it('should test the mapStateToProps method', () => { + describe('mapStateToProps', () => { const state = { auth: { token: '123-456', @@ -52,11 +52,24 @@ describe('routes/settings.tsx', () => { participating: false, } as SettingsState, } as AppState; + it('should test the method', () => { + const mappedProps = mapStateToProps(state); - const mappedProps = mapStateToProps(state); + expect(mappedProps.hasMultipleAccounts).toBeTruthy(); + expect(mappedProps.settings.participating).toBeFalsy(); + }); + + it('should recognize when only one account logged in', () => { + const mappedProps = mapStateToProps({ + ...state, + auth: { + ...state.auth, + token: null, + }, + }); - expect(mappedProps.hasMultipleAccounts).toBeTruthy(); - expect(mappedProps.settings.participating).toBeFalsy(); + expect(mappedProps.hasMultipleAccounts).toBeFalsy(); + }); }); it('should render itself & its children', () => { diff --git a/src/js/store/configureStore.ts b/src/js/store/configureStore.ts index 0781aae1a..ef570075a 100644 --- a/src/js/store/configureStore.ts +++ b/src/js/store/configureStore.ts @@ -12,8 +12,6 @@ import notificationsMiddlware from '../middleware/notifications'; import rootReducer from '../reducers'; import settingsMiddleware from '../middleware/settings'; -const isDev = false; - export default function configureStore() { const engine = filter(createEngine(constants.STORAGE_KEY), [ 'settings', @@ -34,13 +32,6 @@ export default function configureStore() { storageMiddleware, ]; - if (isDev) { - const { createLogger } = require('redux-logger'); - const logger = createLogger({ collapsed: true }); - middlewares.push(logger); - console.log('adss'); - } - let store = createStore( rootReducer, undefined, diff --git a/src/js/utils/__snapshots__/github-api.test.ts.snap b/src/js/utils/__snapshots__/github-api.test.ts.snap index 00b13d719..f2ea912cd 100644 --- a/src/js/utils/__snapshots__/github-api.test.ts.snap +++ b/src/js/utils/__snapshots__/github-api.test.ts.snap @@ -78,6 +78,13 @@ Object { `; exports[`./utils/github-api.ts should format the notification reason 12`] = ` +Object { + "description": "A GitHub Actions workflow run was triggered for your repository", + "type": "Workflow Run", +} +`; + +exports[`./utils/github-api.ts should format the notification reason 13`] = ` Object { "description": "The reason for this notification is not supported by the app.", "type": "Unknown", diff --git a/src/js/utils/comms.test.ts b/src/js/utils/comms.test.ts index d9b787a60..91dda11b9 100644 --- a/src/js/utils/comms.test.ts +++ b/src/js/utils/comms.test.ts @@ -3,6 +3,7 @@ import { reOpenWindow, openExternalLink, setAutoLaunch, + restoreSetting, } from './comms'; const { ipcRenderer, remote, shell } = require('electron'); @@ -32,6 +33,12 @@ describe('utils/comms.ts', () => { expect(ipcRenderer.send).toHaveBeenCalledWith('reopen-window'); }); + it('should restore a setting', () => { + restoreSetting('foo', 'bar'); + expect(ipcRenderer.send).toHaveBeenCalledTimes(1); + expect(ipcRenderer.send).toHaveBeenCalledWith('foo', 'bar'); + }); + it('should open an external link', () => { openExternalLink('http://www.gitify.io/'); expect(shell.openExternal).toHaveBeenCalledTimes(1); diff --git a/src/js/utils/github-api.test.ts b/src/js/utils/github-api.test.ts index 68f4a89a3..27eb705dd 100644 --- a/src/js/utils/github-api.test.ts +++ b/src/js/utils/github-api.test.ts @@ -14,6 +14,7 @@ describe('./utils/github-api.ts', () => { expect(formatReason('state_change')).toMatchSnapshot(); expect(formatReason('subscribed')).toMatchSnapshot(); expect(formatReason('team_mention')).toMatchSnapshot(); + expect(formatReason('ci_activity')).toMatchSnapshot(); expect(formatReason('something_else_unknown' as Reason)).toMatchSnapshot(); }); @@ -25,6 +26,7 @@ describe('./utils/github-api.ts', () => { expect(getNotificationTypeIcon('RepositoryVulnerabilityAlert')).toBe( 'alert' ); + expect(getNotificationTypeIcon('CheckSuite')).toBe('sync'); expect(getNotificationTypeIcon('Unknown' as SubjectType)).toBe('question'); }); }); diff --git a/src/js/utils/github-api.ts b/src/js/utils/github-api.ts index 8684d7987..5849d73b0 100644 --- a/src/js/utils/github-api.ts +++ b/src/js/utils/github-api.ts @@ -45,7 +45,7 @@ export function formatReason( case 'team_mention': return { type: 'Team Mention', description: DESCRIPTIONS['TEAM_MENTION'] }; case 'ci_activity': - return { type: 'Workflow Run', description: DESCRIPTIONS['WORKFLOW_RUN'] }; + return { type: 'Workflow Run', description: DESCRIPTIONS['CI_ACTIVITY'] }; default: return { type: 'Unknown', description: DESCRIPTIONS['UNKNOWN'] }; } diff --git a/yarn.lock b/yarn.lock index df9ada47a..8164f290c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1930,11 +1930,6 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" -deep-diff@^0.3.5: - version "0.3.8" - resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.8.tgz#c01de63efb0eec9798801d40c7e0dae25b582c84" - integrity sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ= - deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -5034,13 +5029,6 @@ redux-actions@^0.10.1: dependencies: reduce-reducers "^0.1.0" -redux-logger@=3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf" - integrity sha1-91VZZvMJjzyIYExEnPC69XeCdL8= - dependencies: - deep-diff "^0.3.5" - redux-mock-store@=1.5.4: version "1.5.4" resolved "https://registry.yarnpkg.com/redux-mock-store/-/redux-mock-store-1.5.4.tgz#90d02495fd918ddbaa96b83aef626287c9ab5872"