MM-64250 - solve js eror while downloading images (#33552) (#33589)

Automatic Merge
Этот коммит содержится в:
Mattermost Build
2025-07-30 15:34:00 +03:00
коммит произвёл GitHub
родитель 7eda0e799c
Коммит 6cbcf4e290
2 изменённых файлов: 85 добавлений и 4 удалений

Просмотреть файл

@@ -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: <p>{'some children'}</p>,
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(
<PostAttachmentContainer {...baseProps}/>, 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(
<PostAttachmentContainer {...baseProps}/>, 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(
<PostAttachmentContainer {...baseProps}/>, 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();
});
});

Просмотреть файл

@@ -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);
}
}