From 3e5b18f305490ce21851cb352e849e3fdf2f5475 Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Mon, 29 Jan 2018 17:38:35 -0500 Subject: [PATCH 01/16] Clean up login/logout saga. --- .flowconfig | 2 + flow-typed/npm/redux-saga_v0.16.x.js | 1475 +++++++++++++++++ .../containers/__tests__/Login.tests.js | 3 +- .../containers/__tests__/Settings.tests.js | 3 +- .../sagas/__tests__/loginFlow.tests.js | 5 +- integration_tests/setupSagas.js | 18 +- src/firebase/__mocks__/login.js | 5 + src/firebase/__mocks__/logout.js | 5 + src/sagas/__tests__/loginFlow.tests.js | 13 +- src/sagas/index.js | 4 +- src/sagas/loginFlow.js | 24 +- 11 files changed, 1522 insertions(+), 35 deletions(-) create mode 100644 flow-typed/npm/redux-saga_v0.16.x.js create mode 100644 src/firebase/__mocks__/login.js create mode 100644 src/firebase/__mocks__/logout.js diff --git a/.flowconfig b/.flowconfig index d6784c4..3e8e900 100644 --- a/.flowconfig +++ b/.flowconfig @@ -3,6 +3,8 @@ .*/node_modules/protobufjs/.* [options] +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe +suppress_comment=\\(.\\|\n\\)*\\$FlowJestError module.name_mapper='^actions\(.*\)$' -> '/src/actions\1' module.name_mapper='^components/\(.*\)$' -> '/src/components/\1' module.name_mapper='^containers/\(.*\)$' -> '/src/containers/\1' diff --git a/flow-typed/npm/redux-saga_v0.16.x.js b/flow-typed/npm/redux-saga_v0.16.x.js new file mode 100644 index 0000000..82312a0 --- /dev/null +++ b/flow-typed/npm/redux-saga_v0.16.x.js @@ -0,0 +1,1475 @@ +// flow-typed signature: ed8e53f40d350cb63a7f201d38c0e1ec +// flow-typed version: fd6d313c44/redux-saga_v0.16.x/flow_>=v0.56.0 + +// @flow + +declare module 'redux-saga' { + import typeof * as _effects from 'redux-saga/effects' + declare export var effects: _effects + + declare export interface Channel { + take: (cb: (msg: mixed) => void) => void, + put: (msg: mixed) => void, + flush: (cb: (msgs: mixed) => void) => void, + close: () => void + } + + declare export interface Task { + isRunning: () => boolean, + isCancelled: () => boolean, + result: () => T | void, + error: () => Error | void, + cancel: () => void, + done: Promise + } + + declare export interface Buffer { + isEmpty: () => boolean, + put: (msg: mixed) => void, + take(): mixed + } + + declare export interface SagaMonitor { + effectTriggered: (options: { + +effectId: number, + +parentEffectId: number, + +label: string, + +effect: Effect + }) => void, + effectResolved: (effectId: number, result: mixed) => void, + effectRejected: (effectId: number, error: Error) => void, + effectCancelled: (effectId: number) => void, + actionDispatched: (action: mixed) => void + } + + declare export type Saga = Generator + + declare export var eventChannel: ( + sub: (emit: (msg: any) => void) => () => void, + buffer?: Buffer, + matcher?: (msg: mixed) => boolean + ) => Channel + + declare export var buffers: { + +none: () => Buffer, + +fixed: (limit?: number) => Buffer, + +dropping: (limit?: number) => Buffer, + +sliding: (limit?: number) => Buffer, + +expanding: (initialSize?: number) => Buffer + } + + declare export var channel: (buffer?: Buffer) => Channel + declare export var END: { +type: '@@redux-saga/CHANNEL_END' } + declare export var CANCEL: Symbol + declare export var delay: (timeout: number) => Promise + + declare type RunSagaOptions = { + +subscribe?: (emit: (input: any) => any) => () => void, + +dispatch?: (output: any) => any, + +getState?: () => any, + +sagaMonitor?: SagaMonitor, + +logger?: ( + level: 'info' | 'warning' | 'error', + ...args: Array + ) => void, + +onError?: (error: Error) => void + } + + declare export var runSaga: { + Saga>(options: RunSagaOptions, saga: Fn): Task, + Saga>( + options: RunSagaOptions, + saga: Fn, + t1: T1 + ): Task, + Saga>( + options: RunSagaOptions, + saga: Fn, + t1: T1, + t2: T2 + ): Task, + Saga>( + options: RunSagaOptions, + saga: Fn, + t1: T1, + t2: T2, + t3: T3 + ): Task, + Saga>( + options: RunSagaOptions, + saga: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): Task, + < + R, + T1, + T2, + T3, + T4, + T5, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => Saga + >( + options: RunSagaOptions, + saga: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): Task, + < + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6) => Saga + >( + options: RunSagaOptions, + saga: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): Task + } + + declare interface SagaMiddleware { + // TODO: This should be aligned with the official redux typings sometime + (api: any): (next: any) => any, + run: { + Saga>(saga: Fn): Task, + Saga>(saga: Fn, t1: T1): Task, + Saga>( + saga: Fn, + t1: T1, + t2: T2 + ): Task, + Saga>( + saga: Fn, + t1: T1, + t2: T2, + t3: T3 + ): Task, + Saga>( + saga: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): Task, + < + R, + T1, + T2, + T3, + T4, + T5, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => Saga + >( + saga: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): Task, + < + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6) => Saga + >( + saga: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): Task + } + } + + declare type createSagaMiddleware = (options?: { + +sagaMonitor?: SagaMonitor, + +logger?: ( + level: 'info' | 'warning' | 'error', + ...args: Array + ) => void, + +onError?: (error: Error) => void + }) => SagaMiddleware + + declare export default createSagaMiddleware + + // Effect types + declare export type PatternPart = string | (any => boolean) + declare export type Pattern = PatternPart | $ReadOnlyArray + + declare export type TakeEffect< + P: Pattern | void, + C: Channel | void, + M: true | void + > = { + +'@@redux-saga/IO': true, + +TAKE: { + +pattern: P, + +channel: C, + +maybe: M + } + } + + declare export type PutEffect = { + +'@@redux-saga/IO': true, + +PUT: { + +action: A, + +channel: C + } + } + + declare export type CallEffect> = { + +'@@redux-saga/IO': true, + +CALL: { + +context: Ctx, + +fn: Fn, + +args: Args + } + } + + declare export type ForkEffect> = { + +'@@redux-saga/IO': true, + +FORK: { + +context: Ctx, + +fn: Fn, + +args: Args + } + } + + declare export type CpsEffect> = { + +'@@redux-saga/IO': true, + +CPS: { + +context: Ctx, + +fn: Fn, + +args: Args + } + } + + declare export type SpawnEffect< + Ctx, + Fn: Function, + Args: $ReadOnlyArray<*> + > = { + +'@@redux-saga/IO': true, + +SPAWN: { + +context: Ctx, + +fn: Fn, + +args: Args + } + } + + declare export type JoinEffect> = { + +'@@redux-saga/IO': true, + +JOIN: T + } + + declare export type CancelEffect< + T: Task<*> | '@@redux-saga/SELF_CANCELLATION' + > = { + +'@@redux-saga/IO': true, + +CANCEL: T + } + + declare export type SelectEffect> = { + +'@@redux-saga/IO': true, + +SELECT: { + +selector: Fn, + +args: Args + } + } + + declare export type ActionChannelEffect = { + +'@@redux-saga/IO': true, + +ACTION_CHANNEL: { + +buffer: B, + +pattern: P + } + } + + declare export type FlushEffect = { + +'@@redux-saga/IO': true, + +FLUSH: Channel + } + + declare export type CancelledEffect = { + +'@@redux-saga/IO': true, + +CANCELLED: {} + } + + declare export type SetContextEffect = { + +'@@redux-saga/IO': true, + +SET_CONTEXT: T + } + + declare export type GetContextEffect = { + +'@@redux-saga/IO': true, + +GET_CONTEXT: string + } + + declare export type RaceEffect< + R: { +[name: string]: Effect } | $ReadOnlyArray + > = { + +'@@redux-saga/IO': true, + +RACE: R + } + + declare export type AllEffect = { + +'@@redux-saga/IO': true, + +ALL: { +[name: string]: Effect } | $ReadOnlyArray + } + + declare export type Effect = + | TakeEffect<*, *, *> + | PutEffect<*, *> + | CallEffect<*, *, *> + | ForkEffect<*, *, *> + | CpsEffect<*, *, *> + | SpawnEffect<*, *, *> + | JoinEffect<*> + | CancelEffect<*> + | SelectEffect<*, *> + | ActionChannelEffect<*, *> + | FlushEffect + | CancelledEffect + | SetContextEffect<*> + | GetContextEffect + | RaceEffect<*> + | AllEffect +} + +declare module 'redux-saga/effects' { + import type { + ActionChannelEffect, + AllEffect, + Buffer, + CallEffect, + CancelEffect, + CancelledEffect, + Channel, + CpsEffect, + Effect, + FlushEffect, + ForkEffect, + GetContextEffect, + JoinEffect, + Pattern, + PutEffect, + RaceEffect, + Saga, + SelectEffect, + SetContextEffect, + SpawnEffect, + TakeEffect, + Task + } from 'redux-saga' + + declare export var take: { + (pattern: P): TakeEffect, + (channel: Channel): TakeEffect, + +maybe: { + (pattern: P): TakeEffect, + (channel: Channel): TakeEffect + } + } + + declare export var put: { + (action: A): PutEffect, + (channel: Channel, action: A): PutEffect + } + + declare export var call: { + // normal calls + R>(fn: Fn): CallEffect, + R>(fn: Fn, t1: T1): CallEffect, + R>( + fn: Fn, + t1: T1, + t2: T2 + ): CallEffect, + R>( + fn: Fn, + t1: T1, + t2: T2, + t3: T3 + ): CallEffect, + R>( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): CallEffect, + R>( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): CallEffect, + < + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6) => R + >( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): CallEffect, + + // with context + R>(cfn: [Ctx, Fn]): CallEffect, + R>( + cfn: [Ctx, Fn], + t1: T1 + ): CallEffect, + R>( + cfn: [Ctx, Fn], + t1: T1, + t2: T2 + ): CallEffect, + R>( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3 + ): CallEffect, + R>( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): CallEffect, + < + Ctx, + R, + T1, + T2, + T3, + T4, + T5, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => R + >( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): CallEffect, + < + Ctx, + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6) => R + >( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): CallEffect + } + + declare export var apply: { + R>(ctx: Ctx, fn: Fn): CallEffect, + R>( + ctx: Ctx, + fn: Fn, + t1: T1 + ): CallEffect, + R>( + ctx: Ctx, + fn: Fn, + t1: T1, + t2: T2 + ): CallEffect, + R>( + ctx: Ctx, + fn: Fn, + t1: T1, + t2: T2, + t3: T3 + ): CallEffect, + R>( + ctx: Ctx, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): CallEffect, + < + Ctx, + R, + T1, + T2, + T3, + T4, + T5, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => R + >( + ctx: Ctx, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): CallEffect, + < + Ctx, + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6) => R + >( + ctx: Ctx, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): CallEffect + } + + declare type NodeCallback = { + (err: Error): void, + (err: null | void | false, result: R): void + } + + declare export var cps: { + // normal calls + ) => void>(fn: Fn): CpsEffect, + ) => void>( + fn: Fn, + t1: T1 + ): CpsEffect, + ) => void>( + fn: Fn, + t1: T1, + t2: T2 + ): CpsEffect, + ) => void>( + fn: Fn, + t1: T1, + t2: T2, + t3: T3 + ): CpsEffect, + < + R, + T1, + T2, + T3, + T4, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, cb: NodeCallback) => void + >( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): CpsEffect, + < + R, + T1, + T2, + T3, + T4, + T5, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, cb: NodeCallback) => void + >( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): CpsEffect, + < + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: ( + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6, + cb: NodeCallback + ) => void + >( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): CpsEffect, + + // with context + ) => void>( + cfn: [Ctx, Fn] + ): CpsEffect, + ) => void>( + cfn: [Ctx, Fn], + t1: T1 + ): CpsEffect, + ) => void>( + cfn: [Ctx, Fn], + t1: T1, + t2: T2 + ): CpsEffect, + < + Ctx, + R, + T1, + T2, + T3, + Fn: (t1: T1, t2: T2, t3: T3, cb: NodeCallback) => void + >( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3 + ): CpsEffect, + < + Ctx, + R, + T1, + T2, + T3, + T4, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, cb: NodeCallback) => void + >( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): CpsEffect, + < + Ctx, + R, + T1, + T2, + T3, + T4, + T5, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, cb: NodeCallback) => void + >( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): CpsEffect, + < + Ctx, + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: ( + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6, + cb: NodeCallback + ) => void + >( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): CpsEffect + } + + declare export var fork: { + // normal calls + R>(fn: Fn): ForkEffect, + R>(fn: Fn, t1: T1): ForkEffect, + R>( + fn: Fn, + t1: T1, + t2: T2 + ): ForkEffect, + R>( + fn: Fn, + t1: T1, + t2: T2, + t3: T3 + ): ForkEffect, + R>( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): ForkEffect, + R>( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): ForkEffect, + < + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6) => R + >( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): ForkEffect, + + // with context + R>(cfn: [Ctx, Fn]): ForkEffect, + R>( + cfn: [Ctx, Fn], + t1: T1 + ): ForkEffect, + R>( + cfn: [Ctx, Fn], + t1: T1, + t2: T2 + ): ForkEffect, + R>( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3 + ): ForkEffect, + R>( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): ForkEffect, + < + Ctx, + R, + T1, + T2, + T3, + T4, + T5, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => R + >( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): ForkEffect, + < + Ctx, + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6) => R + >( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): ForkEffect + } + + declare export var spawn: { + // normal calls + R>(fn: Fn): SpawnEffect, + R>(fn: Fn, t1: T1): SpawnEffect, + R>( + fn: Fn, + t1: T1, + t2: T2 + ): SpawnEffect, + R>( + fn: Fn, + t1: T1, + t2: T2, + t3: T3 + ): SpawnEffect, + R>( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): SpawnEffect, + R>( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): SpawnEffect, + < + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6) => R + >( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): SpawnEffect, + + // with context + R>(cfn: [Ctx, Fn]): SpawnEffect, + R>( + cfn: [Ctx, Fn], + t1: T1 + ): SpawnEffect, + R>( + cfn: [Ctx, Fn], + t1: T1, + t2: T2 + ): SpawnEffect, + R>( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3 + ): SpawnEffect, + R>( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): SpawnEffect, + < + Ctx, + R, + T1, + T2, + T3, + T4, + T5, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => R + >( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): SpawnEffect, + < + Ctx, + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6) => R + >( + cfn: [Ctx, Fn], + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): SpawnEffect + } + + declare export var join: { + >(task: T): JoinEffect, + (task: Task<*>, ...tasks: $ReadOnlyArray>): AllEffect + } + + declare export var cancel: { + (): CancelEffect<'@@redux-saga/SELF_CANCELLATION'>, + >(task: T): CancelEffect, + (task: Task<*>, ...tasks: $ReadOnlyArray>): AllEffect + } + + declare export var select: { + R>(fn: Fn): SelectEffect, + R>( + fn: Fn, + t1: T1 + ): SelectEffect, + R>( + fn: Fn, + t1: T1, + t2: T2 + ): SelectEffect, + R>( + fn: Fn, + t1: T1, + t2: T2, + t3: T3 + ): SelectEffect, + R>( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): SelectEffect, + < + S, + R, + T1, + T2, + T3, + T4, + T5, + Fn: (state: S, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => R + >( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): SelectEffect, + < + S, + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (state: S, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6) => R + >( + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): SelectEffect + } + + declare export var actionChannel: { + (pattern: P): ActionChannelEffect, + (pattern: P, buffer: B): ActionChannelEffect + } + + declare export var flush: { + (channel: Channel): FlushEffect + } + + declare export var cancelled: { + (): CancelledEffect + } + + declare export var setContext: { + (ctx: T): SetContextEffect + } + + declare export var getContext: { + (prop: string): GetContextEffect + } + + declare export var race: { + >( + effects: R + ): RaceEffect + } + + declare export var all: { + (effects: { +[name: string]: Effect }): AllEffect, + (effects: $ReadOnlyArray): AllEffect + } + + declare export var takeEvery: { + // normal calls + Saga>( + pattern: Pattern, + fn: Fn + ): ForkEffect>, + Saga>( + pattern: Pattern, + fn: Fn, + t1: T1 + ): ForkEffect>, + Saga>( + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2 + ): ForkEffect>, + Saga>( + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2, + t3: T3 + ): ForkEffect>, + < + A, + R, + T1, + T2, + T3, + T4, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, action: A) => Saga + >( + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): ForkEffect>, + < + A, + R, + T1, + T2, + T3, + T4, + T5, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, action: A) => Saga + >( + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): ForkEffect>, + < + A, + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, action: A) => Saga + >( + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): ForkEffect>, + + // with channel + Saga>( + channel: Channel, + fn: Fn + ): ForkEffect>, + Saga>( + channel: Channel, + fn: Fn, + t1: T1 + ): ForkEffect>, + Saga>( + channel: Channel, + fn: Fn, + t1: T1, + t2: T2 + ): ForkEffect>, + Saga>( + channel: Channel, + fn: Fn, + t1: T1, + t2: T2, + t3: T3 + ): ForkEffect>, + < + A, + R, + T1, + T2, + T3, + T4, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, action: A) => Saga + >( + channel: Channel, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): ForkEffect>, + < + A, + R, + T1, + T2, + T3, + T4, + T5, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, action: A) => Saga + >( + channel: Channel, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): ForkEffect>, + < + A, + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, action: A) => Saga + >( + channel: Channel, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): ForkEffect> + } + + declare export var takeLatest: { + // normal calls + Saga>( + pattern: Pattern, + fn: Fn + ): ForkEffect>, + Saga>( + pattern: Pattern, + fn: Fn, + t1: T1 + ): ForkEffect>, + Saga>( + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2 + ): ForkEffect>, + Saga>( + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2, + t3: T3 + ): ForkEffect>, + < + A, + R, + T1, + T2, + T3, + T4, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, action: A) => Saga + >( + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): ForkEffect>, + < + A, + R, + T1, + T2, + T3, + T4, + T5, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, action: A) => Saga + >( + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): ForkEffect>, + < + A, + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, action: A) => Saga + >( + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): ForkEffect>, + + // with channel + Saga>( + channel: Channel, + fn: Fn + ): ForkEffect>, + Saga>( + channel: Channel, + fn: Fn, + t1: T1 + ): ForkEffect>, + Saga>( + channel: Channel, + fn: Fn, + t1: T1, + t2: T2 + ): ForkEffect>, + Saga>( + channel: Channel, + fn: Fn, + t1: T1, + t2: T2, + t3: T3 + ): ForkEffect>, + < + A, + R, + T1, + T2, + T3, + T4, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, action: A) => Saga + >( + channel: Channel, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): ForkEffect>, + < + A, + R, + T1, + T2, + T3, + T4, + T5, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, action: A) => Saga + >( + channel: Channel, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): ForkEffect>, + < + A, + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, action: A) => Saga + >( + channel: Channel, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): ForkEffect> + } + + declare export var throttle: { + // normal calls + Saga>( + ms: number, + pattern: Pattern, + fn: Fn + ): ForkEffect, + Saga>( + ms: number, + pattern: Pattern, + fn: Fn, + t1: T1 + ): ForkEffect, + Saga>( + ms: number, + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2 + ): ForkEffect, + Saga>( + ms: number, + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2, + t3: T3 + ): ForkEffect, + < + A, + R, + T1, + T2, + T3, + T4, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, action: A) => Saga + >( + ms: number, + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4 + ): ForkEffect, + < + A, + R, + T1, + T2, + T3, + T4, + T5, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, action: A) => Saga + >( + ms: number, + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5 + ): ForkEffect, + < + A, + R, + T1, + T2, + T3, + T4, + T5, + T6, + Fn: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, action: A) => Saga + >( + ms: number, + pattern: Pattern, + fn: Fn, + t1: T1, + t2: T2, + t3: T3, + t4: T4, + t5: T5, + t6: T6 + ): ForkEffect + } +} diff --git a/integration_tests/containers/__tests__/Login.tests.js b/integration_tests/containers/__tests__/Login.tests.js index d3018e7..f586eae 100644 --- a/integration_tests/containers/__tests__/Login.tests.js +++ b/integration_tests/containers/__tests__/Login.tests.js @@ -6,7 +6,7 @@ import { ConnectedRouter } from 'react-router-redux' import { performUserLogin } from '../../../src/actions' import { APP_FINISHED_LOADING } from '../../../src/actions/types' import setupStore, { history } from '../../setupStore' -import { loginFunction } from '../../setupSagas' +import loginFunction from 'firebase/login' import Login from '../../../src/containers/Login' describe('Login container', () => { @@ -31,6 +31,7 @@ describe('Login container', () => { .find('input[type="password"]') .simulate('change', { target: { value: password } }) wrapper.find('button').simulate('click') + // $FlowJestError expect(loginFunction.calledWith(loginAction)).toBe(true) }) }) diff --git a/integration_tests/containers/__tests__/Settings.tests.js b/integration_tests/containers/__tests__/Settings.tests.js index a831030..88255b7 100644 --- a/integration_tests/containers/__tests__/Settings.tests.js +++ b/integration_tests/containers/__tests__/Settings.tests.js @@ -11,7 +11,7 @@ import { USER_LOGGED_IN } from '../../../src/actions/types' import setupStore, { history } from '../../setupStore' -import { logoutFunction } from '../../setupSagas' +import logoutFunction from 'firebase/logout' import Settings from '../../../src/containers/Settings' describe('Settings container', () => { @@ -55,6 +55,7 @@ describe('Settings container', () => { expect(logoutButton.text()).toContain('Logout') logoutButton.simulate('click') expect(store.getState().ui.userIsLoggedIn).toBe(false) + // $FlowJestError expect(logoutFunction.called).toBe(true) }) }) diff --git a/integration_tests/sagas/__tests__/loginFlow.tests.js b/integration_tests/sagas/__tests__/loginFlow.tests.js index 0dbcbac..1ab6e4a 100644 --- a/integration_tests/sagas/__tests__/loginFlow.tests.js +++ b/integration_tests/sagas/__tests__/loginFlow.tests.js @@ -1,6 +1,7 @@ // @flow import setupStore from '../../setupStore' -import { loginFunction, logoutFunction } from '../../setupSagas' +import loginFunction from 'firebase/login' +import logoutFunction from 'firebase/logout' import { performUserLogin } from 'actions' import { APP_FINISHED_LOADING, PERFORM_USER_LOGOUT } from 'actions/types' @@ -12,11 +13,13 @@ describe('login saga integration', () => { store.dispatch(loginAction) it('should respond to PERFORM_USER_LOGIN instruction', () => { + // $FlowJestError expect(loginFunction.calledWith(loginAction)).toBe(true) }) store.dispatch({ type: PERFORM_USER_LOGOUT }) it('should call logout function', () => { + // $FlowJestError expect(logoutFunction.called).toBe(true) }) diff --git a/integration_tests/setupSagas.js b/integration_tests/setupSagas.js index c789e5e..2d7f9c7 100644 --- a/integration_tests/setupSagas.js +++ b/integration_tests/setupSagas.js @@ -1,5 +1,4 @@ // @flow -import sinon from 'sinon' import { fork } from 'redux-saga/effects' //import loadCurrentSession from '../src/sagas/loadCurrentSession' //import loadSessionMeta from '../src/sagas/loadSessionMeta' @@ -13,8 +12,9 @@ import loginFlow from '../src/sagas/loginFlow' //import sendMessages from '../src/sagas/sendMessages' import switchSessions from '../src/sagas/switchSessions' -export const loginFunction = sinon.spy() -export const logoutFunction = sinon.spy() +// Mock implementations +jest.mock('firebase/login') +jest.mock('firebase/logout') export default function* rootSaga(): Generator<*, *, *> { // yield fork(loadCurrentSession) @@ -22,10 +22,10 @@ export default function* rootSaga(): Generator<*, *, *> { // yield fork(loadSessions) // yield fork(loadUserPreferences) // yield fork(loadUserProfile) - yield fork(loginFlow, loginFunction, logoutFunction), - // yield fork(receiveMessages) - // yield fork(saveUserPreferences) - // yield fork(saveUserProfile) - // yield fork(sendMessages) - yield fork(switchSessions) + yield fork(loginFlow) + // yield fork(receiveMessages) + // yield fork(saveUserPreferences) + // yield fork(saveUserProfile) + // yield fork(sendMessages) + yield fork(switchSessions) } diff --git a/src/firebase/__mocks__/login.js b/src/firebase/__mocks__/login.js new file mode 100644 index 0000000..b2981b4 --- /dev/null +++ b/src/firebase/__mocks__/login.js @@ -0,0 +1,5 @@ +import sinon from 'sinon' + +const login = sinon.spy() + +export default login diff --git a/src/firebase/__mocks__/logout.js b/src/firebase/__mocks__/logout.js new file mode 100644 index 0000000..484008f --- /dev/null +++ b/src/firebase/__mocks__/logout.js @@ -0,0 +1,5 @@ +import sinon from 'sinon' + +const logout = sinon.spy() + +export default logout diff --git a/src/sagas/__tests__/loginFlow.tests.js b/src/sagas/__tests__/loginFlow.tests.js index ec2cf7a..781230b 100644 --- a/src/sagas/__tests__/loginFlow.tests.js +++ b/src/sagas/__tests__/loginFlow.tests.js @@ -9,11 +9,11 @@ import { USER_LOGGED_OUT } from 'actions/types' import loginFlow from '../loginFlow' +jest.mock('firebase/login') +jest.mock('firebase/logout') describe('login saga', () => { - const loginFunction = () => {} - const logoutFunction = () => {} - const gen = loginFlow(loginFunction, logoutFunction) + const gen = loginFlow() it('should wait for app to finish loading', () => { expect(gen.next().value).toEqual(take(APP_FINISHED_LOADING)) @@ -34,7 +34,7 @@ describe('login saga', () => { const loginAction = performUserLogin('test@example.com', 'password') it('should perform login', () => { - expect(gen.next(loginAction).value).toHaveProperty('CALL.fn', loginFunction) + expect(gen.next(loginAction).value).toHaveProperty('CALL.fn') }) it('should ignore additional login instructions', () => { @@ -50,10 +50,7 @@ describe('login saga', () => { const logoutAction = { type: PERFORM_USER_LOGOUT } it('should perform logout', () => { - expect(gen.next(logoutAction).value).toHaveProperty( - 'CALL.fn', - logoutFunction - ) + expect(gen.next(logoutAction).value).toHaveProperty('CALL.fn') }) it('should report user logged out', () => { diff --git a/src/sagas/index.js b/src/sagas/index.js index e84d326..abed601 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -17,8 +17,6 @@ import getSessionMeta from 'firebase/sessionMeta' import getCurrentUserData from 'firebase/getCurrentUserData' import getCurrentUserPreferences from 'firebase/getCurrentUserPreferences' import getCurrentUserProfile from 'firebase/getCurrentUserProfile' -import loginFunction from 'firebase/login' -import logoutFunction from 'firebase/logout' import createMessagesSubscription from 'firebase/messages' import saveCurrentUserPreferences from 'firebase/savePreferences' import getCurrentUserEmail from 'firebase/getCurrentUserEmail' @@ -30,7 +28,7 @@ export default function* rootSaga(): Generator<*, *, *> { yield fork(loadSessionMeta, getSessionMeta) yield fork(loadUserSessions, getCurrentUserData) yield fork(loadUserProfile, getCurrentUserProfile) - yield fork(loginFlow, loginFunction, logoutFunction) + yield fork(loginFlow) yield fork(receiveMessages, createMessagesSubscription) yield fork(saveUserProfile, getCurrentUserEmail, saveProfile) yield fork(sendMessages, sendMessage) diff --git a/src/sagas/loginFlow.js b/src/sagas/loginFlow.js index e5545db..5fe1956 100644 --- a/src/sagas/loginFlow.js +++ b/src/sagas/loginFlow.js @@ -1,5 +1,8 @@ // @flow +import type { Saga } from 'redux-saga' import { take, call, put, select } from 'redux-saga/effects' +import performLogin from 'firebase/login' +import performLogout from 'firebase/logout' import { APP_FINISHED_LOADING, PERFORM_USER_LOGIN, @@ -8,14 +11,12 @@ import { USER_LOGGED_OUT } from 'actions/types' -export default function* login( - loginFunction: Function, - logoutFunction: Function -): Generator<*, *, *> { +export default function* login(): Saga { + // Wait for app to complete loading yield take(APP_FINISHED_LOADING) - let userIsLoggedIn: boolean = ((yield select( - state => state.ui.userIsLoggedIn - ): any): boolean) + + // See if user is logged in + let userIsLoggedIn = yield select(state => state.ui.userIsLoggedIn) while (true) { const action = yield take([ @@ -23,14 +24,13 @@ export default function* login( PERFORM_USER_LOGIN, PERFORM_USER_LOGOUT ]) - if (action.type == PERFORM_USER_LOGIN) { - if (!userIsLoggedIn) { - yield call(loginFunction, action) - } + + if (action.type == PERFORM_USER_LOGIN && !userIsLoggedIn) { + yield call(performLogin, action) } else if (action.type == USER_LOGGED_IN) { userIsLoggedIn = true } else if (action.type == PERFORM_USER_LOGOUT) { - yield call(logoutFunction) + yield call(performLogout) yield put({ type: USER_LOGGED_OUT }) userIsLoggedIn = false } From 0b994ea55eb53d392de178ecfee5d3f55becb4ed Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Mon, 29 Jan 2018 20:53:29 -0500 Subject: [PATCH 02/16] Update loadCurrentSession saga. --- integration_tests/setupSagas.js | 1 + src/firebase/__mocks__/session.js | 6 +---- src/firebase/session.js | 6 +---- .../__tests__/loadCurrentSession.tests.js | 8 +++---- src/sagas/index.js | 3 +-- src/sagas/loadCurrentSession.js | 23 ++++++++----------- 6 files changed, 18 insertions(+), 29 deletions(-) diff --git a/integration_tests/setupSagas.js b/integration_tests/setupSagas.js index 2d7f9c7..3352094 100644 --- a/integration_tests/setupSagas.js +++ b/integration_tests/setupSagas.js @@ -13,6 +13,7 @@ import loginFlow from '../src/sagas/loginFlow' import switchSessions from '../src/sagas/switchSessions' // Mock implementations +jest.mock('firebase/session') jest.mock('firebase/login') jest.mock('firebase/logout') diff --git a/src/firebase/__mocks__/session.js b/src/firebase/__mocks__/session.js index 02b821a..6cb5540 100644 --- a/src/firebase/__mocks__/session.js +++ b/src/firebase/__mocks__/session.js @@ -1,7 +1,7 @@ // @flow import type { SessionSubscription } from '../types' -class Session implements SessionSubscription { +export default class Session implements SessionSubscription { /*eslint-disable no-unused-vars*/ constructor(sessionId: string) {} /*eslint-enable no-unused-vars*/ @@ -12,7 +12,3 @@ class Session implements SessionSubscription { close() {} } - -const createSession = (sessionId: string) => new Session(sessionId) - -export default createSession diff --git a/src/firebase/session.js b/src/firebase/session.js index 5ceebe2..636bf89 100644 --- a/src/firebase/session.js +++ b/src/firebase/session.js @@ -3,7 +3,7 @@ import firebase from '@firebase/app' import '@firebase/database' import type { SessionSubscription, Ref } from './types' -class Session implements SessionSubscription { +export default class Session implements SessionSubscription { ref: Ref constructor(sessionId: string) { @@ -20,7 +20,3 @@ class Session implements SessionSubscription { this.ref.off() } } - -const createSession = (sessionId: string) => new Session(sessionId) - -export default createSession diff --git a/src/sagas/__tests__/loadCurrentSession.tests.js b/src/sagas/__tests__/loadCurrentSession.tests.js index b9f250d..7a6bb0e 100644 --- a/src/sagas/__tests__/loadCurrentSession.tests.js +++ b/src/sagas/__tests__/loadCurrentSession.tests.js @@ -2,7 +2,6 @@ import { take, put } from 'redux-saga/effects' import { createMockTask, cloneableGenerator } from 'redux-saga/utils' import { switchToSession, hydrateSession } from 'actions' -import createSession from '../../firebase/__mocks__/session' import { USER_LOGGED_IN, USER_LOGGED_OUT, @@ -10,9 +9,10 @@ import { } from 'actions/types' import loadCurrentSession, { subscribeToSession } from '../loadCurrentSession' +jest.mock('firebase/session') + describe('loadCurrentSession saga', () => { - const createSession = () => {} - const start = cloneableGenerator(loadCurrentSession)(createSession) + const start = cloneableGenerator(loadCurrentSession)() const firstAction = start.clone() it('should wait for login, logout, or switch session instructions', () => { @@ -52,7 +52,7 @@ describe('loadCurrentSession saga', () => { }) describe('subscribeToSession generator', () => { - const subscription = subscribeToSession(createSession('fakeSessionId')) + const subscription = subscribeToSession('fakeSessionId') it('should wait for event from the channel', () => { expect(subscription.next().value).toHaveProperty('TAKE') diff --git a/src/sagas/index.js b/src/sagas/index.js index abed601..495b113 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -12,7 +12,6 @@ import saveUserProfile from './saveUserProfile' import sendMessages from './sendMessages' import switchSessions from './switchSessions' // Firebase DI -import createSession from 'firebase/session' import getSessionMeta from 'firebase/sessionMeta' import getCurrentUserData from 'firebase/getCurrentUserData' import getCurrentUserPreferences from 'firebase/getCurrentUserPreferences' @@ -24,7 +23,7 @@ import saveProfile from 'firebase/saveProfile' import sendMessage from 'firebase/sendMessage' export default function* rootSaga(): Generator<*, *, *> { - yield fork(loadCurrentSession, createSession) + yield fork(loadCurrentSession) yield fork(loadSessionMeta, getSessionMeta) yield fork(loadUserSessions, getCurrentUserData) yield fork(loadUserProfile, getCurrentUserProfile) diff --git a/src/sagas/loadCurrentSession.js b/src/sagas/loadCurrentSession.js index f351f6d..862a689 100644 --- a/src/sagas/loadCurrentSession.js +++ b/src/sagas/loadCurrentSession.js @@ -9,11 +9,14 @@ import { SWITCH_TO_SESSION } from 'actions/types' import type { Action } from 'actions/types' -import type { SessionSubscription } from 'firebase/types' +import type { Saga } from 'redux-saga' +import Session from 'firebase/session' -export function* subscribeToSession( - session: SessionSubscription -): Generator { +export function* subscribeToSession(sessionId: string): Saga { + // Create the session + const session = new Session(sessionId) + + // Subscribe to changes in the channel const channel = eventChannel(emit => { // Emit on new data session.onSessionData(emit) @@ -34,11 +37,7 @@ export function* subscribeToSession( } } -type CreateSessionFunction = (sessionId: string) => SessionSubscription - -export default function* loadCurrentSession( - createSession: CreateSessionFunction -): Generator<*, *, *> { +export default function* loadCurrentSession(): Saga { let currentSubscription = null let currentSessionId = null @@ -70,11 +69,9 @@ export default function* loadCurrentSession( yield cancel(currentSubscription) } + // Update state variables + currentSubscription = yield fork(subscribeToSession, newSessionId) currentSessionId = newSessionId - currentSubscription = yield fork( - subscribeToSession, - createSession(newSessionId) - ) } } } From c8da3e3aeb9bb1efff661fb1688482771c6a3207 Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Mon, 29 Jan 2018 21:04:46 -0500 Subject: [PATCH 03/16] Update loadSessionMeta saga. --- integration_tests/setupSagas.js | 1 + src/firebase/__mocks__/getSessionMeta.js | 9 +++++++ .../{sessionMeta.js => getSessionMeta.js} | 0 src/sagas/__tests__/loadSessionMeta.tests.js | 21 ++++++++-------- src/sagas/index.js | 3 +-- src/sagas/loadSessionMeta.js | 24 ++++++------------- 6 files changed, 29 insertions(+), 29 deletions(-) create mode 100644 src/firebase/__mocks__/getSessionMeta.js rename src/firebase/{sessionMeta.js => getSessionMeta.js} (100%) diff --git a/integration_tests/setupSagas.js b/integration_tests/setupSagas.js index 3352094..fab8c2f 100644 --- a/integration_tests/setupSagas.js +++ b/integration_tests/setupSagas.js @@ -14,6 +14,7 @@ import switchSessions from '../src/sagas/switchSessions' // Mock implementations jest.mock('firebase/session') +jest.mock('firebase/getSessionMeta') jest.mock('firebase/login') jest.mock('firebase/logout') diff --git a/src/firebase/__mocks__/getSessionMeta.js b/src/firebase/__mocks__/getSessionMeta.js new file mode 100644 index 0000000..98cf781 --- /dev/null +++ b/src/firebase/__mocks__/getSessionMeta.js @@ -0,0 +1,9 @@ +// @flow + +const loadSessionMeta = (sessionId: string): Promise => + new Promise((resolve, reject) => { + const mockData = { name: 'test' } + resolve(mockData) + }) + +export default loadSessionMeta diff --git a/src/firebase/sessionMeta.js b/src/firebase/getSessionMeta.js similarity index 100% rename from src/firebase/sessionMeta.js rename to src/firebase/getSessionMeta.js diff --git a/src/sagas/__tests__/loadSessionMeta.tests.js b/src/sagas/__tests__/loadSessionMeta.tests.js index 12a6a1e..3e69f96 100644 --- a/src/sagas/__tests__/loadSessionMeta.tests.js +++ b/src/sagas/__tests__/loadSessionMeta.tests.js @@ -4,14 +4,15 @@ import { hydrateSessionMeta } from 'actions' import { HYDRATE_USER_DATA } from 'actions/types' import loadMeta, { loadAllMeta, loadSessionMeta } from '../loadSessionMeta' +jest.mock('firebase/getSessionMeta') + const mockData = { name: 'test' } -const mockGetSessionMeta = () => new Promise(resolve => resolve(mockData)) describe('loadSessionMeta generator', () => { - const gen = loadSessionMeta(mockGetSessionMeta, 'sessionId') + const gen = loadSessionMeta('sessionId') it('should call getSessionMeta', () => { - expect(gen.next().value).toEqual(call(mockGetSessionMeta, 'sessionId')) + expect(gen.next().value).toHaveProperty('CALL.fn') }) it('should hydrate the store with the meta', () => { @@ -26,18 +27,20 @@ describe('loadSessionMeta generator', () => { }) describe('loadAllMeta generator', () => { - const gen = loadAllMeta(mockGetSessionMeta) + const gen = loadAllMeta() const sessions = [{ id: 'session1' }, { id: 'session2' }] + // Asking for a list of sessions it('should get an object of session ids from the user', () => { expect(gen.next().value).toHaveProperty('SELECT') }) + // Get meta for all sessions it('should yield an `all` effect to get session meta', () => { expect(gen.next(sessions).value).toHaveProperty('ALL', [ - call(loadSessionMeta, mockGetSessionMeta, 'session1'), - call(loadSessionMeta, mockGetSessionMeta, 'session2') + call(loadSessionMeta, 'session1'), + call(loadSessionMeta, 'session2') ]) }) @@ -47,12 +50,10 @@ describe('loadAllMeta generator', () => { }) describe('loadMeta saga', () => { - const gen = loadMeta(mockGetSessionMeta) + const gen = loadMeta() it('should wait for HYDRATE_USER_DATA instruction', () => { - expect(gen.next().value).toEqual( - takeLatest(HYDRATE_USER_DATA, loadAllMeta, mockGetSessionMeta) - ) + expect(gen.next().value).toEqual(takeLatest(HYDRATE_USER_DATA, loadAllMeta)) }) it('should be done', () => { diff --git a/src/sagas/index.js b/src/sagas/index.js index 495b113..4e82037 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -12,7 +12,6 @@ import saveUserProfile from './saveUserProfile' import sendMessages from './sendMessages' import switchSessions from './switchSessions' // Firebase DI -import getSessionMeta from 'firebase/sessionMeta' import getCurrentUserData from 'firebase/getCurrentUserData' import getCurrentUserPreferences from 'firebase/getCurrentUserPreferences' import getCurrentUserProfile from 'firebase/getCurrentUserProfile' @@ -24,7 +23,7 @@ import sendMessage from 'firebase/sendMessage' export default function* rootSaga(): Generator<*, *, *> { yield fork(loadCurrentSession) - yield fork(loadSessionMeta, getSessionMeta) + yield fork(loadSessionMeta) yield fork(loadUserSessions, getCurrentUserData) yield fork(loadUserProfile, getCurrentUserProfile) yield fork(loginFlow) diff --git a/src/sagas/loadSessionMeta.js b/src/sagas/loadSessionMeta.js index dcd97fd..c0754f4 100644 --- a/src/sagas/loadSessionMeta.js +++ b/src/sagas/loadSessionMeta.js @@ -1,30 +1,20 @@ // @flow +import type { Saga } from 'redux-saga' import { all, select, call, put, takeLatest } from 'redux-saga/effects' import { hydrateSessionMeta } from 'actions' import { HYDRATE_USER_DATA } from 'actions/types' +import getSessionMeta from 'firebase/getSessionMeta' -type GetSessionMeta = (sessionId: string) => Promise - -export function* loadSessionMeta( - getSessionMeta: GetSessionMeta, - sessionId: string -): Generator<*, *, *> { +export function* loadSessionMeta(sessionId: string): Saga { const meta = yield call(getSessionMeta, sessionId) yield put(hydrateSessionMeta(sessionId, meta)) } -export function* loadAllMeta( - getSessionMeta: GetSessionMeta -): Generator<*, *, *> { +export function* loadAllMeta(): Saga { const sessions = yield select(state => state.user.data.sessions) - - yield all( - sessions.map(session => call(loadSessionMeta, getSessionMeta, session.id)) - ) + yield all(sessions.map(session => call(loadSessionMeta, session.id))) } -export default function* loadMeta( - getSessionMeta: GetSessionMeta -): Generator<*, *, *> { - yield takeLatest(HYDRATE_USER_DATA, loadAllMeta, getSessionMeta) +export default function* loadMeta(): Saga { + yield takeLatest(HYDRATE_USER_DATA, loadAllMeta) } From 0b4ea65f2b6e217f850d7c9990cd1bb69726323d Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Mon, 29 Jan 2018 21:16:15 -0500 Subject: [PATCH 04/16] Update loadUserProfile saga. --- integration_tests/setupSagas.js | 1 + src/firebase/__mocks__/getCurrentUserProfile.js | 8 ++++++++ src/sagas/__tests__/loadUserProfile.tests.js | 7 ++++--- src/sagas/index.js | 3 +-- src/sagas/loadUserProfile.js | 12 +++++------- 5 files changed, 19 insertions(+), 12 deletions(-) create mode 100644 src/firebase/__mocks__/getCurrentUserProfile.js diff --git a/integration_tests/setupSagas.js b/integration_tests/setupSagas.js index fab8c2f..c57d20d 100644 --- a/integration_tests/setupSagas.js +++ b/integration_tests/setupSagas.js @@ -15,6 +15,7 @@ import switchSessions from '../src/sagas/switchSessions' // Mock implementations jest.mock('firebase/session') jest.mock('firebase/getSessionMeta') +jest.mock('firebase/getCurrentUserProfile') jest.mock('firebase/login') jest.mock('firebase/logout') diff --git a/src/firebase/__mocks__/getCurrentUserProfile.js b/src/firebase/__mocks__/getCurrentUserProfile.js new file mode 100644 index 0000000..cb5e97d --- /dev/null +++ b/src/firebase/__mocks__/getCurrentUserProfile.js @@ -0,0 +1,8 @@ +// @flow +import type { UserProfile } from 'types' + +const mockData = { displayName: 'test_user', photoURL: null } + +const getCurrentUserProfile = (): UserProfile => mockData + +export default getCurrentUserProfile diff --git a/src/sagas/__tests__/loadUserProfile.tests.js b/src/sagas/__tests__/loadUserProfile.tests.js index 6f8e9df..f51ce0e 100644 --- a/src/sagas/__tests__/loadUserProfile.tests.js +++ b/src/sagas/__tests__/loadUserProfile.tests.js @@ -4,20 +4,21 @@ import { hydrateUserProfile } from 'actions' import { USER_LOGGED_IN } from 'actions/types' import loadUserProfile from '../loadUserProfile' +jest.mock('firebase/getCurrentUserProfile') + const mockId = 'testUserId' const mockAction = { type: USER_LOGGED_IN, id: mockId } const mockData = { displayName: 'test_user', photoURL: null } -const mockGetProfile = () => mockData describe('loadUserProfile saga', () => { - const gen = loadUserProfile(mockGetProfile) + const gen = loadUserProfile() it('should wait for USER_LOGGED_IN', () => { expect(gen.next().value).toEqual(take(USER_LOGGED_IN)) }) it('should call function to get user profile', () => { - expect(gen.next(mockAction).value).toEqual(call(mockGetProfile)) + expect(gen.next(mockAction).value).toHaveProperty('CALL.fn') }) it('should hydrate the redux store with profile', () => { diff --git a/src/sagas/index.js b/src/sagas/index.js index 4e82037..11addc0 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -14,7 +14,6 @@ import switchSessions from './switchSessions' // Firebase DI import getCurrentUserData from 'firebase/getCurrentUserData' import getCurrentUserPreferences from 'firebase/getCurrentUserPreferences' -import getCurrentUserProfile from 'firebase/getCurrentUserProfile' import createMessagesSubscription from 'firebase/messages' import saveCurrentUserPreferences from 'firebase/savePreferences' import getCurrentUserEmail from 'firebase/getCurrentUserEmail' @@ -24,8 +23,8 @@ import sendMessage from 'firebase/sendMessage' export default function* rootSaga(): Generator<*, *, *> { yield fork(loadCurrentSession) yield fork(loadSessionMeta) + yield fork(loadUserProfile) yield fork(loadUserSessions, getCurrentUserData) - yield fork(loadUserProfile, getCurrentUserProfile) yield fork(loginFlow) yield fork(receiveMessages, createMessagesSubscription) yield fork(saveUserProfile, getCurrentUserEmail, saveProfile) diff --git a/src/sagas/loadUserProfile.js b/src/sagas/loadUserProfile.js index bc4ec11..8cd5830 100644 --- a/src/sagas/loadUserProfile.js +++ b/src/sagas/loadUserProfile.js @@ -1,20 +1,18 @@ // @flow +import type { Saga } from 'redux-saga' import { take, call, put } from 'redux-saga/effects' import { hydrateUserProfile } from 'actions' import { USER_LOGGED_IN } from 'actions/types' +import getUserProfile from 'firebase/getCurrentUserProfile' import type { UserProfile } from 'types' import type { Action } from 'actions/types' -type GetUserProfile = () => UserProfile - -export default function* loadUserProfile( - getUserProfile: GetUserProfile -): Generator<*, *, *> { +export default function* loadUserProfile(): Saga { // Wait for user auth to complete while (true) { - const action = ((yield take(USER_LOGGED_IN): any): Action) + const action: Action = yield take(USER_LOGGED_IN) if (action.type === USER_LOGGED_IN) { - const user = ((yield call(getUserProfile): any): UserProfile) + const user: UserProfile = yield call(getUserProfile) yield put(hydrateUserProfile(action.id, user)) } } From e2b708dc84d63afb8ee89a1957a16576efa70662 Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Wed, 31 Jan 2018 16:40:21 -0500 Subject: [PATCH 05/16] Update loadUserSessions saga. --- integration_tests/setupSagas.js | 1 + src/firebase/__mocks__/getCurrentUserData.js | 12 ++++++++++++ src/sagas/__tests__/loadUserSessions.tests.js | 13 ++++++------- src/sagas/index.js | 3 +-- src/sagas/loadUserSessions.js | 14 +++++--------- 5 files changed, 25 insertions(+), 18 deletions(-) create mode 100644 src/firebase/__mocks__/getCurrentUserData.js diff --git a/integration_tests/setupSagas.js b/integration_tests/setupSagas.js index c57d20d..af02419 100644 --- a/integration_tests/setupSagas.js +++ b/integration_tests/setupSagas.js @@ -16,6 +16,7 @@ import switchSessions from '../src/sagas/switchSessions' jest.mock('firebase/session') jest.mock('firebase/getSessionMeta') jest.mock('firebase/getCurrentUserProfile') +jest.mock('firebase/getCurrentUserData') jest.mock('firebase/login') jest.mock('firebase/logout') diff --git a/src/firebase/__mocks__/getCurrentUserData.js b/src/firebase/__mocks__/getCurrentUserData.js new file mode 100644 index 0000000..6f19fdf --- /dev/null +++ b/src/firebase/__mocks__/getCurrentUserData.js @@ -0,0 +1,12 @@ +// @flow +import type { UserDataState } from 'reducers/user/data' + +const mockUserData: UserDataState = { + sessions: [{ id: 'globalSession1' }, { id: 'globalSession2' }] +} + +export default function getCurrentUserData(): Promise { + return new Promise((resolve, reject) => { + resolve(mockUserData) + }) +} diff --git a/src/sagas/__tests__/loadUserSessions.tests.js b/src/sagas/__tests__/loadUserSessions.tests.js index 624a424..b41029f 100644 --- a/src/sagas/__tests__/loadUserSessions.tests.js +++ b/src/sagas/__tests__/loadUserSessions.tests.js @@ -5,16 +5,17 @@ import { hydrateUserData } from 'actions' import { USER_LOGGED_IN } from 'actions/types' import type { UserDataState } from 'reducers/user/data' +jest.mock('firebase/getCurrentUserData') + const mockUserData: UserDataState = { sessions: [{ id: 'globalSession1' }, { id: 'globalSession2' }] } -const mockGetUserData = () => new Promise(resolve => resolve(mockUserData)) describe('load sessions generator', () => { - const gen = loadSessions(mockGetUserData) + const gen = loadSessions() it('should call function to get user data', () => { - expect(gen.next().value).toEqual(call(mockGetUserData)) + expect(gen.next().value).toHaveProperty('CALL.fn') }) it('should hydrate the store with user data', () => { @@ -25,11 +26,9 @@ describe('load sessions generator', () => { }) describe('load sessions saga', () => { - const gen = loadSessionsWatcher(mockGetUserData) + const gen = loadSessionsWatcher() it('should wait for USER_LOGGED_IN action', () => { - expect(gen.next().value).toEqual( - takeEvery(USER_LOGGED_IN, loadSessions, mockGetUserData) - ) + expect(gen.next().value).toEqual(takeEvery(USER_LOGGED_IN, loadSessions)) }) }) diff --git a/src/sagas/index.js b/src/sagas/index.js index 11addc0..00e40a7 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -12,7 +12,6 @@ import saveUserProfile from './saveUserProfile' import sendMessages from './sendMessages' import switchSessions from './switchSessions' // Firebase DI -import getCurrentUserData from 'firebase/getCurrentUserData' import getCurrentUserPreferences from 'firebase/getCurrentUserPreferences' import createMessagesSubscription from 'firebase/messages' import saveCurrentUserPreferences from 'firebase/savePreferences' @@ -24,7 +23,7 @@ export default function* rootSaga(): Generator<*, *, *> { yield fork(loadCurrentSession) yield fork(loadSessionMeta) yield fork(loadUserProfile) - yield fork(loadUserSessions, getCurrentUserData) + yield fork(loadUserSessions) yield fork(loginFlow) yield fork(receiveMessages, createMessagesSubscription) yield fork(saveUserProfile, getCurrentUserEmail, saveProfile) diff --git a/src/sagas/loadUserSessions.js b/src/sagas/loadUserSessions.js index bf05d0f..3e02519 100644 --- a/src/sagas/loadUserSessions.js +++ b/src/sagas/loadUserSessions.js @@ -1,14 +1,12 @@ // @flow +import type { Saga } from 'redux-saga' import { call, put, takeEvery } from 'redux-saga/effects' import { hydrateUserData } from 'actions' import { USER_LOGGED_IN } from 'actions/types' import type { UserDataState } from 'reducers/user/data' +import getCurrentUserData from 'firebase/getCurrentUserData' -type GetUserDataFunction = () => Promise - -export function* loadSessions( - getCurrentUserData: GetUserDataFunction -): Generator<*, *, *> { +export function* loadSessions(): Saga { const data: ?UserDataState = yield call(getCurrentUserData) if (data) { @@ -16,9 +14,7 @@ export function* loadSessions( } } -export default function* loadSessionWatcher( - getCurrentUserData: GetUserDataFunction -): Generator<*, *, *> { +export default function* loadSessionWatcher(): Saga { // Wait for user auth to complete - yield takeEvery(USER_LOGGED_IN, loadSessions, getCurrentUserData) + yield takeEvery(USER_LOGGED_IN, loadSessions) } From 48110832c7e8c7ad0e9a50bdfcc681a963bf6669 Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Wed, 31 Jan 2018 16:51:56 -0500 Subject: [PATCH 06/16] Update receiveMessages saga. --- integration_tests/setupSagas.js | 1 + src/firebase/__mocks__/messages.js | 2 +- src/firebase/messages.js | 6 +----- src/sagas/__tests__/receiveMessages.tests.js | 18 ++++++++++++------ src/sagas/index.js | 3 +-- src/sagas/receiveMessages.js | 16 +++++++--------- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/integration_tests/setupSagas.js b/integration_tests/setupSagas.js index af02419..e27f608 100644 --- a/integration_tests/setupSagas.js +++ b/integration_tests/setupSagas.js @@ -17,6 +17,7 @@ jest.mock('firebase/session') jest.mock('firebase/getSessionMeta') jest.mock('firebase/getCurrentUserProfile') jest.mock('firebase/getCurrentUserData') +jest.mock('firebase/messages') jest.mock('firebase/login') jest.mock('firebase/logout') diff --git a/src/firebase/__mocks__/messages.js b/src/firebase/__mocks__/messages.js index f8dc287..a11fdfe 100644 --- a/src/firebase/__mocks__/messages.js +++ b/src/firebase/__mocks__/messages.js @@ -1,7 +1,7 @@ // @flow import type { MessagesSubscription } from '../types' -export const mockMessage = { +const mockMessage = { id: 'test', from: 'testFrom', text: 'test message text', diff --git a/src/firebase/messages.js b/src/firebase/messages.js index 7126a3c..0451a6e 100644 --- a/src/firebase/messages.js +++ b/src/firebase/messages.js @@ -3,7 +3,7 @@ import firebase from '@firebase/app' import '@firebase/firestore' import type { MessagesSubscription } from './types' -class Messages implements MessagesSubscription { +export default class Messages implements MessagesSubscription { query: Object unsubscribe: ?Function @@ -42,7 +42,3 @@ class Messages implements MessagesSubscription { } } } - -const createSubscription = () => new Messages() - -export default createSubscription diff --git a/src/sagas/__tests__/receiveMessages.tests.js b/src/sagas/__tests__/receiveMessages.tests.js index be3b7a5..5e81182 100644 --- a/src/sagas/__tests__/receiveMessages.tests.js +++ b/src/sagas/__tests__/receiveMessages.tests.js @@ -3,14 +3,20 @@ import { put, take } from 'redux-saga/effects' import { createMockTask, cloneableGenerator } from 'redux-saga/utils' import { receiveMessage } from 'actions' import { USER_LOGGED_IN, USER_LOGGED_OUT } from 'actions/types' -import createMessagesSubscription, { - mockMessage -} from '../../firebase/__mocks__/messages' import receiveMessages, { subscribeToMessages } from '../receiveMessages' +jest.mock('firebase/messages') + +const mockMessage = { + id: 'test', + from: 'testFrom', + text: 'test message text', + result: null, + timestamp: 0 +} + describe('subscribeToMessages generator', () => { - const mockSubscription = createMessagesSubscription() - const gen = subscribeToMessages(mockSubscription) + const gen = subscribeToMessages() it('should wait for a message to be received', () => { expect(gen.next().value).toHaveProperty('TAKE') @@ -28,7 +34,7 @@ describe('subscribeToMessages generator', () => { }) describe('receiveMessages saga', () => { - const gen = cloneableGenerator(receiveMessages)(createMessagesSubscription) + const gen = cloneableGenerator(receiveMessages)() const loginAction = { type: USER_LOGGED_IN } const logoutAction = { type: USER_LOGGED_OUT } diff --git a/src/sagas/index.js b/src/sagas/index.js index 00e40a7..d2205ef 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -13,7 +13,6 @@ import sendMessages from './sendMessages' import switchSessions from './switchSessions' // Firebase DI import getCurrentUserPreferences from 'firebase/getCurrentUserPreferences' -import createMessagesSubscription from 'firebase/messages' import saveCurrentUserPreferences from 'firebase/savePreferences' import getCurrentUserEmail from 'firebase/getCurrentUserEmail' import saveProfile from 'firebase/saveProfile' @@ -25,7 +24,7 @@ export default function* rootSaga(): Generator<*, *, *> { yield fork(loadUserProfile) yield fork(loadUserSessions) yield fork(loginFlow) - yield fork(receiveMessages, createMessagesSubscription) + yield fork(receiveMessages) yield fork(saveUserProfile, getCurrentUserEmail, saveProfile) yield fork(sendMessages, sendMessage) yield fork(switchSessions) diff --git a/src/sagas/receiveMessages.js b/src/sagas/receiveMessages.js index e1a2ed1..18d5938 100644 --- a/src/sagas/receiveMessages.js +++ b/src/sagas/receiveMessages.js @@ -1,13 +1,14 @@ // @flow +import type { Saga } from 'redux-saga' import { eventChannel } from 'redux-saga' import { put, take, fork, cancel, cancelled } from 'redux-saga/effects' import { receiveMessage } from 'actions' import { USER_LOGGED_IN, USER_LOGGED_OUT } from 'actions/types' -import type { MessagesSubscription } from 'firebase/types' +import Messages from 'firebase/messages' + +export function* subscribeToMessages(): Saga { + const subscription = new Messages() -export function* subscribeToMessages( - subscription: MessagesSubscription -): Generator<*, *, *> { const listener = eventChannel(emit => { // Subscribe to message data subscription.onMessageData(emit) @@ -27,9 +28,7 @@ export function* subscribeToMessages( } } -export default function* receiveMessages( - createMessagesSubscription: () => MessagesSubscription -): Generator<*, *, *> { +export default function* receiveMessages(): Saga { let currentSubscription = null while (true) { @@ -38,8 +37,7 @@ export default function* receiveMessages( if (action.type === USER_LOGGED_IN) { // If user is logged in, create a subscription to messages - const subscription = createMessagesSubscription() - currentSubscription = yield fork(subscribeToMessages, subscription) + currentSubscription = yield fork(subscribeToMessages) } else if (action.type === USER_LOGGED_OUT) { // If user is logged out, cancel the subscription if (currentSubscription) { From 909f2a049f965b134c1623f13653b619eb2fd6ee Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Wed, 31 Jan 2018 16:58:51 -0500 Subject: [PATCH 07/16] Update saveUserProfile saga. --- integration_tests/setupSagas.js | 2 ++ src/firebase/__mocks__/getCurrentUserEmail.js | 6 ++++++ src/sagas/__tests__/saveUserProfile.tests.js | 19 +++++++------------ src/sagas/index.js | 4 +--- src/sagas/saveUserProfile.js | 12 +++++++----- 5 files changed, 23 insertions(+), 20 deletions(-) create mode 100644 src/firebase/__mocks__/getCurrentUserEmail.js diff --git a/integration_tests/setupSagas.js b/integration_tests/setupSagas.js index e27f608..7c1784f 100644 --- a/integration_tests/setupSagas.js +++ b/integration_tests/setupSagas.js @@ -18,6 +18,8 @@ jest.mock('firebase/getSessionMeta') jest.mock('firebase/getCurrentUserProfile') jest.mock('firebase/getCurrentUserData') jest.mock('firebase/messages') +jest.mock('firebase/getCurrentUserEmail') +jest.mock('firebase/saveProfile') jest.mock('firebase/login') jest.mock('firebase/logout') diff --git a/src/firebase/__mocks__/getCurrentUserEmail.js b/src/firebase/__mocks__/getCurrentUserEmail.js new file mode 100644 index 0000000..5234fa7 --- /dev/null +++ b/src/firebase/__mocks__/getCurrentUserEmail.js @@ -0,0 +1,6 @@ +// @flow +const mockEmail = 'email@example.com' + +const getCurrentUserEmail = (): string => mockEmail + +export default getCurrentUserEmail diff --git a/src/sagas/__tests__/saveUserProfile.tests.js b/src/sagas/__tests__/saveUserProfile.tests.js index 1a73c18..ca9d5f8 100644 --- a/src/sagas/__tests__/saveUserProfile.tests.js +++ b/src/sagas/__tests__/saveUserProfile.tests.js @@ -1,19 +1,18 @@ // @flow +import type { Saga } from 'redux-saga' import { take, put, call } from 'redux-saga/effects' import { cloneableGenerator } from 'redux-saga/utils' import { changeDisplayName } from 'actions' import { CHANGE_DISPLAY_NAME } from 'actions/types' import saveUserProfile from '../saveUserProfile' +jest.mock('firebase/getCurrentUserEmail') +jest.mock('firebase/saveProfile') + describe('saveUserProfile saga', () => { const mockId = 'testUserId' const mockEmail = 'email@example.com' - const mockGetUserEmail = () => mockEmail - const mockSaveProfile = () => {} - const gen = cloneableGenerator(saveUserProfile)( - mockGetUserEmail, - mockSaveProfile - ) + const gen = cloneableGenerator(saveUserProfile)() const emptyNameAction = { type: CHANGE_DISPLAY_NAME, id: mockId, name: '' } const emptyNameProfile = { @@ -39,9 +38,7 @@ describe('saveUserProfile saga', () => { }) it('should get the users email if no name is present', () => { - expect(withEmptyName.next(emptyNameProfile).value).toEqual( - call(mockGetUserEmail) - ) + expect(withEmptyName.next(emptyNameProfile).value).toHaveProperty('CALL.fn') }) it('should put the users email in place of the name', () => { @@ -54,8 +51,6 @@ describe('saveUserProfile saga', () => { // Take, select withUserName.next() withUserName.next(filledNameAction) - expect(withUserName.next(filledNameProfile).value).toEqual( - call(mockSaveProfile, filledNameProfile) - ) + expect(withUserName.next(filledNameProfile).value).toHaveProperty('CALL.fn') }) }) diff --git a/src/sagas/index.js b/src/sagas/index.js index d2205ef..e9fb8d1 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -14,8 +14,6 @@ import switchSessions from './switchSessions' // Firebase DI import getCurrentUserPreferences from 'firebase/getCurrentUserPreferences' import saveCurrentUserPreferences from 'firebase/savePreferences' -import getCurrentUserEmail from 'firebase/getCurrentUserEmail' -import saveProfile from 'firebase/saveProfile' import sendMessage from 'firebase/sendMessage' export default function* rootSaga(): Generator<*, *, *> { @@ -25,7 +23,7 @@ export default function* rootSaga(): Generator<*, *, *> { yield fork(loadUserSessions) yield fork(loginFlow) yield fork(receiveMessages) - yield fork(saveUserProfile, getCurrentUserEmail, saveProfile) + yield fork(saveUserProfile) yield fork(sendMessages, sendMessage) yield fork(switchSessions) // Preferences diff --git a/src/sagas/saveUserProfile.js b/src/sagas/saveUserProfile.js index d79b0da..a993312 100644 --- a/src/sagas/saveUserProfile.js +++ b/src/sagas/saveUserProfile.js @@ -1,12 +1,13 @@ // @flow +import type { Saga } from 'redux-saga' import { take, put, call, select } from 'redux-saga/effects' import { changeDisplayName } from 'actions' import { CHANGE_DISPLAY_NAME } from 'actions/types' +import getCurrentUserEmail from 'firebase/getCurrentUserEmail' +import saveProfile from 'firebase/saveProfile' +import type { UserProfile } from 'types' -export default function* saveUserProfile( - getCurrentUserEmail: () => string, - saveProfile: Function -): Generator<*, *, *> { +export default function* saveUserProfile(): Saga { while (true) { const action = yield take(CHANGE_DISPLAY_NAME) const profile = yield select(state => state.user.profile) @@ -14,7 +15,8 @@ export default function* saveUserProfile( const email = yield call(getCurrentUserEmail) yield put(changeDisplayName(action.id, email)) } else { - const profileToSave = { + const profileToSave: UserProfile = { + ...profile, displayName: profile.displayName } yield call(saveProfile, profileToSave) From ef396b1f11804d5aeb170cce348aef504681da09 Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Wed, 31 Jan 2018 17:00:29 -0500 Subject: [PATCH 08/16] Update switchSessions saga. --- src/sagas/switchSessions.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sagas/switchSessions.js b/src/sagas/switchSessions.js index ab94d2b..f79abe8 100644 --- a/src/sagas/switchSessions.js +++ b/src/sagas/switchSessions.js @@ -1,4 +1,5 @@ // @flow +import type { Saga } from 'redux-saga' import slug from 'slugg' import { put, select, takeEvery } from 'redux-saga/effects' import sessionIdSelector from 'selectors/sessionId' @@ -7,7 +8,7 @@ import { changeSidebarTab } from 'actions' import { SWITCH_TO_SESSION } from 'actions/types' import type { Action } from 'actions/types' -export function* switchToSession(action: Action): Generator<*, *, *> { +export function* switchToSession(action: Action): Saga { if (action.type !== SWITCH_TO_SESSION) { return } @@ -32,6 +33,6 @@ export function* switchToSession(action: Action): Generator<*, *, *> { } } -export default function* switchSessions(): Generator<*, *, *> { +export default function* switchSessions(): Saga { yield takeEvery(SWITCH_TO_SESSION, switchToSession) } From f6d0c16ad9ed49a3985f9e560dfca05ee1e01d70 Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Wed, 31 Jan 2018 17:10:17 -0500 Subject: [PATCH 09/16] Update preferences sagas. --- integration_tests/setupSagas.js | 2 ++ src/firebase/__mocks__/getCurrentUserPreferences.js | 9 +++++++++ src/firebase/__mocks__/savePreferences.js | 6 ++++++ .../preferences/__tests__/loadPreferences.tests.js | 8 +++++--- .../preferences/__tests__/savePreferences.tests.js | 10 +++++++--- src/sagas/preferences/loadPreferences.js | 9 +++------ src/sagas/preferences/savePreferences.js | 7 +++---- 7 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 src/firebase/__mocks__/getCurrentUserPreferences.js create mode 100644 src/firebase/__mocks__/savePreferences.js diff --git a/integration_tests/setupSagas.js b/integration_tests/setupSagas.js index 7c1784f..a3d229c 100644 --- a/integration_tests/setupSagas.js +++ b/integration_tests/setupSagas.js @@ -15,10 +15,12 @@ import switchSessions from '../src/sagas/switchSessions' // Mock implementations jest.mock('firebase/session') jest.mock('firebase/getSessionMeta') +jest.mock('firebase/getCurrentUserPreferences') jest.mock('firebase/getCurrentUserProfile') jest.mock('firebase/getCurrentUserData') jest.mock('firebase/messages') jest.mock('firebase/getCurrentUserEmail') +jest.mock('firebase/savePreferences') jest.mock('firebase/saveProfile') jest.mock('firebase/login') jest.mock('firebase/logout') diff --git a/src/firebase/__mocks__/getCurrentUserPreferences.js b/src/firebase/__mocks__/getCurrentUserPreferences.js new file mode 100644 index 0000000..c4dcbd2 --- /dev/null +++ b/src/firebase/__mocks__/getCurrentUserPreferences.js @@ -0,0 +1,9 @@ +// @flow +import type { PreferencesState } from 'reducers/preferences' + +const mockData = { theme: 'light', chatPinned: true } + +const getUserPreferences = (): Promise => + new Promise(resolve => resolve(mockData)) + +export default getUserPreferences diff --git a/src/firebase/__mocks__/savePreferences.js b/src/firebase/__mocks__/savePreferences.js new file mode 100644 index 0000000..7da9eb5 --- /dev/null +++ b/src/firebase/__mocks__/savePreferences.js @@ -0,0 +1,6 @@ +// @flow +import type { PreferencesState } from 'reducers/preferences' + +const savePreferences = (preferences: PreferencesState) => {} + +export default savePreferences diff --git a/src/sagas/preferences/__tests__/loadPreferences.tests.js b/src/sagas/preferences/__tests__/loadPreferences.tests.js index a249bf8..bd1c4e3 100644 --- a/src/sagas/preferences/__tests__/loadPreferences.tests.js +++ b/src/sagas/preferences/__tests__/loadPreferences.tests.js @@ -3,19 +3,21 @@ import { take, call, put } from 'redux-saga/effects' import { hydratePreferences } from 'actions' import { USER_LOGGED_IN, APP_FINISHED_LOADING } from 'actions/types' import loadPreferences from '../loadPreferences' +import getPreferences from 'firebase/getCurrentUserPreferences' + +jest.mock('firebase/getCurrentUserPreferences') const mockData = { theme: 'light', chatPinned: true } -const mockGetPreferences = () => new Promise(resolve => resolve(mockData)) describe('loadPreferences saga', () => { - const gen = loadPreferences(mockGetPreferences) + const gen = loadPreferences() it('should wait for USER_LOGGED_IN', () => { expect(gen.next().value).toEqual(take(USER_LOGGED_IN)) }) it('should call function to get user preferences', () => { - expect(gen.next().value).toEqual(call(mockGetPreferences)) + expect(gen.next().value).toEqual(call(getPreferences)) }) it('should hydrate the redux store with preferences', () => { diff --git a/src/sagas/preferences/__tests__/savePreferences.tests.js b/src/sagas/preferences/__tests__/savePreferences.tests.js index 8b361ba..d96d459 100644 --- a/src/sagas/preferences/__tests__/savePreferences.tests.js +++ b/src/sagas/preferences/__tests__/savePreferences.tests.js @@ -2,10 +2,14 @@ import { take, call } from 'redux-saga/effects' import { CHANGE_THEME, TOGGLE_CHAT_PIN } from 'actions/types' import savePreferences from '../savePreferences' +import mockSaveFunction from 'firebase/savePreferences' + +jest.mock('firebase/savePreferences') + +const mockData = { theme: 'light', chatPinned: true } describe('savePreferences saga', () => { - const mockSaveFunction = () => {} - const gen = savePreferences(mockSaveFunction) + const gen = savePreferences() it('should wait for preferences to change', () => { expect(gen.next().value).toEqual(take([CHANGE_THEME, TOGGLE_CHAT_PIN])) @@ -16,7 +20,7 @@ describe('savePreferences saga', () => { }) it('should call the function to save preferences', () => { - expect(gen.next().value).toEqual(call(mockSaveFunction, undefined)) + expect(gen.next(mockData).value).toEqual(call(mockSaveFunction, mockData)) }) it('should continue listening', () => { diff --git a/src/sagas/preferences/loadPreferences.js b/src/sagas/preferences/loadPreferences.js index dc4fbdd..c8e75ba 100644 --- a/src/sagas/preferences/loadPreferences.js +++ b/src/sagas/preferences/loadPreferences.js @@ -1,14 +1,11 @@ // @flow +import type { Saga } from 'redux-saga' import { take, call, put } from 'redux-saga/effects' import { hydratePreferences } from 'actions' import { USER_LOGGED_IN, APP_FINISHED_LOADING } from 'actions/types' -import type { PreferencesState } from 'reducers/preferences' +import getPreferences from 'firebase/getCurrentUserPreferences' -type GetPreferencesFunction = () => Promise - -export default function* loadPreferences( - getPreferences: GetPreferencesFunction -): Generator<*, *, *> { +export default function* loadPreferences(): Saga { while (true) { yield take(USER_LOGGED_IN) const prefs = yield call(getPreferences) diff --git a/src/sagas/preferences/savePreferences.js b/src/sagas/preferences/savePreferences.js index 90dcd59..27e7cfc 100644 --- a/src/sagas/preferences/savePreferences.js +++ b/src/sagas/preferences/savePreferences.js @@ -1,11 +1,10 @@ // @flow +import type { Saga } from 'redux-saga' import { take, call, select } from 'redux-saga/effects' import { CHANGE_THEME, TOGGLE_CHAT_PIN } from 'actions/types' -import type { PreferencesState } from 'reducers/preferences' +import savePreferences from 'firebase/savePreferences' -export default function* saveUserPreferences( - savePreferences: (preferences: PreferencesState) => void -): Generator<*, *, *> { +export default function* saveUserPreferences(): Saga { while (true) { yield take([CHANGE_THEME, TOGGLE_CHAT_PIN]) const preferences = yield select(state => state.preferences) From 7ca561c8ec3c8226daa892027e8bfb461ff9d5b9 Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Wed, 31 Jan 2018 17:10:40 -0500 Subject: [PATCH 10/16] Remove unused preferences index file. --- src/sagas/preferences/index.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/sagas/preferences/index.js diff --git a/src/sagas/preferences/index.js b/src/sagas/preferences/index.js deleted file mode 100644 index e69de29..0000000 From 1cbdcba16e99bc657fd474aa03ab16ad29a5091d Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Wed, 31 Jan 2018 17:17:12 -0500 Subject: [PATCH 11/16] Update sendMessage saga. --- integration_tests/setupSagas.js | 1 + src/firebase/__mocks__/sendMessage.js | 16 ++++++++++++++++ src/sagas/index.js | 13 +++++-------- .../sendMessages/__tests__/index.tests.js | 19 +++++++------------ src/sagas/sendMessages/index.js | 16 +++++----------- 5 files changed, 34 insertions(+), 31 deletions(-) create mode 100644 src/firebase/__mocks__/sendMessage.js diff --git a/integration_tests/setupSagas.js b/integration_tests/setupSagas.js index a3d229c..37ba133 100644 --- a/integration_tests/setupSagas.js +++ b/integration_tests/setupSagas.js @@ -22,6 +22,7 @@ jest.mock('firebase/messages') jest.mock('firebase/getCurrentUserEmail') jest.mock('firebase/savePreferences') jest.mock('firebase/saveProfile') +jest.mock('firebase/sendMessage') jest.mock('firebase/login') jest.mock('firebase/logout') diff --git a/src/firebase/__mocks__/sendMessage.js b/src/firebase/__mocks__/sendMessage.js new file mode 100644 index 0000000..2f0b844 --- /dev/null +++ b/src/firebase/__mocks__/sendMessage.js @@ -0,0 +1,16 @@ +// @flow +import type { MessageResult } from 'types' + +type SendMessageOpts = { + from: string, + text: string, + result: ?MessageResult +} + +const sendMessage = (opts: SendMessageOpts): Promise => { + return new Promise((resolve, reject) => { + resolve() + }) +} + +export default sendMessage diff --git a/src/sagas/index.js b/src/sagas/index.js index e9fb8d1..1a7b1f4 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -1,4 +1,5 @@ // @flow +import type { Saga } from 'redux-saga' import { fork } from 'redux-saga/effects' import loadCurrentSession from './loadCurrentSession' import loadSessionMeta from './loadSessionMeta' @@ -11,12 +12,8 @@ import receiveMessages from './receiveMessages' import saveUserProfile from './saveUserProfile' import sendMessages from './sendMessages' import switchSessions from './switchSessions' -// Firebase DI -import getCurrentUserPreferences from 'firebase/getCurrentUserPreferences' -import saveCurrentUserPreferences from 'firebase/savePreferences' -import sendMessage from 'firebase/sendMessage' -export default function* rootSaga(): Generator<*, *, *> { +export default function* rootSaga(): Saga { yield fork(loadCurrentSession) yield fork(loadSessionMeta) yield fork(loadUserProfile) @@ -24,9 +21,9 @@ export default function* rootSaga(): Generator<*, *, *> { yield fork(loginFlow) yield fork(receiveMessages) yield fork(saveUserProfile) - yield fork(sendMessages, sendMessage) + yield fork(sendMessages) yield fork(switchSessions) // Preferences - yield fork(loadPreferences, getCurrentUserPreferences) - yield fork(savePreferences, saveCurrentUserPreferences) + yield fork(loadPreferences) + yield fork(savePreferences) } diff --git a/src/sagas/sendMessages/__tests__/index.tests.js b/src/sagas/sendMessages/__tests__/index.tests.js index b2acf56..ce0f562 100644 --- a/src/sagas/sendMessages/__tests__/index.tests.js +++ b/src/sagas/sendMessages/__tests__/index.tests.js @@ -3,14 +3,16 @@ import { call, takeEvery } from 'redux-saga/effects' import sendMessages, { sendMessageWithResult } from '../index' import CommandParser from '../commandParser' import { SEND_MESSAGE } from 'actions/types' +import sendMessage from 'firebase/sendMessage' + +jest.mock('firebase/sendMessage') -const mockSendMessage = () => {} const commandParser = new CommandParser() describe('sendMessageWithResult generator', () => { const mockAction = { type: 'SEND_MESSAGE', text: 'test' } const mockName = 'user' - const gen = sendMessageWithResult(mockSendMessage, commandParser, mockAction) + const gen = sendMessageWithResult(commandParser, mockAction) it('should select the users display name from the store', () => { expect(gen.next().value).toHaveProperty('SELECT') @@ -22,24 +24,17 @@ describe('sendMessageWithResult generator', () => { from: mockName, result: null } - expect(gen.next(mockName).value).toEqual( - call(mockSendMessage, expectedMessage) - ) + expect(gen.next(mockName).value).toEqual(call(sendMessage, expectedMessage)) }) }) describe('sendMessages saga', () => { - const gen = sendMessages(mockSendMessage) + const gen = sendMessages() it('should run for every SEND_MESSAGE action', () => { expect(JSON.stringify(gen.next().value)).toEqual( JSON.stringify( - takeEvery( - SEND_MESSAGE, - sendMessageWithResult, - mockSendMessage, - commandParser - ) + takeEvery(SEND_MESSAGE, sendMessageWithResult, commandParser) ) ) }) diff --git a/src/sagas/sendMessages/index.js b/src/sagas/sendMessages/index.js index 80f40dc..73ac440 100644 --- a/src/sagas/sendMessages/index.js +++ b/src/sagas/sendMessages/index.js @@ -1,14 +1,15 @@ // @flow +import type { Saga } from 'redux-saga' import { call, select, takeEvery } from 'redux-saga/effects' import CommandParser from './commandParser' import { SEND_MESSAGE } from 'actions/types' import type { Action } from 'actions/types' +import sendMessage from 'firebase/sendMessage' export function* sendMessageWithResult( - sendMessage: Function, commandParser: CommandParser, action: Action -): Generator<*, *, *> { +): Saga { if (action.type !== SEND_MESSAGE) { return } @@ -22,14 +23,7 @@ export function* sendMessageWithResult( yield call(sendMessage, messageOptions) } -export default function* sendMessages( - sendMessage: Function -): Generator<*, *, *> { +export default function* sendMessages(): Saga { const commandParser = new CommandParser() - yield takeEvery( - SEND_MESSAGE, - sendMessageWithResult, - sendMessage, - commandParser - ) + yield takeEvery(SEND_MESSAGE, sendMessageWithResult, commandParser) } From 7364146eea61b21cc6db209a8f9b8a2dc8b7d7e6 Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Wed, 31 Jan 2018 17:18:05 -0500 Subject: [PATCH 12/16] Add missing mock. --- src/firebase/__mocks__/saveProfile.js | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/firebase/__mocks__/saveProfile.js diff --git a/src/firebase/__mocks__/saveProfile.js b/src/firebase/__mocks__/saveProfile.js new file mode 100644 index 0000000..e72d5c1 --- /dev/null +++ b/src/firebase/__mocks__/saveProfile.js @@ -0,0 +1,6 @@ +// @flow +import type { UserProfile } from 'types' + +const saveProfile = (profile: UserProfile): void => {} + +export default saveProfile From 06f869a5d9daa65fe19f94d61934203718dec706 Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Wed, 31 Jan 2018 17:24:47 -0500 Subject: [PATCH 13/16] Remove CALL.fn property lookup in tests. --- src/sagas/__tests__/loadSessionMeta.tests.js | 3 ++- src/sagas/__tests__/loadUserProfile.tests.js | 3 ++- src/sagas/__tests__/loadUserSessions.tests.js | 3 ++- src/sagas/__tests__/loginFlow.tests.js | 8 +++++--- src/sagas/__tests__/saveUserProfile.tests.js | 10 ++++++++-- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/sagas/__tests__/loadSessionMeta.tests.js b/src/sagas/__tests__/loadSessionMeta.tests.js index 3e69f96..8f51398 100644 --- a/src/sagas/__tests__/loadSessionMeta.tests.js +++ b/src/sagas/__tests__/loadSessionMeta.tests.js @@ -3,6 +3,7 @@ import { call, put, takeLatest } from 'redux-saga/effects' import { hydrateSessionMeta } from 'actions' import { HYDRATE_USER_DATA } from 'actions/types' import loadMeta, { loadAllMeta, loadSessionMeta } from '../loadSessionMeta' +import getSessionMeta from 'firebase/getSessionMeta' jest.mock('firebase/getSessionMeta') @@ -12,7 +13,7 @@ describe('loadSessionMeta generator', () => { const gen = loadSessionMeta('sessionId') it('should call getSessionMeta', () => { - expect(gen.next().value).toHaveProperty('CALL.fn') + expect(gen.next().value).toEqual(call(getSessionMeta, 'sessionId')) }) it('should hydrate the store with the meta', () => { diff --git a/src/sagas/__tests__/loadUserProfile.tests.js b/src/sagas/__tests__/loadUserProfile.tests.js index f51ce0e..9a2de26 100644 --- a/src/sagas/__tests__/loadUserProfile.tests.js +++ b/src/sagas/__tests__/loadUserProfile.tests.js @@ -3,6 +3,7 @@ import { take, call, put } from 'redux-saga/effects' import { hydrateUserProfile } from 'actions' import { USER_LOGGED_IN } from 'actions/types' import loadUserProfile from '../loadUserProfile' +import getUserProfile from 'firebase/getCurrentUserProfile' jest.mock('firebase/getCurrentUserProfile') @@ -18,7 +19,7 @@ describe('loadUserProfile saga', () => { }) it('should call function to get user profile', () => { - expect(gen.next(mockAction).value).toHaveProperty('CALL.fn') + expect(gen.next(mockAction).value).toEqual(call(getUserProfile)) }) it('should hydrate the redux store with profile', () => { diff --git a/src/sagas/__tests__/loadUserSessions.tests.js b/src/sagas/__tests__/loadUserSessions.tests.js index b41029f..c52198f 100644 --- a/src/sagas/__tests__/loadUserSessions.tests.js +++ b/src/sagas/__tests__/loadUserSessions.tests.js @@ -3,6 +3,7 @@ import { call, put, takeEvery } from 'redux-saga/effects' import loadSessionsWatcher, { loadSessions } from '../loadUserSessions' import { hydrateUserData } from 'actions' import { USER_LOGGED_IN } from 'actions/types' +import getCurrentUserData from 'firebase/getCurrentUserData' import type { UserDataState } from 'reducers/user/data' jest.mock('firebase/getCurrentUserData') @@ -15,7 +16,7 @@ describe('load sessions generator', () => { const gen = loadSessions() it('should call function to get user data', () => { - expect(gen.next().value).toHaveProperty('CALL.fn') + expect(gen.next().value).toEqual(call(getCurrentUserData)) }) it('should hydrate the store with user data', () => { diff --git a/src/sagas/__tests__/loginFlow.tests.js b/src/sagas/__tests__/loginFlow.tests.js index 781230b..7204c18 100644 --- a/src/sagas/__tests__/loginFlow.tests.js +++ b/src/sagas/__tests__/loginFlow.tests.js @@ -1,5 +1,5 @@ // @flow -import { take, put } from 'redux-saga/effects' +import { take, put, call } from 'redux-saga/effects' import { performUserLogin } from 'actions' import { APP_FINISHED_LOADING, @@ -9,6 +9,8 @@ import { USER_LOGGED_OUT } from 'actions/types' import loginFlow from '../loginFlow' +import login from 'firebase/login' +import logout from 'firebase/logout' jest.mock('firebase/login') jest.mock('firebase/logout') @@ -34,7 +36,7 @@ describe('login saga', () => { const loginAction = performUserLogin('test@example.com', 'password') it('should perform login', () => { - expect(gen.next(loginAction).value).toHaveProperty('CALL.fn') + expect(gen.next(loginAction).value).toEqual(call(login, loginAction)) }) it('should ignore additional login instructions', () => { @@ -50,7 +52,7 @@ describe('login saga', () => { const logoutAction = { type: PERFORM_USER_LOGOUT } it('should perform logout', () => { - expect(gen.next(logoutAction).value).toHaveProperty('CALL.fn') + expect(gen.next(logoutAction).value).toEqual(call(logout)) }) it('should report user logged out', () => { diff --git a/src/sagas/__tests__/saveUserProfile.tests.js b/src/sagas/__tests__/saveUserProfile.tests.js index ca9d5f8..5214b3a 100644 --- a/src/sagas/__tests__/saveUserProfile.tests.js +++ b/src/sagas/__tests__/saveUserProfile.tests.js @@ -5,6 +5,8 @@ import { cloneableGenerator } from 'redux-saga/utils' import { changeDisplayName } from 'actions' import { CHANGE_DISPLAY_NAME } from 'actions/types' import saveUserProfile from '../saveUserProfile' +import getCurrentUserEmail from 'firebase/getCurrentUserEmail' +import saveProfile from 'firebase/saveProfile' jest.mock('firebase/getCurrentUserEmail') jest.mock('firebase/saveProfile') @@ -38,7 +40,9 @@ describe('saveUserProfile saga', () => { }) it('should get the users email if no name is present', () => { - expect(withEmptyName.next(emptyNameProfile).value).toHaveProperty('CALL.fn') + expect(withEmptyName.next(emptyNameProfile).value).toEqual( + call(getCurrentUserEmail) + ) }) it('should put the users email in place of the name', () => { @@ -51,6 +55,8 @@ describe('saveUserProfile saga', () => { // Take, select withUserName.next() withUserName.next(filledNameAction) - expect(withUserName.next(filledNameProfile).value).toHaveProperty('CALL.fn') + expect(withUserName.next(filledNameProfile).value).toEqual( + call(saveProfile, filledNameProfile) + ) }) }) From d96c90a7f44a997c08addb8d51b3605994d71b39 Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Wed, 31 Jan 2018 17:34:34 -0500 Subject: [PATCH 14/16] Fix flow errors. --- src/firebase/login.js | 12 ++++++------ src/sagas/__tests__/saveUserProfile.tests.js | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/firebase/login.js b/src/firebase/login.js index c5a89ee..09882e3 100644 --- a/src/firebase/login.js +++ b/src/firebase/login.js @@ -1,14 +1,14 @@ // @flow import firebase from '@firebase/app' import '@firebase/auth' +import { PERFORM_USER_LOGIN } from 'actions/types' +import type { Action } from 'actions/types' -type LoginAction = { - type: 'PERFORM_USER_LOGIN', - email: string, - password: string -} +const loginWithEmailAndPassword = (action: Action) => { + if (action.type !== PERFORM_USER_LOGIN) { + return + } -const loginWithEmailAndPassword = (action: LoginAction) => { firebase .auth() .signInWithEmailAndPassword(action.email, action.password) diff --git a/src/sagas/__tests__/saveUserProfile.tests.js b/src/sagas/__tests__/saveUserProfile.tests.js index 5214b3a..f67a5c7 100644 --- a/src/sagas/__tests__/saveUserProfile.tests.js +++ b/src/sagas/__tests__/saveUserProfile.tests.js @@ -26,7 +26,8 @@ describe('saveUserProfile saga', () => { name: 'test' } const filledNameProfile = { - displayName: 'test' + displayName: 'test', + photoURL: null } const withEmptyName = gen.clone() const withUserName = gen.clone() From 45bc7daa9121e2f8fde8bba1a58a27693734fe2b Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Wed, 31 Jan 2018 17:51:43 -0500 Subject: [PATCH 15/16] Update current user logic in sendMessage saga. --- .flowconfig | 1 + src/sagas/sendMessages/__tests__/index.tests.js | 10 ++++++---- src/sagas/sendMessages/index.js | 4 +++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.flowconfig b/.flowconfig index 3e8e900..95c9602 100644 --- a/.flowconfig +++ b/.flowconfig @@ -10,6 +10,7 @@ module.name_mapper='^components/\(.*\)$' -> '/src/components/\1' module.name_mapper='^containers/\(.*\)$' -> '/src/containers/\1' module.name_mapper='^firebase/\(.*\)$' -> '/src/firebase/\1' module.name_mapper='^hoc/\(.*\)$' -> '/src/hoc/\1' +module.name_mapper='^models/\(.*\)$' -> '/src/models/\1' module.name_mapper='^reducers/\(.*\)$' -> '/src/reducers/\1' module.name_mapper='^sagas/\(.*\)$' -> '/src/sagas/\1' module.name_mapper='^selectors/\(.*\)$' -> '/src/selectors/\1' diff --git a/src/sagas/sendMessages/__tests__/index.tests.js b/src/sagas/sendMessages/__tests__/index.tests.js index ce0f562..be4191b 100644 --- a/src/sagas/sendMessages/__tests__/index.tests.js +++ b/src/sagas/sendMessages/__tests__/index.tests.js @@ -11,20 +11,22 @@ const commandParser = new CommandParser() describe('sendMessageWithResult generator', () => { const mockAction = { type: 'SEND_MESSAGE', text: 'test' } - const mockName = 'user' + const mockUser = { + displayName: 'user' + } const gen = sendMessageWithResult(commandParser, mockAction) - it('should select the users display name from the store', () => { + it('should select the current user from the store', () => { expect(gen.next().value).toHaveProperty('SELECT') }) it('should call the sendMessage function', () => { const expectedMessage = { text: mockAction.text, - from: mockName, + from: mockUser.displayName, result: null } - expect(gen.next(mockName).value).toEqual(call(sendMessage, expectedMessage)) + expect(gen.next(mockUser).value).toEqual(call(sendMessage, expectedMessage)) }) }) diff --git a/src/sagas/sendMessages/index.js b/src/sagas/sendMessages/index.js index 73ac440..1fc0181 100644 --- a/src/sagas/sendMessages/index.js +++ b/src/sagas/sendMessages/index.js @@ -4,6 +4,7 @@ import { call, select, takeEvery } from 'redux-saga/effects' import CommandParser from './commandParser' import { SEND_MESSAGE } from 'actions/types' import type { Action } from 'actions/types' +import currentUser from 'selectors/currentUser' import sendMessage from 'firebase/sendMessage' export function* sendMessageWithResult( @@ -15,7 +16,8 @@ export function* sendMessageWithResult( } const { text } = action - const from = yield select(state => state.user.profile.displayName) + const user = yield select(currentUser) + const from = user.displayName const result = commandParser.getMessageResult(text) const messageOptions = { text, from, result } From c2e82614dc5296d183df0ca9eaefd81325173877 Mon Sep 17 00:00:00 2001 From: Jason Nall Date: Wed, 31 Jan 2018 18:04:37 -0500 Subject: [PATCH 16/16] Fix lint errors. --- .eslintrc | 6 +++++- src/firebase/__mocks__/getCurrentUserData.js | 2 +- src/firebase/__mocks__/getSessionMeta.js | 6 +++--- src/firebase/__mocks__/login.js | 1 + src/firebase/__mocks__/logout.js | 1 + src/firebase/__mocks__/savePreferences.js | 2 +- src/firebase/__mocks__/saveProfile.js | 2 +- src/firebase/__mocks__/sendMessage.js | 4 ++-- src/sagas/__tests__/saveUserProfile.tests.js | 1 - 9 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.eslintrc b/.eslintrc index ed4d9fd..bc14b4e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -13,7 +13,11 @@ ], "rules": { "eqeqeq": 0, - "no-unused-vars": 2, + "no-unused-vars": [ + "error", { + "argsIgnorePattern": "^_" + } + ], "import/no-webpack-loader-syntax": 0, "react/prop-types": 0, "react/display-name": 0, diff --git a/src/firebase/__mocks__/getCurrentUserData.js b/src/firebase/__mocks__/getCurrentUserData.js index 6f19fdf..58d3895 100644 --- a/src/firebase/__mocks__/getCurrentUserData.js +++ b/src/firebase/__mocks__/getCurrentUserData.js @@ -6,7 +6,7 @@ const mockUserData: UserDataState = { } export default function getCurrentUserData(): Promise { - return new Promise((resolve, reject) => { + return new Promise(resolve => { resolve(mockUserData) }) } diff --git a/src/firebase/__mocks__/getSessionMeta.js b/src/firebase/__mocks__/getSessionMeta.js index 98cf781..3cd4078 100644 --- a/src/firebase/__mocks__/getSessionMeta.js +++ b/src/firebase/__mocks__/getSessionMeta.js @@ -1,8 +1,8 @@ // @flow +const mockData = { name: 'test' } -const loadSessionMeta = (sessionId: string): Promise => - new Promise((resolve, reject) => { - const mockData = { name: 'test' } +const loadSessionMeta = (_sessionId: string): Promise => + new Promise(resolve => { resolve(mockData) }) diff --git a/src/firebase/__mocks__/login.js b/src/firebase/__mocks__/login.js index b2981b4..30870c6 100644 --- a/src/firebase/__mocks__/login.js +++ b/src/firebase/__mocks__/login.js @@ -1,3 +1,4 @@ +// @flow import sinon from 'sinon' const login = sinon.spy() diff --git a/src/firebase/__mocks__/logout.js b/src/firebase/__mocks__/logout.js index 484008f..50d24ab 100644 --- a/src/firebase/__mocks__/logout.js +++ b/src/firebase/__mocks__/logout.js @@ -1,3 +1,4 @@ +// @flow import sinon from 'sinon' const logout = sinon.spy() diff --git a/src/firebase/__mocks__/savePreferences.js b/src/firebase/__mocks__/savePreferences.js index 7da9eb5..b472e6f 100644 --- a/src/firebase/__mocks__/savePreferences.js +++ b/src/firebase/__mocks__/savePreferences.js @@ -1,6 +1,6 @@ // @flow import type { PreferencesState } from 'reducers/preferences' -const savePreferences = (preferences: PreferencesState) => {} +const savePreferences = (_preferences: PreferencesState) => {} export default savePreferences diff --git a/src/firebase/__mocks__/saveProfile.js b/src/firebase/__mocks__/saveProfile.js index e72d5c1..67b3cd8 100644 --- a/src/firebase/__mocks__/saveProfile.js +++ b/src/firebase/__mocks__/saveProfile.js @@ -1,6 +1,6 @@ // @flow import type { UserProfile } from 'types' -const saveProfile = (profile: UserProfile): void => {} +const saveProfile = (_profile: UserProfile): void => {} export default saveProfile diff --git a/src/firebase/__mocks__/sendMessage.js b/src/firebase/__mocks__/sendMessage.js index 2f0b844..ccbcc6d 100644 --- a/src/firebase/__mocks__/sendMessage.js +++ b/src/firebase/__mocks__/sendMessage.js @@ -7,8 +7,8 @@ type SendMessageOpts = { result: ?MessageResult } -const sendMessage = (opts: SendMessageOpts): Promise => { - return new Promise((resolve, reject) => { +const sendMessage = (_opts: SendMessageOpts): Promise => { + return new Promise(resolve => { resolve() }) } diff --git a/src/sagas/__tests__/saveUserProfile.tests.js b/src/sagas/__tests__/saveUserProfile.tests.js index f67a5c7..3f76062 100644 --- a/src/sagas/__tests__/saveUserProfile.tests.js +++ b/src/sagas/__tests__/saveUserProfile.tests.js @@ -1,5 +1,4 @@ // @flow -import type { Saga } from 'redux-saga' import { take, put, call } from 'redux-saga/effects' import { cloneableGenerator } from 'redux-saga/utils' import { changeDisplayName } from 'actions'