diff --git a/src/client/actions/CallActions.test.ts b/src/client/actions/CallActions.test.ts index 9b998e9..8da26eb 100644 --- a/src/client/actions/CallActions.test.ts +++ b/src/client/actions/CallActions.test.ts @@ -1,26 +1,38 @@ jest.mock('../socket') jest.mock('../window') -jest.mock('../store') jest.mock('./SocketActions') import * as CallActions from './CallActions' import * as SocketActions from './SocketActions' import * as constants from '../constants' import socket from '../socket' -import storeMock from '../store' import { callId, getUserMedia } from '../window' -import { MockStore } from 'redux-mock-store' -import { bindActionCreators } from 'redux' +import { bindActionCreators, createStore, AnyAction, combineReducers, applyMiddleware } from 'redux' +import reducers from '../reducers' +import { middlewares } from '../middlewares' jest.useFakeTimers() -describe('reducers/alerts', () => { +describe('CallActions', () => { - const store: MockStore = storeMock as any - const callActions = bindActionCreators(CallActions, store.dispatch) + let callActions: typeof CallActions + + function allActions(state: AnyAction[] = [], action: AnyAction) { + return [...state, action] + } + + const configureStore = () => createStore( + combineReducers({...reducers, allActions }), + applyMiddleware(...middlewares), + ) + let store: ReturnType beforeEach(() => { - store.clearActions(); + store = createStore( + combineReducers({ allActions }), + applyMiddleware(...middlewares), + ) + callActions = bindActionCreators(CallActions, store.dispatch); (getUserMedia as any).fail(false); (SocketActions.handshake as jest.Mock).mockReturnValue(jest.fn()) }) @@ -35,9 +47,8 @@ describe('reducers/alerts', () => { it('calls handshake.init when connected & got camera stream', async () => { const promise = callActions.init() socket.emit('connect') - expect(store.getActions()).toEqual([{ - type: constants.INIT_PENDING, - }, { + await promise + expect(store.getState().allActions.slice(1)).toEqual([{ type: constants.NOTIFY, payload: { id: jasmine.any(String), @@ -45,15 +56,14 @@ describe('reducers/alerts', () => { type: 'warning', }, }, { - type: constants.MESSAGE_ADD, + type: constants.STREAM_ADD, payload: { - image: null, - message: 'Connected to server socket', - timestamp: jasmine.any(String), - userId: '[PeerCalls]', + stream: jasmine.anything(), + userId: constants.ME, }, + }, { + type: constants.INIT, }]) - await promise expect((SocketActions.handshake as jest.Mock).mock.calls).toEqual([[{ socket, roomName: callId, @@ -65,23 +75,13 @@ describe('reducers/alerts', () => { const promise = callActions.init() socket.emit('connect') socket.emit('disconnect') - expect(store.getActions()).toEqual([{ - type: constants.INIT_PENDING, - }, { + expect(store.getState().allActions.slice(1)).toEqual([{ type: constants.NOTIFY, payload: { id: jasmine.any(String), message: 'Connected to server socket', type: 'warning', }, - }, { - type: constants.MESSAGE_ADD, - payload: { - image: null, - message: 'Connected to server socket', - timestamp: jasmine.any(String), - userId: '[PeerCalls]', - }, }, { type: constants.NOTIFY, payload: { @@ -89,14 +89,6 @@ describe('reducers/alerts', () => { message: 'Server socket disconnected', type: 'error', }, - }, { - type: constants.MESSAGE_ADD, - payload: { - image: null, - message: 'Server socket disconnected', - timestamp: jasmine.any(String), - userId: '[PeerCalls]', - }, }]) await promise }) diff --git a/src/client/actions/CallActions.ts b/src/client/actions/CallActions.ts index c746725..18ca3ed 100644 --- a/src/client/actions/CallActions.ts +++ b/src/client/actions/CallActions.ts @@ -4,7 +4,8 @@ import * as StreamActions from './StreamActions' import * as constants from '../constants' import socket from '../socket' import { callId, getUserMedia } from '../window' -import { Dispatch, GetState } from '../store' +import { Dispatch, GetState, ThunkResult } from '../store' +import { makeAction } from '../async' export interface InitAction { type: 'INIT' @@ -19,20 +20,21 @@ const initialize = (): InitializeAction => ({ type: 'INIT', }) -export const init = () => async (dispatch: Dispatch, getState: GetState) => { - dispatch(initialize()) +export const init = (): ThunkResult> => +async (dispatch, getState) => { + const socket = await dispatch(connect()) + const stream = await dispatch(getCameraStream()) - const socket = await connect(dispatch) - const stream = await getCameraStream(dispatch) - - SocketActions.handshake({ + dispatch(SocketActions.handshake({ socket, roomName: callId, stream, - })(dispatch, getState) + })) + + dispatch(initialize()) } -export async function connect (dispatch: Dispatch) { +export const connect = () => (dispatch: Dispatch) => { return new Promise(resolve => { socket.once('connect', () => { resolve(socket) @@ -46,7 +48,7 @@ export async function connect (dispatch: Dispatch) { }) } -export async function getCameraStream (dispatch: Dispatch) { +export const getCameraStream = () => async (dispatch: Dispatch) => { try { const stream = await getUserMedia({ video: { facingMode: 'user' }, diff --git a/src/client/actions/NotifyActions.ts b/src/client/actions/NotifyActions.ts index 72c3c16..513cd83 100644 --- a/src/client/actions/NotifyActions.ts +++ b/src/client/actions/NotifyActions.ts @@ -27,15 +27,15 @@ function notify(dispatch: Dispatch, type: NotifyType, args: string[]) { } export const info = (...args: any[]): ThunkResult => { - return dispatch => notify(dispatch, 'info', args) + return dispatch => dispatch(notify(dispatch, 'info', args)) } export const warning = (...args: any[]): ThunkResult => { - return dispatch => notify(dispatch, 'warning', args) + return dispatch => dispatch(notify(dispatch, 'warning', args)) } export const error = (...args: any[]): ThunkResult => { - return dispatch => notify(dispatch, 'error', args) + return dispatch => dispatch(notify(dispatch, 'error', args)) } function addNotification(payload: Notification): NotificationAddAction { diff --git a/src/client/async/action.ts b/src/client/async/action.ts index 4fa3865..e243757 100644 --- a/src/client/async/action.ts +++ b/src/client/async/action.ts @@ -1,4 +1,4 @@ -import { Action } from 'redux' +import { Action, Dispatch } from 'redux' export type PendingAction = Action & Promise

& { status: 'pending' @@ -49,9 +49,9 @@ export function isPendingAction( export function makeAction( type: T, impl: (...args: A) => Promise

, -): (...args: A) => PendingAction{ +): (...args: A) => PendingAction { return (...args: A) => { - const pendingAction= impl(...args) as PendingAction + const pendingAction = impl(...args) as PendingAction pendingAction.type = type pendingAction.status = 'pending' return pendingAction diff --git a/src/client/async/middleware.ts b/src/client/async/middleware.ts index cad7720..b0e0510 100644 --- a/src/client/async/middleware.ts +++ b/src/client/async/middleware.ts @@ -1,13 +1,20 @@ import { AnyAction, Middleware } from 'redux' -import { isPendingAction, ResolvedAction, PendingAction, RejectedAction } from './action' +import { isPendingAction, ResolvedAction, RejectedAction } from './action' +import _debug from 'debug' + +const debug = _debug('peercalls:async') export const middleware: Middleware = store => next => (action: AnyAction) => { if (!isPendingAction(action)) { + debug('NOT pending %o', action) return next(action) } + debug('Pending: %s %s', action.type, action.status) + const promise = action .then(payload => { + debug('Resolved: %s resolved', action.type) const resolvedAction: ResolvedAction = { payload, type: action.type, @@ -17,6 +24,7 @@ export const middleware: Middleware = store => next => (action: AnyAction) => { }) // Propagate this action. Only attach listeners to the promise. + debug('Calling next for %s %s', action.type, action.status) next({ type: action.type, status: 'pending', @@ -24,6 +32,7 @@ export const middleware: Middleware = store => next => (action: AnyAction) => { const promise2 = promise .catch((err: Error) => { + debug('Rejected: %s rejected %s', action.type, err.stack) const rejectedAction: RejectedAction = { payload: err, type: action.type, @@ -32,5 +41,7 @@ export const middleware: Middleware = store => next => (action: AnyAction) => { store.dispatch(rejectedAction) }) - return promise2.then(() => action) + return promise2.then(() => { + return action + }) } diff --git a/src/client/constants.ts b/src/client/constants.ts index 616d2a1..e433276 100644 --- a/src/client/constants.ts +++ b/src/client/constants.ts @@ -6,9 +6,6 @@ export const ALERT_DISMISS = 'ALERT_DISMISS' export const ALERT_CLEAR = 'ALERT_CLEAR' export const INIT = 'INIT' -export const INIT_PENDING = `${INIT}_PENDING` -export const INIT_FULFILLED = `${INIT}_FULFILLED` -export const INIT_REJECTED = `${INIT}_REJECTED` export const ME = '_me_' diff --git a/src/client/middlewares.ts b/src/client/middlewares.ts index 785af08..7bf52b7 100644 --- a/src/client/middlewares.ts +++ b/src/client/middlewares.ts @@ -1,8 +1,9 @@ +import { Middleware } from 'redux' import logger from 'redux-logger' -import promiseMiddleware from 'redux-promise-middleware' import thunk from 'redux-thunk' +import { middleware as asyncMiddleware } from './async' -export const middlewares = [thunk, promiseMiddleware()] +export const middlewares: Middleware[] = [thunk, asyncMiddleware] export const create = (log = false) => { const m = middlewares.slice() log && m.push(logger) diff --git a/src/client/store.ts b/src/client/store.ts index 5e069e0..476c618 100644 --- a/src/client/store.ts +++ b/src/client/store.ts @@ -1,7 +1,6 @@ import { Action, applyMiddleware, createStore as _createStore, Store as ReduxStore } from 'redux' import { ThunkAction, ThunkDispatch } from 'redux-thunk' import { create } from './middlewares' -import { middleware as asyncMiddleware }from './async' import reducers from './reducers' export const middlewares = create( @@ -10,7 +9,7 @@ export const middlewares = create( export const createStore = () => _createStore( reducers, - applyMiddleware(...middlewares, asyncMiddleware), + applyMiddleware(...middlewares), ) export default createStore()