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

jli pushed a commit to branch fix-app-prefix-export
in repository https://gitbox.apache.org/repos/asf/superset.git

commit ce99812b9d4e0b654a3f522a0598ae9e974ba34a
Author: Joe Li <[email protected]>
AuthorDate: Fri Dec 19 00:22:00 2025 -0800

    test(explore): add exportChart streaming export URL prefix tests
    
    Adds tests to verify URL prefixes are correctly applied when using
    onStartStreamingExport callback for subdirectory deployments.
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-Authored-By: Claude Opus 4.5 <[email protected]>
---
 .../src/explore/exploreUtils/exportChart.test.ts   | 147 +++++++++++++++++++++
 1 file changed, 147 insertions(+)

diff --git a/superset-frontend/src/explore/exploreUtils/exportChart.test.ts 
b/superset-frontend/src/explore/exploreUtils/exportChart.test.ts
new file mode 100644
index 0000000000..9189b844d7
--- /dev/null
+++ b/superset-frontend/src/explore/exploreUtils/exportChart.test.ts
@@ -0,0 +1,147 @@
+/**
+ * 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 { exportChart } from '.';
+
+// Mock pathUtils to control app root prefix
+jest.mock('src/utils/pathUtils', () => ({
+  ensureAppRoot: jest.fn((path: string) => path),
+}));
+
+// Mock SupersetClient
+jest.mock('@superset-ui/core', () => ({
+  ...jest.requireActual('@superset-ui/core'),
+  SupersetClient: {
+    postForm: jest.fn(),
+    get: jest.fn().mockResolvedValue({ json: {} }),
+    post: jest.fn().mockResolvedValue({ json: {} }),
+  },
+  getChartBuildQueryRegistry: jest.fn().mockReturnValue({
+    get: jest.fn().mockReturnValue(() => () => ({})),
+  }),
+  getChartMetadataRegistry: jest.fn().mockReturnValue({
+    get: jest.fn().mockReturnValue({ parseMethod: 'json' }),
+  }),
+}));
+
+const { ensureAppRoot } = jest.requireMock('src/utils/pathUtils');
+
+
+describe('exportChart URL prefix for streaming export', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+    // Default: no prefix
+    ensureAppRoot.mockImplementation((path: string) => path);
+  });
+
+  // Minimal formData that won't trigger legacy API (useLegacyApi = false)
+  const baseFormData = {
+    datasource: '1__table',
+    viz_type: 'table',
+  };
+
+  describe('v1 API endpoint', () => {
+    test('passes prefixed URL to onStartStreamingExport when app root is 
configured', async () => {
+      const appRoot = '/superset';
+      ensureAppRoot.mockImplementation((path: string) => `${appRoot}${path}`);
+
+      const onStartStreamingExport = jest.fn();
+
+      await exportChart({
+        formData: baseFormData,
+        resultFormat: 'csv',
+        onStartStreamingExport: onStartStreamingExport as unknown as null,
+      });
+
+      expect(onStartStreamingExport).toHaveBeenCalledTimes(1);
+      const callArgs = onStartStreamingExport.mock.calls[0][0];
+      expect(callArgs.url).toBe('/superset/api/v1/chart/data');
+      expect(callArgs.exportType).toBe('csv');
+    });
+
+    test('passes unprefixed URL when no app root is configured', async () => {
+      ensureAppRoot.mockImplementation((path: string) => path);
+
+      const onStartStreamingExport = jest.fn();
+
+      await exportChart({
+        formData: baseFormData,
+        resultFormat: 'csv',
+        onStartStreamingExport: onStartStreamingExport as unknown as null,
+      });
+
+      expect(onStartStreamingExport).toHaveBeenCalledTimes(1);
+      const callArgs = onStartStreamingExport.mock.calls[0][0];
+      expect(callArgs.url).toBe('/api/v1/chart/data');
+    });
+
+    test('passes nested prefix for deeply nested deployments', async () => {
+      const appRoot = '/my-company/analytics/superset';
+      ensureAppRoot.mockImplementation((path: string) => `${appRoot}${path}`);
+
+      const onStartStreamingExport = jest.fn();
+
+      await exportChart({
+        formData: baseFormData,
+        resultFormat: 'xlsx',
+        onStartStreamingExport: onStartStreamingExport as unknown as null,
+      });
+
+      expect(onStartStreamingExport).toHaveBeenCalledTimes(1);
+      const callArgs = onStartStreamingExport.mock.calls[0][0];
+      expect(callArgs.url).toBe(
+        '/my-company/analytics/superset/api/v1/chart/data',
+      );
+      expect(callArgs.exportType).toBe('xlsx');
+    });
+  });
+
+  describe('exportType passthrough', () => {
+    test('passes csv exportType for CSV exports', async () => {
+      const onStartStreamingExport = jest.fn();
+
+      await exportChart({
+        formData: baseFormData,
+        resultFormat: 'csv',
+        onStartStreamingExport: onStartStreamingExport as unknown as null,
+      });
+
+      expect(onStartStreamingExport).toHaveBeenCalledWith(
+        expect.objectContaining({
+          exportType: 'csv',
+        }),
+      );
+    });
+
+    test('passes xlsx exportType for Excel exports', async () => {
+      const onStartStreamingExport = jest.fn();
+
+      await exportChart({
+        formData: baseFormData,
+        resultFormat: 'xlsx',
+        onStartStreamingExport: onStartStreamingExport as unknown as null,
+      });
+
+      expect(onStartStreamingExport).toHaveBeenCalledWith(
+        expect.objectContaining({
+          exportType: 'xlsx',
+        }),
+      );
+    });
+  });
+});

Reply via email to