diff --git a/__mocks__/redux-mock-store.js b/__mocks__/redux-mock-store.js deleted file mode 100644 index 6282d915afd..00000000000 --- a/__mocks__/redux-mock-store.js +++ /dev/null @@ -1,6 +0,0 @@ -import configureMockStore from 'redux-mock-store'; // eslint-disable-line -import thunk from 'redux-thunk'; - -const mockStore = configureMockStore([thunk]); - -export default mockStore; diff --git a/flow-typed/npm/redux-mock-store_v1.2.x.js b/flow-typed/npm/redux-mock-store_v1.2.x.js deleted file mode 100644 index 96e1ddb14c3..00000000000 --- a/flow-typed/npm/redux-mock-store_v1.2.x.js +++ /dev/null @@ -1,35 +0,0 @@ -// flow-typed signature: 6e958768eab6cb706d8c4ff13fd4d5ab -// flow-typed version: c6154227d1/redux-mock-store_v1.2.x/flow_>=v0.25.x <=v0.103.x - -declare module "redux-mock-store" { - /* - S = State - A = Action - */ - - declare type mockStore = { - (state: S): mockStoreWithoutMiddleware - }; - declare type DispatchAPI = (action: A) => A; - declare type Dispatch = DispatchAPI; - declare type mockStoreWithoutMiddleware = { - getState(): S, - getActions(): Array, - dispatch: Dispatch, - clearActions(): void, - subscribe(callback: () => void): () => void, - replaceReducer(nextReducer: Function): void - }; - - declare type Middleware = any => any => any; - - declare module.exports: (middlewares: ?Array) => mockStore; -} - -// Filename aliases -declare module "redux-mock-store/src/index" { - declare module.exports: $Exports<"redux-mock-store">; -} -declare module "redux-mock-store/src/index.js" { - declare module.exports: $Exports<"redux-mock-store">; -} diff --git a/flow-typed/redux-mock-store_v1.2.x.js b/flow-typed/redux-mock-store_v1.2.x.js new file mode 100644 index 00000000000..50eba86f8c5 --- /dev/null +++ b/flow-typed/redux-mock-store_v1.2.x.js @@ -0,0 +1,34 @@ +declare module 'redux-mock-store' { + /* + S = State + A = Action + */ + + declare type mockStore = { (state: S): mockStoreWithoutMiddleware, ... }; + declare interface Dispatch { + (action: A): A; + ((Function, Function) => T): T; + } + + declare type mockStoreWithoutMiddleware = { + getState(): S, + getActions(): Array, + dispatch: Dispatch, + clearActions(): void, + subscribe(callback: () => void): () => void, + replaceReducer(nextReducer: Function): void, + ... + }; + + declare type Middleware = (any) => any => any; + + declare module.exports: (middlewares: ?Array) => mockStore; +} + +// Filename aliases +declare module 'redux-mock-store/src/index' { + declare module.exports: $Exports<'redux-mock-store'>; +} +declare module 'redux-mock-store/src/index.js' { + declare module.exports: $Exports<'redux-mock-store'>; +} diff --git a/src/__tests__/lib/exampleData.js b/src/__tests__/lib/exampleData.js index 24a082bcf6f..78d626d9665 100644 --- a/src/__tests__/lib/exampleData.js +++ b/src/__tests__/lib/exampleData.js @@ -217,7 +217,7 @@ export const unicodeEmojiReaction: Reaction = deepFreeze({ emoji_name: 'thumbs_up', }); -const displayRecipientFromUser = (user: User): PmRecipientUser => { +export const displayRecipientFromUser = (user: User): PmRecipientUser => { const { email, full_name, user_id: id } = user; return deepFreeze({ email, diff --git a/src/chat/__tests__/narrowsReducer-test.js b/src/chat/__tests__/narrowsReducer-test.js index 5484b5c451e..45c344434a4 100644 --- a/src/chat/__tests__/narrowsReducer-test.js +++ b/src/chat/__tests__/narrowsReducer-test.js @@ -1,3 +1,4 @@ +/* @flow strict-local */ import deepFreeze from 'deep-freeze'; import narrowsReducer from '../narrowsReducer'; @@ -25,20 +26,23 @@ describe('narrowsReducer', () => { const streamNarrowStr = JSON.stringify(streamNarrow('some stream')); const topicNarrowStr = JSON.stringify(topicNarrow('some stream', 'some topic')); - test('handles unknown action and no previous state by returning initial state', () => { - const newState = narrowsReducer(undefined, {}); - expect(newState).toBeDefined(); - }); - describe('EVENT_NEW_MESSAGE', () => { test('if not caught up in narrow, do not add message in home narrow', () => { const initialState = deepFreeze({ [HOME_NARROW_STR]: [1, 2], }); + const message = eg.streamMessage({ + id: 3, + display_recipient: 'some stream', + subject: 'some topic', + flags: [], + }); + const action = deepFreeze({ + ...eg.eventNewMessageActionBase, type: EVENT_NEW_MESSAGE, - message: { id: 3, display_recipient: 'some stream', subject: 'some topic', flags: [] }, + message, caughtUp: {}, }); @@ -56,9 +60,11 @@ describe('narrowsReducer', () => { [HOME_NARROW_STR]: [1, 2], }); + const message = eg.streamMessage({ id: 3, flags: [] }); + const action = deepFreeze({ - type: EVENT_NEW_MESSAGE, - message: { id: 3, flags: [] }, + ...eg.eventNewMessageActionBase, + message, caughtUp: { [HOME_NARROW_STR]: { older: false, @@ -79,25 +85,26 @@ describe('narrowsReducer', () => { test('if new message key does not exist do not create it', () => { const initialState = deepFreeze({ - [JSON.stringify(topicNarrow('some stream', 'some topic'))]: [1, 2], + [topicNarrowStr]: [1, 2], + }); + + const message = eg.streamMessage({ + id: 3, + flags: [], + display_recipient: 'stream', + subject: 'topic', }); const action = deepFreeze({ - type: EVENT_NEW_MESSAGE, - message: { - id: 3, - type: 'stream', - display_recipient: 'stream', - subject: 'topic', - flags: [], - }, + ...eg.eventNewMessageActionBase, + message, caughtUp: {}, }); const newState = narrowsReducer(initialState, action); const expectedState = { - [JSON.stringify(topicNarrow('some stream', 'some topic'))]: [1, 2], + [topicNarrowStr]: [1, 2], }; expect(newState).toEqual(expectedState); }); @@ -107,14 +114,13 @@ describe('narrowsReducer', () => { const initialState = deepFreeze({ [ALL_PRIVATE_NARROW_STR]: [], }); - const message = { + const message = eg.pmMessage({ id: 1, - type: 'private', display_recipient: [{ email: 'me@example.com' }, { email: 'a@a.com' }, { email: 'b@b.com' }], flags: [], - }; + }); const action = deepFreeze({ - type: EVENT_NEW_MESSAGE, + ...eg.eventNewMessageActionBase, message, ownEmail: 'me@example.com', caughtUp: { @@ -135,15 +141,16 @@ describe('narrowsReducer', () => { [HOME_NARROW_STR]: [1, 2], [topicNarrowStr]: [2], }); - const message = { + + const message = eg.streamMessage({ id: 3, - type: 'stream', + flags: [], display_recipient: 'some stream', subject: 'some topic', - flags: [], - }; + }); + const action = deepFreeze({ - type: EVENT_NEW_MESSAGE, + ...eg.eventNewMessageActionBase, message, caughtUp: { [HOME_NARROW_STR]: { @@ -158,7 +165,7 @@ describe('narrowsReducer', () => { }); const expectedState = { [HOME_NARROW_STR]: [1, 2], - [topicNarrowStr]: [2, 3], + [topicNarrowStr]: [2, message.id], }; const newState = narrowsReducer(initialState, action); @@ -173,13 +180,14 @@ describe('narrowsReducer', () => { [narrowWithSelfStr]: [], }); - const message = { + const message = eg.pmMessage({ id: 1, display_recipient: [{ email: 'me@example.com' }], flags: [], - }; + }); + const action = deepFreeze({ - type: EVENT_NEW_MESSAGE, + ...eg.eventNewMessageActionBase, ownEmail: 'me@example.com', message, caughtUp: { @@ -189,8 +197,8 @@ describe('narrowsReducer', () => { }); const expectedState = { - [HOME_NARROW_STR]: [1], - [narrowWithSelfStr]: [1], + [HOME_NARROW_STR]: [message.id], + [narrowWithSelfStr]: [message.id], }; const newState = narrowsReducer(initialState, action); @@ -209,15 +217,15 @@ describe('narrowsReducer', () => { [groupNarrowStr]: [2, 4], }); - const message = { + const message = eg.streamMessage({ id: 5, - type: 'stream', + flags: [], display_recipient: 'some stream', subject: 'some topic', - flags: [], - }; + }); + const action = deepFreeze({ - type: EVENT_NEW_MESSAGE, + ...eg.eventNewMessageActionBase, message, caughtUp: { [HOME_NARROW_STR]: { older: false, newer: true }, @@ -227,10 +235,10 @@ describe('narrowsReducer', () => { }); const expectedState = { - [HOME_NARROW_STR]: [1, 2, 5], + [HOME_NARROW_STR]: [1, 2, message.id], [ALL_PRIVATE_NARROW_STR]: [1, 2], - [streamNarrowStr]: [2, 3, 5], - [topicNarrowStr]: [2, 3, 5], + [streamNarrowStr]: [2, 3, message.id], + [topicNarrowStr]: [2, 3, message.id], [privateNarrowStr]: [2, 4], [groupNarrowStr]: [2, 4], }; @@ -246,15 +254,10 @@ describe('narrowsReducer', () => { [HOME_NARROW_STR]: [1], }); - const message = { - id: 3, - type: 'stream', - display_recipient: 'stream name', - subject: 'some topic', - flags: [], - }; + const message = eg.streamMessage({ id: 3, flags: [] }); + const action = deepFreeze({ - type: EVENT_NEW_MESSAGE, + ...eg.eventNewMessageActionBase, message, caughtUp: { [HOME_NARROW_STR]: { older: false, newer: true }, @@ -262,7 +265,7 @@ describe('narrowsReducer', () => { }); const expectedState = { - [HOME_NARROW_STR]: [1, 3], + [HOME_NARROW_STR]: [1, message.id], }; const newState = narrowsReducer(initialState, action); @@ -281,17 +284,11 @@ describe('narrowsReducer', () => { [groupNarrowStr]: [2, 4], }); - const message = { - id: 5, - type: 'private', - sender_email: 'someone@example.com', - display_recipient: [{ email: 'me@example.com' }, { email: 'mark@example.com' }], - flags: [], - }; + const message = eg.pmMessage({ id: 5, flags: [] }); + const action = deepFreeze({ - type: EVENT_NEW_MESSAGE, + ...eg.eventNewMessageActionBase, message, - ownEmail: 'me@example.com', caughtUp: { [HOME_NARROW_STR]: { older: false, newer: true }, [ALL_PRIVATE_NARROW_STR]: { older: false, newer: true }, @@ -300,11 +297,11 @@ describe('narrowsReducer', () => { }); const expectedState = { - [HOME_NARROW_STR]: [1, 2, 5], - [ALL_PRIVATE_NARROW_STR]: [1, 2, 5], + [HOME_NARROW_STR]: [1, 2, message.id], + [ALL_PRIVATE_NARROW_STR]: [1, 2, message.id], [streamNarrowStr]: [2, 3], [topicNarrowStr]: [2, 3], - [privateNarrowStr]: [2, 4, 5], + [privateNarrowStr]: [2, 4, message.id], [groupNarrowStr]: [2, 4], }; @@ -375,11 +372,18 @@ describe('narrowsReducer', () => { const initialState = deepFreeze({ [HOME_NARROW_STR]: [1, 2, 3], }); + const action = deepFreeze({ type: MESSAGE_FETCH_COMPLETE, + anchor: 2, narrow: privateNarrow('mark@example.com'), messages: [], + numBefore: 100, + numAfter: 100, + foundNewest: false, + foundOldest: false, }); + const expectedState = { [HOME_NARROW_STR]: [1, 2, 3], [JSON.stringify(privateNarrow('mark@example.com'))]: [], @@ -397,8 +401,17 @@ describe('narrowsReducer', () => { const action = deepFreeze({ type: MESSAGE_FETCH_COMPLETE, + anchor: 2, narrow: [], - messages: [{ id: 2 }, { id: 3 }, { id: 4 }], + messages: [ + eg.streamMessage({ id: 2 }), + eg.streamMessage({ id: 3 }), + eg.streamMessage({ id: 4 }), + ], + numBefore: 100, + numAfter: 100, + foundNewest: false, + foundOldest: false, }); const expectedState = { @@ -418,8 +431,16 @@ describe('narrowsReducer', () => { const action = deepFreeze({ type: MESSAGE_FETCH_COMPLETE, + anchor: 2, narrow: [], - messages: [{ id: 4, timestamp: 2 }, { id: 3, timestamp: 1 }], + messages: [ + eg.streamMessage({ id: 3, timestamp: 2 }), + eg.streamMessage({ id: 4, timestamp: 1 }), + ], + numBefore: 100, + numAfter: 100, + foundNewest: false, + foundOldest: false, }); const expectedState = { @@ -441,7 +462,14 @@ describe('narrowsReducer', () => { anchor: FIRST_UNREAD_ANCHOR, type: MESSAGE_FETCH_COMPLETE, narrow: [], - messages: [{ id: 3, timestamp: 2 }, { id: 4, timestamp: 1 }], + messages: [ + eg.streamMessage({ id: 3, timestamp: 2 }), + eg.streamMessage({ id: 4, timestamp: 1 }), + ], + numBefore: 100, + numAfter: 100, + foundNewest: false, + foundOldest: false, }); const expectedState = { @@ -462,7 +490,14 @@ describe('narrowsReducer', () => { anchor: LAST_MESSAGE_ANCHOR, type: MESSAGE_FETCH_COMPLETE, narrow: [], - messages: [{ id: 3, timestamp: 2 }, { id: 4, timestamp: 1 }], + messages: [ + eg.streamMessage({ id: 3, timestamp: 2 }), + eg.streamMessage({ id: 4, timestamp: 1 }), + ], + numBefore: 100, + numAfter: 0, + foundNewest: true, + foundOldest: false, }); const expectedState = { @@ -491,6 +526,14 @@ describe('narrowsReducer', () => { }); describe('EVENT_UPDATE_MESSAGE_FLAGS', () => { + const allMessages = { + // Flow doesn't like number literals as keys...but it also wants + // them to be numbers. See https://github.com/facebook/flow/issues/380. + [1]: eg.streamMessage({ id: 1 }) /* eslint-disable-line no-useless-computed-key */, + [2]: eg.streamMessage({ id: 2 }) /* eslint-disable-line no-useless-computed-key */, + [4]: eg.streamMessage({ id: 4 }) /* eslint-disable-line no-useless-computed-key */, + }; + test('Do nothing if the is:starred narrow has not been fetched', () => { const initialState = deepFreeze({ [HOME_NARROW_STR]: [1, 2], @@ -498,6 +541,12 @@ describe('narrowsReducer', () => { const action = deepFreeze({ type: EVENT_UPDATE_MESSAGE_FLAGS, + id: 1, + allMessages, + all: false, + flag: 'starred', + operation: 'add', + messages: [4, 2], }); const newState = narrowsReducer(initialState, action); @@ -512,6 +561,11 @@ describe('narrowsReducer', () => { const action = deepFreeze({ type: EVENT_UPDATE_MESSAGE_FLAGS, + id: 1, + all: false, + allMessages, + operation: 'add', + messages: [1], flag: 'read', }); @@ -530,6 +584,9 @@ describe('narrowsReducer', () => { const action = deepFreeze({ type: EVENT_UPDATE_MESSAGE_FLAGS, + id: 1, + allMessages, + all: false, flag: 'starred', operation: 'add', messages: [4, 2], @@ -555,6 +612,9 @@ describe('narrowsReducer', () => { const action = deepFreeze({ type: EVENT_UPDATE_MESSAGE_FLAGS, + id: 1, + allMessages, + all: false, flag: 'starred', operation: 'remove', messages: [4, 2], diff --git a/src/chat/__tests__/narrowsSelectors-test.js b/src/chat/__tests__/narrowsSelectors-test.js index 775c953c00b..6c3e0db1744 100644 --- a/src/chat/__tests__/narrowsSelectors-test.js +++ b/src/chat/__tests__/narrowsSelectors-test.js @@ -1,5 +1,4 @@ -import deepFreeze from 'deep-freeze'; - +/* @flow strict-local */ import { getFirstMessageId, getLastMessageId, @@ -17,153 +16,115 @@ import { groupNarrow, } from '../../utils/narrow'; import { NULL_SUBSCRIPTION } from '../../nullObjects'; +import * as eg from '../../__tests__/lib/exampleData'; describe('getMessagesForNarrow', () => { + const message = eg.streamMessage({ id: 123 }); + const messages = { + // Flow doesn't like number literals as keys...but it also wants + // them to be numbers. + [123]: message /* eslint-disable-line no-useless-computed-key */, + }; + const outboxMessage = eg.makeOutboxMessage({ + narrow: HOME_NARROW, + }); + test('if no outbox messages returns messages with no change', () => { - const state = deepFreeze({ + const state = eg.reduxState({ narrows: { '[]': [123], }, - messages: { - 123: { id: 123 }, - }, + messages, outbox: [], }); - const expectedState = deepFreeze([state.messages[123]]); - - const anchor = getMessagesForNarrow(state, HOME_NARROW); + const result = getMessagesForNarrow(state, HOME_NARROW); - expect(anchor).toEqual(expectedState); + expect(result).toEqual([state.messages[123]]); }); test('combine messages and outbox in same narrow', () => { - const state = deepFreeze({ + const state = eg.reduxState({ narrows: { '[]': [123], }, - messages: { - 123: { id: 123 }, - }, - outbox: [ - { - email: 'donald@zulip.com', - narrow: HOME_NARROW, - parsedContent: '

