diff --git a/webapp/channels/src/components/post_view/post_attachment_container/post_attachment_container.test.tsx b/webapp/channels/src/components/post_view/post_attachment_container/post_attachment_container.test.tsx index 87ca407d2e..d9678ffead 100644 --- a/webapp/channels/src/components/post_view/post_attachment_container/post_attachment_container.test.tsx +++ b/webapp/channels/src/components/post_view/post_attachment_container/post_attachment_container.test.tsx @@ -12,6 +12,8 @@ import PostAttachmentContainer from './post_attachment_container'; import type {Props} from './post_attachment_container'; describe('PostAttachmentContainer', () => { + const mockHistoryPush = jest.fn(); + const baseProps: Props = { children:

{'some children'}

, className: 'permalink', @@ -31,11 +33,19 @@ describe('PostAttachmentContainer', () => { }, posts: {posts: {}}, preferences: {myPreferences: {}}, - }, - }; + beforeEach(() => { + mockHistoryPush.mockClear(); + + // Mock useHistory + jest.doMock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({push: mockHistoryPush}), + })); + }); + test('should render correctly', () => { renderWithContext( , initialState, @@ -48,4 +58,58 @@ describe('PostAttachmentContainer', () => { expect(screen.getByText('some children')).toBeInTheDocument(); }); + + test('should handle clicks on elements with non-string className without throwing error', () => { + renderWithContext( + , initialState, + ); + + const button = screen.getByRole('button'); + + // Create a real DOM element and modify its className to be an object + const mockElement = document.createElement('div'); + Object.defineProperty(mockElement, 'className', { + value: {baseVal: 'some-class'}, + writable: false, + }); + Object.defineProperty(mockElement, 'tagName', { + value: 'DIV', + writable: false, + }); + + const mockEvent = { + target: mockElement, + stopPropagation: jest.fn(), + } as any; + + // This should not throw the "className.includes is not a function" error + expect(() => { + button.onclick?.(mockEvent); + }).not.toThrow(); + }); + + test('should handle clicks on elements without className property', () => { + renderWithContext( + , initialState, + ); + + const button = screen.getByRole('button'); + + // Create a real DOM element and remove its className + const mockElement = document.createElement('div'); + delete (mockElement as any).className; + Object.defineProperty(mockElement, 'tagName', { + value: 'DIV', + writable: false, + }); + + const mockEvent = { + target: mockElement, + stopPropagation: jest.fn(), + } as any; + + expect(() => { + button.onclick?.(mockEvent); + }).not.toThrow(); + }); }); diff --git a/webapp/channels/src/components/post_view/post_attachment_container/post_attachment_container.tsx b/webapp/channels/src/components/post_view/post_attachment_container/post_attachment_container.tsx index 7d546288cf..027dc62504 100644 --- a/webapp/channels/src/components/post_view/post_attachment_container/post_attachment_container.tsx +++ b/webapp/channels/src/components/post_view/post_attachment_container/post_attachment_container.tsx @@ -31,6 +31,22 @@ const getTeamAndPostIdFromLink = (link: string) => { return match?.params; }; +const getElementClassName = (element: EventTarget | null): string => { + if (!element || !(element instanceof Element)) { + return ''; + } + + if (typeof element.className === 'object' && 'baseVal' in element.className) { + return (element.className as any).baseVal; + } + + if (typeof element.className === 'string') { + return element.className; + } + + return ''; +}; + const PostAttachmentContainer = (props: Props) => { const {children, className, link, preventClickAction} = props; const history = useHistory(); @@ -48,11 +64,12 @@ const PostAttachmentContainer = (props: Props) => { const {tagName} = e.target; e.stopPropagation(); const elements = ['A', 'IMG', 'BUTTON', 'I']; + const targetClassName = getElementClassName(e.target); if ( !elements.includes(tagName) && e.target.getAttribute('role') !== 'button' && - e.target.className !== `attachment attachment--${className}` + targetClassName !== `attachment attachment--${className}` ) { const classNames = [ 'icon icon-menu-down', @@ -65,7 +82,7 @@ const PostAttachmentContainer = (props: Props) => { dispatch(focusPost(params.postId, link, currentUserId, {skipRedirectReplyPermalink: true})); return; } - if (!classNames.some((className) => e.target.className.includes(className)) && e.target.id !== 'image-name-text') { + if (!classNames.some((className) => targetClassName.includes(className)) && e.target.id !== 'image-name-text') { history.push(link); } }