rusackas commented on code in PR #39461:
URL: https://github.com/apache/superset/pull/39461#discussion_r3229478393
##########
superset-frontend/src/explore/components/SaveModal.test.tsx:
##########
@@ -330,139 +329,43 @@ test('renders InfoTooltip icon next to Dataset Name
label when datasource type i
expect(labelContainer).toContainElement(infoTooltip);
});
-test('make sure slice_id in the URLSearchParams before the redirect', () => {
- const myProps = {
- ...defaultProps,
- slice: { slice_id: 1, slice_name: 'title', owners: [1] },
- actions: {
- setFormData: jest.fn(),
- updateSlice: jest.fn(() => Promise.resolve({ id: 1 })),
- getSliceDashboards: jest.fn(),
- },
- user: { userId: 1 },
- history: {
- replace: jest.fn(),
- },
- dispatch: jest.fn(),
- };
-
- const saveModal = new TestSaveModal(myProps);
- const result = saveModal.handleRedirect(
- 'https://example.com/?name=John&age=30',
+test('createRedirectParams sets slice_id in the URLSearchParams', () => {
+ const result = createRedirectParams(
+ '?name=John&age=30',
{ id: 1 },
+ 'overwrite',
);
expect(result.get('slice_id')).toEqual('1');
+ expect(result.get('save_action')).toEqual('overwrite');
});
-test('removes form_data_key from URL parameters after save', () => {
- const myProps = {
- ...defaultProps,
- slice: { slice_id: 1, slice_name: 'title', owners: [1] },
- actions: {
- setFormData: jest.fn(),
- updateSlice: jest.fn(() => Promise.resolve({ id: 1 })),
- getSliceDashboards: jest.fn(),
- },
- user: { userId: 1 },
- history: {
- replace: jest.fn(),
- },
- dispatch: jest.fn(),
- };
-
- const saveModal = new TestSaveModal(myProps);
-
+test('createRedirectParams removes form_data_key from URL parameters', () => {
// Test with form_data_key in the URL
const urlWithFormDataKey = '?form_data_key=12345&other_param=value';
- const result = saveModal.handleRedirect(urlWithFormDataKey, { id: 1 });
+ const result = createRedirectParams(
+ urlWithFormDataKey,
+ { id: 1 },
+ 'overwrite',
+ );
// form_data_key should be removed
expect(result.has('form_data_key')).toBe(false);
// other parameters should remain
expect(result.get('other_param')).toEqual('value');
expect(result.get('slice_id')).toEqual('1');
- expect(result.has('save_action')).toBe(false);
+ expect(result.get('save_action')).toEqual('overwrite');
});
-test('dispatches removeChartState when saving and going to dashboard', async
() => {
- // Spy on the removeChartState action creator
- const removeChartStateSpy = jest.spyOn(
- dashboardStateActions,
- 'removeChartState',
- );
-
- // Mock the dashboard API response
- const dashboardId = 123;
- const dashboardUrl = '/superset/dashboard/test-dashboard/';
- fetchMock.get(`glob:*/api/v1/dashboard/${dashboardId}*`, {
- result: {
- id: dashboardId,
- dashboard_title: 'Test Dashboard',
- url: dashboardUrl,
- },
- });
-
- const mockDispatch = jest.fn();
- const mockHistory = {
- push: jest.fn(),
- replace: jest.fn(),
- };
- const chartId = 42;
- const mockUpdateSlice = jest.fn(() => Promise.resolve({ id: chartId }));
- const mockSetFormData = jest.fn();
-
- const myProps = {
- ...defaultProps,
- slice: { slice_id: 1, slice_name: 'title', owners: [1] },
- actions: {
- setFormData: mockSetFormData,
- updateSlice: mockUpdateSlice,
- getSliceDashboards: jest.fn(() => Promise.resolve([])),
- saveSliceFailed: jest.fn(),
- },
- user: { userId: 1 },
- history: mockHistory,
- dispatch: mockDispatch,
- };
-
- const saveModal = new TestSaveModal(myProps);
- saveModal.state = {
- action: 'overwrite',
- newSliceName: 'test chart',
- datasetName: 'test dataset',
- dashboard: { label: 'Test Dashboard', value: dashboardId },
- saveStatus: null,
- isLoading: false,
- tabsData: [],
- };
-
- // Mock onHide to prevent errors
- saveModal.onHide = jest.fn();
-
- // Trigger save and go to dashboard (gotodash = true)
- await saveModal.saveOrOverwrite(true);
-
- // Wait for async operations
- await waitFor(() => {
- expect(mockUpdateSlice).toHaveBeenCalled();
- expect(mockSetFormData).toHaveBeenCalled();
- });
-
- // Verify removeChartState was called with the correct chart ID
- expect(removeChartStateSpy).toHaveBeenCalledWith(chartId);
-
- // Verify the action was dispatched (check the action object directly)
- expect(mockDispatch).toHaveBeenCalled();
- expect(mockDispatch).toHaveBeenCalledWith({
- type: 'REMOVE_CHART_STATE',
- chartId,
- });
-
- // Verify navigation happened
- expect(mockHistory.push).toHaveBeenCalled();
-
- // Clean up
- removeChartStateSpy.mockRestore();
+/**
+ * TODO: This test was written for the class component version of SaveModal.
+ * Since SaveModal has been converted to a function component, this test
+ * needs to be rewritten to test through component rendering and user
interaction.
+ * The test should verify that clicking "Save & go to dashboard" dispatches
+ * removeChartState with the correct chart ID.
+ */
+test('dispatches removeChartState when saving and going to dashboard -
placeholder', () => {
Review Comment:
Dropped in a396b654df1e5e0485e3ed9b18e8536e469c2555 — removed the three
`expect(true).toBe(true)` placeholders rather than rewriting them inline;
tracking the FC-shaped replacements in the follow-up below.
##########
superset-frontend/src/explore/components/SaveModal.test.tsx:
##########
@@ -483,66 +386,26 @@ test('renders tab selector when saving as', async () => {
expect(tabSelector).toBeDisabled();
});
-test('onDashboardChange triggers tabs load for existing dashboard', async ()
=> {
- const dashboardId = mockEvent.value;
-
- fetchMock.get(`glob:*/api/v1/dashboard/${dashboardId}/tabs`, {
- json: {
- result: {
- tab_tree: [
- { value: 'tab1', title: 'Main Tab' },
- { value: 'tab2', title: 'Tab' },
- ],
- },
- },
- });
- const component = new TestSaveModal(defaultProps);
- const loadTabsMock = jest
- .fn()
- .mockResolvedValue([{ value: 'tab1', title: 'Main Tab' }]);
- component.loadTabs = loadTabsMock;
- await component.onDashboardChange({
- value: dashboardId,
- label: 'Test Dashboard',
- });
- expect(loadTabsMock).toHaveBeenCalledWith(dashboardId);
+/**
+ * TODO: This test was written for the class component version of SaveModal.
+ * Since SaveModal has been converted to a function component, this test
+ * needs to be rewritten to test through component rendering and user
interaction.
+ * The test should verify that selecting a dashboard triggers tab loading.
+ */
+test('onDashboardChange triggers tabs load for existing dashboard -
placeholder', () => {
Review Comment:
Dropped in a396b654df1e5e0485e3ed9b18e8536e469c2555 — removed the three
`expect(true).toBe(true)` placeholders rather than rewriting them inline;
tracking the FC-shaped replacements in the follow-up below.
##########
superset-frontend/src/explore/components/SaveModal.test.tsx:
##########
@@ -483,66 +386,26 @@ test('renders tab selector when saving as', async () => {
expect(tabSelector).toBeDisabled();
});
-test('onDashboardChange triggers tabs load for existing dashboard', async ()
=> {
- const dashboardId = mockEvent.value;
-
- fetchMock.get(`glob:*/api/v1/dashboard/${dashboardId}/tabs`, {
- json: {
- result: {
- tab_tree: [
- { value: 'tab1', title: 'Main Tab' },
- { value: 'tab2', title: 'Tab' },
- ],
- },
- },
- });
- const component = new TestSaveModal(defaultProps);
- const loadTabsMock = jest
- .fn()
- .mockResolvedValue([{ value: 'tab1', title: 'Main Tab' }]);
- component.loadTabs = loadTabsMock;
- await component.onDashboardChange({
- value: dashboardId,
- label: 'Test Dashboard',
- });
- expect(loadTabsMock).toHaveBeenCalledWith(dashboardId);
+/**
+ * TODO: This test was written for the class component version of SaveModal.
+ * Since SaveModal has been converted to a function component, this test
+ * needs to be rewritten to test through component rendering and user
interaction.
+ * The test should verify that selecting a dashboard triggers tab loading.
+ */
+test('onDashboardChange triggers tabs load for existing dashboard -
placeholder', () => {
+ // See TODO comment above
+ expect(true).toBe(true);
});
-test('onTabChange correctly updates selectedTab via forceUpdate', () => {
- const component = new TestSaveModal(defaultProps);
-
- component.state = {
- ...component.state,
- tabsData: [
- {
- value: 'tab1',
- title: 'Main Tab',
- key: 'tab1',
- children: [
- {
- value: 'tab2',
- title: 'Analytics Tab',
- key: 'tab2',
- },
- ],
- },
- ],
- };
-
- component.setState = function (this: any, stateUpdate: any) {
- if (typeof stateUpdate === 'function') {
- this.state = { ...this.state, ...stateUpdate(this.state) };
- } else {
- this.state = { ...this.state, ...stateUpdate };
- }
- }.bind(component);
-
- component.onTabChange('tab2');
-
- expect(component.state.selectedTab).toEqual({
- value: 'tab2',
- label: 'Analytics Tab',
- });
+/**
+ * TODO: This test was written for the class component version of SaveModal.
+ * Since SaveModal has been converted to a function component, this test
+ * needs to be rewritten to test through component rendering and user
interaction.
+ * The test should verify that changing the tab selection updates the
component state.
+ */
+test('onTabChange correctly updates selectedTab - placeholder', () => {
Review Comment:
Dropped in a396b654df1e5e0485e3ed9b18e8536e469c2555 — removed the three
`expect(true).toBe(true)` placeholders rather than rewriting them inline;
tracking the FC-shaped replacements in the follow-up below.
##########
superset-frontend/src/components/CopyToClipboard/index.tsx:
##########
@@ -16,126 +16,137 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Component, cloneElement, ReactElement } from 'react';
+import { cloneElement, isValidElement, ReactElement, useCallback } from
'react';
import { t } from '@apache-superset/core/translation';
import { css, SupersetTheme } from '@apache-superset/core/theme';
import copyTextToClipboard from 'src/utils/copy';
import { Tooltip } from '@superset-ui/core/components';
import withToasts from '../MessageToasts/withToasts';
import type { CopyToClipboardProps } from './types';
-const defaultProps: Partial<CopyToClipboardProps> = {
- copyNode: <span>{t('Copy')}</span>,
- onCopyEnd: () => {},
- shouldShowText: true,
- wrapped: true,
- tooltipText: t('Copy to clipboard'),
- hideTooltip: false,
-};
+function CopyToClip({
+ copyNode = <span>{t('Copy')}</span>,
+ onCopyEnd = () => {},
+ shouldShowText = true,
+ wrapped = true,
+ tooltipText = t('Copy to clipboard'),
+ hideTooltip = false,
+ disabled,
+ getText,
+ text,
+ addSuccessToast,
+ addDangerToast,
+}: CopyToClipboardProps) {
+ const copyToClipboard = useCallback(
+ (textToCopy: Promise<string>) => {
+ copyTextToClipboard(() => textToCopy)
+ .then(() => {
+ addSuccessToast(t('Copied to clipboard!'));
+ })
+ .catch(() => {
+ addDangerToast(
+ t(
+ 'Sorry, your browser does not support copying. Use Ctrl / Cmd +
C!',
+ ),
+ );
+ })
+ .finally(() => {
+ if (onCopyEnd) onCopyEnd();
+ });
+ },
+ [addSuccessToast, addDangerToast, onCopyEnd],
+ );
-class CopyToClip extends Component<CopyToClipboardProps> {
- static defaultProps = defaultProps;
-
- constructor(props: CopyToClipboardProps) {
- super(props);
- this.copyToClipboard = this.copyToClipboard.bind(this);
- this.onClick = this.onClick.bind(this);
- }
-
- onClick() {
- if (this.props.disabled) {
+ const onClick = useCallback(() => {
+ if (disabled) {
return;
}
- if (this.props.getText) {
- this.props.getText((d: string) => {
- this.copyToClipboard(Promise.resolve(d));
+ if (getText) {
+ getText((d: string) => {
+ copyToClipboard(Promise.resolve(d));
});
} else {
- this.copyToClipboard(Promise.resolve(this.props.text || ''));
+ copyToClipboard(Promise.resolve(text || ''));
}
- }
-
- getDecoratedCopyNode() {
- const copyNode = this.props.copyNode as ReactElement;
- const { disabled } = this.props;
- return cloneElement(copyNode, {
- style: {
- ...copyNode.props.style,
- cursor: disabled ? 'not-allowed' : 'pointer',
- },
- onClick: disabled ? undefined : this.onClick,
- 'aria-disabled': disabled || undefined,
- tabIndex: disabled ? -1 : copyNode.props.tabIndex,
- });
- }
+ }, [disabled, getText, text, copyToClipboard]);
- copyToClipboard(textToCopy: Promise<string>) {
- copyTextToClipboard(() => textToCopy)
- .then(() => {
- this.props.addSuccessToast(t('Copied to clipboard!'));
- })
- .catch(() => {
- this.props.addDangerToast(
- t(
- 'Sorry, your browser does not support copying. Use Ctrl / Cmd +
C!',
- ),
- );
- })
- .finally(() => {
- if (this.props.onCopyEnd) this.props.onCopyEnd();
+ const getDecoratedCopyNode = useCallback(() => {
+ const cursor = disabled ? 'not-allowed' : 'pointer';
+ if (isValidElement(copyNode)) {
Review Comment:
Done in a396b654df1e5e0485e3ed9b18e8536e469c2555 —
`CopyToClipboard.test.tsx` now covers both `copyNode" as string and `copyNode`
as number, asserting the component renders inside a `role="button"` wrapper
without throwing.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]