Hello

', - sender_full_name: 'donald', - timestamp: 12, - }, - ], + messages, + outbox: [outboxMessage], caughtUp: { [HOME_NARROW_STR]: { older: false, newer: true }, }, }); - const anchor = getMessagesForNarrow(state, HOME_NARROW); + const result = getMessagesForNarrow(state, HOME_NARROW); - const expectedState = deepFreeze([ - { id: 123 }, - { - email: 'donald@zulip.com', - narrow: [], - parsedContent: '

Hello

', - sender_full_name: 'donald', - timestamp: 12, - }, - ]); - - expect(anchor).toEqual(expectedState); + expect(result).toEqual([message, outboxMessage]); }); test('do not combine messages and outbox if not caught up', () => { - const state = deepFreeze({ + const state = eg.reduxState({ narrows: { [HOME_NARROW_STR]: [123], }, - messages: { - 123: { id: 123 }, - }, - outbox: [ - { - email: 'donald@zulip.com', - narrow: HOME_NARROW, - parsedContent: '

Hello

', - sender_full_name: 'donald', - timestamp: 12, - }, - ], + messages, + outbox: [outboxMessage], }); - const expectedState = deepFreeze([state.messages[123]]); + const result = getMessagesForNarrow(state, HOME_NARROW); - const anchor = getMessagesForNarrow(state, HOME_NARROW); - - expect(anchor).toEqual(expectedState); + expect(result).toEqual([state.messages[123]]); }); test('do not combine messages and outbox in different narrow', () => { - const state = deepFreeze({ + const state = eg.reduxState({ narrows: { [JSON.stringify(privateNarrow('john@example.com'))]: [123], }, - messages: { - 123: { id: 123 }, - }, - outbox: [ - { - email: 'donald@zulip.com', - narrow: streamNarrow('denmark', 'denmark'), - parsedContent: '

