This is an automated email from the ASF dual-hosted git repository.
sadpandajoe pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 240a4267f85 test(explore): add datasourcesReducer unit tests (#44840)
240a4267f85 is described below
commit 240a4267f856183a4dd6bf7f00af341c5aca3d02
Author: Joe Li <[email protected]>
AuthorDate: Fri Oct 2 10:33:56 2026 -0700
test(explore): add datasourcesReducer unit tests (#44840)
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
---
.../explore/reducers/datasourcesReducer.test.ts | 171 +++++++++++++++++++++
1 file changed, 171 insertions(+)
diff --git a/superset-frontend/src/explore/reducers/datasourcesReducer.test.ts
b/superset-frontend/src/explore/reducers/datasourcesReducer.test.ts
new file mode 100644
index 00000000000..a0117344689
--- /dev/null
+++ b/superset-frontend/src/explore/reducers/datasourcesReducer.test.ts
@@ -0,0 +1,171 @@
+/**
+ * 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 { DatasourceType } from '@superset-ui/core';
+import { Dataset } from '@superset-ui/chart-controls';
+import { SET_DATASOURCE } from '../actions/datasourcesActions';
+import { HYDRATE_EXPLORE, HydrateExplore } from '../actions/hydrateExplore';
+import datasourcesReducer from './datasourcesReducer';
+
+type DatasourcesState = Parameters<typeof datasourcesReducer>[0];
+type DatasourcesAction = Parameters<typeof datasourcesReducer>[1];
+
+const makeDataset = (overrides: Partial<Dataset> = {}): Dataset =>
+ ({
+ id: 1,
+ type: DatasourceType.Table,
+ columns: [],
+ metrics: [],
+ column_formats: {},
+ verbose_map: {},
+ main_dttm_col: '__timestamp',
+ datasource_name: 'test datasource',
+ description: null,
+ ...overrides,
+ }) as Dataset;
+
+const setDatasourceAction = (datasource: Dataset): DatasourcesAction => ({
+ type: SET_DATASOURCE,
+ datasource,
+});
+
+const hydrateAction = (
+ datasources: Record<string, Dataset> | undefined,
+): DatasourcesAction =>
+ ({
+ type: HYDRATE_EXPLORE,
+ data: { datasources },
+ }) as unknown as HydrateExplore;
+
+test('SET_DATASOURCE adds a datasource keyed by its uid without dropping
others', () => {
+ const existing = makeDataset({ id: 1, uid: '1__table' });
+ const added = makeDataset({ id: 2, uid: '2__table' });
+
+ const newState = datasourcesReducer(
+ { '1__table': existing },
+ setDatasourceAction(added),
+ );
+
+ expect(newState).toEqual({ '1__table': existing, '2__table': added });
+});
+
+test('SET_DATASOURCE derives the key from id and type when uid is missing', ()
=> {
+ const added = makeDataset({ id: 2, uid: undefined });
+
+ const newState = datasourcesReducer({}, setDatasourceAction(added));
+
+ expect(newState).toEqual({ '2__table': added });
+});
+
+test('SET_DATASOURCE replaces the entry that has the same key', () => {
+ const original = makeDataset({ id: 1, uid: '1__table' });
+ const other = makeDataset({ id: 2, uid: '2__table' });
+ const updated = makeDataset({
+ id: 1,
+ uid: '1__table',
+ datasource_name: 'renamed',
+ });
+
+ const newState = datasourcesReducer(
+ { '1__table': original, '2__table': other },
+ setDatasourceAction(updated),
+ );
+
+ expect(newState).toEqual({ '1__table': updated, '2__table': other });
+ expect(newState['1__table']).toBe(updated);
+});
+
+test('SET_DATASOURCE does not mutate the previous state', () => {
+ const state: DatasourcesState = {
+ '1__table': makeDataset({ id: 1, uid: '1__table' }),
+ };
+ const snapshot = { ...state };
+
+ const newState = datasourcesReducer(
+ state,
+ setDatasourceAction(makeDataset({ id: 2, uid: '2__table' })),
+ );
+
+ expect(newState).not.toBe(state);
+ expect(state).toEqual(snapshot);
+});
+
+test('SET_DATASOURCE works when the state is undefined', () => {
+ const added = makeDataset({ id: 2, uid: '2__table' });
+
+ const newState = datasourcesReducer(
+ undefined as unknown as DatasourcesState,
+ setDatasourceAction(added),
+ );
+
+ expect(newState).toEqual({ '2__table': added });
+});
+
+test('HYDRATE_EXPLORE replaces the whole map with the payload datasources', ()
=> {
+ const stale = makeDataset({ id: 1, uid: '1__table' });
+ const fresh = makeDataset({ id: 3, uid: '3__table' });
+
+ const newState = datasourcesReducer(
+ { '1__table': stale },
+ hydrateAction({ '3__table': fresh }),
+ );
+
+ expect(newState).toEqual({ '3__table': fresh });
+ expect(newState).not.toHaveProperty('1__table');
+});
+
+test('HYDRATE_EXPLORE returns a copy rather than the payload object', () => {
+ const payloadDatasources = { '3__table': makeDataset({ id: 3 }) };
+
+ const newState = datasourcesReducer({}, hydrateAction(payloadDatasources));
+
+ expect(newState).toEqual(payloadDatasources);
+ expect(newState).not.toBe(payloadDatasources);
+});
+
+test('HYDRATE_EXPLORE without datasources resets to an empty map', () => {
+ const newState = datasourcesReducer(
+ { '1__table': makeDataset({ id: 1, uid: '1__table' }) },
+ hydrateAction(undefined),
+ );
+
+ expect(newState).toEqual({});
+});
+
+test('returns an empty map for undefined state and an unknown action', () => {
+ const newState = datasourcesReducer(
+ undefined as unknown as DatasourcesState,
+ {
+ type: 'UNKNOWN_ACTION',
+ } as unknown as DatasourcesAction,
+ );
+
+ expect(newState).toEqual({});
+});
+
+test('returns the same state reference for an unknown action', () => {
+ const state: DatasourcesState = {
+ '1__table': makeDataset({ id: 1, uid: '1__table' }),
+ };
+
+ const newState = datasourcesReducer(state, {
+ type: 'UNKNOWN_ACTION',
+ } as unknown as DatasourcesAction);
+
+ expect(newState).toBe(state);
+});