diff --git a/webapp/channels/src/components/commercial_support_modal/__snapshots__/commercial_support_modal.test.tsx.snap b/webapp/channels/src/components/commercial_support_modal/__snapshots__/commercial_support_modal.test.tsx.snap
index 62c963d8ae..55bd21b256 100644
--- a/webapp/channels/src/components/commercial_support_modal/__snapshots__/commercial_support_modal.test.tsx.snap
+++ b/webapp/channels/src/components/commercial_support_modal/__snapshots__/commercial_support_modal.test.tsx.snap
@@ -60,22 +60,6 @@ exports[`components/CommercialSupportModal should match snapshot 1`] = `
}
}
/>
-
- }
- mode="info"
- onDismiss={[Function]}
- />
@@ -86,6 +70,26 @@ exports[`components/CommercialSupportModal should match snapshot 1`] = `
/>
+
diff --git a/webapp/channels/src/components/commercial_support_modal/commercial_support_modal.scss b/webapp/channels/src/components/commercial_support_modal/commercial_support_modal.scss
index 31655eb01f..94c91dbd75 100644
--- a/webapp/channels/src/components/commercial_support_modal/commercial_support_modal.scss
+++ b/webapp/channels/src/components/commercial_support_modal/commercial_support_modal.scss
@@ -24,3 +24,13 @@
font-family: Metropolis, sans-serif;
font-weight: 600 !important;
}
+
+.CommercialSupportModal__error {
+ margin-top: 12px;
+
+ .error-text {
+ display: inline-block;
+ color: var(--error-text);
+ word-break: break-word;
+ }
+}
diff --git a/webapp/channels/src/components/commercial_support_modal/commercial_support_modal.test.tsx b/webapp/channels/src/components/commercial_support_modal/commercial_support_modal.test.tsx
index 2954d0d2c2..dc0a4e68be 100644
--- a/webapp/channels/src/components/commercial_support_modal/commercial_support_modal.test.tsx
+++ b/webapp/channels/src/components/commercial_support_modal/commercial_support_modal.test.tsx
@@ -4,22 +4,94 @@
import {shallow} from 'enzyme';
import React from 'react';
+import {Client4} from 'mattermost-redux/client';
+
import CommercialSupportModal from 'components/commercial_support_modal/commercial_support_modal';
import {TestHelper} from 'utils/test_helper';
describe('components/CommercialSupportModal', () => {
+ beforeAll(() => {
+ // Mock getSystemRoute to return a valid URL
+ jest.spyOn(Client4, 'getSystemRoute').mockImplementation(() => 'http://localhost:8065/api/v4/system');
+
+ // Mock createObjectURL
+ window.URL.createObjectURL = jest.fn().mockReturnValue('mock-url');
+ });
+
+ afterAll(() => {
+ jest.restoreAllMocks();
+
+ // @ts-expect-error - TS doesn't like deleting built-in methods
+ delete window.URL.createObjectURL;
+ });
+
+ const baseProps = {
+ onExited: jest.fn(),
+ showBannerWarning: false,
+ isCloud: false,
+ currentUser: TestHelper.getUserMock(),
+ packetContents: [
+ {id: 'basic.server.logs', label: 'Server Logs', selected: true, mandatory: true},
+ ],
+ };
+
test('should match snapshot', () => {
- const mockUser = TestHelper.getUserMock();
- const wrapper = shallow(
-
,
- );
+ const wrapper = shallow(
);
expect(wrapper).toMatchSnapshot();
});
+
+ test('should show error message when download fails', async () => {
+ const errorMessage = 'Failed to download';
+ const detailedError = 'Permission denied';
+
+ // Mock the fetch call to return an error
+ global.fetch = jest.fn().mockImplementation(() =>
+ Promise.resolve({
+ ok: false,
+ json: () => Promise.resolve({
+ message: errorMessage,
+ detailed_error: detailedError,
+ }),
+ }),
+ );
+
+ const wrapper = shallow
();
+
+ // Trigger download
+ const instance = wrapper.instance();
+ await instance.downloadSupportPacket();
+ wrapper.update();
+
+ // Verify error message is shown
+ const errorDiv = wrapper.find('.CommercialSupportModal__error');
+ expect(errorDiv.exists()).toBe(true);
+ expect(errorDiv.find('.error-text').text()).toBe(`${errorMessage}: ${detailedError}`);
+
+ // Verify loading state is reset
+ expect(wrapper.state('loading')).toBe(false);
+ });
+
+ test('should clear error when starting new download', async () => {
+ // Mock the fetch call to succeed
+ global.fetch = jest.fn().mockImplementation(() =>
+ Promise.resolve({
+ ok: true,
+ blob: () => Promise.resolve(new Blob()),
+ headers: {get: () => null},
+ }),
+ );
+
+ const wrapper = shallow();
+
+ // Set initial error state
+ wrapper.setState({error: 'Previous error'});
+
+ // Start download
+ const instance = wrapper.instance();
+ await instance.downloadSupportPacket();
+
+ // Verify error is cleared
+ expect(wrapper.state('error')).toBeUndefined();
+ });
});
diff --git a/webapp/channels/src/components/commercial_support_modal/commercial_support_modal.tsx b/webapp/channels/src/components/commercial_support_modal/commercial_support_modal.tsx
index e7f1d2c87d..63d7aa1837 100644
--- a/webapp/channels/src/components/commercial_support_modal/commercial_support_modal.tsx
+++ b/webapp/channels/src/components/commercial_support_modal/commercial_support_modal.tsx
@@ -39,6 +39,7 @@ type State = {
showBannerWarning: boolean;
packetContents: SupportPacketContent[];
loading: boolean;
+ error?: string;
};
export default class CommercialSupportModal extends React.PureComponent {
@@ -106,11 +107,18 @@ export default class CommercialSupportModal extends React.PureComponent {
- this.setState({loading: true});
+ this.setState({loading: true, error: undefined});
const res = await fetch(this.genereateDownloadURLWithParams(), {
method: 'GET',
headers: {'Content-Type': 'application/zip'},
});
+ if (!res.ok) {
+ const data = await res.json();
+ const error = data.message + ': ' + data.detailed_error;
+ this.setState({loading: false, error});
+ return;
+ }
+
const blob = await res.blob();
this.setState({loading: false});
@@ -214,6 +222,11 @@ export default class CommercialSupportModal extends React.PureComponent
))}
+ {this.state.error && (
+
+ {this.state.error}
+
+ )}