Hello

', - sender_full_name: 'donald', - timestamp: 12, - }, - ], + messages, + outbox: [{ ...outboxMessage, narrow: streamNarrow('denmark') }], }); - const anchor = getMessagesForNarrow(state, privateNarrow('john@example.com')); + const result = getMessagesForNarrow(state, privateNarrow('john@example.com')); - const expectedState = deepFreeze([{ id: 123 }]); - - expect(anchor).toEqual(expectedState); + expect(result).toEqual([message]); }); }); describe('getFirstMessageId', () => { test('return undefined when there are no messages', () => { - const state = deepFreeze({ + const state = eg.reduxState({ narrows: { '[]': [], }, outbox: [], }); - const anchor = getFirstMessageId(state, HOME_NARROW); + const result = getFirstMessageId(state, HOME_NARROW); - expect(anchor).toEqual(undefined); + expect(result).toEqual(undefined); }); test('returns first message id', () => { - const state = deepFreeze({ + const state = eg.reduxState({ narrows: { '[]': [1, 2, 3], }, messages: { - 1: { id: 1 }, - 2: { id: 2 }, - 3: { id: 3 }, + [1]: eg.streamMessage({ id: 1 }) /* eslint-disable-line no-useless-computed-key */, + [2]: eg.streamMessage({ id: 2 }) /* eslint-disable-line no-useless-computed-key */, + [3]: eg.streamMessage({ id: 3 }) /* eslint-disable-line no-useless-computed-key */, }, outbox: [], }); - const anchor = getFirstMessageId(state, HOME_NARROW); + const result = getFirstMessageId(state, HOME_NARROW); - expect(anchor).toEqual(1); + expect(result).toEqual(1); }); }); describe('getLastMessageId', () => { test('return undefined when there are no messages', () => { - const state = deepFreeze({ + const state = eg.reduxState({ narrows: { '[]': [], }, @@ -171,69 +132,70 @@ describe('getLastMessageId', () => { outbox: [], }); - const anchor = getLastMessageId(state, HOME_NARROW); + const result = getLastMessageId(state, HOME_NARROW); - expect(anchor).toEqual(undefined); + expect(result).toEqual(undefined); }); test('returns last message id', () => { - const state = deepFreeze({ + const state = eg.reduxState({ narrows: { '[]': [1, 2, 3], }, messages: { - 1: { id: 1 }, - 2: { id: 2 }, - 3: { id: 3 }, + [1]: eg.streamMessage({ id: 1 }) /* eslint-disable-line no-useless-computed-key */, + [2]: eg.streamMessage({ id: 2 }) /* eslint-disable-line no-useless-computed-key */, + [3]: eg.streamMessage({ id: 3 }) /* eslint-disable-line no-useless-computed-key */, }, outbox: [], }); - const anchor = getLastMessageId(state, HOME_NARROW); + const result = getLastMessageId(state, HOME_NARROW); - expect(anchor).toEqual(3); + expect(result).toEqual(3); }); }); describe('getStreamInNarrow', () => { - const state = deepFreeze({ - streams: [{ name: 'stream' }, { name: 'steam2' }, { name: 'stream3' }], - subscriptions: [ - { name: 'stream', in_home_view: false }, - { name: 'stream2', in_home_view: true }, - ], + const stream1 = eg.makeStream({ name: 'stream' }); + const stream2 = eg.makeStream({ name: 'stream2' }); + const stream3 = eg.makeStream({ name: 'stream3' }); + const stream4 = eg.makeStream({ name: 'stream4' }); + const sub1 = { ...eg.makeSubscription({ stream: stream1 }), in_home_view: false }; + const sub2 = { ...eg.makeSubscription({ stream: stream2 }), in_home_view: true }; + + const state = eg.reduxState({ + streams: [stream1, stream2, stream3], + subscriptions: [sub1, sub2], }); test('return subscription if stream in narrow is subscribed', () => { - const narrow = streamNarrow('stream'); + const narrow = streamNarrow(stream1.name); - expect(getStreamInNarrow(state, narrow)).toEqual({ name: 'stream', in_home_view: false }); + expect(getStreamInNarrow(state, narrow)).toEqual(sub1); }); test('return stream if stream in narrow is not subscribed', () => { - const narrow = streamNarrow('stream3'); + const narrow = streamNarrow(stream3.name); - expect(getStreamInNarrow(state, narrow)).toEqual({ name: 'stream3', in_home_view: true }); + expect(getStreamInNarrow(state, narrow)).toEqual({ ...stream3, in_home_view: true }); }); test('return NULL_SUBSCRIPTION if stream in narrow is not valid', () => { - const narrow = streamNarrow('stream4'); + const narrow = streamNarrow(stream4.name); expect(getStreamInNarrow(state, narrow)).toEqual(NULL_SUBSCRIPTION); }); test('return NULL_SUBSCRIPTION is narrow is not topic or stream', () => { - expect(getStreamInNarrow(state, undefined)).toEqual(NULL_SUBSCRIPTION); expect(getStreamInNarrow(state, privateNarrow('abc@zulip.com'))).toEqual(NULL_SUBSCRIPTION); - expect(getStreamInNarrow(state, topicNarrow('stream4', 'topic'))).toEqual(NULL_SUBSCRIPTION); + expect(getStreamInNarrow(state, topicNarrow(stream4.name, 'topic'))).toEqual(NULL_SUBSCRIPTION); }); }); describe('isNarrowValid', () => { test('narrowing to a special narrow is always valid', () => { - const state = { - realm: {}, - }; + const state = eg.reduxState(); const narrow = STARRED_NARROW; const result = isNarrowValid(state, narrow); @@ -242,11 +204,12 @@ describe('isNarrowValid', () => { }); test('narrowing to an existing stream is valid', () => { - const state = { - realm: {}, - streams: [{ name: 'some stream' }], - }; - const narrow = streamNarrow('some stream'); + const stream = eg.makeStream({ name: 'some stream' }); + + const state = eg.reduxState({ + streams: [stream], + }); + const narrow = streamNarrow(stream.name); const result = isNarrowValid(state, narrow); @@ -254,10 +217,9 @@ describe('isNarrowValid', () => { }); test('narrowing to a non-existing stream is invalid', () => { - const state = { - realm: {}, + const state = eg.reduxState({ streams: [], - }; + }); const narrow = streamNarrow('nonexisting'); const result = isNarrowValid(state, narrow); @@ -266,11 +228,12 @@ describe('isNarrowValid', () => { }); test('narrowing to an existing stream is valid regardless of topic', () => { - const state = { - realm: {}, - streams: [{ name: 'some stream' }], - }; - const narrow = topicNarrow('some stream', 'topic does not matter'); + const stream = eg.makeStream({ name: 'some stream' }); + + const state = eg.reduxState({ + streams: [stream], + }); + const narrow = topicNarrow(stream.name, 'topic does not matter'); const result = isNarrowValid(state, narrow); @@ -278,15 +241,17 @@ describe('isNarrowValid', () => { }); test('narrowing to a PM with existing user is valid', () => { - const state = { + const user = eg.makeUser({ name: 'bob' }); + const state = eg.reduxState({ realm: { + ...eg.realmState(), crossRealmBots: [], nonActiveUsers: [], }, streams: [], - users: [{ email: 'bob@example.com' }], - }; - const narrow = privateNarrow('bob@example.com'); + users: [user], + }); + const narrow = privateNarrow(user.email); const result = isNarrowValid(state, narrow); @@ -294,15 +259,17 @@ describe('isNarrowValid', () => { }); test('narrowing to a PM with non-existing user is not valid', () => { - const state = { + const user = eg.makeUser({ name: 'bob' }); + const state = eg.reduxState({ realm: { + ...eg.realmState(), crossRealmBots: [], nonActiveUsers: [], }, streams: [], users: [], - }; - const narrow = privateNarrow('bob@example.com'); + }); + const narrow = privateNarrow(user.email); const result = isNarrowValid(state, narrow); @@ -310,15 +277,19 @@ describe('isNarrowValid', () => { }); test('narrowing to a group chat with non-existing user is not valid', () => { - const state = { + const john = eg.makeUser({ name: 'john' }); + const mark = eg.makeUser({ name: 'mark' }); + + const state = eg.reduxState({ realm: { + ...eg.realmState(), crossRealmBots: [], nonActiveUsers: [], }, streams: [], - users: [{ email: 'john@example.com' }, { email: 'mark@example.com' }], - }; - const narrow = groupNarrow(['john@example.com', 'mark@example.com']); + users: [john, mark], + }); + const narrow = groupNarrow([john.email, mark.email]); const result = isNarrowValid(state, narrow); @@ -326,14 +297,15 @@ describe('isNarrowValid', () => { }); test('narrowing to a group chat with non-existing users is also valid', () => { - const state = { + const state = eg.reduxState({ realm: { + ...eg.realmState(), crossRealmBots: [], nonActiveUsers: [], }, streams: [], users: [], - }; + }); const narrow = groupNarrow(['john@example.com', 'mark@example.com']); const result = isNarrowValid(state, narrow); @@ -342,15 +314,17 @@ describe('isNarrowValid', () => { }); test('narrowing to a PM with bots is valid', () => { - const state = { + const bot = eg.makeCrossRealmBot({ name: 'some-bot' }); + const state = eg.reduxState({ realm: { - crossRealmBots: [{ email: 'some-bot@example.com' }], + ...eg.realmState(), + crossRealmBots: [bot], nonActiveUsers: [], }, streams: [], users: [], - }; - const narrow = privateNarrow('some-bot@example.com'); + }); + const narrow = privateNarrow(bot.email); const result = isNarrowValid(state, narrow); @@ -358,15 +332,17 @@ describe('isNarrowValid', () => { }); test('narrowing to non active users is valid', () => { - const state = { + const notActiveUser = eg.makeUser({ name: 'not active' }); + const state = eg.reduxState({ realm: { + ...eg.realmState(), crossRealmBots: [], - nonActiveUsers: [{ email: 'not-active@example.com' }], + nonActiveUsers: [notActiveUser], }, streams: [], users: [], - }; - const narrow = privateNarrow('not-active@example.com'); + }); + const narrow = privateNarrow(notActiveUser.email); const result = isNarrowValid(state, narrow); diff --git a/src/message/__tests__/fetchActions-test.js b/src/message/__tests__/fetchActions-test.js index 4bc5901497d..2c3ab150460 100644 --- a/src/message/__tests__/fetchActions-test.js +++ b/src/message/__tests__/fetchActions-test.js @@ -1,9 +1,12 @@ -import mockStore from 'redux-mock-store'; // eslint-disable-line +import configureStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; import { fetchMessages, fetchOlder, fetchNewer } from '../fetchActions'; import { streamNarrow, HOME_NARROW, HOME_NARROW_STR } from '../../utils/narrow'; import { navStateWithNarrow } from '../../utils/testHelpers'; +const mockStore = configureStore([thunk]); + const narrow = streamNarrow('some stream'); const streamNarrowStr = JSON.stringify(narrow); diff --git a/src/message/__tests__/messageActions-test.js b/src/message/__tests__/messageActions-test.js index 1e2d58e13ca..999ba875293 100644 --- a/src/message/__tests__/messageActions-test.js +++ b/src/message/__tests__/messageActions-test.js @@ -1,8 +1,15 @@ -import mockStore from 'redux-mock-store'; // eslint-disable-line +/* @flow strict-local */ +import configureStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; import { doNarrow } from '../messagesActions'; import { streamNarrow, HOME_NARROW, privateNarrow } from '../../utils/narrow'; import { navStateWithNarrow } from '../../utils/testHelpers'; +import * as eg from '../../__tests__/lib/exampleData'; +import type { GlobalState } from '../../reduxTypes'; +import type { Action } from '../../actionTypes'; + +const mockStore = configureStore([thunk]); const streamNarrowObj = streamNarrow('some stream'); const streamNarrowStr = JSON.stringify(streamNarrowObj); @@ -10,54 +17,51 @@ const streamNarrowStr = JSON.stringify(streamNarrowObj); describe('messageActions', () => { describe('doNarrow', () => { test('when no messages in new narrow and caughtUp is false, actions to fetch messages and switch narrow are dispatched', () => { - const store = mockStore({ - realm: {}, - session: { isHydrated: true }, - caughtUp: { - [streamNarrowStr]: { - newer: false, - older: true, - }, - }, - ...navStateWithNarrow(HOME_NARROW), - narrows: {}, - streams: [ - { - name: 'some stream', + const store = mockStore( + eg.reduxState({ + session: { ...eg.baseReduxState.session, isHydrated: true }, + caughtUp: { + [streamNarrowStr]: { + newer: false, + older: true, + }, }, - ], - }); + ...navStateWithNarrow(HOME_NARROW), + narrows: {}, + streams: [eg.makeStream({ name: 'some stream' })], + }), + ); store.dispatch(doNarrow(streamNarrowObj)); const actions = store.getActions(); expect(actions).toHaveLength(3); - expect(actions[0].type).toBe('DO_NARROW'); - expect(actions[1].type).toBe('MESSAGE_FETCH_START'); - expect(actions[1].narrow).toEqual(streamNarrowObj); - expect(actions[2].type).toBe('Navigation/PUSH'); + const [action0, action1, action2] = actions; + expect(action0.type).toBe('DO_NARROW'); + expect(action1.type).toBe('MESSAGE_FETCH_START'); + if (action1.type === 'MESSAGE_FETCH_START') { + expect(action1.narrow).toEqual(streamNarrowObj); + } + expect(action2.type).toBe('Navigation/PUSH'); }); test('when no messages in new narrow but caught up, only switch to narrow, do not fetch', () => { - const store = mockStore({ - realm: {}, - session: { isHydrated: true }, - caughtUp: { - [streamNarrowStr]: { - newer: true, - older: true, + const store = mockStore( + eg.reduxState({ + session: { ...eg.baseReduxState.session, isHydrated: true }, + caughtUp: { + [streamNarrowStr]: { + newer: true, + older: true, + }, }, - }, - ...navStateWithNarrow(HOME_NARROW), - narrows: { - [streamNarrowStr]: [], - }, - streams: [ - { - name: 'some stream', + ...navStateWithNarrow(HOME_NARROW), + narrows: { + [streamNarrowStr]: [], }, - ], - }); + streams: [eg.makeStream({ name: 'some stream' })], + }), + ); store.dispatch(doNarrow(streamNarrowObj)); const actions = store.getActions(); @@ -73,52 +77,49 @@ describe('messageActions', () => { }); test('when messages in new narrow are too few and not caught up, fetch more messages', () => { - const store = mockStore({ - realm: {}, - session: { isHydrated: true }, - caughtUp: {}, - ...navStateWithNarrow(HOME_NARROW), - narrows: { - [streamNarrowStr]: [1], - }, - messages: { - 1: {}, - }, - streams: [ - { - name: 'some stream', + const store = mockStore( + eg.reduxState({ + session: { ...eg.baseReduxState.session, isHydrated: true }, + caughtUp: {}, + ...navStateWithNarrow(HOME_NARROW), + narrows: { + [streamNarrowStr]: [1], + }, + messages: { + [1]: eg.streamMessage() /* eslint-disable-line no-useless-computed-key */, }, - ], - }); + streams: [eg.makeStream({ name: 'some stream' })], + }), + ); store.dispatch(doNarrow(streamNarrowObj)); const actions = store.getActions(); expect(actions).toHaveLength(3); - expect(actions[0].type).toBe('DO_NARROW'); - expect(actions[1].type).toBe('MESSAGE_FETCH_START'); - expect(actions[1].narrow).toEqual(streamNarrowObj); - expect(actions[2].type).toBe('Navigation/PUSH'); + const [action0, action1, action2] = actions; + expect(action0.type).toBe('DO_NARROW'); + expect(action1.type).toBe('MESSAGE_FETCH_START'); + if (action1.type === 'MESSAGE_FETCH_START') { + expect(action1.narrow).toEqual(streamNarrowObj); + } + expect(action2.type).toBe('Navigation/PUSH'); }); test('if new narrow stream is not valid, do nothing', () => { - const store = mockStore({ - realm: {}, - session: { isHydrated: true }, - caughtUp: {}, - ...navStateWithNarrow(HOME_NARROW), - narrows: { - [streamNarrowStr]: [1], - }, - messages: { - 1: {}, - }, - streams: [ - { - name: 'some updated stream', + const store = mockStore( + eg.reduxState({ + session: { ...eg.baseReduxState.session, isHydrated: true }, + caughtUp: {}, + ...navStateWithNarrow(HOME_NARROW), + narrows: { + [streamNarrowStr]: [1], }, - ], - }); + messages: { + [1]: eg.streamMessage({ id: 1 }) /* eslint-disable-line no-useless-computed-key */, + }, + streams: [eg.makeStream({ name: 'some updated stream' })], + }), + ); store.dispatch(doNarrow(streamNarrowObj)); const actions = store.getActions(); @@ -127,28 +128,28 @@ describe('messageActions', () => { }); test('if new narrow user is deactivated, do nothing', () => { - const store = mockStore({ - realm: { - crossRealmBots: [], - nonActiveUsers: [], - }, - session: { isHydrated: true }, - caughtUp: {}, - ...navStateWithNarrow(HOME_NARROW), - narrows: { - [streamNarrowStr]: [1], - }, - messages: { - 1: {}, - }, - users: [ - { - email: 'ab@a.com', + const inactiveUser = eg.makeUser({ name: 'inactive', is_active: false }); + + const store = mockStore( + eg.reduxState({ + realm: eg.realmState({ + crossRealmBots: [], + nonActiveUsers: [], // TODO: Shouldn't `inactiveUser` go here? + }), + session: { ...eg.baseReduxState.session, isHydrated: true }, + caughtUp: {}, + ...navStateWithNarrow(HOME_NARROW), + narrows: { + [streamNarrowStr]: [1], + }, + messages: { + [1]: eg.streamMessage({ id: 1 }) /* eslint-disable-line no-useless-computed-key */, }, - ], - }); + users: [eg.makeUser({ name: 'bob', is_active: true })], + }), + ); - store.dispatch(doNarrow(privateNarrow('a@a.com'))); + store.dispatch(doNarrow(privateNarrow(inactiveUser.email))); const actions = store.getActions(); expect(actions).toHaveLength(0); diff --git a/src/pm-conversations/__tests__/pmConversationsSelectors-test.js b/src/pm-conversations/__tests__/pmConversationsSelectors-test.js index f665209a1ac..a507aa02b3d 100644 --- a/src/pm-conversations/__tests__/pmConversationsSelectors-test.js +++ b/src/pm-conversations/__tests__/pmConversationsSelectors-test.js @@ -1,17 +1,21 @@ -import deepFreeze from 'deep-freeze'; - +/* @flow strict-local */ import { getRecentConversations } from '../pmConversationsSelectors'; import { ALL_PRIVATE_NARROW_STR } from '../../utils/narrow'; +import * as eg from '../../__tests__/lib/exampleData'; describe('getRecentConversations', () => { + const userJohn = { ...eg.makeUser({ name: 'John' }), user_id: 1 }; + const userMark = { ...eg.makeUser({ name: 'Mark' }), user_id: 2 }; + test('when no messages, return no conversations', () => { - const state = deepFreeze({ - realm: { email: 'me@example.com' }, - users: [{ user_id: 0, email: 'me@example.com' }], + const state = eg.reduxState({ + realm: eg.realmState({ email: eg.selfUser.email }), + users: [eg.selfUser], narrows: { [ALL_PRIVATE_NARROW_STR]: [], }, unread: { + ...eg.baseReduxState.unread, pms: [], huddles: [], }, @@ -23,75 +27,86 @@ describe('getRecentConversations', () => { }); test('returns unique list of recipients, includes conversations with self', () => { - const state = deepFreeze({ - realm: { email: 'me@example.com' }, - users: [ - { user_id: 0, email: 'me@example.com' }, - { user_id: 1, email: 'john@example.com' }, - { user_id: 2, email: 'mark@example.com' }, + const meAndJohnPm1 = eg.pmMessage({ + id: 1, + display_recipient: [ + eg.displayRecipientFromUser(eg.selfUser), + eg.displayRecipientFromUser(userJohn), + ], + }); + + const meAndMarkPm = eg.pmMessage({ + id: 2, + display_recipient: [ + eg.displayRecipientFromUser(eg.selfUser), + eg.displayRecipientFromUser(userMark), ], + }); + + const meAndJohnPm2 = eg.pmMessage({ + id: 3, + display_recipient: [ + eg.displayRecipientFromUser(eg.selfUser), + eg.displayRecipientFromUser(userJohn), + ], + }); + + const meOnlyPm = eg.pmMessage({ + id: 4, + display_recipient: [eg.displayRecipientFromUser(eg.selfUser)], + }); + + const meJohnAndMarkPm = eg.pmMessage({ + id: 0, + display_recipient: [ + eg.displayRecipientFromUser(eg.selfUser), + eg.displayRecipientFromUser(userMark), + eg.displayRecipientFromUser(userJohn), + ], + }); + + const state = eg.reduxState({ + realm: eg.realmState({ email: eg.selfUser.email }), + users: [eg.selfUser, userJohn, userMark], narrows: { - [ALL_PRIVATE_NARROW_STR]: [0, 1, 2, 3, 4], + [ALL_PRIVATE_NARROW_STR]: [ + meJohnAndMarkPm.id, + meAndJohnPm1.id, + meAndMarkPm.id, + meAndJohnPm2.id, + meOnlyPm.id, + ], }, messages: { - 1: { - id: 1, - type: 'private', - display_recipient: [ - { id: 0, email: 'me@example.com' }, - { id: 1, email: 'john@example.com' }, - ], - }, - 2: { - id: 2, - type: 'private', - display_recipient: [ - { id: 0, email: 'me@example.com' }, - { id: 2, email: 'mark@example.com' }, - ], - }, - 3: { - id: 3, - type: 'private', - display_recipient: [ - { id: 0, email: 'me@example.com' }, - { id: 1, email: 'john@example.com' }, - ], - }, - 4: { - id: 4, - type: 'private', - display_recipient: [{ id: 0, email: 'me@example.com' }], - }, - 0: { - id: 0, - type: 'private', - display_recipient: [ - { id: 0, email: 'me@example.com' }, - { id: 1, email: 'john@example.com' }, - { id: 2, email: 'mark@example.com' }, - ], - }, + [meAndJohnPm1.id]: meAndJohnPm1, + [meAndMarkPm.id]: meAndMarkPm, + [meAndJohnPm2.id]: meAndJohnPm2, + [meOnlyPm.id]: meOnlyPm, + [meJohnAndMarkPm.id]: meJohnAndMarkPm, }, unread: { + ...eg.baseReduxState.unread, pms: [ { - sender_id: 0, - unread_message_ids: [4], + sender_id: eg.selfUser.user_id, + unread_message_ids: [meOnlyPm.id], }, { - sender_id: 1, - unread_message_ids: [1, 3], + sender_id: userJohn.user_id, + unread_message_ids: [meAndJohnPm1.id, meAndJohnPm2.id], }, { - sender_id: 2, - unread_message_ids: [2], + sender_id: userMark.user_id, + unread_message_ids: [meAndMarkPm.id], }, ], huddles: [ { - user_ids_string: '0,1,2', - unread_message_ids: [5], + user_ids_string: [eg.selfUser.user_id, userJohn.user_id, userMark.user_id] + .sort() + .map(String) + .join(','), + unread_message_ids: [5], // TODO: where does this come from??? }, ], }, @@ -99,27 +114,30 @@ describe('getRecentConversations', () => { const expectedResult = [ { - ids: '0', - recipients: 'me@example.com', - msgId: 4, + ids: eg.selfUser.user_id.toString(), + recipients: eg.selfUser.email, + msgId: meOnlyPm.id, unread: 1, }, { - ids: '1', - recipients: 'john@example.com', - msgId: 3, + ids: userJohn.user_id.toString(), + recipients: userJohn.email, + msgId: meAndJohnPm2.id, unread: 2, }, { - ids: '2', - recipients: 'mark@example.com', - msgId: 2, + ids: userMark.user_id.toString(), + recipients: userMark.email, + msgId: meAndMarkPm.id, unread: 1, }, { - ids: '0,1,2', - recipients: 'john@example.com,mark@example.com', - msgId: 0, + ids: [eg.selfUser.user_id, userJohn.user_id, userMark.user_id] + .sort() + .map(String) + .join(','), + recipients: [userJohn.email, userMark.email].sort().join(','), + msgId: meJohnAndMarkPm.id, unread: 1, }, ]; @@ -130,83 +148,99 @@ describe('getRecentConversations', () => { }); test('returns recipients sorted by last activity', () => { - const state = deepFreeze({ - realm: { email: 'me@example.com' }, - users: [ - { user_id: 0, email: 'me@example.com' }, - { user_id: 1, email: 'john@example.com' }, - { user_id: 2, email: 'mark@example.com' }, + // Maybe we can share these definitions with the above test; + // first, we have to sort out why the IDs are different. + + const meAndMarkPm1 = eg.pmMessage({ + id: 1, + display_recipient: [ + eg.displayRecipientFromUser(eg.selfUser), + eg.displayRecipientFromUser(userMark), ], + }); + + const meAndJohnPm1 = eg.pmMessage({ + id: 2, + display_recipient: [ + eg.displayRecipientFromUser(eg.selfUser), + eg.displayRecipientFromUser(userJohn), + ], + }); + + const meAndMarkPm2 = eg.pmMessage({ + id: 3, + display_recipient: [ + eg.displayRecipientFromUser(eg.selfUser), + eg.displayRecipientFromUser(userMark), + ], + }); + + const meAndJohnPm2 = eg.pmMessage({ + id: 4, + display_recipient: [ + eg.displayRecipientFromUser(eg.selfUser), + eg.displayRecipientFromUser(userJohn), + ], + }); + + const meJohnAndMarkPm = eg.pmMessage({ + id: 5, + display_recipient: [ + eg.displayRecipientFromUser(eg.selfUser), + eg.displayRecipientFromUser(userJohn), + eg.displayRecipientFromUser(userMark), + ], + }); + + const meOnlyPm = eg.pmMessage({ + id: 6, + display_recipient: [eg.displayRecipientFromUser(eg.selfUser)], + }); + + const state = eg.reduxState({ + realm: eg.realmState({ email: eg.selfUser.email }), + users: [eg.selfUser, userJohn, userMark], narrows: { - [ALL_PRIVATE_NARROW_STR]: [1, 2, 3, 4, 5, 6], + [ALL_PRIVATE_NARROW_STR]: [ + meAndMarkPm1.id, + meAndJohnPm1.id, + meAndMarkPm2.id, + meAndJohnPm2.id, + meJohnAndMarkPm.id, + meOnlyPm.id, + ], }, messages: { - 2: { - id: 2, - type: 'private', - display_recipient: [ - { id: 0, email: 'me@example.com' }, - { id: 1, email: 'john@example.com' }, - ], - }, - 1: { - id: 1, - type: 'private', - display_recipient: [ - { id: 0, email: 'me@example.com' }, - { id: 2, email: 'mark@example.com' }, - ], - }, - 4: { - id: 4, - type: 'private', - display_recipient: [ - { id: 0, email: 'me@example.com' }, - { id: 1, email: 'john@example.com' }, - ], - }, - 3: { - id: 3, - type: 'private', - display_recipient: [ - { id: 0, email: 'me@example.com' }, - { id: 2, email: 'mark@example.com' }, - ], - }, - 5: { - id: 5, - type: 'private', - display_recipient: [ - { id: 0, email: 'me@example.com' }, - { id: 1, email: 'john@example.com' }, - { id: 2, email: 'mark@example.com' }, - ], - }, - 6: { - id: 6, - type: 'private', - display_recipient: [{ id: 0, email: 'me@example.com' }], - }, + [meAndJohnPm1.id]: meAndJohnPm1, + [meAndMarkPm1.id]: meAndMarkPm1, + [meAndJohnPm2.id]: meAndJohnPm2, + [meAndMarkPm2.id]: meAndMarkPm2, + [meJohnAndMarkPm.id]: meJohnAndMarkPm, + [meOnlyPm.id]: meOnlyPm, }, unread: { + ...eg.baseReduxState.unread, pms: [ { - sender_id: 0, - unread_message_ids: [4], + sender_id: eg.selfUser.user_id, + unread_message_ids: [meAndJohnPm2.id], }, { - sender_id: 1, - unread_message_ids: [1, 3], + sender_id: userJohn.user_id, + unread_message_ids: [meAndMarkPm1.id, meAndMarkPm2.id], }, { - sender_id: 2, - unread_message_ids: [2], + sender_id: userMark.user_id, + unread_message_ids: [meAndJohnPm1.id], }, ], huddles: [ { - user_ids_string: '0,1,2', - unread_message_ids: [5], + user_ids_string: [eg.selfUser.user_id, userJohn.user_id, userMark.user_id] + .sort() + .map(String) + .join(','), + unread_message_ids: [meJohnAndMarkPm.id], }, ], }, @@ -214,27 +248,30 @@ describe('getRecentConversations', () => { const expectedResult = [ { - ids: '0', - recipients: 'me@example.com', - msgId: 6, + ids: eg.selfUser.user_id.toString(), + recipients: eg.selfUser.email, + msgId: meOnlyPm.id, unread: 1, }, { - ids: '0,1,2', - recipients: 'john@example.com,mark@example.com', - msgId: 5, + ids: [eg.selfUser.user_id, userJohn.user_id, userMark.user_id] + .sort() + .map(String) + .join(','), + recipients: [userJohn.email, userMark.email].sort().join(','), + msgId: meJohnAndMarkPm.id, unread: 1, }, { - ids: '1', - recipients: 'john@example.com', - msgId: 4, + ids: userJohn.user_id.toString(), + recipients: userJohn.email, + msgId: meAndJohnPm2.id, unread: 2, }, { - ids: '2', - recipients: 'mark@example.com', - msgId: 3, + ids: userMark.user_id.toString(), + recipients: userMark.email, + msgId: meAndMarkPm2.id, unread: 1, }, ]; diff --git a/src/reduxTypes.js b/src/reduxTypes.js index a7533442acb..d1466a3d086 100644 --- a/src/reduxTypes.js +++ b/src/reduxTypes.js @@ -153,9 +153,11 @@ export type FlagName = $Keys; * See also `NarrowsState`, which is an index on this data that identifies * messages belonging to a given narrow. */ -export type MessagesState = {| +export type MessagesState = { + // MessagesState should be exact; we're waiting for Flow v0.111.0. See note + // in src/utils/jsonable.js. [id: number]: $Exact, -|}; +}; export type MigrationsState = {| version?: string, diff --git a/src/topics/__tests__/topicsSelectors-test.js b/src/topics/__tests__/topicsSelectors-test.js index 1c7b3e0ca22..ede0a76e10a 100644 --- a/src/topics/__tests__/topicsSelectors-test.js +++ b/src/topics/__tests__/topicsSelectors-test.js @@ -1,11 +1,11 @@ -import deepFreeze from 'deep-freeze'; - +/* @flow strict-local */ import { getTopicsForNarrow, getLastMessageTopic, getTopicsForStream } from '../topicSelectors'; import { HOME_NARROW, streamNarrow } from '../../utils/narrow'; +import * as eg from '../../__tests__/lib/exampleData'; describe('getTopicsForNarrow', () => { test('when no topics return an empty list', () => { - const state = deepFreeze({}); + const state = eg.reduxState(); const topics = getTopicsForNarrow(state, HOME_NARROW); @@ -13,14 +13,15 @@ describe('getTopicsForNarrow', () => { }); test('when there are topics in the active narrow, return them as string array', () => { - const state = deepFreeze({ - streams: [{ stream_id: 123, name: 'hello' }], + const stream = { ...eg.makeStream({ name: 'hello' }), stream_id: 123 }; + const state = eg.reduxState({ + streams: [stream], topics: { - 123: [{ name: 'hi' }, { name: 'wow' }], + [stream.stream_id]: [{ name: 'hi', max_id: 123 }, { name: 'wow', max_id: 234 }], }, }); - const topics = getTopicsForNarrow(state, streamNarrow('hello')); + const topics = getTopicsForNarrow(state, streamNarrow(stream.name)); expect(topics).toEqual(['hi', 'wow']); }); @@ -28,7 +29,7 @@ describe('getTopicsForNarrow', () => { describe('getLastMessageTopic', () => { test('when no messages in narrow return an empty string', () => { - const state = deepFreeze({ + const state = eg.reduxState({ narrows: {}, }); @@ -39,32 +40,32 @@ describe('getLastMessageTopic', () => { test('when one or more messages return the topic of the last one', () => { const narrow = streamNarrow('hello'); - const state = deepFreeze({ - flags: { - mentioned: {}, - }, + const message1 = eg.streamMessage({ id: 1 }); + const message2 = eg.streamMessage({ id: 2, subject: 'some topic' }); + const state = eg.reduxState({ narrows: { [JSON.stringify(narrow)]: [1, 2], }, messages: { - 1: { id: 1 }, - 2: { id: 2, subject: 'some topic' }, + [message1.id]: message1, + [message2.id]: message2, }, }); const topic = getLastMessageTopic(state, narrow); - expect(topic).toEqual('some topic'); + expect(topic).toEqual(message2.subject); }); }); describe('getTopicsForStream', () => { test('when no topics loaded for given stream return undefined', () => { - const state = deepFreeze({ + const state = eg.reduxState({ streams: [], topics: {}, mute: [], unread: { + ...eg.baseReduxState.unread, streams: [], }, }); @@ -75,27 +76,31 @@ describe('getTopicsForStream', () => { }); test('when topics loaded for given stream return them', () => { - const state = deepFreeze({ - streams: [{ stream_id: 123, name: 'stream 123' }], + const stream = { ...eg.makeStream({ name: 'stream 123' }), stream_id: 123 }; + const state = eg.reduxState({ + streams: [stream], topics: { - 123: [{ name: 'topic', max_id: 456 }], + [stream.stream_id]: [{ name: 'topic', max_id: 456 }], }, mute: [], unread: { + ...eg.baseReduxState.unread, streams: [], }, }); - const topics = getTopicsForStream(state, 123); + const topics = getTopicsForStream(state, stream.stream_id); expect(topics).toEqual([{ name: 'topic', max_id: 456, isMuted: false, unreadCount: 0 }]); }); test('Return list of topic object with isMuted, unreadCount, topic name and max id in it.', () => { - const state = deepFreeze({ - streams: [{ stream_id: 1, name: 'stream 1' }], + const stream = { ...eg.makeStream({ name: 'stream 1' }), stream_id: 1 }; + + const state = eg.reduxState({ + streams: [stream], topics: { - 1: [ + [stream.stream_id]: [ { name: 'topic 1', max_id: 5 }, { name: 'topic 2', max_id: 6 }, { name: 'topic 3', max_id: 7 }, @@ -105,6 +110,7 @@ describe('getTopicsForStream', () => { }, mute: [['stream 1', 'topic 1'], ['stream 1', 'topic 3'], ['stream 2', 'topic 2']], unread: { + ...eg.baseReduxState.unread, streams: [ { stream_id: 1, diff --git a/src/utils/testHelpers.js b/src/utils/testHelpers.js index 57f7e0a7a65..e913b89283a 100644 --- a/src/utils/testHelpers.js +++ b/src/utils/testHelpers.js @@ -1,11 +1,20 @@ /* @flow strict-local */ import type { Narrow } from '../types'; +import type { NavigationState } from '../reduxTypes'; -export const navStateWithNarrow = (narrow: Narrow): {| nav: mixed |} => ({ +export const navStateWithNarrow = (narrow: Narrow): {| nav: NavigationState |} => ({ nav: { - index: 0, + key: 'StackRouterRoot', + index: 1, + isTransitioning: false, routes: [ { + key: 'id-1592948746166-1', + params: {}, + routeName: 'main', + }, + { + key: 'id-1592948746166-2', routeName: 'chat', params: { narrow,