Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 31 additions & 12 deletions shared/chat/inbox/reselect-conversation.test.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
/// <reference types="jest" />
import * as T from '@/constants/types'
import * as Tabs from '@/constants/tabs'
import {resetAllStores} from '@/util/zustand'
import {useConfigState} from '@/stores/config'

jest.mock('@/constants/router', () => ({
getModalStack: jest.fn(() => []),
getVisibleScreen: jest.fn(() => undefined),
navigateToInbox: jest.fn(),
navigateToThread: jest.fn(),
}))

jest.mock('@/constants/chat/common', () => ({
...jest.requireActual('@/constants/chat/common'),
getSelectedConversation: jest.fn(),
}))

import * as Common from '@/constants/chat/common'
import {navigateToInbox, navigateToThread} from '@/constants/router'
import {installFakeNavigator, makeRootState, restoreNavigator, type FakeNavigator} from '@/test/fake-navigator'
import {maybeChangeSelectedConversation} from './metadata'

const newConvID = 'ff00ff00'
Expand All @@ -25,12 +19,21 @@ const mockedSelected = Common.getSelectedConversation as jest.Mock
const layout = (over: Partial<T.RPCChat.UIInboxReselectInfo>): T.RPCChat.UIInboxLayout =>
({reselectInfo: {oldConvID: '', ...over}}) as T.RPCChat.UIInboxLayout

let nav: FakeNavigator

// navigateToInbox defers a tick, so every assertion below has to let that tick run.
const runDeferredNavigation = () => jest.advanceTimersByTime(1)

beforeEach(() => {
jest.useFakeTimers()
nav = installFakeNavigator()
useConfigState.setState({loggedIn: true})
global.isMobile = true
})

afterEach(() => {
restoreNavigator()
jest.useRealTimers()
jest.clearAllMocks()
resetAllStores()
global.isMobile = false
Expand All @@ -45,16 +48,17 @@ test('a reselect while a conversation creation is pending does not pop to the in

maybeChangeSelectedConversation(layout({newConvID}))

expect(navigateToInbox).not.toHaveBeenCalled()
expect(navigateToThread).not.toHaveBeenCalled()
runDeferredNavigation()
expect(nav.actions).toEqual([])
})

test('a reselect while the create error screen is up does not pop to the inbox', () => {
mockedSelected.mockReturnValue(T.Chat.pendingErrorConversationIDKey)

maybeChangeSelectedConversation(layout({newConvID}))

expect(navigateToInbox).not.toHaveBeenCalled()
runDeferredNavigation()
expect(nav.actions).toEqual([])
})

// the real "we are on a dead conversation" case still has to bounce
Expand All @@ -63,5 +67,20 @@ test('a reselect with nothing selected still goes to the inbox on mobile', () =>

maybeChangeSelectedConversation(layout({newConvID}))

expect(navigateToInbox).toHaveBeenCalledWith(false)
runDeferredNavigation()
// navigateToInbox(false): stay on the chat tab and pop its stack back to the inbox
expect(nav.types()).toContain('POP_TO')
expect(nav.lastAction()?.payload).toMatchObject({name: 'chatRoot'})
})

// The bounce is navigateToInbox(false): it must not pull the user off whatever tab they
// are on. Only the chat tab's own stack gets popped.
test('a reselect while another tab is up leaves that tab alone', () => {
nav = installFakeNavigator({rootState: makeRootState({tab: Tabs.teamsTab})})
mockedSelected.mockReturnValue(T.Chat.noConversationIDKey)

maybeChangeSelectedConversation(layout({newConvID}))

runDeferredNavigation()
expect(nav.actions).toEqual([])
})
7 changes: 6 additions & 1 deletion shared/common-adapters/name-with-icon.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ jest.mock('@/stores/followers', () => ({
},
}))
jest.mock('@/teams/use-teams-list', () => ({useTeamsListNameToIDMap: () => new Map<string, string>()}))
jest.mock('@/constants/router', () => ({navToProfile: jest.fn()}))
jest.mock('./avatar', () => ({
__esModule: true,
default: ({
Expand Down Expand Up @@ -73,17 +72,22 @@ jest.mock('./text', () => ({
require('react').createElement('span', null, children),
}))

import {installFakeNavigator, restoreNavigator, type FakeNavigator} from '@/test/fake-navigator'
import {NameWithIcon} from './name-with-icon'

const avatar = () => screen.queryByTestId('avatar')
const followIcon = () => screen.queryByTestId('follow-icon')

let nav: FakeNavigator

describe('NameWithIcon', () => {
beforeEach(() => {
mockFollowerReads.length = 0
nav = installFakeNavigator()
})
afterEach(() => {
cleanup()
restoreNavigator()
mockFollowers.followers = new Set()
mockFollowers.following = new Set()
})
Expand Down Expand Up @@ -209,6 +213,7 @@ describe('NameWithIcon', () => {
render(<NameWithIcon username="testuser" onClick={onClick} />)
screen.getByTestId('clickable').click()
expect(onClick).toHaveBeenCalledWith('testuser')
expect(nav.actions).toEqual([])
})

test('no onClick means no clickable wrapper', () => {
Expand Down
3 changes: 3 additions & 0 deletions shared/constants/nav-debug.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Flip to log every navigation the router makes. Its own module so that the router store
// can read it without depending on the navigation adapter.
export const DEBUG_NAV = __DEV__ && (false as boolean)
8 changes: 8 additions & 0 deletions shared/constants/nav-tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ let modalRouteNames: ReadonlySet<string> | undefined
export const setModalRouteNames = (names: Iterable<string>) => {
modalRouteNames = new Set<string>(names)
}
// For tests that register their own: returns a function that puts back the registration as
// it is now, including "not registered".
export const saveModalRouteNames = () => {
const saved = modalRouteNames
return () => {
modalRouteNames = saved
}
}
export const isModalRouteName = (name: string) => {
if (!modalRouteNames) {
throw new Error('modalRouteNames not registered; call setModalRouteNames at startup')
Expand Down
5 changes: 3 additions & 2 deletions shared/constants/navigate-append-once-root-has.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ afterEach(() => {
jest.restoreAllMocks()
})

// Each test pushes distinct params: navigateAppend's module-private `_pendingAppend` dupe cache
// would otherwise swallow a same-shaped push from an earlier test.
// These drive the real container adapter, whose Navigator lives for the whole file. Each test
// pushes distinct params: its in-flight dupe check would otherwise swallow a same-shaped push
// from an earlier test.
const pushOf = (username: string) =>
expect.objectContaining({payload: {name: 'username', params: {username}}, type: 'PUSH'})

Expand Down
Loading