mirror of
https://github.com/apache/superset.git
synced 2026-04-07 02:21:51 +00:00
test(sqllab): add extension slot contract tests for all 7 host components (#38776)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
70
superset-frontend/spec/helpers/extensionTestHelpers.ts
Normal file
70
superset-frontend/spec/helpers/extensionTestHelpers.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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 type { ReactElement } from 'react';
|
||||
import { views, menus, commands } from 'src/core';
|
||||
import { resetContributions } from 'src/core/commands';
|
||||
|
||||
const disposables: Array<{ dispose: () => void }> = [];
|
||||
|
||||
/**
|
||||
* Cleans up all tracked extension registrations (views, menus, commands).
|
||||
* Call in afterEach to prevent state leaks between tests.
|
||||
*/
|
||||
export const cleanupExtensions = () => {
|
||||
disposables.forEach(d => d.dispose());
|
||||
disposables.length = 0;
|
||||
resetContributions();
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers a test view at a given location and tracks it for cleanup.
|
||||
*/
|
||||
export const registerTestView = (
|
||||
location: string,
|
||||
id: string,
|
||||
name: string,
|
||||
provider: () => ReactElement,
|
||||
) => {
|
||||
const disposable = views.registerView({ id, name }, location, provider);
|
||||
disposables.push(disposable);
|
||||
return disposable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers a toolbar action (command + menu item) and tracks both for cleanup.
|
||||
* Primary actions require an icon to render in PanelToolbar.
|
||||
*/
|
||||
export const registerToolbarAction = (
|
||||
viewId: string,
|
||||
commandId: string,
|
||||
title: string,
|
||||
callback: () => void,
|
||||
group: 'primary' | 'secondary' = 'primary',
|
||||
) => {
|
||||
const cmdDisposable = commands.registerCommand(
|
||||
{ id: commandId, title, icon: 'FileOutlined' },
|
||||
callback,
|
||||
);
|
||||
const menuDisposable = menus.registerMenuItem(
|
||||
{ command: commandId, view: `test-view-${commandId}` },
|
||||
viewId,
|
||||
group,
|
||||
);
|
||||
disposables.push(cmdDisposable, menuDisposable);
|
||||
};
|
||||
@@ -20,8 +20,11 @@ import React from 'react';
|
||||
import { render, userEvent, waitFor } from 'spec/helpers/testing-library';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
import useStoredSidebarWidth from 'src/components/ResizableSidebar/useStoredSidebarWidth';
|
||||
import { views } from 'src/core';
|
||||
import { ViewLocations } from 'src/SqlLab/contributions';
|
||||
import {
|
||||
registerTestView,
|
||||
cleanupExtensions,
|
||||
} from 'spec/helpers/extensionTestHelpers';
|
||||
import AppLayout from './index';
|
||||
|
||||
jest.mock('src/components/ResizableSidebar/useStoredSidebarWidth');
|
||||
@@ -63,6 +66,8 @@ beforeEach(() => {
|
||||
(useStoredSidebarWidth as jest.Mock).mockReturnValue([250, jest.fn()]);
|
||||
});
|
||||
|
||||
afterEach(cleanupExtensions);
|
||||
|
||||
test('renders two panels', () => {
|
||||
const { getAllByTestId } = render(<AppLayout {...defaultProps} />, {
|
||||
useRedux: true,
|
||||
@@ -94,10 +99,20 @@ test('calls setWidth on sidebar resize when not hidden', async () => {
|
||||
await waitFor(() => expect(setWidth).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
test('right sidebar is hidden when no extensions registered', () => {
|
||||
const { queryByText } = render(<AppLayout {...defaultProps} />, {
|
||||
useRedux: true,
|
||||
initialState,
|
||||
});
|
||||
// No right sidebar content — the third Splitter.Panel is conditionally omitted
|
||||
expect(queryByText('Right Sidebar Content')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders right sidebar when view is contributed at rightSidebar location', () => {
|
||||
views.registerView(
|
||||
{ id: 'test-right-sidebar-view', name: 'Test Right Sidebar View' },
|
||||
registerTestView(
|
||||
ViewLocations.sqllab.rightSidebar,
|
||||
'test-right-sidebar-view',
|
||||
'Test Right Sidebar View',
|
||||
() => React.createElement('div', null, 'Right Sidebar Content'),
|
||||
);
|
||||
|
||||
@@ -110,5 +125,6 @@ test('renders right sidebar when view is contributed at rightSidebar location',
|
||||
);
|
||||
|
||||
expect(getByText('Child')).toBeInTheDocument();
|
||||
expect(getByText('Right Sidebar Content')).toBeInTheDocument();
|
||||
expect(getAllByTestId('mock-panel')).toHaveLength(3);
|
||||
});
|
||||
|
||||
@@ -25,6 +25,11 @@ import {
|
||||
defaultQueryEditor,
|
||||
extraQueryEditor3,
|
||||
} from 'src/SqlLab/fixtures';
|
||||
import { ViewLocations } from 'src/SqlLab/contributions';
|
||||
import {
|
||||
registerToolbarAction,
|
||||
cleanupExtensions,
|
||||
} from 'spec/helpers/extensionTestHelpers';
|
||||
|
||||
const mockedProps = {
|
||||
queryEditorId: defaultQueryEditor.id,
|
||||
@@ -81,7 +86,11 @@ const setup = (overrides = {}) => (
|
||||
<QueryHistory {...mockedProps} {...overrides} />
|
||||
);
|
||||
|
||||
afterEach(() => fetchMock.clearHistory().removeRoutes());
|
||||
afterEach(() => {
|
||||
fetchMock.clearHistory().removeRoutes();
|
||||
cleanupExtensions();
|
||||
mockedIsFeatureEnabled.mockReset();
|
||||
});
|
||||
|
||||
test('Renders an empty state for query history', () => {
|
||||
render(setup(), { useRedux: true, initialState });
|
||||
@@ -242,3 +251,44 @@ test('displays multiple queries with newest query first', async () => {
|
||||
|
||||
isFeatureEnabledMock.mockClear();
|
||||
});
|
||||
|
||||
test('renders contributed toolbar action in queryHistory slot', () => {
|
||||
registerToolbarAction(
|
||||
ViewLocations.sqllab.queryHistory,
|
||||
'test-history-action',
|
||||
'History Action',
|
||||
jest.fn(),
|
||||
);
|
||||
|
||||
const stateWithQueries = {
|
||||
...initialState,
|
||||
sqlLab: {
|
||||
...initialState.sqlLab,
|
||||
queries: {
|
||||
testQuery: {
|
||||
id: 'testQuery',
|
||||
sqlEditorId: defaultQueryEditor.id,
|
||||
sql: 'SELECT 1',
|
||||
state: QueryState.Success,
|
||||
startDttm: Date.now(),
|
||||
endDttm: Date.now() + 100,
|
||||
progress: 100,
|
||||
rows: 1,
|
||||
cached: false,
|
||||
changed_on: new Date().toISOString(),
|
||||
db: 'main',
|
||||
dbId: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
render(setup(), {
|
||||
useRedux: true,
|
||||
initialState: stateWithQueries,
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: 'History Action' }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -41,6 +41,11 @@ import {
|
||||
queryWithNoQueryLimit,
|
||||
failedQueryWithFrontendTimeoutErrors,
|
||||
} from 'src/SqlLab/fixtures';
|
||||
import { ViewLocations } from 'src/SqlLab/contributions';
|
||||
import {
|
||||
registerToolbarAction,
|
||||
cleanupExtensions,
|
||||
} from 'spec/helpers/extensionTestHelpers';
|
||||
|
||||
jest.mock('src/components/ErrorMessage', () => ({
|
||||
ErrorMessageWithStackTrace: () => <div data-test="error-message">Error</div>,
|
||||
@@ -152,6 +157,7 @@ describe('ResultSet', () => {
|
||||
// Add cleanup after each test
|
||||
afterEach(async () => {
|
||||
fetchMock.clearHistory();
|
||||
cleanupExtensions();
|
||||
// Wait for any pending effects to complete
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
});
|
||||
@@ -753,4 +759,35 @@ describe('ResultSet', () => {
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test('renders contributed toolbar action in results slot', async () => {
|
||||
registerToolbarAction(
|
||||
ViewLocations.sqllab.results,
|
||||
'test-results-action',
|
||||
'Results Action',
|
||||
jest.fn(),
|
||||
);
|
||||
|
||||
const { getByTestId } = setup(
|
||||
mockedProps,
|
||||
mockStore({
|
||||
...initialState,
|
||||
user,
|
||||
sqlLab: {
|
||||
...initialState.sqlLab,
|
||||
queries: {
|
||||
[queries[0].id]: queries[0],
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByTestId('table-container')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: 'Results Action' }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,12 +16,21 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render, waitFor, screen } from 'spec/helpers/testing-library';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import SouthPane from 'src/SqlLab/components/SouthPane';
|
||||
import { STATUS_OPTIONS } from 'src/SqlLab/constants';
|
||||
import { initialState, table, defaultQueryEditor } from 'src/SqlLab/fixtures';
|
||||
import { denormalizeTimestamp } from '@superset-ui/core';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { ViewLocations } from 'src/SqlLab/contributions';
|
||||
import {
|
||||
registerTestView,
|
||||
registerToolbarAction,
|
||||
cleanupExtensions,
|
||||
} from 'spec/helpers/extensionTestHelpers';
|
||||
|
||||
afterEach(cleanupExtensions);
|
||||
|
||||
const mockedProps = {
|
||||
queryEditorId: defaultQueryEditor.id,
|
||||
@@ -181,3 +190,69 @@ test('should remove tab', async () => {
|
||||
expect(tabs).toHaveLength(totalTabs - 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('renders contributed tab content via ViewListExtension', () => {
|
||||
registerTestView(
|
||||
ViewLocations.sqllab.panels,
|
||||
'test-panel',
|
||||
'Test Panel',
|
||||
() => React.createElement('div', null, 'Contributed Panel Content'),
|
||||
);
|
||||
|
||||
const { container } = render(<SouthPane {...mockedProps} />, {
|
||||
useRedux: true,
|
||||
initialState: mockState,
|
||||
});
|
||||
|
||||
const tabs = Array.from(container.querySelectorAll('[role="tab"]')).filter(
|
||||
tab => !tab.classList.contains('ant-tabs-tab-remove'),
|
||||
);
|
||||
// Base tabs (Results + Query history) + 2 table previews + 1 extension
|
||||
expect(tabs).toHaveLength(mockState.sqlLab.tables.length + 3);
|
||||
expect(tabs.find(tab => tab.textContent === 'Test Panel')).toBeTruthy();
|
||||
expect(screen.getByText('Contributed Panel Content')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders slot-wide toolbar actions via PanelToolbar', () => {
|
||||
registerToolbarAction(
|
||||
ViewLocations.sqllab.panels,
|
||||
'test-panels-action',
|
||||
'Panels Action',
|
||||
jest.fn(),
|
||||
);
|
||||
|
||||
render(<SouthPane {...mockedProps} />, {
|
||||
useRedux: true,
|
||||
initialState: mockState,
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: 'Panels Action' }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders per-view toolbar actions for contributed tab', () => {
|
||||
registerTestView(
|
||||
ViewLocations.sqllab.panels,
|
||||
'test-per-view-panel',
|
||||
'Per-View Panel',
|
||||
() => React.createElement('div', null, 'Per-View Content'),
|
||||
);
|
||||
registerToolbarAction(
|
||||
'test-per-view-panel',
|
||||
'test-per-view-action',
|
||||
'Per-View Action',
|
||||
jest.fn(),
|
||||
);
|
||||
|
||||
render(<SouthPane {...mockedProps} />, {
|
||||
useRedux: true,
|
||||
initialState: mockState,
|
||||
});
|
||||
|
||||
// Content is rendered via forceRender: true even when tab is not active.
|
||||
// Use { hidden: true } to find button in non-active tab pane.
|
||||
expect(
|
||||
screen.getByRole('button', { name: 'Per-View Action', hidden: true }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -17,31 +17,16 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { render, screen } from 'spec/helpers/testing-library';
|
||||
import { MenuItemType } from '@superset-ui/core/components/Menu';
|
||||
import SqlEditorTopBar, {
|
||||
SqlEditorTopBarProps,
|
||||
} from 'src/SqlLab/components/SqlEditorTopBar';
|
||||
import { ViewLocations } from 'src/SqlLab/contributions';
|
||||
import {
|
||||
registerToolbarAction,
|
||||
cleanupExtensions,
|
||||
} from 'spec/helpers/extensionTestHelpers';
|
||||
|
||||
jest.mock('src/components/PanelToolbar', () => ({
|
||||
__esModule: true,
|
||||
default: ({
|
||||
viewId,
|
||||
defaultPrimaryActions,
|
||||
defaultSecondaryActions,
|
||||
}: {
|
||||
viewId: string;
|
||||
defaultPrimaryActions?: React.ReactNode;
|
||||
defaultSecondaryActions?: MenuItemType[];
|
||||
}) => (
|
||||
<div
|
||||
data-test="mock-panel-toolbar"
|
||||
data-view-id={viewId}
|
||||
data-default-secondary-count={defaultSecondaryActions?.length ?? 0}
|
||||
>
|
||||
{defaultPrimaryActions}
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
afterEach(cleanupExtensions);
|
||||
|
||||
const defaultProps: SqlEditorTopBarProps = {
|
||||
queryEditorId: 'test-query-editor-id',
|
||||
@@ -55,24 +40,6 @@ const defaultProps: SqlEditorTopBarProps = {
|
||||
const setup = (props?: Partial<SqlEditorTopBarProps>) =>
|
||||
render(<SqlEditorTopBar {...defaultProps} {...props} />);
|
||||
|
||||
test('renders SqlEditorTopBar component', () => {
|
||||
setup();
|
||||
const panelToolbar = screen.getByTestId('mock-panel-toolbar');
|
||||
expect(panelToolbar).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders PanelToolbar with correct viewId', () => {
|
||||
setup();
|
||||
const panelToolbar = screen.getByTestId('mock-panel-toolbar');
|
||||
expect(panelToolbar).toHaveAttribute('data-view-id', 'sqllab.editor');
|
||||
});
|
||||
|
||||
test('renders PanelToolbar with correct secondary actions count', () => {
|
||||
setup();
|
||||
const panelToolbar = screen.getByTestId('mock-panel-toolbar');
|
||||
expect(panelToolbar).toHaveAttribute('data-default-secondary-count', '2');
|
||||
});
|
||||
|
||||
test('renders defaultPrimaryActions', () => {
|
||||
setup();
|
||||
expect(
|
||||
@@ -98,9 +65,24 @@ test('renders with custom primary actions', () => {
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders with empty secondary actions', () => {
|
||||
setup({ defaultSecondaryActions: [] });
|
||||
|
||||
const panelToolbar = screen.getByTestId('mock-panel-toolbar');
|
||||
expect(panelToolbar).toHaveAttribute('data-default-secondary-count', '0');
|
||||
test('renders contributed toolbar action in editor slot', () => {
|
||||
registerToolbarAction(
|
||||
ViewLocations.sqllab.editor,
|
||||
'test-editor-action',
|
||||
'Test Editor Action',
|
||||
jest.fn(),
|
||||
);
|
||||
setup();
|
||||
expect(
|
||||
screen.getByRole('button', { name: 'Test Editor Action' }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders nothing when no toolbar actions registered and no default actions', () => {
|
||||
setup({
|
||||
defaultPrimaryActions: undefined,
|
||||
defaultSecondaryActions: [],
|
||||
});
|
||||
// PanelToolbar returns null when there are no actions at all
|
||||
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -16,26 +16,82 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render, screen } from 'spec/helpers/testing-library';
|
||||
import StatusBar from 'src/SqlLab/components/StatusBar';
|
||||
import { ViewLocations } from 'src/SqlLab/contributions';
|
||||
import {
|
||||
registerTestView,
|
||||
cleanupExtensions,
|
||||
} from 'spec/helpers/extensionTestHelpers';
|
||||
|
||||
jest.mock('src/core/views', () => ({
|
||||
views: {
|
||||
getViews: jest.fn().mockReturnValue([{ id: 'test-status-bar' }]),
|
||||
registerView: jest.fn(),
|
||||
},
|
||||
}));
|
||||
let consoleErrorSpy: jest.SpyInstance;
|
||||
|
||||
jest.mock('src/components/ViewListExtension', () => ({
|
||||
__esModule: true,
|
||||
default: ({ viewId }: { viewId: string }) => (
|
||||
<div data-test="mock-view-extension" data-view-id={viewId}>
|
||||
ViewListExtension
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
test('renders StatusBar component', () => {
|
||||
render(<StatusBar />);
|
||||
expect(screen.getByTestId('mock-view-extension')).toBeInTheDocument();
|
||||
afterEach(() => {
|
||||
cleanupExtensions();
|
||||
if (consoleErrorSpy) {
|
||||
consoleErrorSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
test('renders extension content when registered at statusBar slot', () => {
|
||||
registerTestView(
|
||||
ViewLocations.sqllab.statusBar,
|
||||
'test-status',
|
||||
'Test Status',
|
||||
() => React.createElement('div', null, 'Status Extension'),
|
||||
);
|
||||
render(<StatusBar />);
|
||||
expect(screen.getByText('Status Extension')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('does not render container when no extensions registered', () => {
|
||||
const { container } = render(<StatusBar />);
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
test('extension throwing during render does not crash host', () => {
|
||||
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
const ThrowingComponent = () => {
|
||||
throw new Error('Extension error');
|
||||
};
|
||||
|
||||
registerTestView(
|
||||
ViewLocations.sqllab.statusBar,
|
||||
'throwing-ext',
|
||||
'Throwing',
|
||||
() => React.createElement(ThrowingComponent),
|
||||
);
|
||||
registerTestView(
|
||||
ViewLocations.sqllab.statusBar,
|
||||
'healthy-ext',
|
||||
'Healthy',
|
||||
() => React.createElement('div', null, 'Healthy Content'),
|
||||
);
|
||||
|
||||
render(<StatusBar />);
|
||||
|
||||
// Healthy extension still renders despite the throwing extension
|
||||
expect(screen.getByText('Healthy Content')).toBeInTheDocument();
|
||||
// Verify the error boundary caught the throwing extension
|
||||
expect(consoleErrorSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('renders multiple extensions in status bar', () => {
|
||||
registerTestView(
|
||||
ViewLocations.sqllab.statusBar,
|
||||
'test-status-1',
|
||||
'Status One',
|
||||
() => React.createElement('div', null, 'Extension One'),
|
||||
);
|
||||
registerTestView(
|
||||
ViewLocations.sqllab.statusBar,
|
||||
'test-status-2',
|
||||
'Status Two',
|
||||
() => React.createElement('div', null, 'Extension Two'),
|
||||
);
|
||||
render(<StatusBar />);
|
||||
expect(screen.getByText('Extension One')).toBeInTheDocument();
|
||||
expect(screen.getByText('Extension Two')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -22,6 +22,11 @@ import { render, screen, waitFor } from 'spec/helpers/testing-library';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { initialState, defaultQueryEditor } from 'src/SqlLab/fixtures';
|
||||
|
||||
import { ViewLocations } from 'src/SqlLab/contributions';
|
||||
import {
|
||||
registerToolbarAction,
|
||||
cleanupExtensions,
|
||||
} from 'spec/helpers/extensionTestHelpers';
|
||||
import TableExploreTree from '.';
|
||||
|
||||
jest.mock(
|
||||
@@ -74,6 +79,7 @@ beforeEach(() => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
fetchMock.clearHistory();
|
||||
cleanupExtensions();
|
||||
});
|
||||
|
||||
const getInitialState = (overrides = {}) => ({
|
||||
@@ -239,3 +245,22 @@ test('renders refresh button for schema list', async () => {
|
||||
const refreshButton = screen.getByRole('button', { name: /reload/i });
|
||||
expect(refreshButton).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders contributed toolbar action in leftSidebar slot', async () => {
|
||||
registerToolbarAction(
|
||||
ViewLocations.sqllab.leftSidebar,
|
||||
'test-left-action',
|
||||
'Left Sidebar Action',
|
||||
jest.fn(),
|
||||
);
|
||||
|
||||
renderComponent();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('public')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: 'Left Sidebar Action' }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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 } from 'spec/helpers/testing-library';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import PanelToolbar from 'src/components/PanelToolbar';
|
||||
import {
|
||||
registerToolbarAction,
|
||||
cleanupExtensions,
|
||||
} from 'spec/helpers/extensionTestHelpers';
|
||||
|
||||
afterEach(cleanupExtensions);
|
||||
|
||||
test('click executes registered command callback', async () => {
|
||||
const callback = jest.fn();
|
||||
registerToolbarAction(
|
||||
'test.clickLocation',
|
||||
'test-click-cmd',
|
||||
'Click Me',
|
||||
callback,
|
||||
);
|
||||
|
||||
render(<PanelToolbar viewId="test.clickLocation" />);
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Click Me' }));
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('renders nothing when no actions registered', () => {
|
||||
const { container } = render(<PanelToolbar viewId="empty.location" />);
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
Reference in New Issue
Block a user