This is an automated email from the ASF dual-hosted git repository.

beto pushed a commit to branch file-handler2
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 80f6f93ea2309709f2f44bf957b8c530714d107c
Author: Beto Dealmeida <[email protected]>
AuthorDate: Wed Nov 19 15:10:38 2025 -0500

    Add tests
---
 .../src/pages/FileHandler/index.test.tsx           | 335 +++++++++++++++++++++
 1 file changed, 335 insertions(+)

diff --git a/superset-frontend/src/pages/FileHandler/index.test.tsx 
b/superset-frontend/src/pages/FileHandler/index.test.tsx
new file mode 100644
index 0000000000..2e9cf6b473
--- /dev/null
+++ b/superset-frontend/src/pages/FileHandler/index.test.tsx
@@ -0,0 +1,335 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { render, screen, waitFor } from 'spec/helpers/testing-library';
+import { MemoryRouter, Route } from 'react-router-dom';
+import FileHandler from './index';
+
+const mockAddDangerToast = jest.fn();
+const mockAddSuccessToast = jest.fn();
+const mockHistoryPush = jest.fn();
+
+// Mock the withToasts HOC
+jest.mock('src/components/MessageToasts/withToasts', () => ({
+  __esModule: true,
+  default: (Component: any) => (props: any) => (
+    <Component
+      {...props}
+      addDangerToast={mockAddDangerToast}
+      addSuccessToast={mockAddSuccessToast}
+    />
+  ),
+}));
+
+// Mock the UploadDataModal
+jest.mock('src/features/databases/UploadDataModel', () => ({
+  __esModule: true,
+  default: ({ show, onHide, type, allowedExtensions }: any) => (
+    <div data-test="upload-modal">
+      <div data-test="modal-show">{show.toString()}</div>
+      <div data-test="modal-type">{type}</div>
+      <div data-test="modal-extensions">{allowedExtensions.join(',')}</div>
+      <button onClick={onHide}>Close</button>
+    </div>
+  ),
+}));
+
+// Mock react-router-dom's useHistory
+jest.mock('react-router-dom', () => ({
+  ...jest.requireActual('react-router-dom'),
+  useHistory: () => ({
+    push: mockHistoryPush,
+  }),
+}));
+
+// Mock the File API
+class MockFile {
+  name: string;
+
+  constructor(name: string) {
+    this.name = name;
+  }
+}
+
+interface MockFileHandle {
+  getFile: () => Promise<MockFile>;
+}
+
+const createMockFileHandle = (fileName: string): MockFileHandle => ({
+  getFile: async () => new MockFile(fileName),
+});
+
+const setupLaunchQueue = (fileHandle: MockFileHandle | null = null) => {
+  let savedConsumer: ((params: any) => void) | null = null;
+  (window as any).launchQueue = {
+    setConsumer: (consumer: (params: any) => void) => {
+      savedConsumer = consumer;
+      // Automatically trigger the consumer if a fileHandle is provided
+      if (fileHandle) {
+        setTimeout(() => {
+          consumer({
+            files: [fileHandle],
+          });
+        }, 0);
+      }
+    },
+  };
+  return {
+    triggerConsumer: (params: any) => {
+      if (savedConsumer) {
+        savedConsumer(params);
+      }
+    },
+  };
+};
+
+beforeEach(() => {
+  jest.clearAllMocks();
+  delete (window as any).launchQueue;
+});
+
+test('shows error when launchQueue is not supported', async () => {
+  render(
+    <MemoryRouter initialEntries={['/file-handler']}>
+      <Route path="/file-handler">
+        <FileHandler />
+      </Route>
+    </MemoryRouter>,
+    { useRedux: true },
+  );
+
+  await waitFor(() => {
+    expect(mockAddDangerToast).toHaveBeenCalledWith(
+      'File handling is not supported in this browser. Please use a modern 
browser like Chrome or Edge.',
+    );
+    expect(mockHistoryPush).toHaveBeenCalledWith('/superset/welcome/');
+  });
+});
+
+test('redirects when no files are provided', async () => {
+  const { triggerConsumer } = setupLaunchQueue();
+
+  render(
+    <MemoryRouter initialEntries={['/file-handler']}>
+      <Route path="/file-handler">
+        <FileHandler />
+      </Route>
+    </MemoryRouter>,
+    { useRedux: true },
+  );
+
+  // Trigger the consumer with no files
+  triggerConsumer({ files: [] });
+
+  await waitFor(() => {
+    expect(mockHistoryPush).toHaveBeenCalledWith('/superset/welcome/');
+  });
+});
+
+test('handles CSV file correctly', async () => {
+  const fileHandle = createMockFileHandle('test.csv');
+  setupLaunchQueue(fileHandle);
+
+  render(
+    <MemoryRouter initialEntries={['/file-handler']}>
+      <Route path="/file-handler">
+        <FileHandler />
+      </Route>
+    </MemoryRouter>,
+    { useRedux: true },
+  );
+
+  const modal = await screen.findByTestId('upload-modal');
+  expect(modal).toBeInTheDocument();
+  expect(screen.getByTestId('modal-show')).toHaveTextContent('true');
+  expect(screen.getByTestId('modal-type')).toHaveTextContent('csv');
+  expect(screen.getByTestId('modal-extensions')).toHaveTextContent('.csv');
+});
+
+test('handles Excel (.xls) file correctly', async () => {
+  const fileHandle = createMockFileHandle('test.xls');
+  setupLaunchQueue(fileHandle);
+
+  render(
+    <MemoryRouter initialEntries={['/file-handler']}>
+      <Route path="/file-handler">
+        <FileHandler />
+      </Route>
+    </MemoryRouter>,
+    { useRedux: true },
+  );
+
+  const modal = await screen.findByTestId('upload-modal');
+  expect(modal).toBeInTheDocument();
+  expect(screen.getByTestId('modal-type')).toHaveTextContent('excel');
+  expect(screen.getByTestId('modal-extensions')).toHaveTextContent(
+    '.xls,.xlsx',
+  );
+});
+
+test('handles Excel (.xlsx) file correctly', async () => {
+  const fileHandle = createMockFileHandle('test.xlsx');
+  setupLaunchQueue(fileHandle);
+
+  render(
+    <MemoryRouter initialEntries={['/file-handler']}>
+      <Route path="/file-handler">
+        <FileHandler />
+      </Route>
+    </MemoryRouter>,
+    { useRedux: true },
+  );
+
+  const modal = await screen.findByTestId('upload-modal');
+  expect(modal).toBeInTheDocument();
+  expect(screen.getByTestId('modal-type')).toHaveTextContent('excel');
+  expect(screen.getByTestId('modal-extensions')).toHaveTextContent(
+    '.xls,.xlsx',
+  );
+});
+
+test('handles Parquet file correctly', async () => {
+  const fileHandle = createMockFileHandle('test.parquet');
+  setupLaunchQueue(fileHandle);
+
+  render(
+    <MemoryRouter initialEntries={['/file-handler']}>
+      <Route path="/file-handler">
+        <FileHandler />
+      </Route>
+    </MemoryRouter>,
+    { useRedux: true },
+  );
+
+  const modal = await screen.findByTestId('upload-modal');
+  expect(modal).toBeInTheDocument();
+  expect(screen.getByTestId('modal-type')).toHaveTextContent('columnar');
+  expect(screen.getByTestId('modal-extensions')).toHaveTextContent('.parquet');
+});
+
+test('shows error for unsupported file type', async () => {
+  const { triggerConsumer } = setupLaunchQueue();
+
+  render(
+    <MemoryRouter initialEntries={['/file-handler']}>
+      <Route path="/file-handler">
+        <FileHandler />
+      </Route>
+    </MemoryRouter>,
+    { useRedux: true },
+  );
+
+  // Trigger with unsupported file
+  const fileHandle = createMockFileHandle('test.pdf');
+  triggerConsumer({ files: [fileHandle] });
+
+  await waitFor(() => {
+    expect(mockAddDangerToast).toHaveBeenCalledWith(
+      'Unsupported file type. Please use CSV, Excel, or Columnar files.',
+    );
+    expect(mockHistoryPush).toHaveBeenCalledWith('/superset/welcome/');
+  });
+});
+
+test('handles file with uppercase extension', async () => {
+  const fileHandle = createMockFileHandle('test.CSV');
+  setupLaunchQueue(fileHandle);
+
+  render(
+    <MemoryRouter initialEntries={['/file-handler']}>
+      <Route path="/file-handler">
+        <FileHandler />
+      </Route>
+    </MemoryRouter>,
+    { useRedux: true },
+  );
+
+  const modal = await screen.findByTestId('upload-modal');
+  expect(modal).toBeInTheDocument();
+  expect(screen.getByTestId('modal-type')).toHaveTextContent('csv');
+});
+
+test('handles errors during file processing', async () => {
+  const { triggerConsumer } = setupLaunchQueue();
+
+  render(
+    <MemoryRouter initialEntries={['/file-handler']}>
+      <Route path="/file-handler">
+        <FileHandler />
+      </Route>
+    </MemoryRouter>,
+    { useRedux: true },
+  );
+
+  // Trigger with a file handle that throws an error
+  const errorFileHandle = {
+    getFile: async () => {
+      throw new Error('File access denied');
+    },
+  };
+
+  triggerConsumer({ files: [errorFileHandle] });
+
+  await waitFor(() => {
+    expect(mockAddDangerToast).toHaveBeenCalledWith(
+      'Failed to open file. Please try again.',
+    );
+    expect(mockHistoryPush).toHaveBeenCalledWith('/superset/welcome/');
+  });
+});
+
+test('modal close redirects to welcome page', async () => {
+  const fileHandle = createMockFileHandle('test.csv');
+  setupLaunchQueue(fileHandle);
+
+  render(
+    <MemoryRouter initialEntries={['/file-handler']}>
+      <Route path="/file-handler">
+        <FileHandler />
+      </Route>
+    </MemoryRouter>,
+    { useRedux: true },
+  );
+
+  const modal = await screen.findByTestId('upload-modal');
+  expect(modal).toBeInTheDocument();
+
+  // Click the close button in the mocked modal
+  const closeButton = screen.getByRole('button', { name: 'Close' });
+  closeButton.click();
+
+  await waitFor(() => {
+    expect(mockHistoryPush).toHaveBeenCalledWith('/superset/welcome/');
+  });
+});
+
+test('shows loading state while waiting for file', () => {
+  setupLaunchQueue();
+
+  render(
+    <MemoryRouter initialEntries={['/file-handler']}>
+      <Route path="/file-handler">
+        <FileHandler />
+      </Route>
+    </MemoryRouter>,
+    { useRedux: true },
+  );
+
+  // Should show loading initially before file is processed
+  expect(screen.getByRole('status')).toBeInTheDocument();
+});

Reply via email to