mirror of
https://github.com/grafana/grafana.git
synced 2026-02-03 20:49:50 -05:00
Chore: Backwards-compatible changes for react 19 (#117157)
* test stability improvements for react 19 * fix queryHistory test * slightly nicer mock * kick CI
This commit is contained in:
parent
95630a764c
commit
8a6c0b8b4d
36 changed files with 310 additions and 201 deletions
|
|
@ -14,9 +14,6 @@ test.describe(
|
|||
test('Graph panel is auto-migrated', async ({ gotoDashboardPage, page }) => {
|
||||
await gotoDashboardPage({ uid: DASHBOARD_ID });
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
await expect(page.getByTestId(UPLOT_MAIN_DIV_SELECTOR).first()).toBeHidden();
|
||||
|
||||
await gotoDashboardPage({ uid: DASHBOARD_ID });
|
||||
|
||||
await expect(page.getByTestId(UPLOT_MAIN_DIV_SELECTOR).first()).toBeVisible();
|
||||
});
|
||||
|
|
@ -24,9 +21,6 @@ test.describe(
|
|||
test('Annotation markers exist for time regions', async ({ gotoDashboardPage, selectors, page }) => {
|
||||
const dashboardPage = await gotoDashboardPage({ uid: DASHBOARD_ID });
|
||||
await expect(page.getByText(DASHBOARD_NAME)).toBeVisible();
|
||||
await expect(page.getByTestId(UPLOT_MAIN_DIV_SELECTOR).first()).toBeHidden();
|
||||
|
||||
await gotoDashboardPage({ uid: DASHBOARD_ID });
|
||||
|
||||
// Check Business Hours panel
|
||||
const businessHoursPanel = dashboardPage.getByGrafanaSelector(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import '@testing-library/jest-dom';
|
||||
import { MessageChannel, MessagePort } from 'node:worker_threads';
|
||||
import { TextEncoder, TextDecoder } from 'util';
|
||||
|
||||
import { matchers } from '@grafana/test-utils/matchers';
|
||||
|
|
@ -84,3 +85,22 @@ global.ResizeObserver = class ResizeObserver {
|
|||
this.#isObserving = false;
|
||||
}
|
||||
};
|
||||
|
||||
// originally using just global.MessageChannel = MessageChannel
|
||||
// however this results in open handles in jest tests
|
||||
// see https://github.com/facebook/react/issues/26608#issuecomment-1734172596
|
||||
global.MessageChannel = class {
|
||||
constructor() {
|
||||
const channel = new MessageChannel();
|
||||
this.port1 = new Proxy(channel.port1, {
|
||||
set(port1, prop, value) {
|
||||
const result = Reflect.set(port1, prop, value);
|
||||
if (prop === 'onmessage') {
|
||||
port1.unref();
|
||||
}
|
||||
return result;
|
||||
},
|
||||
});
|
||||
this.port2 = channel.port2;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { useMenuFocus } from './hooks';
|
|||
|
||||
/** @internal */
|
||||
export interface SubMenuProps {
|
||||
parentItemRef: React.RefObject<HTMLElement>;
|
||||
parentItemRef: React.RefObject<HTMLElement | null>;
|
||||
/** List of menu items of the subMenu */
|
||||
items?: Array<ReactElement<MenuItemProps>>;
|
||||
/** Open */
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ export const RadialText = memo(
|
|||
fontSize={valueFontSize}
|
||||
fill={theme.colors.text.primary}
|
||||
textAnchor="middle"
|
||||
dominantBaseline="text-bottom"
|
||||
dominantBaseline="text-after-edge"
|
||||
>
|
||||
<tspan fontSize={unitFontSize}>{displayValue.prefix ?? ''}</tspan>
|
||||
<tspan>{displayValue.text}</tspan>
|
||||
|
|
@ -136,7 +136,7 @@ export const RadialText = memo(
|
|||
x={centerX}
|
||||
y={nameY}
|
||||
textAnchor="middle"
|
||||
dominantBaseline="text-bottom"
|
||||
dominantBaseline="text-after-edge"
|
||||
fill={nameColor}
|
||||
>
|
||||
{displayValue.title}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export const TooltipPlugin = ({
|
|||
renderTooltip,
|
||||
...otherProps
|
||||
}: TooltipPluginProps) => {
|
||||
const plotInstance = useRef<uPlot>();
|
||||
const plotInstance = useRef<uPlot>(undefined);
|
||||
const theme = useTheme2();
|
||||
const [focusedSeriesIdx, setFocusedSeriesIdx] = useState<number | null>(null);
|
||||
const [focusedPointIdx, setFocusedPointIdx] = useState<number | null>(null);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export function useDelayedSwitch(value: boolean, options: DelayOptions = {}): bo
|
|||
const { duration = 250, delay = 250 } = options;
|
||||
|
||||
const [delayedValue, setDelayedValue] = useState(value);
|
||||
const onStartTime = useRef<Date | undefined>();
|
||||
const onStartTime = useRef<Date | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
let timeout: ReturnType<typeof setTimeout> | undefined;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { HTMLAttributes } from 'react';
|
|||
|
||||
import { Button, IconSize } from '@grafana/ui';
|
||||
|
||||
interface Props extends HTMLAttributes<HTMLButtonElement> {
|
||||
interface Props extends Omit<HTMLAttributes<HTMLButtonElement>, 'onToggle'> {
|
||||
isCollapsed: boolean;
|
||||
onToggle: (isCollapsed: boolean) => void;
|
||||
// Todo: this should be made compulsory for a11y purposes
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ describe('pause rule', () => {
|
|||
expect(byText(/uninitialized/i).get()).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(byRole('button').get());
|
||||
expect(await byText(/loading/i).find()).toBeInTheDocument();
|
||||
|
||||
expect(await byText(/success/i).find()).toBeInTheDocument();
|
||||
expect(await byText(/result/i).find()).toBeInTheDocument();
|
||||
|
|
@ -68,7 +67,6 @@ describe('pause rule', () => {
|
|||
expect(await byText(/uninitialized/i).find()).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(byRole('button').get());
|
||||
expect(await byText(/loading/i).find()).toBeInTheDocument();
|
||||
expect(byText(/success/i).query()).not.toBeInTheDocument();
|
||||
expect(await byText(/error:(.+)oops/i).find()).toBeInTheDocument();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -84,8 +84,8 @@ export function useAsync<Result, Args extends unknown[] = unknown[]>(
|
|||
error: undefined,
|
||||
result: initialValue,
|
||||
});
|
||||
const promiseRef = useRef<Promise<Result>>();
|
||||
const argsRef = useRef<Args>();
|
||||
const promiseRef = useRef<Promise<Result>>(undefined);
|
||||
const argsRef = useRef<Args>(undefined);
|
||||
|
||||
const methods = useSyncedRef({
|
||||
execute(...params: Args) {
|
||||
|
|
|
|||
|
|
@ -155,8 +155,8 @@ export function useRuleGroupConsistencyCheck() {
|
|||
const { isGroupInSync } = useRuleGroupIsInSync();
|
||||
const [groupConsistent, setGroupConsistent] = useState<boolean | undefined>();
|
||||
|
||||
const apiCheckInterval = useRef<ReturnType<typeof setTimeout> | undefined>();
|
||||
const timeoutInterval = useRef<ReturnType<typeof setTimeout> | undefined>();
|
||||
const apiCheckInterval = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
||||
const timeoutInterval = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
|
@ -245,8 +245,8 @@ export function useRuleGroupConsistencyCheck() {
|
|||
export function usePrometheusConsistencyCheck() {
|
||||
const { matchingPromRuleExists } = useMatchingPromRuleExists();
|
||||
|
||||
const removalConsistencyInterval = useRef<number | undefined>();
|
||||
const creationConsistencyInterval = useRef<number | undefined>();
|
||||
const removalConsistencyInterval = useRef<number | undefined>(undefined);
|
||||
const creationConsistencyInterval = useRef<number | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ function useScopesRow(onApply: () => void) {
|
|||
function useGlobalScopesSearch(searchQuery: string, parentId?: string | null) {
|
||||
const { selectScope, searchAllNodes, getScopeNodes } = useScopeServicesState();
|
||||
const [actions, setActions] = useState<CommandPaletteAction[] | undefined>(undefined);
|
||||
const searchQueryRef = useRef<string>();
|
||||
const searchQueryRef = useRef<string>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if ((!parentId || parentId === 'scopes') && searchQuery && config.featureToggles.scopeSearchAllLevels) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,21 @@
|
|||
import userEvent from '@testing-library/user-event';
|
||||
import { render, screen } from 'test/test-utils';
|
||||
|
||||
import { FieldType, getDefaultTimeRange, LoadingState, toDataFrame } from '@grafana/data';
|
||||
import {
|
||||
DataSourceInstanceSettings,
|
||||
DataSourcePluginMeta,
|
||||
DataSourceRef,
|
||||
FieldType,
|
||||
getDefaultTimeRange,
|
||||
LoadingState,
|
||||
toDataFrame,
|
||||
} from '@grafana/data';
|
||||
import { getPanelPlugin } from '@grafana/data/test';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { config, setPluginImportUtils } from '@grafana/runtime';
|
||||
import { SceneQueryRunner, SceneTimeRange, VizPanel, VizPanelMenu } from '@grafana/scenes';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
|
||||
import { MockDataSourceApi } from '../../../../../test/mocks/datasource_srv';
|
||||
import { DashboardScene } from '../../scene/DashboardScene';
|
||||
import { VizPanelLinks, VizPanelLinksMenu } from '../../scene/PanelLinks';
|
||||
import { panelMenuBehavior } from '../../scene/PanelMenuBehavior';
|
||||
|
|
@ -19,8 +28,34 @@ jest.mock('./utils.ts', () => ({
|
|||
getGithubMarkdown: () => new Uint8Array(1024 * 1024).toString(),
|
||||
}));
|
||||
|
||||
const grafanaDsInstanceSettings = {
|
||||
name: 'Grafana',
|
||||
uid: '-- Grafana --',
|
||||
meta: {
|
||||
annotations: true,
|
||||
} as DataSourcePluginMeta,
|
||||
readOnly: false,
|
||||
type: 'grafana',
|
||||
} as DataSourceInstanceSettings;
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getDataSourceSrv: () => {
|
||||
return {
|
||||
get: (ref: DataSourceRef) => {
|
||||
return Promise.resolve(new MockDataSourceApi(grafanaDsInstanceSettings));
|
||||
},
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
setPluginImportUtils({
|
||||
importPanelPlugin: (id: string) => Promise.resolve(getPanelPlugin({})),
|
||||
getPanelPluginFromCache: (id: string) => undefined,
|
||||
});
|
||||
|
||||
async function setup() {
|
||||
const { panel } = await buildTestScene();
|
||||
const { panel } = buildTestScene();
|
||||
panel.getPlugin = () => getPanelPlugin({ skipDataQuery: false });
|
||||
|
||||
return render(<HelpWizard panel={panel} onClose={() => {}} />);
|
||||
|
|
@ -39,6 +74,9 @@ describe('HelpWizard', () => {
|
|||
config.supportBundlesEnabled = false;
|
||||
setup();
|
||||
|
||||
// waiting for data tab to be visible ensures all async processing is done
|
||||
await screen.findByTestId('data-testid Tab Data');
|
||||
|
||||
expect(screen.queryByText('You can also retrieve a support bundle')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
|
@ -80,7 +118,7 @@ describe('SupportSnapshot', () => {
|
|||
});
|
||||
});
|
||||
|
||||
async function buildTestScene() {
|
||||
function buildTestScene() {
|
||||
const menu = new VizPanelMenu({
|
||||
$behaviors: [panelMenuBehavior],
|
||||
});
|
||||
|
|
@ -126,7 +164,5 @@ async function buildTestScene() {
|
|||
body: DefaultGridLayoutManager.fromVizPanels([panel]),
|
||||
});
|
||||
|
||||
await new Promise((r) => setTimeout(r, 1));
|
||||
|
||||
return { scene, panel, menu };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ interface Props {
|
|||
export function HelpWizard({ panel, onClose }: Props) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const service = useMemo(() => new SupportSnapshotService(panel), [panel]);
|
||||
const plugin = panel.getPlugin();
|
||||
const plugin = useMemo(() => panel.getPlugin(), [panel]);
|
||||
|
||||
const {
|
||||
currentTab,
|
||||
|
|
|
|||
|
|
@ -350,7 +350,7 @@ describe('DashboardScenePage', () => {
|
|||
|
||||
await waitForDashboardToRender();
|
||||
|
||||
expect(await screen.queryByText('Start your new dashboard by adding a visualization')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Start your new dashboard by adding a visualization')).not.toBeInTheDocument();
|
||||
|
||||
// Hacking a bit, accessing private cache property to get access to the underlying DashboardScene object
|
||||
const dashboardScenesCache = getDashboardScenePageStateManager().getCache();
|
||||
|
|
@ -360,7 +360,7 @@ describe('DashboardScenePage', () => {
|
|||
act(() => {
|
||||
dashboard.removePanel(panels[0]);
|
||||
});
|
||||
expect(await screen.queryByText('Start your new dashboard by adding a visualization')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Start your new dashboard by adding a visualization')).not.toBeInTheDocument();
|
||||
|
||||
act(() => {
|
||||
dashboard.removePanel(panels[1]);
|
||||
|
|
@ -372,7 +372,7 @@ describe('DashboardScenePage', () => {
|
|||
});
|
||||
|
||||
expect(await screen.findByTitle('Panel Added')).toBeInTheDocument();
|
||||
expect(await screen.queryByText('Start your new dashboard by adding a visualization')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Start your new dashboard by adding a visualization')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -407,8 +407,7 @@ describe('PanelDataQueriesTab', () => {
|
|||
const modelMock = await createModelMock();
|
||||
render(<PanelDataQueriesTabRendered model={modelMock}></PanelDataQueriesTabRendered>);
|
||||
|
||||
await screen.findByTestId('query-editor-rows');
|
||||
expect(screen.getAllByTestId('query-editor-row')).toHaveLength(1);
|
||||
expect(await screen.findAllByTestId('query-editor-row')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('allow to add a new query when user clicks on add new', async () => {
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export function SaveDashboardAsForm({ dashboard, changeInfo }: Props) {
|
|||
|
||||
const [contentSent, setContentSent] = useState<{ title?: string; folderUid?: string }>({});
|
||||
|
||||
const validationTimeoutRef = useRef<NodeJS.Timeout>();
|
||||
const validationTimeoutRef = useRef<NodeJS.Timeout>(undefined);
|
||||
|
||||
// Validate title on form mount to catch invalid default values
|
||||
useEffect(() => {
|
||||
|
|
@ -59,14 +59,18 @@ export function SaveDashboardAsForm({ dashboard, changeInfo }: Props) {
|
|||
// Cleanup timeout on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
clearTimeout(validationTimeoutRef.current);
|
||||
if (validationTimeoutRef.current) {
|
||||
clearTimeout(validationTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleTitleChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
setValue('title', e.target.value, { shouldDirty: true });
|
||||
clearTimeout(validationTimeoutRef.current);
|
||||
if (validationTimeoutRef.current) {
|
||||
clearTimeout(validationTimeoutRef.current);
|
||||
}
|
||||
validationTimeoutRef.current = setTimeout(() => {
|
||||
trigger('title');
|
||||
}, 400);
|
||||
|
|
@ -75,7 +79,9 @@ export function SaveDashboardAsForm({ dashboard, changeInfo }: Props) {
|
|||
);
|
||||
|
||||
const onSave = async (overwrite: boolean) => {
|
||||
clearTimeout(validationTimeoutRef.current);
|
||||
if (validationTimeoutRef.current) {
|
||||
clearTimeout(validationTimeoutRef.current);
|
||||
}
|
||||
|
||||
const isTitleValid = await trigger('title');
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export const usePanelLatestData = (
|
|||
options: GetDataOptions,
|
||||
checkSchema?: boolean
|
||||
): UsePanelLatestData => {
|
||||
const querySubscription = useRef<Unsubscribable>();
|
||||
const querySubscription = useRef<Unsubscribable>(undefined);
|
||||
const [latestData, setLatestData] = useState<PanelData>();
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { BootData, getDefaultTimeRange } from '@grafana/data';
|
||||
|
|
@ -96,9 +96,11 @@ describe('ShareModal', () => {
|
|||
describe('with current time range and panel', () => {
|
||||
it('should generate share url absolute time', async () => {
|
||||
render(<ShareLink {...props} />);
|
||||
expect(await screen.findByRole('textbox', { name: 'Link URL' })).toHaveValue(
|
||||
'http://server/#!/test?from=1000&to=2000&orgId=1&viewPanel=22'
|
||||
);
|
||||
const linkUrl = await screen.findByRole('textbox', { name: 'Link URL' });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(linkUrl).toHaveValue('http://server/#!/test?from=1000&to=2000&orgId=1&viewPanel=22');
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate render url', async () => {
|
||||
|
|
@ -107,9 +109,10 @@ describe('ShareModal', () => {
|
|||
|
||||
const base = 'http://dashboards.grafana.com/render/d-solo/abcdefghi/my-dash';
|
||||
const params = '?from=1000&to=2000&orgId=1&panelId=22&hideLogo=true&width=1000&height=500&scale=1&tz=UTC';
|
||||
expect(
|
||||
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
|
||||
).toHaveAttribute('href', base + params);
|
||||
const linkUrl = await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage });
|
||||
await waitFor(() => {
|
||||
expect(linkUrl).toHaveAttribute('href', base + params);
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate render url for scripted dashboard', async () => {
|
||||
|
|
@ -118,17 +121,19 @@ describe('ShareModal', () => {
|
|||
|
||||
const base = 'http://dashboards.grafana.com/render/dashboard-solo/script/my-dash.js';
|
||||
const params = '?from=1000&to=2000&orgId=1&panelId=22&hideLogo=true&width=1000&height=500&scale=1&tz=UTC';
|
||||
expect(
|
||||
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
|
||||
).toHaveAttribute('href', base + params);
|
||||
const linkUrl = await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage });
|
||||
await waitFor(() => {
|
||||
expect(linkUrl).toHaveAttribute('href', base + params);
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove panel id when no panel in scope', async () => {
|
||||
props.panel = undefined;
|
||||
render(<ShareLink {...props} />);
|
||||
expect(await screen.findByRole('textbox', { name: 'Link URL' })).toHaveValue(
|
||||
'http://server/#!/test?from=1000&to=2000&orgId=1'
|
||||
);
|
||||
const linkUrl = await screen.findByRole('textbox', { name: 'Link URL' });
|
||||
await waitFor(() => {
|
||||
expect(linkUrl).toHaveValue('http://server/#!/test?from=1000&to=2000&orgId=1');
|
||||
});
|
||||
});
|
||||
|
||||
it('should add theme when specified', async () => {
|
||||
|
|
@ -136,9 +141,10 @@ describe('ShareModal', () => {
|
|||
render(<ShareLink {...props} />);
|
||||
|
||||
await userEvent.click(screen.getByLabelText('Light'));
|
||||
expect(await screen.findByRole('textbox', { name: 'Link URL' })).toHaveValue(
|
||||
'http://server/#!/test?from=1000&to=2000&orgId=1&theme=light'
|
||||
);
|
||||
const linkUrl = await screen.findByRole('textbox', { name: 'Link URL' });
|
||||
await waitFor(() => {
|
||||
expect(linkUrl).toHaveValue('http://server/#!/test?from=1000&to=2000&orgId=1&theme=light');
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove editPanel from image url when is first param in querystring', async () => {
|
||||
|
|
@ -147,24 +153,28 @@ describe('ShareModal', () => {
|
|||
|
||||
const base = 'http://server';
|
||||
const path = '/#!/test';
|
||||
expect(await screen.findByRole('textbox', { name: 'Link URL' })).toHaveValue(
|
||||
base + path + '?editPanel=1&from=1000&to=2000&orgId=1'
|
||||
);
|
||||
expect(
|
||||
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
|
||||
).toHaveAttribute(
|
||||
'href',
|
||||
base + path + '?from=1000&to=2000&orgId=1&panelId=1&hideLogo=true&width=1000&height=500&scale=1&tz=UTC'
|
||||
);
|
||||
const textboxUrl = await screen.findByRole('textbox', { name: 'Link URL' });
|
||||
const linkUrl = await screen.findByRole('link', {
|
||||
name: selectors.pages.SharePanelModal.linkToRenderedImage,
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(textboxUrl).toHaveValue(base + path + '?editPanel=1&from=1000&to=2000&orgId=1');
|
||||
expect(linkUrl).toHaveAttribute(
|
||||
'href',
|
||||
base + path + '?from=1000&to=2000&orgId=1&panelId=1&hideLogo=true&width=1000&height=500&scale=1&tz=UTC'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should shorten url', async () => {
|
||||
render(<ShareLink {...props} />);
|
||||
|
||||
await userEvent.click(await screen.findByLabelText('Shorten URL'));
|
||||
expect(await screen.findByRole('textbox', { name: 'Link URL' })).toHaveValue(
|
||||
`http://localhost:3000/goto/${mockUid}`
|
||||
);
|
||||
const linkUrl = await screen.findByRole('textbox', { name: 'Link URL' });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(linkUrl).toHaveValue(`http://localhost:3000/goto/${mockUid}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate render url without shareView param', async () => {
|
||||
|
|
@ -173,9 +183,10 @@ describe('ShareModal', () => {
|
|||
|
||||
const base = 'http://dashboards.grafana.com/render/d-solo/abcdefghi/my-dash';
|
||||
const params = '?from=1000&to=2000&orgId=1&panelId=22&hideLogo=true&width=1000&height=500&scale=1&tz=UTC';
|
||||
expect(
|
||||
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
|
||||
).toHaveAttribute('href', base + params);
|
||||
const linkUrl = await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage });
|
||||
await waitFor(() => {
|
||||
expect(linkUrl).toHaveAttribute('href', base + params);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -209,11 +220,12 @@ describe('when appUrl is set in the grafana config', () => {
|
|||
mockLocationHref('http://dashboards.grafana.com/?orgId=1');
|
||||
render(<ShareLink {...props} />);
|
||||
|
||||
expect(
|
||||
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
|
||||
).toHaveAttribute(
|
||||
'href',
|
||||
`http://dashboards.grafana.com/render/d-solo/${mockDashboard.uid}?orgId=1&from=1000&to=2000&panelId=${mockPanel.id}&hideLogo=true&width=1000&height=500&scale=1&tz=UTC`
|
||||
);
|
||||
const linkUrl = await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage });
|
||||
await waitFor(() => {
|
||||
expect(linkUrl).toHaveAttribute(
|
||||
'href',
|
||||
`http://dashboards.grafana.com/render/d-solo/${mockDashboard.uid}?orgId=1&from=1000&to=2000&panelId=${mockPanel.id}&hideLogo=true&width=1000&height=500&scale=1&tz=UTC`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ export const ExplorePaneContainer = connector(ExplorePaneContainerUnconnected);
|
|||
|
||||
function useStopQueries(exploreId: string) {
|
||||
const paneSelector = useMemo(() => getExploreItemSelector(exploreId), [exploreId]);
|
||||
const paneRef = useRef<ReturnType<typeof paneSelector>>();
|
||||
const paneRef = useRef<ReturnType<typeof paneSelector>>(undefined);
|
||||
paneRef.current = useSelector(paneSelector);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export const changeDatasource = async (name: string) => {
|
|||
};
|
||||
|
||||
export const inputQuery = async (query: string, exploreId = 'left') => {
|
||||
const input = withinExplore(exploreId).getByRole('textbox', { name: 'query' });
|
||||
const input = await withinExplore(exploreId).findByRole('textbox', { name: 'query' });
|
||||
await userEvent.clear(input);
|
||||
await userEvent.type(input, query);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ describe('SignupInvitedPage', () => {
|
|||
await setupTestContext();
|
||||
|
||||
expect(
|
||||
screen.getByRole('heading', {
|
||||
await screen.findByRole('heading', {
|
||||
name: /hello some user\./i,
|
||||
})
|
||||
).toBeInTheDocument();
|
||||
|
|
@ -70,7 +70,7 @@ describe('SignupInvitedPage', () => {
|
|||
it('then the invited by should be correct', async () => {
|
||||
await setupTestContext();
|
||||
|
||||
const view = screen.getByText(
|
||||
const view = await screen.findByText(
|
||||
/has invited you to join grafana and the organization please complete the following and choose a password to accept your invitation and continue:/i
|
||||
);
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ describe('SignupInvitedPage', () => {
|
|||
it('then the organization invited to should be correct', async () => {
|
||||
await setupTestContext();
|
||||
|
||||
const view = screen.getByText(
|
||||
const view = await screen.findByText(
|
||||
/has invited you to join grafana and the organization please complete the following and choose a password to accept your invitation and continue:/i
|
||||
);
|
||||
|
||||
|
|
@ -90,10 +90,10 @@ describe('SignupInvitedPage', () => {
|
|||
it('then the form should include form data', async () => {
|
||||
await setupTestContext();
|
||||
|
||||
expect(screen.getByPlaceholderText(/email@example\.com/i)).toHaveValue('some.user@localhost');
|
||||
expect(screen.getByPlaceholderText(/name \(optional\)/i)).toHaveValue('Some User');
|
||||
expect(screen.getByPlaceholderText(/username/i)).toHaveValue('some.user@localhost');
|
||||
expect(screen.getByPlaceholderText(/password/i)).toHaveValue('');
|
||||
expect(await screen.findByPlaceholderText(/email@example\.com/i)).toHaveValue('some.user@localhost');
|
||||
expect(await screen.findByPlaceholderText(/name \(optional\)/i)).toHaveValue('Some User');
|
||||
expect(await screen.findByPlaceholderText(/username/i)).toHaveValue('some.user@localhost');
|
||||
expect(await screen.findByPlaceholderText(/password/i)).toHaveValue('');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -105,9 +105,9 @@ describe('SignupInvitedPage', () => {
|
|||
|
||||
await userEvent.click(screen.getByRole('button', { name: /sign up/i }));
|
||||
|
||||
await waitFor(() => expect(screen.getByText(/email is required/i)).toBeInTheDocument());
|
||||
expect(screen.getByText(/username is required/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/password is required/i)).toBeInTheDocument();
|
||||
await waitFor(async () => expect(await screen.findByText(/email is required/i)).toBeInTheDocument());
|
||||
expect(await screen.findByText(/username is required/i)).toBeInTheDocument();
|
||||
expect(await screen.findByText(/password is required/i)).toBeInTheDocument();
|
||||
expect(postSpy).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
|
@ -116,8 +116,8 @@ describe('SignupInvitedPage', () => {
|
|||
it('then correct form data should be posted', async () => {
|
||||
const { postSpy } = await setupTestContext();
|
||||
|
||||
await userEvent.type(screen.getByPlaceholderText(/password/i), 'pass@word1');
|
||||
await userEvent.click(screen.getByRole('button', { name: /sign up/i }));
|
||||
await userEvent.type(await screen.findByPlaceholderText(/password/i), 'pass@word1');
|
||||
await userEvent.click(await screen.findByRole('button', { name: /sign up/i }));
|
||||
|
||||
await waitFor(() => expect(postSpy).toHaveBeenCalledTimes(1));
|
||||
expect(postSpy).toHaveBeenCalledWith('/api/user/invite/complete', {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { render, screen, within } from '@testing-library/react';
|
||||
import { render, screen, waitFor, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { Playlist } from '../../api/clients/playlist/v0alpha1';
|
||||
|
|
@ -93,7 +93,9 @@ describe('PlaylistForm', () => {
|
|||
|
||||
expect(rows()).toHaveLength(3);
|
||||
await userEvent.click(within(rows()[2]).getByRole('button', { name: /delete playlist item/i }));
|
||||
expect(rows()).toHaveLength(2);
|
||||
await waitFor(() => {
|
||||
expect(rows()).toHaveLength(2);
|
||||
});
|
||||
expectCorrectRow({ index: 0, type: 'dashboard_by_uid', value: 'uid_1' });
|
||||
expectCorrectRow({ index: 1, type: 'dashboard_by_uid', value: 'uid_2' });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -543,7 +543,6 @@ describe('ProvisioningWizard', () => {
|
|||
mockMutationState,
|
||||
]);
|
||||
|
||||
// Delay submission to keep button in "Submitting..." state
|
||||
mockSubmitData.mockImplementation(
|
||||
() => new Promise((resolve) => setTimeout(() => resolve({ data: { metadata: { name: 'test-conn' } } }), 100))
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { screen } from '@testing-library/react';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { config, locationService, setBackendSrv } from '@grafana/runtime';
|
||||
|
|
@ -323,18 +323,24 @@ describe('Tree', () => {
|
|||
|
||||
await searchScopes('Applications');
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(2);
|
||||
expectScopesHeadline('Results');
|
||||
await waitFor(() => {
|
||||
expectScopesHeadline('Results');
|
||||
});
|
||||
|
||||
await searchScopes('unknown');
|
||||
expect(fetchNodesSpy).toHaveBeenCalledTimes(3);
|
||||
expectScopesHeadline('No results found for your query');
|
||||
await waitFor(() => {
|
||||
expectScopesHeadline('No results found for your query');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should only show Recommended when there are no leaf container nodes visible', async () => {
|
||||
await openSelector();
|
||||
await expandResultApplications();
|
||||
await expandResultApplicationsCloud();
|
||||
expectScopesHeadline('Recommended');
|
||||
await waitFor(() => {
|
||||
expectScopesHeadline('Recommended');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should open to a specific path when scopes and scope_node are applied', async () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { act, render, screen, waitFor } from '@testing-library/react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { omit } from 'lodash';
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ const defaultProps = {
|
|||
resourcePickerData,
|
||||
getSubscriptions: jest
|
||||
.fn()
|
||||
.mockResolvedValue(createMockSubscriptions().map((sub) => ({ label: sub.name, value: sub.id }))),
|
||||
.mockResolvedValue(createMockSubscriptions().map((sub) => ({ text: sub.name, value: sub.id }))),
|
||||
getLocations: jest.fn().mockResolvedValue(createMockLocations()),
|
||||
getMetricNamespaces: jest.fn().mockResolvedValue(createMockMetricsNamespaces()),
|
||||
}),
|
||||
|
|
@ -369,7 +369,7 @@ describe('AzureMonitor ResourcePicker', () => {
|
|||
);
|
||||
const subscriptionExpand = await screen.findByLabelText('Expand Primary Subscription');
|
||||
await userEvent.click(subscriptionExpand);
|
||||
const error = await screen.queryByRole('alert');
|
||||
const error = screen.queryByRole('alert');
|
||||
expect(error).toBeNull();
|
||||
});
|
||||
|
||||
|
|
@ -398,9 +398,9 @@ describe('AzureMonitor ResourcePicker', () => {
|
|||
|
||||
describe('when rendering resource picker without any selectable entry types', () => {
|
||||
it('renders no checkboxes', async () => {
|
||||
await act(async () => {
|
||||
render(<ResourcePicker {...defaultProps} selectableEntryTypes={[]} />);
|
||||
});
|
||||
render(<ResourcePicker {...defaultProps} selectableEntryTypes={[]} />);
|
||||
// wait for search to be visible to allow for async operations
|
||||
await screen.findByLabelText('Resource search');
|
||||
const checkboxes = screen.queryAllByRole('checkbox');
|
||||
expect(checkboxes.length).toBe(0);
|
||||
});
|
||||
|
|
@ -412,7 +412,10 @@ describe('AzureMonitor ResourcePicker', () => {
|
|||
});
|
||||
it('should not render filters if feature toggle disabled', async () => {
|
||||
config.featureToggles.azureResourcePickerUpdates = false;
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} queryType="metrics" />));
|
||||
render(<ResourcePicker {...defaultProps} queryType="metrics" />);
|
||||
|
||||
// wait for search to be visible to allow for async operations
|
||||
await screen.findByLabelText('Resource search');
|
||||
|
||||
expect(
|
||||
screen.queryByTestId(selectors.components.queryEditor.resourcePicker.filters.subscription.input)
|
||||
|
|
@ -426,31 +429,36 @@ describe('AzureMonitor ResourcePicker', () => {
|
|||
});
|
||||
|
||||
it('should render subscription filter and load subscription options', async () => {
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} />));
|
||||
render(<ResourcePicker {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(defaultProps.datasource.getSubscriptions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
const subscriptionFilter = screen.getByTestId(
|
||||
const subscriptionFilter = await screen.findByTestId(
|
||||
selectors.components.queryEditor.resourcePicker.filters.subscription.input
|
||||
);
|
||||
expect(subscriptionFilter).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render resource type filter for metrics query type', async () => {
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} queryType="metrics" />));
|
||||
render(<ResourcePicker {...defaultProps} queryType="metrics" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(defaultProps.datasource.getMetricNamespaces).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
const resourceTypeFilter = screen.getByTestId(selectors.components.queryEditor.resourcePicker.filters.type.input);
|
||||
const resourceTypeFilter = await screen.findByTestId(
|
||||
selectors.components.queryEditor.resourcePicker.filters.type.input
|
||||
);
|
||||
expect(resourceTypeFilter).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not render resource type filter for logs query type', async () => {
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} />));
|
||||
render(<ResourcePicker {...defaultProps} />);
|
||||
|
||||
// wait for search to be visible to allow for async operations
|
||||
await screen.findByLabelText('Resource search');
|
||||
|
||||
const resourceTypeFilter = screen.queryByTestId(
|
||||
selectors.components.queryEditor.resourcePicker.filters.type.input
|
||||
|
|
@ -459,39 +467,36 @@ describe('AzureMonitor ResourcePicker', () => {
|
|||
});
|
||||
|
||||
it('should render location filter and load location options', async () => {
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} />));
|
||||
render(<ResourcePicker {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(defaultProps.datasource.getLocations).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
const locationFilter = screen.getByTestId(selectors.components.queryEditor.resourcePicker.filters.location.input);
|
||||
const locationFilter = await screen.findByTestId(
|
||||
selectors.components.queryEditor.resourcePicker.filters.location.input
|
||||
);
|
||||
expect(locationFilter).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Combobox tests seem to be quite finnicky when it comes to selecting options
|
||||
// I've had to add multiple {ArrowDown} key-presses as sometimes the expected option isn't
|
||||
// at the top of the list
|
||||
it('should call fetchInitialRows when subscription filter changes', async () => {
|
||||
const user = userEvent.setup();
|
||||
const mockFetchInitialRows = jest.spyOn(resourcePickerData, 'fetchInitialRows');
|
||||
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} />));
|
||||
render(<ResourcePicker {...defaultProps} />);
|
||||
|
||||
const subscriptionFilter = await screen.getByTestId(
|
||||
const subscriptionFilter = await screen.findByTestId(
|
||||
selectors.components.queryEditor.resourcePicker.filters.subscription.input
|
||||
);
|
||||
await act(async () => {
|
||||
await user.click(subscriptionFilter);
|
||||
await user.type(subscriptionFilter, 'Primary Subscription {ArrowDown}{ArrowDown}{ArrowDown}{Enter}');
|
||||
});
|
||||
await user.click(subscriptionFilter);
|
||||
await user.type(subscriptionFilter, 'Primary Subscription{ArrowDown}{Enter}');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockFetchInitialRows).toHaveBeenCalledWith(
|
||||
'logs',
|
||||
undefined,
|
||||
expect.objectContaining({
|
||||
subscriptions: ['def-456'],
|
||||
subscriptions: ['def-123'],
|
||||
types: [],
|
||||
locations: [],
|
||||
})
|
||||
|
|
@ -503,14 +508,12 @@ describe('AzureMonitor ResourcePicker', () => {
|
|||
const user = userEvent.setup();
|
||||
const mockFetchInitialRows = jest.spyOn(resourcePickerData, 'fetchInitialRows');
|
||||
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} />));
|
||||
render(<ResourcePicker {...defaultProps} />);
|
||||
|
||||
const locationFilter = await screen.getByTestId(
|
||||
const locationFilter = await screen.findByTestId(
|
||||
selectors.components.queryEditor.resourcePicker.filters.location.input
|
||||
);
|
||||
await act(async () => {
|
||||
await user.click(locationFilter);
|
||||
});
|
||||
await user.click(locationFilter);
|
||||
await user.type(locationFilter, 'North Europe{ArrowDown}{Enter}');
|
||||
|
||||
await waitFor(() => {
|
||||
|
|
@ -530,12 +533,10 @@ describe('AzureMonitor ResourcePicker', () => {
|
|||
const user = userEvent.setup();
|
||||
const mockFetchInitialRows = jest.spyOn(resourcePickerData, 'fetchInitialRows');
|
||||
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} queryType="metrics" />));
|
||||
render(<ResourcePicker {...defaultProps} queryType="metrics" />);
|
||||
|
||||
const typeFilter = await screen.getByTestId(selectors.components.queryEditor.resourcePicker.filters.type.input);
|
||||
await act(async () => {
|
||||
await user.click(typeFilter);
|
||||
});
|
||||
const typeFilter = await screen.findByTestId(selectors.components.queryEditor.resourcePicker.filters.type.input);
|
||||
await user.click(typeFilter);
|
||||
|
||||
await user.type(typeFilter, 'Kubernetes services {ArrowDown}{Enter}');
|
||||
await waitFor(() => {
|
||||
|
|
@ -559,23 +560,26 @@ describe('AzureMonitor ResourcePicker', () => {
|
|||
});
|
||||
it('should not render tabbed view if feature toggle disabled', async () => {
|
||||
config.featureToggles.azureResourcePickerUpdates = false;
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} />));
|
||||
render(<ResourcePicker {...defaultProps} />);
|
||||
|
||||
// wait for search to be visible to allow for async operations
|
||||
await screen.findByLabelText('Resource search');
|
||||
|
||||
expect(screen.queryByTestId(e2eSelectors.components.Tab.title('Browse'))).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId(e2eSelectors.components.Tab.title('Recent'))).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render tabbed view', async () => {
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} />));
|
||||
render(<ResourcePicker {...defaultProps} />);
|
||||
|
||||
expect(screen.queryByTestId(e2eSelectors.components.Tab.title('Browse'))).toBeInTheDocument();
|
||||
expect(screen.queryByTestId(e2eSelectors.components.Tab.title('Recent'))).toBeInTheDocument();
|
||||
expect(await screen.findByTestId(e2eSelectors.components.Tab.title('Browse'))).toBeInTheDocument();
|
||||
expect(await screen.findByTestId(e2eSelectors.components.Tab.title('Recent'))).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render tabbed view with no recent resources', async () => {
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} />));
|
||||
render(<ResourcePicker {...defaultProps} />);
|
||||
|
||||
const recent = await screen.getByTestId(e2eSelectors.components.Tab.title('Recent'));
|
||||
const recent = await screen.findByTestId(e2eSelectors.components.Tab.title('Recent'));
|
||||
await userEvent.click(recent);
|
||||
|
||||
expect(screen.getByText('No recent resources found')).toBeInTheDocument();
|
||||
|
|
@ -610,9 +614,9 @@ describe('AzureMonitor ResourcePicker', () => {
|
|||
},
|
||||
];
|
||||
window.localStorage.setItem(RECENT_RESOURCES_KEY(defaultProps.queryType), JSON.stringify(recentResources));
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} />));
|
||||
render(<ResourcePicker {...defaultProps} />);
|
||||
|
||||
const recent = await screen.getByTestId(e2eSelectors.components.Tab.title('Recent'));
|
||||
const recent = await screen.findByTestId(e2eSelectors.components.Tab.title('Recent'));
|
||||
await userEvent.click(recent);
|
||||
|
||||
expect(screen.getByText(recentResources[0].name)).toBeInTheDocument();
|
||||
|
|
@ -651,9 +655,9 @@ describe('AzureMonitor ResourcePicker', () => {
|
|||
const queryType = 'metrics';
|
||||
window.localStorage.setItem(RECENT_RESOURCES_KEY(queryType), JSON.stringify(recentResources));
|
||||
const onApply = jest.fn();
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} queryType={queryType} onApply={onApply} />));
|
||||
render(<ResourcePicker {...defaultProps} queryType={queryType} onApply={onApply} />);
|
||||
|
||||
const recent = await screen.getByTestId(e2eSelectors.components.Tab.title('Recent'));
|
||||
const recent = await screen.findByTestId(e2eSelectors.components.Tab.title('Recent'));
|
||||
await userEvent.click(recent);
|
||||
|
||||
const checkbox = await screen.findByLabelText(recentResources[0].name);
|
||||
|
|
@ -705,9 +709,9 @@ describe('AzureMonitor ResourcePicker', () => {
|
|||
const queryType = 'metrics';
|
||||
window.localStorage.setItem(RECENT_RESOURCES_KEY(queryType), JSON.stringify(recentResources));
|
||||
const onApply = jest.fn();
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} queryType={queryType} onApply={onApply} />));
|
||||
render(<ResourcePicker {...defaultProps} queryType={queryType} onApply={onApply} />);
|
||||
|
||||
const recent = await screen.getByTestId(e2eSelectors.components.Tab.title('Recent'));
|
||||
const recent = await screen.findByTestId(e2eSelectors.components.Tab.title('Recent'));
|
||||
await userEvent.click(recent);
|
||||
|
||||
const checkbox = await screen.findByLabelText(recentResources[0].name);
|
||||
|
|
@ -769,9 +773,9 @@ describe('AzureMonitor ResourcePicker', () => {
|
|||
const queryType = 'metrics';
|
||||
window.localStorage.setItem(RECENT_RESOURCES_KEY(queryType), JSON.stringify(recentResources));
|
||||
const onApply = jest.fn();
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} queryType={queryType} onApply={onApply} />));
|
||||
render(<ResourcePicker {...defaultProps} queryType={queryType} onApply={onApply} />);
|
||||
|
||||
const recent = await screen.getByTestId(e2eSelectors.components.Tab.title('Recent'));
|
||||
const recent = await screen.findByTestId(e2eSelectors.components.Tab.title('Recent'));
|
||||
await userEvent.click(recent);
|
||||
|
||||
const checkbox = await screen.findByLabelText(recentResources[0].name);
|
||||
|
|
@ -814,7 +818,7 @@ describe('AzureMonitor ResourcePicker', () => {
|
|||
expect(JSON.parse(window.localStorage.getItem(RECENT_RESOURCES_KEY(queryType)) || '[]')).toHaveLength(30);
|
||||
|
||||
const onApply = jest.fn();
|
||||
await act(async () => render(<ResourcePicker {...defaultProps} queryType={queryType} onApply={onApply} />));
|
||||
render(<ResourcePicker {...defaultProps} queryType={queryType} onApply={onApply} />);
|
||||
|
||||
const subscriptionButton = await screen.findByRole('button', { name: 'Expand Primary Subscription' });
|
||||
expect(subscriptionButton).toBeInTheDocument();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import { VariableModel } from '@grafana/data';
|
||||
|
||||
|
|
@ -41,10 +41,7 @@ const props: Props = {
|
|||
describe('VariableQueryEditor', () => {
|
||||
it('renders correctly', async () => {
|
||||
const { container } = render(<CloudMonitoringVariableQueryEditor {...props} />);
|
||||
const select = await screen.findByRole('combobox');
|
||||
waitFor(() => {
|
||||
expect(select).toHaveValue('projects');
|
||||
});
|
||||
await screen.findByText('Projects');
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ exports[`VariableQueryEditor renders correctly 1`] = `
|
|||
<div
|
||||
class="css-8nwx1l-singleValue css-0"
|
||||
>
|
||||
Loading...
|
||||
Projects
|
||||
</div>
|
||||
<div
|
||||
class="css-1eu65zc"
|
||||
|
|
|
|||
|
|
@ -236,13 +236,13 @@ describe('Render', () => {
|
|||
},
|
||||
});
|
||||
await waitFor(async () => {
|
||||
expect(await screen.getByText('logGroup-foo')).toBeInTheDocument();
|
||||
expect(await screen.getByText('logGroup-bar')).toBeInTheDocument();
|
||||
expect(await screen.queryByText('logGroup-baz')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('logGroup-foo')).toBeInTheDocument();
|
||||
expect(screen.getByText('logGroup-bar')).toBeInTheDocument();
|
||||
expect(screen.queryByText('logGroup-baz')).not.toBeInTheDocument();
|
||||
|
||||
await userEvent.click(screen.getByText('Show all'));
|
||||
|
||||
expect(await screen.getByText('logGroup-baz')).toBeInTheDocument();
|
||||
expect(screen.getByText('logGroup-baz')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { CoreApp, PluginType } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
import { PyroscopeDataSource } from '../datasource';
|
||||
import { mockFetchPyroscopeDatasourceSettings } from '../mocks';
|
||||
|
|
@ -17,7 +18,11 @@ describe('QueryEditor', () => {
|
|||
it('should render without error', async () => {
|
||||
setup();
|
||||
|
||||
expect(await screen.findByDisplayValue('process_cpu-cpu')).toBeDefined();
|
||||
// wait for CodeEditor
|
||||
expect(await screen.findByTestId(selectors.components.CodeEditor.container)).toBeDefined();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByDisplayValue('process_cpu-cpu')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render without error if empty profileTypes', async () => {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
"tslib": "2.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@grafana/e2e-selectors": "12.4.0-pre",
|
||||
"@grafana/plugin-configs": "12.4.0-pre",
|
||||
"@testing-library/dom": "10.4.1",
|
||||
"@testing-library/jest-dom": "6.6.4",
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ describe('MonacoQueryField', () => {
|
|||
test('Renders with no errors', async () => {
|
||||
renderComponent();
|
||||
|
||||
const monacoEditor = await screen.findByTestId(selectors.components.ReactMonacoEditor.editorLazy);
|
||||
const monacoEditor = await screen.findByTestId(selectors.components.QueryField.container);
|
||||
expect(monacoEditor).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
import { CoreApp, DataSourcePluginMeta, PluginType } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { BackendSrv, getBackendSrv, setBackendSrv } from '@grafana/runtime';
|
||||
|
||||
import { ParcaDataSource } from '../datasource';
|
||||
|
|
@ -23,9 +24,14 @@ describe('QueryEditor', () => {
|
|||
});
|
||||
|
||||
it('should render without error', async () => {
|
||||
setBackendSrv({ ...origBackendSrv, fetch: fetchMock });
|
||||
setup();
|
||||
|
||||
expect(await screen.findByText(/process_cpu - cpu/)).toBeDefined();
|
||||
// wait for CodeEditor
|
||||
expect(await screen.findByTestId(selectors.components.CodeEditor.container)).toBeDefined();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/process_cpu - cpu/)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render options', async () => {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
"tslib": "2.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@grafana/e2e-selectors": "12.4.0-pre",
|
||||
"@grafana/plugin-configs": "12.4.0-pre",
|
||||
"@testing-library/dom": "10.4.1",
|
||||
"@testing-library/react": "16.3.0",
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ describe('AnnoListPanel', () => {
|
|||
it('then it should show a no annotations message', async () => {
|
||||
await setupTestContext({ results: [] });
|
||||
|
||||
expect(screen.getByText(/no annotations found/i)).toBeInTheDocument();
|
||||
expect(await screen.findByText(/no annotations found/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -124,11 +124,11 @@ describe('AnnoListPanel', () => {
|
|||
|
||||
expect(screen.queryByText(/no annotations found/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/result email/i)).not.toBeInTheDocument();
|
||||
expect(screen.getByText(/result text/i)).toBeInTheDocument();
|
||||
expect(screen.getByRole('img')).toBeInTheDocument();
|
||||
expect(screen.getByText('Result tag A')).toBeInTheDocument();
|
||||
expect(screen.getByText('Result tag B')).toBeInTheDocument();
|
||||
expect(screen.getByText(/2021-01-01T00:00:00.000Z/i)).toBeInTheDocument();
|
||||
expect(await screen.findByText(/result text/i)).toBeInTheDocument();
|
||||
expect(await screen.findByRole('img')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Result tag A')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Result tag B')).toBeInTheDocument();
|
||||
expect(await screen.findByText(/2021-01-01T00:00:00.000Z/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders annotation item's html content", async () => {
|
||||
|
|
@ -137,7 +137,7 @@ describe('AnnoListPanel', () => {
|
|||
});
|
||||
|
||||
getMock.mockClear();
|
||||
expect(screen.getByRole('link')).toBeInTheDocument();
|
||||
expect(await screen.findByRole('link')).toBeInTheDocument();
|
||||
expect(getMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
|
@ -146,10 +146,10 @@ describe('AnnoListPanel', () => {
|
|||
await setupTestContext({ results: [{ ...defaultResult, login: undefined }] });
|
||||
|
||||
expect(screen.queryByRole('img')).not.toBeInTheDocument();
|
||||
expect(screen.getByText(/result text/i)).toBeInTheDocument();
|
||||
expect(screen.getByText('Result tag A')).toBeInTheDocument();
|
||||
expect(screen.getByText('Result tag B')).toBeInTheDocument();
|
||||
expect(screen.getByText(/2021-01-01T00:00:00.000Z/i)).toBeInTheDocument();
|
||||
expect(await screen.findByText(/result text/i)).toBeInTheDocument();
|
||||
expect(await screen.findByText('Result tag A')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Result tag B')).toBeInTheDocument();
|
||||
expect(await screen.findByText(/2021-01-01T00:00:00.000Z/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -158,10 +158,10 @@ describe('AnnoListPanel', () => {
|
|||
await setupTestContext({ results: [{ ...defaultResult, time: undefined }] });
|
||||
|
||||
expect(screen.queryByText(/2021-01-01T00:00:00.000Z/i)).not.toBeInTheDocument();
|
||||
expect(screen.getByText(/result text/i)).toBeInTheDocument();
|
||||
expect(screen.getByRole('img')).toBeInTheDocument();
|
||||
expect(screen.getByText('Result tag A')).toBeInTheDocument();
|
||||
expect(screen.getByText('Result tag B')).toBeInTheDocument();
|
||||
expect(await screen.findByText(/result text/i)).toBeInTheDocument();
|
||||
expect(await screen.findByRole('img')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Result tag A')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Result tag B')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -172,10 +172,10 @@ describe('AnnoListPanel', () => {
|
|||
});
|
||||
|
||||
expect(screen.queryByRole('img')).not.toBeInTheDocument();
|
||||
expect(screen.getByText(/result text/i)).toBeInTheDocument();
|
||||
expect(screen.getByText('Result tag A')).toBeInTheDocument();
|
||||
expect(screen.getByText('Result tag B')).toBeInTheDocument();
|
||||
expect(screen.getByText(/2021-01-01T00:00:00.000Z/i)).toBeInTheDocument();
|
||||
expect(await screen.findByText(/result text/i)).toBeInTheDocument();
|
||||
expect(await screen.findByText('Result tag A')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Result tag B')).toBeInTheDocument();
|
||||
expect(await screen.findByText(/2021-01-01T00:00:00.000Z/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -186,10 +186,10 @@ describe('AnnoListPanel', () => {
|
|||
});
|
||||
|
||||
expect(screen.queryByText(/2021-01-01T00:00:00.000Z/i)).not.toBeInTheDocument();
|
||||
expect(screen.getByText(/result text/i)).toBeInTheDocument();
|
||||
expect(screen.getByRole('img')).toBeInTheDocument();
|
||||
expect(screen.getByText('Result tag A')).toBeInTheDocument();
|
||||
expect(screen.getByText('Result tag B')).toBeInTheDocument();
|
||||
expect(await screen.findByText(/result text/i)).toBeInTheDocument();
|
||||
expect(await screen.findByRole('img')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Result tag A')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Result tag B')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -201,9 +201,9 @@ describe('AnnoListPanel', () => {
|
|||
|
||||
expect(screen.queryByText('Result tag A')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Result tag B')).not.toBeInTheDocument();
|
||||
expect(screen.getByText(/result text/i)).toBeInTheDocument();
|
||||
expect(screen.getByRole('img')).toBeInTheDocument();
|
||||
expect(screen.getByText(/2021-01-01T00:00:00.000Z/i)).toBeInTheDocument();
|
||||
expect(await screen.findByText(/result text/i)).toBeInTheDocument();
|
||||
expect(await screen.findByRole('img')).toBeInTheDocument();
|
||||
expect(await screen.findByText(/2021-01-01T00:00:00.000Z/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -212,8 +212,8 @@ describe('AnnoListPanel', () => {
|
|||
const { getMock, pushSpy } = await setupTestContext();
|
||||
|
||||
getMock.mockClear();
|
||||
expect(screen.getByRole('button', { name: /result text/i })).toBeInTheDocument();
|
||||
await userEvent.click(screen.getByRole('button', { name: /result text/i }));
|
||||
expect(await screen.findByRole('button', { name: /result text/i })).toBeInTheDocument();
|
||||
await userEvent.click(await screen.findByRole('button', { name: /result text/i }));
|
||||
await waitFor(() => expect(getMock).toHaveBeenCalledTimes(1));
|
||||
|
||||
expect(getMock).toHaveBeenCalledWith('/api/search', { dashboardUIDs: '7MeksYbmk' });
|
||||
|
|
@ -226,9 +226,9 @@ describe('AnnoListPanel', () => {
|
|||
results: [{ ...defaultResult, dashboardUID: null, panelId: 0 }],
|
||||
});
|
||||
getMock.mockClear();
|
||||
expect(screen.getByRole('button', { name: /result text/i })).toBeInTheDocument();
|
||||
expect(await screen.findByRole('button', { name: /result text/i })).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: /result text/i }));
|
||||
await userEvent.click(await screen.findByRole('button', { name: /result text/i }));
|
||||
|
||||
expect(getMock).not.toHaveBeenCalled();
|
||||
expect(partialSpy).toHaveBeenCalledTimes(1);
|
||||
|
|
@ -240,9 +240,9 @@ describe('AnnoListPanel', () => {
|
|||
results: [{ ...defaultResult, dashboardUID: null }],
|
||||
});
|
||||
getMock.mockClear();
|
||||
expect(screen.getByRole('button', { name: /result text/i })).toBeInTheDocument();
|
||||
expect(await screen.findByRole('button', { name: /result text/i })).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: /result text/i }));
|
||||
await userEvent.click(await screen.findByRole('button', { name: /result text/i }));
|
||||
|
||||
expect(getMock).not.toHaveBeenCalled();
|
||||
expect(partialSpy).toHaveBeenCalledTimes(1);
|
||||
|
|
@ -256,8 +256,8 @@ describe('AnnoListPanel', () => {
|
|||
|
||||
getMock.mockClear();
|
||||
|
||||
expect(screen.getByRole('button', { name: /result tag b/i })).toBeInTheDocument();
|
||||
await userEvent.click(screen.getByRole('button', { name: /result tag b/i }));
|
||||
expect(await screen.findByRole('button', { name: /result tag b/i })).toBeInTheDocument();
|
||||
await userEvent.click(await screen.findByRole('button', { name: /result tag b/i }));
|
||||
|
||||
expect(getMock).toHaveBeenCalledTimes(1);
|
||||
expect(getMock).toHaveBeenCalledWith(
|
||||
|
|
@ -270,8 +270,8 @@ describe('AnnoListPanel', () => {
|
|||
},
|
||||
expect.stringMatching(/^anno-list-panel-\d\.\d+/) // string is appended with Math.random()
|
||||
);
|
||||
expect(screen.getByText(/filter:/i)).toBeInTheDocument();
|
||||
expect(screen.getAllByText(/result tag b/i)).toHaveLength(2);
|
||||
expect(await screen.findByText(/filter:/i)).toBeInTheDocument();
|
||||
expect(await screen.findAllByText(/result tag b/i)).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -280,8 +280,8 @@ describe('AnnoListPanel', () => {
|
|||
const { getMock } = await setupTestContext();
|
||||
|
||||
getMock.mockClear();
|
||||
expect(screen.getByRole('img')).toBeInTheDocument();
|
||||
await userEvent.click(screen.getByRole('img'));
|
||||
expect(await screen.findByRole('img')).toBeInTheDocument();
|
||||
await userEvent.click(await screen.findByRole('img'));
|
||||
|
||||
expect(getMock).toHaveBeenCalledTimes(1);
|
||||
expect(getMock).toHaveBeenCalledWith(
|
||||
|
|
@ -295,8 +295,8 @@ describe('AnnoListPanel', () => {
|
|||
},
|
||||
expect.stringMatching(/^anno-list-panel-\d\.\d+/) // string is appended with Math.random()
|
||||
);
|
||||
expect(screen.getByText(/filter:/i)).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /result email/i })).toBeInTheDocument();
|
||||
expect(await screen.findByText(/filter:/i)).toBeInTheDocument();
|
||||
expect(await screen.findByRole('button', { name: /result email/i })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -306,7 +306,7 @@ describe('AnnoListPanel', () => {
|
|||
const { getMock } = await setupTestContext();
|
||||
|
||||
getMock.mockClear();
|
||||
expect(screen.getByRole('img')).toBeInTheDocument();
|
||||
expect(await screen.findByRole('img')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import './global-jquery-shim';
|
||||
|
||||
import { TransformStream } from 'node:stream/web';
|
||||
import { MessageChannel, MessagePort } from 'node:worker_threads';
|
||||
import { TextEncoder, TextDecoder } from 'util';
|
||||
|
||||
// we need to isolate the `@grafana/data` module here now that it depends on `@grafana/i18n`
|
||||
|
|
@ -126,6 +127,26 @@ global.ResizeObserver = class ResizeObserver {
|
|||
}
|
||||
};
|
||||
|
||||
// originally using just global.MessageChannel = MessageChannel
|
||||
// however this results in open handles in jest tests
|
||||
// see https://github.com/facebook/react/issues/26608#issuecomment-1734172596
|
||||
global.MessageChannel = class {
|
||||
port1: MessagePort;
|
||||
port2: MessagePort;
|
||||
constructor() {
|
||||
const channel = new MessageChannel();
|
||||
this.port1 = new Proxy(channel.port1, {
|
||||
set(port1, prop, value) {
|
||||
const result = Reflect.set(port1, prop, value);
|
||||
if (prop === 'onmessage') {
|
||||
port1.unref();
|
||||
}
|
||||
return result;
|
||||
},
|
||||
});
|
||||
this.port2 = channel.port2;
|
||||
}
|
||||
};
|
||||
global.BroadcastChannel = class BroadcastChannel {
|
||||
onmessage() {}
|
||||
onmessageerror() {}
|
||||
|
|
|
|||
|
|
@ -2742,6 +2742,7 @@ __metadata:
|
|||
dependencies:
|
||||
"@emotion/css": "npm:11.13.5"
|
||||
"@grafana/data": "npm:12.4.0-pre"
|
||||
"@grafana/e2e-selectors": "npm:12.4.0-pre"
|
||||
"@grafana/plugin-configs": "npm:12.4.0-pre"
|
||||
"@grafana/runtime": "npm:12.4.0-pre"
|
||||
"@grafana/schema": "npm:12.4.0-pre"
|
||||
|
|
@ -3059,6 +3060,7 @@ __metadata:
|
|||
dependencies:
|
||||
"@emotion/css": "npm:11.13.5"
|
||||
"@grafana/data": "npm:12.4.0-pre"
|
||||
"@grafana/e2e-selectors": "npm:12.4.0-pre"
|
||||
"@grafana/plugin-configs": "npm:12.4.0-pre"
|
||||
"@grafana/runtime": "npm:12.4.0-pre"
|
||||
"@grafana/schema": "npm:12.4.0-pre"
|
||||
|
|
|
|||
Loading…
Reference in a new issue