mirror of
https://github.com/mattermost/mattermost.git
synced 2026-02-03 20:40:00 -05:00
MM-67269 - Fix popout windows for subpath deployments (#35027)
* MM-67269 - Fix popout windows for subpath deployments Popout windows were failing with 404 errors when Mattermost is served from a subpath (e.g., https://company.com/mattermost). The popout functions were constructing URLs without including the subpath prefix. Changes: - Updated popoutThread() and popoutRhsPlugin() to use getBasePath() helper function which includes window.basename - Added unit tests to verify popout URLs include subpath when configured - Follows established pattern used throughout codebase (getSiteURL, cookie paths, React Router) This ensures popout windows open at the correct URL: /subpath/_popout/... instead of /_popout/... Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com> * Clear mocks and set default base path in tests --------- Co-authored-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com> Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
parent
eeaf9c8e3e
commit
fb22f56635
2 changed files with 46 additions and 2 deletions
|
|
@ -2,6 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import DesktopApp from 'utils/desktop_api';
|
||||
import {getBasePath} from 'utils/url';
|
||||
import {isDesktopApp} from 'utils/user_agent';
|
||||
|
||||
import {FOCUS_REPLY_POST, popoutRhsPlugin, popoutThread} from './popout_windows';
|
||||
|
|
@ -20,6 +21,10 @@ jest.mock('utils/user_agent', () => ({
|
|||
isDesktopApp: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('utils/url', () => ({
|
||||
getBasePath: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('./browser_popouts', () => {
|
||||
const mockFn = jest.fn();
|
||||
(globalThis as typeof globalThis & {mockSetupBrowserPopout: typeof mockFn}).mockSetupBrowserPopout = mockFn;
|
||||
|
|
@ -33,6 +38,7 @@ jest.mock('./browser_popouts', () => {
|
|||
|
||||
const mockDesktopApp = DesktopApp as jest.Mocked<typeof DesktopApp>;
|
||||
const mockIsDesktopApp = isDesktopApp as jest.MockedFunction<typeof isDesktopApp>;
|
||||
const mockGetBasePath = getBasePath as jest.MockedFunction<typeof getBasePath>;
|
||||
|
||||
const getMockSetupBrowserPopout = () => {
|
||||
return (globalThis as typeof globalThis & {mockSetupBrowserPopout: jest.MockedFunction<() => unknown>}).mockSetupBrowserPopout;
|
||||
|
|
@ -42,6 +48,9 @@ describe('popout_windows', () => {
|
|||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
getMockSetupBrowserPopout().mockClear();
|
||||
|
||||
// Default: no subpath
|
||||
mockGetBasePath.mockReturnValue('');
|
||||
});
|
||||
|
||||
describe('popoutThread', () => {
|
||||
|
|
@ -121,6 +130,23 @@ describe('popout_windows', () => {
|
|||
expect(mockOnFocusPost).toHaveBeenCalledTimes(1);
|
||||
expect(mockOnFocusPost).toHaveBeenCalledWith('post-123', '/team/pl/post-123');
|
||||
});
|
||||
|
||||
it('should include subpath in popout URL when basename is set', async () => {
|
||||
mockIsDesktopApp.mockReturnValue(false);
|
||||
mockGetBasePath.mockReturnValue('/company/mattermost');
|
||||
const mockListeners = {
|
||||
sendToPopout: jest.fn(),
|
||||
onMessageFromPopout: jest.fn(),
|
||||
onClosePopout: jest.fn(),
|
||||
};
|
||||
getMockSetupBrowserPopout().mockReturnValue(mockListeners);
|
||||
|
||||
await popoutThread('Thread - {channelName} - {teamName} - {serverName}', 'thread-123', 'test-team', mockOnFocusPost);
|
||||
|
||||
expect(getMockSetupBrowserPopout()).toHaveBeenCalledWith(
|
||||
'/company/mattermost/_popout/thread/test-team/thread-123',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('popoutRhsPlugin', () => {
|
||||
|
|
@ -173,6 +199,23 @@ describe('popout_windows', () => {
|
|||
|
||||
expect(result).toEqual(mockListeners);
|
||||
});
|
||||
|
||||
it('should include subpath in popout URL when basename is set', async () => {
|
||||
mockIsDesktopApp.mockReturnValue(false);
|
||||
mockGetBasePath.mockReturnValue('/company/mattermost');
|
||||
const mockListeners = {
|
||||
sendToPopout: jest.fn(),
|
||||
onMessageFromPopout: jest.fn(),
|
||||
onClosePopout: jest.fn(),
|
||||
};
|
||||
getMockSetupBrowserPopout().mockReturnValue(mockListeners);
|
||||
|
||||
await popoutRhsPlugin('{pluginDisplayName} - {serverName}', 'test-plugin-id', 'test-team', 'test-channel');
|
||||
|
||||
expect(getMockSetupBrowserPopout()).toHaveBeenCalledWith(
|
||||
'/company/mattermost/_popout/rhs/test-team/test-channel/plugin/test-plugin-id',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import type {PopoutViewProps} from '@mattermost/desktop-api';
|
|||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
import DesktopApp from 'utils/desktop_api';
|
||||
import {getBasePath} from 'utils/url';
|
||||
import {isDesktopApp} from 'utils/user_agent';
|
||||
|
||||
import BrowserPopouts from './browser_popouts';
|
||||
|
|
@ -27,7 +28,7 @@ export async function popoutThread(
|
|||
onFocusPost: (postId: string, returnTo: string) => void,
|
||||
) {
|
||||
const popoutListeners = await popout(
|
||||
`/_popout/thread/${teamName}/${threadId}`,
|
||||
`${getBasePath()}/_popout/thread/${teamName}/${threadId}`,
|
||||
{
|
||||
isRHS: true,
|
||||
titleTemplate,
|
||||
|
|
@ -54,7 +55,7 @@ export async function popoutRhsPlugin(
|
|||
channelName: string,
|
||||
) {
|
||||
const listeners = await popout(
|
||||
`/_popout/rhs/${teamName}/${channelName}/plugin/${pluginId}`,
|
||||
`${getBasePath()}/_popout/rhs/${teamName}/${channelName}/plugin/${pluginId}`,
|
||||
{
|
||||
isRHS: true,
|
||||
titleTemplate,
|
||||
|
|
|
|||
Loading…
Reference in a new issue