Chore: upgrade to msw v2 (#82270)

* Update dependency msw to v2

* close

* minor fixes

* fetch import changes

* fix some alerting tests

* fix another alerting test

* fix systemjs tests

* don't return undefined in json mocks

* don't return undefined in json response

* add type

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This commit is contained in:
Ashley Harrison 2024-02-14 13:16:44 +00:00 committed by GitHub
parent 4dcc59244a
commit cf65d91ee9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 712 additions and 383 deletions

View File

@ -183,7 +183,7 @@
"jest-matcher-utils": "29.7.0", "jest-matcher-utils": "29.7.0",
"lerna": "7.4.1", "lerna": "7.4.1",
"mini-css-extract-plugin": "2.8.0", "mini-css-extract-plugin": "2.8.0",
"msw": "1.3.2", "msw": "2.2.0",
"mutationobserver-shim": "0.3.7", "mutationobserver-shim": "0.3.7",
"ngtemplate-loader": "2.1.0", "ngtemplate-loader": "2.1.0",
"node-notifier": "10.0.1", "node-notifier": "10.0.1",

View File

@ -1,7 +1,7 @@
import 'whatwg-fetch'; // fetch polyfill import 'whatwg-fetch'; // fetch polyfill
import { fireEvent, render as rtlRender, screen } from '@testing-library/react'; import { fireEvent, render as rtlRender, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { rest } from 'msw'; import { HttpResponse, http } from 'msw';
import { SetupServer, setupServer } from 'msw/node'; import { SetupServer, setupServer } from 'msw/node';
import React from 'react'; import React from 'react';
import { TestProvider } from 'test/helpers/TestProvider'; import { TestProvider } from 'test/helpers/TestProvider';
@ -47,14 +47,11 @@ describe('NestedFolderPicker', () => {
beforeAll(() => { beforeAll(() => {
window.HTMLElement.prototype.scrollIntoView = function () {}; window.HTMLElement.prototype.scrollIntoView = function () {};
server = setupServer( server = setupServer(
rest.get('/api/folders/:uid', (_, res, ctx) => { http.get('/api/folders/:uid', () => {
return res( return HttpResponse.json({
ctx.status(200),
ctx.json({
title: folderA.item.title, title: folderA.item.title,
uid: folderA.item.uid, uid: folderA.item.uid,
}) });
);
}) })
); );
server.listen(); server.listen();

View File

@ -1,3 +1,4 @@
import 'whatwg-fetch';
import { render, waitFor, waitForElementToBeRemoved, within } from '@testing-library/react'; import { render, waitFor, waitForElementToBeRemoved, within } from '@testing-library/react';
import { setupServer } from 'msw/node'; import { setupServer } from 'msw/node';
import React from 'react'; import React from 'react';
@ -11,7 +12,6 @@ import { backendSrv } from 'app/core/services/backend_srv';
import { DashboardSearchItem, DashboardSearchItemType } from 'app/features/search/types'; import { DashboardSearchItem, DashboardSearchItemType } from 'app/features/search/types';
import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types'; import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types';
import { RuleWithLocation } from 'app/types/unified-alerting'; import { RuleWithLocation } from 'app/types/unified-alerting';
import 'whatwg-fetch';
import { import {
RulerAlertingRuleDTO, RulerAlertingRuleDTO,

View File

@ -1,10 +1,9 @@
import { render, screen } from '@testing-library/react'; import 'whatwg-fetch';
import { render, screen, waitFor } from '@testing-library/react';
import { setupServer } from 'msw/node'; import { setupServer } from 'msw/node';
import React from 'react'; import React from 'react';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import 'whatwg-fetch';
import { setBackendSrv } from '@grafana/runtime'; import { setBackendSrv } from '@grafana/runtime';
import { backendSrv } from 'app/core/services/backend_srv'; import { backendSrv } from 'app/core/services/backend_srv';
import { configureStore } from 'app/store/configureStore'; import { configureStore } from 'app/store/configureStore';
@ -76,8 +75,10 @@ describe('GrafanaAlertmanagerDeliveryWarning', () => {
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={GRAFANA_RULES_SOURCE_NAME} /> <GrafanaAlertmanagerDeliveryWarning currentAlertmanager={GRAFANA_RULES_SOURCE_NAME} />
); );
await waitFor(() => {
expect(container).toBeEmptyDOMElement(); expect(container).toBeEmptyDOMElement();
}); });
});
it('Should render no warning when choice is All but no active AM instances', async () => { it('Should render no warning when choice is All but no active AM instances', async () => {
mockAlertmanagerChoiceResponse(server, { mockAlertmanagerChoiceResponse(server, {
@ -89,9 +90,11 @@ describe('GrafanaAlertmanagerDeliveryWarning', () => {
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={GRAFANA_RULES_SOURCE_NAME} /> <GrafanaAlertmanagerDeliveryWarning currentAlertmanager={GRAFANA_RULES_SOURCE_NAME} />
); );
await waitFor(() => {
expect(container).toBeEmptyDOMElement(); expect(container).toBeEmptyDOMElement();
}); });
}); });
});
function renderWithStore(element: JSX.Element) { function renderWithStore(element: JSX.Element) {
const store = configureStore(); const store = configureStore();

View File

@ -1,20 +1,25 @@
import { rest } from 'msw';
import { setupServer } from 'msw/node';
// bit of setup to mock HTTP request responses // bit of setup to mock HTTP request responses
import 'whatwg-fetch'; import 'whatwg-fetch';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import { SupportedPlugin } from '../types/pluginBridges'; import { SupportedPlugin } from '../types/pluginBridges';
export const NON_EXISTING_PLUGIN = '__does_not_exist__'; export const NON_EXISTING_PLUGIN = '__does_not_exist__';
const server = setupServer( const server = setupServer(
rest.get(`/api/plugins/${NON_EXISTING_PLUGIN}/settings`, async (_req, res, ctx) => res(ctx.status(404))), http.get(`/api/plugins/${NON_EXISTING_PLUGIN}/settings`, async () =>
rest.get(`/api/plugins/${SupportedPlugin.Incident}/settings`, async (_req, res, ctx) => { HttpResponse.json(
return res( {},
ctx.json({ {
status: 404,
}
)
),
http.get(`/api/plugins/${SupportedPlugin.Incident}/settings`, async () => {
return HttpResponse.json({
enabled: true, enabled: true,
}) });
);
}) })
); );

View File

@ -1,4 +1,5 @@
import { rest } from 'msw'; import 'whatwg-fetch';
import { http, HttpResponse } from 'msw';
import { SetupServer } from 'msw/node'; import { SetupServer } from 'msw/node';
import { AlertmanagerChoice, AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types'; import { AlertmanagerChoice, AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types';
@ -14,15 +15,15 @@ import receiversMock from './receivers.mock.json';
export default (server: SetupServer) => { export default (server: SetupServer) => {
server.use( server.use(
// this endpoint is a grafana built-in alertmanager // this endpoint is a grafana built-in alertmanager
rest.get('/api/alertmanager/grafana/config/api/v1/alerts', (_req, res, ctx) => http.get('/api/alertmanager/grafana/config/api/v1/alerts', () =>
res(ctx.json<AlertManagerCortexConfig>(alertmanagerMock)) HttpResponse.json<AlertManagerCortexConfig>(alertmanagerMock)
), ),
// this endpoint is only available for the built-in alertmanager // this endpoint is only available for the built-in alertmanager
rest.get('/api/alertmanager/grafana/config/api/v1/receivers', (_req, res, ctx) => http.get('/api/alertmanager/grafana/config/api/v1/receivers', () =>
res(ctx.json<ReceiversStateDTO[]>(receiversMock)) HttpResponse.json<ReceiversStateDTO[]>(receiversMock)
), ),
// this endpoint will respond if the OnCall plugin is installed // this endpoint will respond if the OnCall plugin is installed
rest.get('/api/plugins/grafana-oncall-app/settings', (_req, res, ctx) => res(ctx.status(404))) http.get('/api/plugins/grafana-oncall-app/settings', () => HttpResponse.json({}, { status: 404 }))
); );
// this endpoint is for rendering the "additional AMs to configure" warning // this endpoint is for rendering the "additional AMs to configure" warning
@ -41,12 +42,18 @@ export const setupTestEndpointMock = (server: SetupServer) => {
const mock = jest.fn(); const mock = jest.fn();
server.use( server.use(
rest.post('/api/alertmanager/grafana/config/api/v1/receivers/test', async (req, res, ctx) => { http.post(
const requestBody = await req.json(); '/api/alertmanager/grafana/config/api/v1/receivers/test',
async ({ request }) => {
const requestBody = await request.json();
mock(requestBody); mock(requestBody);
return res.once(ctx.status(200)); return HttpResponse.json({});
}) },
{
once: true,
}
)
); );
return mock; return mock;
@ -56,12 +63,18 @@ export const setupSaveEndpointMock = (server: SetupServer) => {
const mock = jest.fn(); const mock = jest.fn();
server.use( server.use(
rest.post('/api/alertmanager/grafana/config/api/v1/alerts', async (req, res, ctx) => { http.post(
const requestBody = await req.json(); '/api/alertmanager/grafana/config/api/v1/alerts',
async ({ request }) => {
const requestBody = await request.json();
mock(requestBody); mock(requestBody);
return res.once(ctx.status(200)); return HttpResponse.json({});
}) },
{
once: true,
}
)
); );
return mock; return mock;

View File

@ -1,4 +1,5 @@
import { rest } from 'msw'; import 'whatwg-fetch';
import { http, HttpResponse } from 'msw';
import { SetupServer } from 'msw/lib/node'; import { SetupServer } from 'msw/lib/node';
import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types'; import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types';
@ -10,14 +11,20 @@ export const MIMIR_DATASOURCE_UID = 'mimir';
export default (server: SetupServer) => { export default (server: SetupServer) => {
server.use( server.use(
rest.get(`/api/alertmanager/${MIMIR_DATASOURCE_UID}/config/api/v1/alerts`, (_req, res, ctx) => http.get(`/api/alertmanager/${MIMIR_DATASOURCE_UID}/config/api/v1/alerts`, () =>
res(ctx.json<AlertManagerCortexConfig>(mimirAlertmanagerMock)) HttpResponse.json(mimirAlertmanagerMock)
), ),
rest.get(`/api/datasources/proxy/uid/${MIMIR_DATASOURCE_UID}/api/v1/status/buildinfo`, (_req, res, ctx) => http.get(`/api/datasources/proxy/uid/${MIMIR_DATASOURCE_UID}/api/v1/status/buildinfo`, () =>
res(ctx.status(404)) HttpResponse.json<AlertManagerCortexConfig>(
{
template_files: {},
alertmanager_config: {},
},
{ status: 404 }
)
), ),
// this endpoint will respond if the OnCall plugin is installed // this endpoint will respond if the OnCall plugin is installed
rest.get('/api/plugins/grafana-oncall-app/settings', (_req, res, ctx) => res(ctx.status(404))) http.get('/api/plugins/grafana-oncall-app/settings', () => HttpResponse.json({}, { status: 404 }))
); );
return server; return server;

View File

@ -1,4 +1,5 @@
import { rest } from 'msw'; import 'whatwg-fetch';
import { http, HttpResponse } from 'msw';
import { SetupServer } from 'msw/lib/node'; import { SetupServer } from 'msw/lib/node';
import { AlertmanagerStatus } from 'app/plugins/datasource/alertmanager/types'; import { AlertmanagerStatus } from 'app/plugins/datasource/alertmanager/types';
@ -10,11 +11,11 @@ export const VANILLA_ALERTMANAGER_DATASOURCE_UID = 'alertmanager';
export default (server: SetupServer) => { export default (server: SetupServer) => {
server.use( server.use(
rest.get(`/api/alertmanager/${VANILLA_ALERTMANAGER_DATASOURCE_UID}/api/v2/status`, (_req, res, ctx) => http.get(`/api/alertmanager/${VANILLA_ALERTMANAGER_DATASOURCE_UID}/api/v2/status`, () =>
res(ctx.json<AlertmanagerStatus>(vanillaAlertManagerConfig)) HttpResponse.json<AlertmanagerStatus>(vanillaAlertManagerConfig)
), ),
// this endpoint will respond if the OnCall plugin is installed // this endpoint will respond if the OnCall plugin is installed
rest.get('/api/plugins/grafana-oncall-app/settings', (_req, res, ctx) => res(ctx.status(404))) http.get('/api/plugins/grafana-oncall-app/settings', () => HttpResponse.json({}, { status: 404 }))
); );
return server; return server;

View File

@ -1,7 +1,7 @@
import { rest } from 'msw'; import 'whatwg-fetch';
import { http, HttpResponse } from 'msw';
import { SetupServer, setupServer } from 'msw/node'; import { SetupServer, setupServer } from 'msw/node';
import 'whatwg-fetch';
import { AlertmanagersChoiceResponse } from 'app/features/alerting/unified/api/alertmanagerApi'; import { AlertmanagersChoiceResponse } from 'app/features/alerting/unified/api/alertmanagerApi';
import { mockAlertmanagerChoiceResponse } from 'app/features/alerting/unified/mocks/alertmanagerApi'; import { mockAlertmanagerChoiceResponse } from 'app/features/alerting/unified/mocks/alertmanagerApi';
import { AlertmanagerChoice } from 'app/plugins/datasource/alertmanager/types'; import { AlertmanagerChoice } from 'app/plugins/datasource/alertmanager/types';
@ -33,16 +33,15 @@ export function createMockGrafanaServer() {
// a user must alsso have permissions for the folder (namespace) in which the alert rule is stored // a user must alsso have permissions for the folder (namespace) in which the alert rule is stored
function mockFolderAccess(server: SetupServer, accessControl: Partial<Record<AccessControlAction, boolean>>) { function mockFolderAccess(server: SetupServer, accessControl: Partial<Record<AccessControlAction, boolean>>) {
server.use( server.use(
rest.get('/api/folders/:uid', (req, res, ctx) => { http.get('/api/folders/:uid', ({ request }) => {
const uid = req.params.uid; const url = new URL(request.url);
const uid = url.searchParams.get('uid');
return res( return HttpResponse.json({
ctx.json({
title: 'My Folder', title: 'My Folder',
uid, uid,
accessControl, accessControl,
}) });
);
}) })
); );
@ -51,8 +50,8 @@ function mockFolderAccess(server: SetupServer, accessControl: Partial<Record<Acc
function mockGrafanaIncidentPluginSettings(server: SetupServer) { function mockGrafanaIncidentPluginSettings(server: SetupServer) {
server.use( server.use(
rest.get('/api/plugins/grafana-incident-app/settings', (_, res, ctx) => { http.get('/api/plugins/grafana-incident-app/settings', () => {
return res(ctx.status(200)); return HttpResponse.json({});
}) })
); );
} }

View File

@ -1,4 +1,6 @@
import 'whatwg-fetch';
import { render, screen, waitFor } from '@testing-library/react'; import { render, screen, waitFor } from '@testing-library/react';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node'; import { setupServer } from 'msw/node';
import React from 'react'; import React from 'react';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
@ -18,6 +20,7 @@ import { AlertmanagersChoiceResponse } from '../../api/alertmanagerApi';
import { useIsRuleEditable } from '../../hooks/useIsRuleEditable'; import { useIsRuleEditable } from '../../hooks/useIsRuleEditable';
import { getCloudRule, getGrafanaRule } from '../../mocks'; import { getCloudRule, getGrafanaRule } from '../../mocks';
import { mockAlertmanagerChoiceResponse } from '../../mocks/alertmanagerApi'; import { mockAlertmanagerChoiceResponse } from '../../mocks/alertmanagerApi';
import { SupportedPlugin } from '../../types/pluginBridges';
import { RuleDetails } from './RuleDetails'; import { RuleDetails } from './RuleDetails';
@ -42,7 +45,13 @@ const ui = {
}, },
}; };
const server = setupServer(); const server = setupServer(
http.get(`/api/plugins/${SupportedPlugin.Incident}/settings`, async () => {
return HttpResponse.json({
enabled: false,
});
})
);
const alertmanagerChoiceMockedResponse: AlertmanagersChoiceResponse = { const alertmanagerChoiceMockedResponse: AlertmanagersChoiceResponse = {
alertmanagersChoice: AlertmanagerChoice.Internal, alertmanagersChoice: AlertmanagerChoice.Internal,
@ -74,6 +83,7 @@ beforeEach(() => {
], ],
}); });
server.resetHandlers(); server.resetHandlers();
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
}); });
describe('RuleDetails RBAC', () => { describe('RuleDetails RBAC', () => {
@ -107,7 +117,6 @@ describe('RuleDetails RBAC', () => {
it('Should not render Silence button for users wihout the instance create permission', async () => { it('Should not render Silence button for users wihout the instance create permission', async () => {
// Arrange // Arrange
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(false); jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(false);
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
// Act // Act
renderRuleDetails(grafanaRule); renderRuleDetails(grafanaRule);
@ -118,8 +127,6 @@ describe('RuleDetails RBAC', () => {
}); });
it('Should render Silence button for users with the instance create permissions', async () => { it('Should render Silence button for users with the instance create permissions', async () => {
mockAlertmanagerChoiceResponse(server, alertmanagerChoiceMockedResponse);
// Arrange // Arrange
jest jest
.spyOn(contextSrv, 'hasPermission') .spyOn(contextSrv, 'hasPermission')

View File

@ -108,10 +108,12 @@ describe('Rules group tests', () => {
groups: [group], groups: [group],
}; };
it('Should hide delete and edit group buttons', () => { it('Should hide delete and edit group buttons', async () => {
// Act // Act
mockUseHasRuler(true, true); mockUseHasRuler(true, true);
mockFolderApi(server).folder('cpu-usage', mockFolder({ uid: 'cpu-usage', canSave: false }));
renderRulesGroup(namespace, group); renderRulesGroup(namespace, group);
expect(await screen.findByTestId('rule-group')).toBeInTheDocument();
// Assert // Assert
expect(ui.deleteGroupButton.query()).not.toBeInTheDocument(); expect(ui.deleteGroupButton.query()).not.toBeInTheDocument();

View File

@ -1,9 +1,9 @@
import 'whatwg-fetch';
import { render, waitFor } from '@testing-library/react'; import { render, waitFor } from '@testing-library/react';
import { rest } from 'msw'; import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node'; import { setupServer } from 'msw/node';
import React from 'react'; import React from 'react';
import { byRole, byText } from 'testing-library-selector'; import { byRole, byText } from 'testing-library-selector';
import 'whatwg-fetch';
import { DataFrameJSON } from '@grafana/data'; import { DataFrameJSON } from '@grafana/data';
import { setBackendSrv } from '@grafana/runtime'; import { setBackendSrv } from '@grafana/runtime';
@ -20,9 +20,8 @@ beforeAll(() => {
server.listen({ onUnhandledRequest: 'error' }); server.listen({ onUnhandledRequest: 'error' });
server.use( server.use(
rest.get('/api/v1/rules/history', (req, res, ctx) => http.get('/api/v1/rules/history', () =>
res( HttpResponse.json<DataFrameJSON>({
ctx.json<DataFrameJSON>({
data: { data: {
values: [ values: [
[1681739580000, 1681739580000, 1681739580000], [1681739580000, 1681739580000, 1681739580000],
@ -67,7 +66,6 @@ beforeAll(() => {
}, },
}) })
) )
)
); );
}); });
@ -100,8 +98,8 @@ describe('LokiStateHistory', () => {
it('should render no entries message when no records are returned', async () => { it('should render no entries message when no records are returned', async () => {
server.use( server.use(
rest.get('/api/v1/rules/history', (req, res, ctx) => http.get('/api/v1/rules/history', () =>
res(ctx.json<DataFrameJSON>({ data: { values: [] }, schema: { fields: [] } })) HttpResponse.json<DataFrameJSON>({ data: { values: [] }, schema: { fields: [] } })
) )
); );

View File

@ -1,10 +1,9 @@
import 'whatwg-fetch';
import { renderHook, waitFor } from '@testing-library/react'; import { renderHook, waitFor } from '@testing-library/react';
import { rest } from 'msw'; import { http, HttpResponse } from 'msw';
import { SetupServer, setupServer } from 'msw/node'; import { SetupServer, setupServer } from 'msw/node';
import { TestProvider } from 'test/helpers/TestProvider'; import { TestProvider } from 'test/helpers/TestProvider';
import 'whatwg-fetch';
import { DataSourceSettings } from '@grafana/data'; import { DataSourceSettings } from '@grafana/data';
import { setBackendSrv } from '@grafana/runtime'; import { setBackendSrv } from '@grafana/runtime';
import { backendSrv } from 'app/core/services/backend_srv'; import { backendSrv } from 'app/core/services/backend_srv';
@ -215,8 +214,8 @@ function setupAlertmanagerDataSource(
}; };
server.use( server.use(
rest.get('/api/datasources', (_req, res, ctx) => { http.get('/api/datasources', () => {
return res(ctx.json([dsSettings])); return HttpResponse.json([dsSettings]);
}) })
); );
} }

View File

@ -1,7 +1,7 @@
import { uniqueId } from 'lodash';
import { rest } from 'msw';
import { setupServer, SetupServer } from 'msw/node';
import 'whatwg-fetch'; import 'whatwg-fetch';
import { uniqueId } from 'lodash';
import { http, HttpResponse } from 'msw';
import { setupServer, SetupServer } from 'msw/node';
import { DataSourceInstanceSettings, PluginMeta } from '@grafana/data'; import { DataSourceInstanceSettings, PluginMeta } from '@grafana/data';
import { setBackendSrv } from '@grafana/runtime'; import { setBackendSrv } from '@grafana/runtime';
@ -190,85 +190,74 @@ export function mockApi(server: SetupServer) {
configure(builder); configure(builder);
server.use( server.use(
rest.get(`api/alertmanager/${amName}/config/api/v1/alerts`, (req, res, ctx) => http.get(`api/alertmanager/${amName}/config/api/v1/alerts`, () =>
res( HttpResponse.json<AlertManagerCortexConfig>({
ctx.status(200),
ctx.json<AlertManagerCortexConfig>({
alertmanager_config: builder.build(), alertmanager_config: builder.build(),
template_files: {}, template_files: {},
}) })
) )
)
); );
}, },
eval: (response: AlertingQueryResponse) => { eval: (response: AlertingQueryResponse) => {
server.use( server.use(
rest.post('/api/v1/eval', (_, res, ctx) => { http.post('/api/v1/eval', () => {
return res(ctx.status(200), ctx.json(response)); return HttpResponse.json(response);
}) })
); );
}, },
grafanaNotifiers: (response: NotifierDTO[]) => { grafanaNotifiers: (response: NotifierDTO[]) => {
server.use( server.use(http.get(`api/alert-notifiers`, () => HttpResponse.json(response)));
rest.get(`api/alert-notifiers`, (req, res, ctx) => res(ctx.status(200), ctx.json<NotifierDTO[]>(response)))
);
}, },
plugins: { plugins: {
getPluginSettings: (response: PluginMeta) => { getPluginSettings: (response: PluginMeta) => {
server.use( server.use(http.get(`api/plugins/${response.id}/settings`, () => HttpResponse.json(response)));
rest.get(`api/plugins/${response.id}/settings`, (req, res, ctx) =>
res(ctx.status(200), ctx.json<PluginMeta>(response))
)
);
}, },
}, },
oncall: { oncall: {
getOnCallIntegrations: (response: OnCallIntegrationDTO[]) => { getOnCallIntegrations: (response: OnCallIntegrationDTO[]) => {
server.use( server.use(
rest.get(`api/plugin-proxy/grafana-oncall-app/api/internal/v1/alert_receive_channels`, (_, res, ctx) => http.get(`api/plugin-proxy/grafana-oncall-app/api/internal/v1/alert_receive_channels`, () =>
res(ctx.status(200), ctx.json<OnCallIntegrationDTO[]>(response)) HttpResponse.json<OnCallIntegrationDTO[]>(response)
) )
); );
}, },
features: (response: string[]) => { features: (response: string[]) => {
server.use( server.use(
rest.get(`api/plugin-proxy/grafana-oncall-app/api/internal/v1/features`, (_, res, ctx) => http.get(`api/plugin-proxy/grafana-oncall-app/api/internal/v1/features`, () => HttpResponse.json(response))
res(ctx.status(200), ctx.json<string[]>(response))
)
); );
}, },
validateIntegrationName: (invalidNames: string[]) => { validateIntegrationName: (invalidNames: string[]) => {
server.use( server.use(
rest.get( http.get(
`api/plugin-proxy/grafana-oncall-app/api/internal/v1/alert_receive_channels/validate_name`, `api/plugin-proxy/grafana-oncall-app/api/internal/v1/alert_receive_channels/validate_name`,
(req, res, ctx) => { ({ request }) => {
const isValid = !invalidNames.includes(req.url.searchParams.get('verbal_name') ?? ''); const url = new URL(request.url);
return res(ctx.status(isValid ? 200 : 409), ctx.json<boolean>(isValid)); const isValid = !invalidNames.includes(url.searchParams.get('verbal_name') ?? '');
return HttpResponse.json(isValid, {
status: isValid ? 200 : 409,
});
} }
) )
); );
}, },
createIntegraion: () => { createIntegraion: () => {
server.use( server.use(
rest.post<CreateIntegrationDTO>( http.post<{}, CreateIntegrationDTO>(
`api/plugin-proxy/grafana-oncall-app/api/internal/v1/alert_receive_channels`, `api/plugin-proxy/grafana-oncall-app/api/internal/v1/alert_receive_channels`,
async (req, res, ctx) => { async ({ request }) => {
const body = await req.json<CreateIntegrationDTO>(); const body = await request.json();
const integrationId = uniqueId('oncall-integration-'); const integrationId = uniqueId('oncall-integration-');
return res( return HttpResponse.json<NewOnCallIntegrationDTO>({
ctx.status(200),
ctx.json<NewOnCallIntegrationDTO>({
id: integrationId, id: integrationId,
integration: body.integration, integration: body.integration,
integration_url: `https://oncall-endpoint.example.com/${integrationId}`, integration_url: `https://oncall-endpoint.example.com/${integrationId}`,
verbal_name: body.verbal_name, verbal_name: body.verbal_name,
connected_escalations_chains_count: 0, connected_escalations_chains_count: 0,
}) });
);
} }
) )
); );
@ -281,21 +270,15 @@ export function mockAlertRuleApi(server: SetupServer) {
return { return {
prometheusRuleNamespaces: (dsName: string, response: PromRulesResponse) => { prometheusRuleNamespaces: (dsName: string, response: PromRulesResponse) => {
server.use( server.use(
rest.get(`api/prometheus/${dsName}/api/v1/rules`, (req, res, ctx) => http.get(`api/prometheus/${dsName}/api/v1/rules`, () => HttpResponse.json<PromRulesResponse>(response))
res(ctx.status(200), ctx.json<PromRulesResponse>(response))
)
); );
}, },
rulerRules: (dsName: string, response: RulerRulesConfigDTO) => { rulerRules: (dsName: string, response: RulerRulesConfigDTO) => {
server.use( server.use(http.get(`/api/ruler/${dsName}/api/v1/rules`, () => HttpResponse.json(response)));
rest.get(`/api/ruler/${dsName}/api/v1/rules`, (req, res, ctx) => res(ctx.status(200), ctx.json(response)))
);
}, },
rulerRuleGroup: (dsName: string, namespace: string, group: string, response: RulerRuleGroupDTO) => { rulerRuleGroup: (dsName: string, namespace: string, group: string, response: RulerRuleGroupDTO) => {
server.use( server.use(
rest.get(`/api/ruler/${dsName}/api/v1/rules/${namespace}/${group}`, (req, res, ctx) => http.get(`/api/ruler/${dsName}/api/v1/rules/${namespace}/${group}`, () => HttpResponse.json(response))
res(ctx.status(200), ctx.json(response))
)
); );
}, },
}; };
@ -312,9 +295,7 @@ export function mockFeatureDiscoveryApi(server: SetupServer) {
* @param response Use `buildInfoResponse` to get a pre-defined response for Prometheus and Mimir * @param response Use `buildInfoResponse` to get a pre-defined response for Prometheus and Mimir
*/ */
discoverDsFeatures: (dsSettings: DataSourceInstanceSettings, response: PromBuildInfoResponse) => { discoverDsFeatures: (dsSettings: DataSourceInstanceSettings, response: PromBuildInfoResponse) => {
server.use( server.use(http.get(`${dsSettings.url}/api/v1/status/buildinfo`, () => HttpResponse.json(response)));
rest.get(`${dsSettings.url}/api/v1/status/buildinfo`, (_, res, ctx) => res(ctx.status(200), ctx.json(response)))
);
}, },
}; };
} }
@ -323,16 +304,20 @@ export function mockProvisioningApi(server: SetupServer) {
return { return {
exportRuleGroup: (folderUid: string, groupName: string, response: Record<string, string>) => { exportRuleGroup: (folderUid: string, groupName: string, response: Record<string, string>) => {
server.use( server.use(
rest.get(`/api/v1/provisioning/folder/${folderUid}/rule-groups/${groupName}/export`, (req, res, ctx) => http.get(`/api/v1/provisioning/folder/${folderUid}/rule-groups/${groupName}/export`, ({ request }) => {
res(ctx.status(200), ctx.text(response[req.url.searchParams.get('format') ?? 'yaml'])) const url = new URL(request.url);
) const format = url.searchParams.get('format') ?? 'yaml';
return HttpResponse.text(response[format]);
})
); );
}, },
exportReceiver: (response: Record<string, string>) => { exportReceiver: (response: Record<string, string>) => {
server.use( server.use(
rest.get(`/api/v1/provisioning/contact-points/export/`, (req, res, ctx) => http.get(`/api/v1/provisioning/contact-points/export/`, ({ request }) => {
res(ctx.status(200), ctx.text(response[req.url.searchParams.get('format') ?? 'yaml'])) const url = new URL(request.url);
) const format = url.searchParams.get('format') ?? 'yaml';
return HttpResponse.text(response[format]);
})
); );
}, },
}; };
@ -344,43 +329,51 @@ export function mockExportApi(server: SetupServer) {
// exportRule requires ruleUid parameter and doesn't allow folderUid and group parameters // exportRule requires ruleUid parameter and doesn't allow folderUid and group parameters
exportRule: (ruleUid: string, response: Record<string, string>) => { exportRule: (ruleUid: string, response: Record<string, string>) => {
server.use( server.use(
rest.get('/api/ruler/grafana/api/v1/export/rules', (req, res, ctx) => { http.get('/api/ruler/grafana/api/v1/export/rules', ({ request }) => {
if (req.url.searchParams.get('ruleUid') === ruleUid) { const url = new URL(request.url);
return res(ctx.status(200), ctx.text(response[req.url.searchParams.get('format') ?? 'yaml'])); if (url.searchParams.get('ruleUid') === ruleUid) {
const format = url.searchParams.get('format') ?? 'yaml';
return HttpResponse.text(response[format]);
} }
return res(ctx.status(500)); return HttpResponse.text('', { status: 500 });
}) })
); );
}, },
// exportRulesGroup requires folderUid and group parameters and doesn't allow ruleUid parameter // exportRulesGroup requires folderUid and group parameters and doesn't allow ruleUid parameter
exportRulesGroup: (folderUid: string, group: string, response: Record<string, string>) => { exportRulesGroup: (folderUid: string, group: string, response: Record<string, string>) => {
server.use( server.use(
rest.get('/api/ruler/grafana/api/v1/export/rules', (req, res, ctx) => { http.get('/api/ruler/grafana/api/v1/export/rules', ({ request }) => {
if (req.url.searchParams.get('folderUid') === folderUid && req.url.searchParams.get('group') === group) { const url = new URL(request.url);
return res(ctx.status(200), ctx.text(response[req.url.searchParams.get('format') ?? 'yaml'])); if (url.searchParams.get('folderUid') === folderUid && url.searchParams.get('group') === group) {
const format = url.searchParams.get('format') ?? 'yaml';
return HttpResponse.text(response[format]);
} }
return res(ctx.status(500)); return HttpResponse.text('', { status: 500 });
}) })
); );
}, },
// exportRulesFolder requires folderUid parameter // exportRulesFolder requires folderUid parameter
exportRulesFolder: (folderUid: string, response: Record<string, string>) => { exportRulesFolder: (folderUid: string, response: Record<string, string>) => {
server.use( server.use(
rest.get('/api/ruler/grafana/api/v1/export/rules', (req, res, ctx) => { http.get('/api/ruler/grafana/api/v1/export/rules', ({ request }) => {
if (req.url.searchParams.get('folderUid') === folderUid) { const url = new URL(request.url);
return res(ctx.status(200), ctx.text(response[req.url.searchParams.get('format') ?? 'yaml'])); if (url.searchParams.get('folderUid') === folderUid) {
const format = url.searchParams.get('format') ?? 'yaml';
return HttpResponse.text(response[format]);
} }
return res(ctx.status(500)); return HttpResponse.text('', { status: 500 });
}) })
); );
}, },
modifiedExport: (namespaceUID: string, response: Record<string, string>) => { modifiedExport: (namespaceUID: string, response: Record<string, string>) => {
server.use( server.use(
rest.post(`/api/ruler/grafana/api/v1/rules/${namespaceUID}/export`, (req, res, ctx) => { http.post(`/api/ruler/grafana/api/v1/rules/${namespaceUID}/export`, ({ request }) => {
return res(ctx.status(200), ctx.text(response[req.url.searchParams.get('format') ?? 'yaml'])); const url = new URL(request.url);
const format = url.searchParams.get('format') ?? 'yaml';
return HttpResponse.text(response[format]);
}) })
); );
}, },
@ -390,7 +383,7 @@ export function mockExportApi(server: SetupServer) {
export function mockFolderApi(server: SetupServer) { export function mockFolderApi(server: SetupServer) {
return { return {
folder: (folderUid: string, response: FolderDTO) => { folder: (folderUid: string, response: FolderDTO) => {
server.use(rest.get(`/api/folders/${folderUid}`, (_, res, ctx) => res(ctx.status(200), ctx.json(response)))); server.use(http.get(`/api/folders/${folderUid}`, () => HttpResponse.json(response)));
}, },
}; };
} }
@ -398,7 +391,7 @@ export function mockFolderApi(server: SetupServer) {
export function mockSearchApi(server: SetupServer) { export function mockSearchApi(server: SetupServer) {
return { return {
search: (results: DashboardSearchItem[]) => { search: (results: DashboardSearchItem[]) => {
server.use(rest.get(`/api/search`, (_, res, ctx) => res(ctx.status(200), ctx.json(results)))); server.use(http.get(`/api/search`, () => HttpResponse.json(results)));
}, },
}; };
} }
@ -406,14 +399,10 @@ export function mockSearchApi(server: SetupServer) {
export function mockDashboardApi(server: SetupServer) { export function mockDashboardApi(server: SetupServer) {
return { return {
search: (results: DashboardSearchItem[]) => { search: (results: DashboardSearchItem[]) => {
server.use(rest.get(`/api/search`, (_, res, ctx) => res(ctx.status(200), ctx.json(results)))); server.use(http.get(`/api/search`, () => HttpResponse.json(results)));
}, },
dashboard: (response: DashboardDTO) => { dashboard: (response: DashboardDTO) => {
server.use( server.use(http.get(`/api/dashboards/uid/${response.dashboard.uid}`, () => HttpResponse.json(response)));
rest.get(`/api/dashboards/uid/${response.dashboard.uid}`, (_, res, ctx) =>
res(ctx.status(200), ctx.json(response))
)
);
}, },
}; };
} }

View File

@ -1,4 +1,5 @@
import { rest } from 'msw'; import 'whatwg-fetch';
import { http, HttpResponse } from 'msw';
import { SetupServer } from 'msw/node'; import { SetupServer } from 'msw/node';
import { PromRulesResponse } from 'app/types/unified-alerting-dto'; import { PromRulesResponse } from 'app/types/unified-alerting-dto';
@ -6,9 +7,9 @@ import { PromRulesResponse } from 'app/types/unified-alerting-dto';
import { PreviewResponse, PREVIEW_URL, PROM_RULES_URL } from '../api/alertRuleApi'; import { PreviewResponse, PREVIEW_URL, PROM_RULES_URL } from '../api/alertRuleApi';
export function mockPreviewApiResponse(server: SetupServer, result: PreviewResponse) { export function mockPreviewApiResponse(server: SetupServer, result: PreviewResponse) {
server.use(rest.post(PREVIEW_URL, (req, res, ctx) => res(ctx.json<PreviewResponse>(result)))); server.use(http.post(PREVIEW_URL, () => HttpResponse.json(result)));
} }
export function mockPromRulesApiResponse(server: SetupServer, result: PromRulesResponse) { export function mockPromRulesApiResponse(server: SetupServer, result: PromRulesResponse) {
server.use(rest.get(PROM_RULES_URL, (req, res, ctx) => res(ctx.json<PromRulesResponse>(result)))); server.use(http.get(PROM_RULES_URL, () => HttpResponse.json(result)));
} }

View File

@ -1,4 +1,5 @@
import { rest } from 'msw'; import 'whatwg-fetch';
import { http, HttpResponse } from 'msw';
import { SetupServer } from 'msw/node'; import { SetupServer } from 'msw/node';
import { import {
@ -14,7 +15,7 @@ export const defaultAlertmanagerChoiceResponse: AlertmanagersChoiceResponse = {
numExternalAlertmanagers: 0, numExternalAlertmanagers: 0,
}; };
export function mockAlertmanagerChoiceResponse(server: SetupServer, response: AlertmanagersChoiceResponse) { export function mockAlertmanagerChoiceResponse(server: SetupServer, response: AlertmanagersChoiceResponse) {
server.use(rest.get('/api/v1/ngalert', (req, res, ctx) => res(ctx.status(200), ctx.json(response)))); server.use(http.get('/api/v1/ngalert', () => HttpResponse.json(response)));
} }
export const emptyExternalAlertmanagersResponse: ExternalAlertmanagersResponse = { export const emptyExternalAlertmanagersResponse: ExternalAlertmanagersResponse = {
@ -24,7 +25,7 @@ export const emptyExternalAlertmanagersResponse: ExternalAlertmanagersResponse =
}, },
}; };
export function mockAlertmanagersResponse(server: SetupServer, response: ExternalAlertmanagersResponse) { export function mockAlertmanagersResponse(server: SetupServer, response: ExternalAlertmanagersResponse) {
server.use(rest.get('/api/v1/ngalert/alertmanagers', (req, res, ctx) => res(ctx.status(200), ctx.json(response)))); server.use(http.get('/api/v1/ngalert/alertmanagers', () => HttpResponse.json(response)));
} }
export function mockAlertmanagerConfigResponse( export function mockAlertmanagerConfigResponse(
@ -33,8 +34,8 @@ export function mockAlertmanagerConfigResponse(
response: AlertManagerCortexConfig response: AlertManagerCortexConfig
) { ) {
server.use( server.use(
rest.get(`/api/alertmanager/${getDatasourceAPIUid(alertManagerSourceName)}/config/api/v1/alerts`, (req, res, ctx) => http.get(`/api/alertmanager/${getDatasourceAPIUid(alertManagerSourceName)}/config/api/v1/alerts`, () =>
res(ctx.status(200), ctx.json(response)) HttpResponse.json(response)
) )
); );
} }

View File

@ -1,4 +1,5 @@
import { rest } from 'msw'; import 'whatwg-fetch';
import { http, HttpResponse } from 'msw';
import { SetupServer } from 'msw/lib/node'; import { SetupServer } from 'msw/lib/node';
import { PluginMeta } from '@grafana/data'; import { PluginMeta } from '@grafana/data';
@ -7,8 +8,11 @@ import { SupportedPlugin } from '../types/pluginBridges';
export function mockPluginSettings(server: SetupServer, plugin: SupportedPlugin, response?: PluginMeta) { export function mockPluginSettings(server: SetupServer, plugin: SupportedPlugin, response?: PluginMeta) {
server.use( server.use(
rest.get(`/api/plugins/${plugin}/settings`, (_req, res, ctx) => { http.get(`/api/plugins/${plugin}/settings`, () => {
return response ? res(ctx.status(200), ctx.json(response)) : res(ctx.status(404)); if (response) {
return HttpResponse.json(response);
}
return HttpResponse.json({}, { status: 404 });
}) })
); );
} }

View File

@ -1,14 +1,11 @@
import { rest } from 'msw'; import 'whatwg-fetch';
import { http, HttpResponse } from 'msw';
import { SetupServer } from 'msw/node'; import { SetupServer } from 'msw/node';
import { RulerRuleGroupDTO, RulerRulesConfigDTO } from '../../../../types/unified-alerting-dto'; import { RulerRuleGroupDTO, RulerRulesConfigDTO } from '../../../../types/unified-alerting-dto';
export function mockRulerRulesApiResponse(server: SetupServer, rulesSourceName: string, response: RulerRulesConfigDTO) { export function mockRulerRulesApiResponse(server: SetupServer, rulesSourceName: string, response: RulerRulesConfigDTO) {
server.use( server.use(http.get(`/api/ruler/${rulesSourceName}/api/v1/rules`, () => HttpResponse.json(response)));
rest.get(`/api/ruler/${rulesSourceName}/api/v1/rules`, (req, res, ctx) =>
res(ctx.json<RulerRulesConfigDTO>(response))
)
);
} }
export function mockRulerRulesGroupApiResponse( export function mockRulerRulesGroupApiResponse(
@ -19,8 +16,6 @@ export function mockRulerRulesGroupApiResponse(
response: RulerRuleGroupDTO response: RulerRuleGroupDTO
) { ) {
server.use( server.use(
rest.get(`/api/ruler/${rulesSourceName}/api/v1/rules/${namespace}/${group}`, (req, res, ctx) => http.get(`/api/ruler/${rulesSourceName}/api/v1/rules/${namespace}/${group}`, () => HttpResponse.json(response))
res(ctx.json(response))
)
); );
} }

View File

@ -1,12 +1,13 @@
import { rest } from 'msw'; import 'whatwg-fetch';
import { http, HttpResponse } from 'msw';
import { SetupServer } from 'msw/node'; import { SetupServer } from 'msw/node';
import { previewTemplateUrl, TemplatePreviewResponse } from '../api/templateApi'; import { previewTemplateUrl, TemplatePreviewResponse } from '../api/templateApi';
export function mockPreviewTemplateResponse(server: SetupServer, response: TemplatePreviewResponse) { export function mockPreviewTemplateResponse(server: SetupServer, response: TemplatePreviewResponse) {
server.use(rest.post(previewTemplateUrl, (req, res, ctx) => res(ctx.status(200), ctx.json(response)))); server.use(http.post(previewTemplateUrl, () => HttpResponse.json(response)));
} }
export function mockPreviewTemplateResponseRejected(server: SetupServer) { export function mockPreviewTemplateResponseRejected(server: SetupServer) {
server.use(rest.post(previewTemplateUrl, (req, res, ctx) => res(ctx.status(500), ctx.json('error')))); server.use(http.post(previewTemplateUrl, () => HttpResponse.json('error', { status: 500 })));
} }

View File

@ -1,7 +1,7 @@
import 'whatwg-fetch'; // fetch polyfill import 'whatwg-fetch';
import { render as rtlRender, screen } from '@testing-library/react'; import { render as rtlRender, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { rest } from 'msw'; import { HttpResponse, http } from 'msw';
import { setupServer, SetupServer } from 'msw/node'; import { setupServer, SetupServer } from 'msw/node';
import React, { ComponentProps } from 'react'; import React, { ComponentProps } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer'; import AutoSizer from 'react-virtualized-auto-sizer';
@ -120,25 +120,19 @@ describe('browse-dashboards BrowseDashboardsPage', () => {
beforeAll(() => { beforeAll(() => {
server = setupServer( server = setupServer(
rest.get('/api/folders/:uid', (_, res, ctx) => { http.get('/api/folders/:uid', () => {
return res( return HttpResponse.json({
ctx.status(200),
ctx.json({
title: folderA.item.title, title: folderA.item.title,
uid: folderA.item.uid, uid: folderA.item.uid,
}) });
);
}), }),
rest.get('/api/search', (_, res, ctx) => { http.get('/api/search', () => {
return res(ctx.status(200), ctx.json({})); return HttpResponse.json({});
}), }),
rest.get('/api/search/sorting', (_, res, ctx) => { http.get('/api/search/sorting', () => {
return res( return HttpResponse.json({
ctx.status(200),
ctx.json({
sortOptions: [], sortOptions: [],
}) });
);
}) })
); );
server.listen(); server.listen();

View File

@ -1,6 +1,6 @@
import 'whatwg-fetch'; // fetch polyfill import 'whatwg-fetch';
import { render as rtlRender, screen } from '@testing-library/react'; import { render as rtlRender, screen } from '@testing-library/react';
import { rest } from 'msw'; import { http, HttpResponse } from 'msw';
import { SetupServer, setupServer } from 'msw/node'; import { SetupServer, setupServer } from 'msw/node';
import React from 'react'; import React from 'react';
import { TestProvider } from 'test/helpers/TestProvider'; import { TestProvider } from 'test/helpers/TestProvider';
@ -47,20 +47,17 @@ describe('browse-dashboards BrowseFolderAlertingPage', () => {
beforeAll(() => { beforeAll(() => {
server = setupServer( server = setupServer(
rest.get('/api/folders/:uid', (_, res, ctx) => { http.get('/api/folders/:uid', () => {
return res( return HttpResponse.json({
ctx.status(200),
ctx.json({
title: mockFolderName, title: mockFolderName,
uid: mockFolderUid, uid: mockFolderUid,
}) });
);
}), }),
rest.get('api/ruler/grafana/api/v1/rules', (_, res, ctx) => { http.get('api/ruler/grafana/api/v1/rules', () => {
return res(ctx.status(200), ctx.json(mockRulerRulesResponse)); return HttpResponse.json(mockRulerRulesResponse);
}), }),
rest.get('api/prometheus/grafana/api/v1/rules', (_, res, ctx) => { http.get('api/prometheus/grafana/api/v1/rules', () => {
return res(ctx.status(200), ctx.json(mockPrometheusRulesResponse)); return HttpResponse.json(mockPrometheusRulesResponse);
}) })
); );
server.listen(); server.listen();

View File

@ -1,6 +1,6 @@
import 'whatwg-fetch'; // fetch polyfill import 'whatwg-fetch';
import { render as rtlRender, screen } from '@testing-library/react'; import { render as rtlRender, screen } from '@testing-library/react';
import { rest } from 'msw'; import { http, HttpResponse } from 'msw';
import { SetupServer, setupServer } from 'msw/node'; import { SetupServer, setupServer } from 'msw/node';
import React from 'react'; import React from 'react';
import { TestProvider } from 'test/helpers/TestProvider'; import { TestProvider } from 'test/helpers/TestProvider';
@ -47,25 +47,19 @@ describe('browse-dashboards BrowseFolderLibraryPanelsPage', () => {
beforeAll(() => { beforeAll(() => {
server = setupServer( server = setupServer(
rest.get('/api/folders/:uid', (_, res, ctx) => { http.get('/api/folders/:uid', () => {
return res( return HttpResponse.json({
ctx.status(200),
ctx.json({
title: mockFolderName, title: mockFolderName,
uid: mockFolderUid, uid: mockFolderUid,
}) });
);
}), }),
rest.get('/api/library-elements', (_, res, ctx) => { http.get('/api/library-elements', () => {
return res( return HttpResponse.json({
ctx.status(200),
ctx.json({
result: mockLibraryElementsResponse, result: mockLibraryElementsResponse,
}) });
);
}), }),
rest.get('/api/search/sorting', (_, res, ctx) => { http.get('/api/search/sorting', () => {
return res(ctx.status(200), ctx.json({})); return HttpResponse.json({});
}) })
); );
server.listen(); server.listen();

View File

@ -1,9 +1,9 @@
import 'whatwg-fetch';
import { screen, waitFor } from '@testing-library/react'; import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { rest } from 'msw'; import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node'; import { setupServer } from 'msw/node';
import 'whatwg-fetch';
import { BootData, DataQuery } from '@grafana/data/src'; import { BootData, DataQuery } from '@grafana/data/src';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { reportInteraction, setEchoSrv } from '@grafana/runtime'; import { reportInteraction, setEchoSrv } from '@grafana/runtime';
@ -87,20 +87,27 @@ afterEach(() => {
}); });
const getNonExistentPublicDashboardResponse = () => const getNonExistentPublicDashboardResponse = () =>
rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => { http.get('/api/dashboards/uid/:dashboardUid/public-dashboards', () => {
return res( return HttpResponse.json(
ctx.status(404), {
ctx.json({
message: 'Public dashboard not found', message: 'Public dashboard not found',
messageId: 'publicdashboards.notFound', messageId: 'publicdashboards.notFound',
statusCode: 404, statusCode: 404,
traceID: '', traceID: '',
}) },
{
status: 404,
}
); );
}); });
const getErrorPublicDashboardResponse = () => const getErrorPublicDashboardResponse = () =>
rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => { http.get('/api/dashboards/uid/:dashboardUid/public-dashboards', () => {
return res(ctx.status(500)); return HttpResponse.json(
{},
{
status: 500,
}
);
}); });
const alertTests = () => { const alertTests = () => {
@ -277,14 +284,11 @@ describe('SharePublic - Already persisted', () => {
}); });
it('when modal is opened, then time range switch is enabled and not checked when its not checked in the db', async () => { it('when modal is opened, then time range switch is enabled and not checked when its not checked in the db', async () => {
server.use( server.use(
rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => { http.get('/api/dashboards/uid/:dashboardUid/public-dashboards', () => {
return res( return HttpResponse.json({
ctx.status(200),
ctx.json({
...pubdashResponse, ...pubdashResponse,
timeSelectionEnabled: false, timeSelectionEnabled: false,
}) });
);
}) })
); );
@ -303,17 +307,16 @@ describe('SharePublic - Already persisted', () => {
}); });
it('when pubdash is disabled in the db, then link url is not copyable and switch is checked', async () => { it('when pubdash is disabled in the db, then link url is not copyable and switch is checked', async () => {
server.use( server.use(
rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => { http.get('/api/dashboards/uid/:dashboardUid/public-dashboards', ({ request }) => {
return res( const url = new URL(request.url);
ctx.status(200), const dashboardUid = url.searchParams.get('dashboardUid');
ctx.json({ return HttpResponse.json({
isEnabled: false, isEnabled: false,
annotationsEnabled: false, annotationsEnabled: false,
uid: 'a-uid', uid: 'a-uid',
dashboardUid: req.params.dashboardUid, dashboardUid,
accessToken: 'an-access-token', accessToken: 'an-access-token',
}) });
);
}) })
); );
@ -339,15 +342,14 @@ describe('SharePublic - Report interactions', () => {
jest.clearAllMocks(); jest.clearAllMocks();
server.use(getExistentPublicDashboardResponse()); server.use(getExistentPublicDashboardResponse());
server.use( server.use(
rest.patch('/api/dashboards/uid/:dashboardUid/public-dashboards/:uid', (req, res, ctx) => http.patch('/api/dashboards/uid/:dashboardUid/public-dashboards/:uid', ({ request }) => {
res( const url = new URL(request.url);
ctx.status(200), const dashboardUid = url.searchParams.get('dashboardUid');
ctx.json({ return HttpResponse.json({
...pubdashResponse, ...pubdashResponse,
dashboardUid: req.params.dashboardUid, dashboardUid,
});
}) })
)
)
); );
}); });

View File

@ -1,5 +1,6 @@
import 'whatwg-fetch';
import { fireEvent, render, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react'; import { fireEvent, render, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
import { rest } from 'msw'; import { http, HttpResponse } from 'msw';
import React from 'react'; import React from 'react';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
@ -32,15 +33,14 @@ export const pubdashResponse: sharePublicDashboardUtils.PublicDashboard = {
}; };
export const getExistentPublicDashboardResponse = (publicDashboard?: Partial<PublicDashboard>) => export const getExistentPublicDashboardResponse = (publicDashboard?: Partial<PublicDashboard>) =>
rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => { http.get('/api/dashboards/uid/:dashboardUid/public-dashboards', ({ request }) => {
return res( const url = new URL(request.url);
ctx.status(200), const dashboardUid = url.searchParams.get('dashboardUid');
ctx.json({ return HttpResponse.json({
...pubdashResponse, ...pubdashResponse,
...publicDashboard, ...publicDashboard,
dashboardUid: req.params.dashboardUid, dashboardUid,
}) });
);
}); });
export const renderSharePublicDashboard = async ( export const renderSharePublicDashboard = async (

View File

@ -1,8 +1,8 @@
import 'whatwg-fetch';
import { render, screen, waitForElementToBeRemoved, within } from '@testing-library/react'; import { render, screen, waitForElementToBeRemoved, within } from '@testing-library/react';
import { rest } from 'msw'; import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node'; import { setupServer } from 'msw/node';
import React from 'react'; import React from 'react';
import 'whatwg-fetch';
import { BrowserRouter } from 'react-router-dom'; import { BrowserRouter } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider'; import { TestProvider } from 'test/helpers/TestProvider';
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock'; import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
@ -60,10 +60,13 @@ const paginationResponse: Omit<PublicDashboardListWithPaginationResponse, 'publi
}; };
const server = setupServer( const server = setupServer(
rest.get('/api/dashboards/public-dashboards', (_, res, ctx) => http.get('/api/dashboards/public-dashboards', () =>
res(ctx.status(200), ctx.json({ ...paginationResponse, publicDashboards: publicDashboardListResponse })) HttpResponse.json({
...paginationResponse,
publicDashboards: publicDashboardListResponse,
})
), ),
rest.delete('/api/dashboards/uid/:dashboardUid/public-dashboards/:uid', (_, res, ctx) => res(ctx.status(200))) http.delete('/api/dashboards/uid/:dashboardUid/public-dashboards/:uid', () => HttpResponse.json({}))
); );
jest.mock('@grafana/runtime', () => ({ jest.mock('@grafana/runtime', () => ({
@ -122,8 +125,8 @@ describe('Show table', () => {
}; };
server.use( server.use(
rest.get('/api/dashboards/public-dashboards', (req, res, ctx) => { http.get('/api/dashboards/public-dashboards', () => {
return res(ctx.status(200), ctx.json(emptyListRS)); return HttpResponse.json(emptyListRS);
}) })
); );
@ -171,8 +174,8 @@ describe('Orphaned public dashboard', () => {
publicDashboards: [...publicDashboardListResponse, ...orphanedDashboardListResponse], publicDashboards: [...publicDashboardListResponse, ...orphanedDashboardListResponse],
}; };
server.use( server.use(
rest.get('/api/dashboards/public-dashboards', (req, res, ctx) => { http.get('/api/dashboards/public-dashboards', () => {
return res(ctx.status(200), ctx.json(response)); return HttpResponse.json(response);
}) })
); );
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true); jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);

View File

@ -1,4 +1,5 @@
import { rest } from 'msw'; import 'whatwg-fetch';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node'; import { setupServer } from 'msw/node';
export const mockSystemModule = `System.register(['./dependencyA'], function (_export, _context) { export const mockSystemModule = `System.register(['./dependencyA'], function (_export, _context) {
@ -85,38 +86,104 @@ export const mockModuleWithDefineMethod = `ace.define(function() {
});`; });`;
const server = setupServer( const server = setupServer(
rest.get('/public/plugins/mockAmdModule/module.js', async (_req, res, ctx) => http.get(
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModule)) '/public/plugins/mockAmdModule/module.js',
() =>
new HttpResponse(mockAmdModule, {
headers: {
'Content-Type': 'text/javascript',
},
})
), ),
rest.get('/public/plugins/mockSystemModule/module.js', async (_req, res, ctx) => http.get(
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockSystemModule)) '/public/plugins/mockSystemModule/module.js',
() =>
new HttpResponse(mockSystemModule, {
headers: {
'Content-Type': 'text/javascript',
},
})
), ),
rest.get('http://my-cdn.com/plugins/my-plugin/v1.0.0/public/plugins/my-plugin/module.js', async (_req, res, ctx) => http.get(
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModule)) 'http://my-cdn.com/plugins/my-plugin/v1.0.0/public/plugins/my-plugin/module.js',
() =>
new HttpResponse(mockAmdModule, {
headers: {
'Content-Type': 'text/javascript',
},
})
), ),
rest.get('/public/plugins/mockAmdModuleNamedNoDeps/module.js', async (_req, res, ctx) => http.get(
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModuleNamedNoDeps)) '/public/plugins/mockAmdModuleNamedNoDeps/module.js',
() =>
new HttpResponse(mockAmdModuleNamedNoDeps, {
headers: {
'Content-Type': 'text/javascript',
},
})
), ),
rest.get('/public/plugins/mockAmdModuleNamedWithDeps/module.js', async (_req, res, ctx) => http.get(
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModuleNamedWithDeps)) '/public/plugins/mockAmdModuleNamedWithDeps/module.js',
() =>
new HttpResponse(mockAmdModuleNamedWithDeps, {
headers: {
'Content-Type': 'text/javascript',
},
})
), ),
rest.get('/public/plugins/mockAmdModuleNamedWithDeps2/module.js', async (_req, res, ctx) => http.get(
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModuleNamedWithDeps2)) '/public/plugins/mockAmdModuleNamedWithDeps2/module.js',
() =>
new HttpResponse(mockAmdModuleNamedWithDeps2, {
headers: {
'Content-Type': 'text/javascript',
},
})
), ),
rest.get('/public/plugins/mockAmdModuleNamedWithDeps3/module.js', async (_req, res, ctx) => http.get(
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModuleNamedWithDeps3)) '/public/plugins/mockAmdModuleNamedWithDeps3/module.js',
() =>
new HttpResponse(mockAmdModuleNamedWithDeps3, {
headers: {
'Content-Type': 'text/javascript',
},
})
), ),
rest.get('/public/plugins/mockAmdModuleOnlyFunction/module.js', async (_req, res, ctx) => http.get(
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModuleOnlyFunction)) '/public/plugins/mockAmdModuleOnlyFunction/module.js',
() =>
new HttpResponse(mockAmdModuleOnlyFunction, {
headers: {
'Content-Type': 'text/javascript',
},
})
), ),
rest.get('/public/plugins/mockAmdModuleWithComments/module.js', async (_req, res, ctx) => http.get(
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModuleWithComments)) '/public/plugins/mockAmdModuleWithComments/module.js',
() =>
new HttpResponse(mockAmdModuleWithComments, {
headers: {
'Content-Type': 'text/javascript',
},
})
), ),
rest.get('/public/plugins/mockAmdModuleWithComments2/module.js', async (_req, res, ctx) => http.get(
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModuleWithComments2)) '/public/plugins/mockAmdModuleWithComments2/module.js',
() =>
new HttpResponse(mockAmdModuleWithComments2, {
headers: {
'Content-Type': 'text/javascript',
},
})
), ),
rest.get('/public/plugins/mockModuleWithDefineMethod/module.js', async (_req, res, ctx) => http.get(
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockModuleWithDefineMethod)) '/public/plugins/mockModuleWithDefineMethod/module.js',
() =>
new HttpResponse(mockModuleWithDefineMethod, {
headers: {
'Content-Type': 'text/javascript',
},
})
) )
); );

View File

@ -28,11 +28,13 @@ describe('SystemJS Loader Hooks', () => {
const originalFetch = systemJSPrototype.fetch; const originalFetch = systemJSPrototype.fetch;
const originalResolve = systemJSPrototype.resolve; const originalResolve = systemJSPrototype.resolve;
systemJSPrototype.resolve = (moduleId: string) => moduleId;
systemJSPrototype.shouldFetch = () => true;
beforeAll(() => { beforeAll(() => {
server.listen(); server.listen();
systemJSPrototype.resolve = (moduleId: string) => moduleId;
systemJSPrototype.shouldFetch = () => true;
// because server.listen() patches fetch, we need to reassign this to the systemJSPrototype
// this is identical to what happens in the original code: https://github.com/systemjs/systemjs/blob/main/src/features/fetch-load.js#L12
systemJSPrototype.fetch = window.fetch;
}); });
afterEach(() => server.resetHandlers()); afterEach(() => server.resetHandlers());
afterAll(() => { afterAll(() => {
@ -43,7 +45,11 @@ describe('SystemJS Loader Hooks', () => {
describe('decorateSystemJSFetch', () => { describe('decorateSystemJSFetch', () => {
it('wraps AMD modules in an AMD iife', async () => { it('wraps AMD modules in an AMD iife', async () => {
const basicResult = await decorateSystemJSFetch(originalFetch, '/public/plugins/mockAmdModule/module.js', {}); const basicResult = await decorateSystemJSFetch(
systemJSPrototype.fetch,
'/public/plugins/mockAmdModule/module.js',
{}
);
const basicSource = await basicResult.text(); const basicSource = await basicResult.text();
const basicExpected = `(function(define) { const basicExpected = `(function(define) {
${mockAmdModule} ${mockAmdModule}
@ -51,7 +57,7 @@ describe('SystemJS Loader Hooks', () => {
expect(basicSource).toBe(basicExpected); expect(basicSource).toBe(basicExpected);
const mockAmdModuleNamedNoDepsResult = await decorateSystemJSFetch( const mockAmdModuleNamedNoDepsResult = await decorateSystemJSFetch(
originalFetch, systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleNamedNoDeps/module.js', '/public/plugins/mockAmdModuleNamedNoDeps/module.js',
{} {}
); );
@ -63,7 +69,7 @@ describe('SystemJS Loader Hooks', () => {
expect(mockAmdModuleNamedNoDepsSource).toBe(mockAmdModuleNamedNoDepsExpected); expect(mockAmdModuleNamedNoDepsSource).toBe(mockAmdModuleNamedNoDepsExpected);
const mockAmdModuleNamedWithDepsResult = await decorateSystemJSFetch( const mockAmdModuleNamedWithDepsResult = await decorateSystemJSFetch(
originalFetch, systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleNamedWithDeps/module.js', '/public/plugins/mockAmdModuleNamedWithDeps/module.js',
{} {}
); );
@ -75,7 +81,7 @@ describe('SystemJS Loader Hooks', () => {
expect(mockAmdModuleNamedWithDepsSource).toBe(mockAmdModuleNamedWithDepsExpected); expect(mockAmdModuleNamedWithDepsSource).toBe(mockAmdModuleNamedWithDepsExpected);
const mockAmdModuleNamedWithDeps2Result = await decorateSystemJSFetch( const mockAmdModuleNamedWithDeps2Result = await decorateSystemJSFetch(
originalFetch, systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleNamedWithDeps2/module.js', '/public/plugins/mockAmdModuleNamedWithDeps2/module.js',
{} {}
); );
@ -87,7 +93,7 @@ describe('SystemJS Loader Hooks', () => {
expect(mockAmdModuleNamedWithDeps2Source).toBe(mockAmdModuleNamedWithDeps2Expected); expect(mockAmdModuleNamedWithDeps2Source).toBe(mockAmdModuleNamedWithDeps2Expected);
const mockAmdModuleNamedWithDeps3Result = await decorateSystemJSFetch( const mockAmdModuleNamedWithDeps3Result = await decorateSystemJSFetch(
originalFetch, systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleNamedWithDeps3/module.js', '/public/plugins/mockAmdModuleNamedWithDeps3/module.js',
{} {}
); );
@ -99,7 +105,7 @@ describe('SystemJS Loader Hooks', () => {
expect(mockAmdModuleNamedWithDeps3Source).toBe(mockAmdModuleNamedWithDeps3Expected); expect(mockAmdModuleNamedWithDeps3Source).toBe(mockAmdModuleNamedWithDeps3Expected);
const mockAmdModuleOnlyFunctionResult = await decorateSystemJSFetch( const mockAmdModuleOnlyFunctionResult = await decorateSystemJSFetch(
originalFetch, systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleOnlyFunction/module.js', '/public/plugins/mockAmdModuleOnlyFunction/module.js',
{} {}
); );
@ -111,7 +117,7 @@ describe('SystemJS Loader Hooks', () => {
expect(mockAmdModuleOnlyFunctionSource).toBe(mockAmdModuleOnlyFunctionExpected); expect(mockAmdModuleOnlyFunctionSource).toBe(mockAmdModuleOnlyFunctionExpected);
const mockAmdModuleWithCommentsResult = await decorateSystemJSFetch( const mockAmdModuleWithCommentsResult = await decorateSystemJSFetch(
originalFetch, systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleWithComments/module.js', '/public/plugins/mockAmdModuleWithComments/module.js',
{} {}
); );
@ -123,7 +129,7 @@ describe('SystemJS Loader Hooks', () => {
expect(mockAmdModuleWithCommentsSource).toBe(mockAmdModuleWithCommentsExpected); expect(mockAmdModuleWithCommentsSource).toBe(mockAmdModuleWithCommentsExpected);
const mockAmdModuleWithComments2Result = await decorateSystemJSFetch( const mockAmdModuleWithComments2Result = await decorateSystemJSFetch(
originalFetch, systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleWithComments2/module.js', '/public/plugins/mockAmdModuleWithComments2/module.js',
{} {}
); );
@ -136,14 +142,14 @@ describe('SystemJS Loader Hooks', () => {
}); });
it("doesn't wrap system modules in an AMD iife", async () => { it("doesn't wrap system modules in an AMD iife", async () => {
const url = '/public/plugins/mockSystemModule/module.js'; const url = '/public/plugins/mockSystemModule/module.js';
const result = await decorateSystemJSFetch(originalFetch, url, {}); const result = await decorateSystemJSFetch(systemJSPrototype.fetch, url, {});
const source = await result.text(); const source = await result.text();
expect(source).toBe(mockSystemModule); expect(source).toBe(mockSystemModule);
}); });
it("doesn't wrap modules with a define method in an AMD iife", async () => { it("doesn't wrap modules with a define method in an AMD iife", async () => {
const url = '/public/plugins/mockModuleWithDefineMethod/module.js'; const url = '/public/plugins/mockModuleWithDefineMethod/module.js';
const result = await decorateSystemJSFetch(originalFetch, url, {}); const result = await decorateSystemJSFetch(systemJSPrototype.fetch, url, {});
const source = await result.text(); const source = await result.text();
expect(source).toBe(mockModuleWithDefineMethod); expect(source).toBe(mockModuleWithDefineMethod);
@ -151,13 +157,13 @@ describe('SystemJS Loader Hooks', () => {
it('only transforms plugin source code hosted on cdn with cdn paths', async () => { it('only transforms plugin source code hosted on cdn with cdn paths', async () => {
config.pluginsCDNBaseURL = 'http://my-cdn.com/plugins'; config.pluginsCDNBaseURL = 'http://my-cdn.com/plugins';
const cdnUrl = 'http://my-cdn.com/plugins/my-plugin/v1.0.0/public/plugins/my-plugin/module.js'; const cdnUrl = 'http://my-cdn.com/plugins/my-plugin/v1.0.0/public/plugins/my-plugin/module.js';
const cdnResult = await decorateSystemJSFetch(originalFetch, cdnUrl, {}); const cdnResult = await decorateSystemJSFetch(systemJSPrototype.fetch, cdnUrl, {});
const cdnSource = await cdnResult.text(); const cdnSource = await cdnResult.text();
expect(cdnSource).toContain('var pluginPath = "http://my-cdn.com/plugins/my-plugin/v1.0.0/public/plugins/";'); expect(cdnSource).toContain('var pluginPath = "http://my-cdn.com/plugins/my-plugin/v1.0.0/public/plugins/";');
const url = '/public/plugins/mockAmdModule/module.js'; const url = '/public/plugins/mockAmdModule/module.js';
const result = await decorateSystemJSFetch(originalFetch, url, {}); const result = await decorateSystemJSFetch(systemJSPrototype.fetch, url, {});
const source = await result.text(); const source = await result.text();
expect(source).toContain('var pluginPath = "/public/plugins/";'); expect(source).toContain('var pluginPath = "/public/plugins/";');
}); });

View File

@ -27,6 +27,23 @@ module.exports = (path, options) => {
delete pkg['exports']; delete pkg['exports'];
delete pkg['module']; delete pkg['module'];
} }
// Jest + jsdom acts like a browser (i.e., it looks for "browser" imports
// under pkg.exports), but msw knows that you're operating in a Node
// environment:
//
// https://github.com/mswjs/msw/issues/1786#issuecomment-1782559851
//
// The MSW project's recommended workaround is to disable Jest's
// customExportConditions completely, so no packages use their browser's
// versions. We'll instead clear export conditions only for MSW.
//
// Taken from https://github.com/mswjs/msw/issues/1786#issuecomment-1787730599
if (pkg.name === 'msw') {
delete pkg.exports['./node'].browser;
}
if (pkg.name === '@mswjs/interceptors') {
delete pkg.exports;
}
return pkg; return pkg;
}, },
}); });

245
yarn.lock
View File

@ -1865,6 +1865,24 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@bundled-es-modules/cookie@npm:^2.0.0":
version: 2.0.0
resolution: "@bundled-es-modules/cookie@npm:2.0.0"
dependencies:
cookie: "npm:^0.5.0"
checksum: 10/c8ef02aa5d3f6c786cfa407e1c93b4af29c600eb09990973f47a7a49e4771c1bec37c8f8e567638bb9cbc41f4e38d065ff1d8eaf9bf91f0c3613a6d60bc82c8c
languageName: node
linkType: hard
"@bundled-es-modules/statuses@npm:^1.0.1":
version: 1.0.1
resolution: "@bundled-es-modules/statuses@npm:1.0.1"
dependencies:
statuses: "npm:^2.0.1"
checksum: 10/9bf6a2bcf040a66fb805da0e1446041fd9def7468bb5da29c5ce02adf121a3f7cec123664308059a62a46fcaee666add83094b76df6dce72e5cafa8e6bebe60d
languageName: node
linkType: hard
"@colors/colors@npm:1.5.0": "@colors/colors@npm:1.5.0":
version: 1.5.0 version: 1.5.0
resolution: "@colors/colors@npm:1.5.0" resolution: "@colors/colors@npm:1.5.0"
@ -4298,6 +4316,45 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@inquirer/confirm@npm:^3.0.0":
version: 3.0.0
resolution: "@inquirer/confirm@npm:3.0.0"
dependencies:
"@inquirer/core": "npm:^7.0.0"
"@inquirer/type": "npm:^1.2.0"
checksum: 10/ed16dc0e5b22115474853ca57dbe3dacdcd15bcb37cc50020e8e76ff8d0875d62d8b63b93b3092c653faeb6c83a139eac997ff05638b0f1f78ae919f29ee29d4
languageName: node
linkType: hard
"@inquirer/core@npm:^7.0.0":
version: 7.0.0
resolution: "@inquirer/core@npm:7.0.0"
dependencies:
"@inquirer/type": "npm:^1.2.0"
"@types/mute-stream": "npm:^0.0.4"
"@types/node": "npm:^20.11.16"
"@types/wrap-ansi": "npm:^3.0.0"
ansi-escapes: "npm:^4.3.2"
chalk: "npm:^4.1.2"
cli-spinners: "npm:^2.9.2"
cli-width: "npm:^4.1.0"
figures: "npm:^3.2.0"
mute-stream: "npm:^1.0.0"
run-async: "npm:^3.0.0"
signal-exit: "npm:^4.1.0"
strip-ansi: "npm:^6.0.1"
wrap-ansi: "npm:^6.2.0"
checksum: 10/78c0ef4bb82cb7be23f16a80c9cff02839c77e378d272327f49878788a4c3b1cc00137387317053d242a87634954850a4d2546b3e48b1abd27130a21f598afef
languageName: node
linkType: hard
"@inquirer/type@npm:^1.2.0":
version: 1.2.0
resolution: "@inquirer/type@npm:1.2.0"
checksum: 10/12f68a16d8995efb409bd243d6ccc501b366e8009630a075071a9d9497cebd36bbd6c46d7d59b37435629e5e50236394679f414f7676b68b913ecc28a85cba0a
languageName: node
linkType: hard
"@internationalized/date@npm:^3.5.1": "@internationalized/date@npm:^3.5.1":
version: 3.5.1 version: 3.5.1
resolution: "@internationalized/date@npm:3.5.1" resolution: "@internationalized/date@npm:3.5.1"
@ -5134,6 +5191,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@mswjs/cookies@npm:^1.1.0":
version: 1.1.0
resolution: "@mswjs/cookies@npm:1.1.0"
checksum: 10/168ed1966e579a4f454e6d2af5a015150cca570ac4c660f5b656e7bc021afacbf0b3d4ed3d03e9293550f3965c28ce1e293fa7037c6cf46ed7e268e21a1053a4
languageName: node
linkType: hard
"@mswjs/interceptors@npm:^0.17.10": "@mswjs/interceptors@npm:^0.17.10":
version: 0.17.10 version: 0.17.10
resolution: "@mswjs/interceptors@npm:0.17.10" resolution: "@mswjs/interceptors@npm:0.17.10"
@ -5150,6 +5214,20 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@mswjs/interceptors@npm:^0.25.16":
version: 0.25.16
resolution: "@mswjs/interceptors@npm:0.25.16"
dependencies:
"@open-draft/deferred-promise": "npm:^2.2.0"
"@open-draft/logger": "npm:^0.3.0"
"@open-draft/until": "npm:^2.0.0"
is-node-process: "npm:^1.2.0"
outvariant: "npm:^1.2.1"
strict-event-emitter: "npm:^0.5.1"
checksum: 10/d8fb74db45a63971e9da7367c8d120343c8f49fec7bcc3f0c77c04c3f628d307b70875f52e4a99df561547b92d0d53edacc421e42d69940d44999254b5d028b5
languageName: node
linkType: hard
"@ndelangen/get-tarball@npm:^3.0.7": "@ndelangen/get-tarball@npm:^3.0.7":
version: 3.0.7 version: 3.0.7
resolution: "@ndelangen/get-tarball@npm:3.0.7" resolution: "@ndelangen/get-tarball@npm:3.0.7"
@ -5588,6 +5666,23 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@open-draft/deferred-promise@npm:^2.2.0":
version: 2.2.0
resolution: "@open-draft/deferred-promise@npm:2.2.0"
checksum: 10/bc3bb1668a555bb87b33383cafcf207d9561e17d2ca0d9e61b7ce88e82b66e36a333d3676c1d39eb5848022c03c8145331fcdc828ba297f88cb1de9c5cef6c19
languageName: node
linkType: hard
"@open-draft/logger@npm:^0.3.0":
version: 0.3.0
resolution: "@open-draft/logger@npm:0.3.0"
dependencies:
is-node-process: "npm:^1.2.0"
outvariant: "npm:^1.4.0"
checksum: 10/7a280f170bcd4e91d3eedbefe628efd10c3bd06dd2461d06a7fdbced89ef457a38785847f88cc630fb4fd7dfa176d6f77aed17e5a9b08000baff647433b5ff78
languageName: node
linkType: hard
"@open-draft/until@npm:^1.0.3": "@open-draft/until@npm:^1.0.3":
version: 1.0.3 version: 1.0.3
resolution: "@open-draft/until@npm:1.0.3" resolution: "@open-draft/until@npm:1.0.3"
@ -5595,6 +5690,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@open-draft/until@npm:^2.0.0, @open-draft/until@npm:^2.1.0":
version: 2.1.0
resolution: "@open-draft/until@npm:2.1.0"
checksum: 10/622be42950afc8e89715d0fd6d56cbdcd13e36625e23b174bd3d9f06f80e25f9adf75d6698af93bca1e1bf465b9ce00ec05214a12189b671fb9da0f58215b6f4
languageName: node
linkType: hard
"@opentelemetry/api-logs@npm:0.45.1": "@opentelemetry/api-logs@npm:0.45.1":
version: 0.45.1 version: 0.45.1
resolution: "@opentelemetry/api-logs@npm:0.45.1" resolution: "@opentelemetry/api-logs@npm:0.45.1"
@ -8581,6 +8683,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/cookie@npm:^0.6.0":
version: 0.6.0
resolution: "@types/cookie@npm:0.6.0"
checksum: 10/b883348d5bf88695fbc2c2276b1c49859267a55cae3cf11ea1dccc1b3be15b466e637ce3242109ba27d616c77c6aa4efe521e3d557110b4fdd9bc332a12445c2
languageName: node
linkType: hard
"@types/cross-spawn@npm:^6.0.2": "@types/cross-spawn@npm:^6.0.2":
version: 6.0.3 version: 6.0.3
resolution: "@types/cross-spawn@npm:6.0.3" resolution: "@types/cross-spawn@npm:6.0.3"
@ -9363,6 +9472,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/mute-stream@npm:^0.0.4":
version: 0.0.4
resolution: "@types/mute-stream@npm:0.0.4"
dependencies:
"@types/node": "npm:*"
checksum: 10/af8d83ad7b68ea05d9357985daf81b6c9b73af4feacb2f5c2693c7fd3e13e5135ef1bd083ce8d5bdc8e97acd28563b61bb32dec4e4508a8067fcd31b8a098632
languageName: node
linkType: hard
"@types/node-fetch@npm:^2.6.4": "@types/node-fetch@npm:^2.6.4":
version: 2.6.4 version: 2.6.4
resolution: "@types/node-fetch@npm:2.6.4" resolution: "@types/node-fetch@npm:2.6.4"
@ -9382,7 +9500,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/node@npm:*, @types/node@npm:20.11.17, @types/node@npm:>=13.7.0": "@types/node@npm:*, @types/node@npm:20.11.17, @types/node@npm:>=13.7.0, @types/node@npm:^20.11.16":
version: 20.11.17 version: 20.11.17
resolution: "@types/node@npm:20.11.17" resolution: "@types/node@npm:20.11.17"
dependencies: dependencies:
@ -9808,6 +9926,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/statuses@npm:^2.0.4":
version: 2.0.4
resolution: "@types/statuses@npm:2.0.4"
checksum: 10/3a806c3b96d1845e3e7441fbf0839037e95f717334760ddb7c29223c9a34a7206b68e2998631f89f1a1e3ef5b67b15652f6e8fa14987ebd7f6d38587c1bffd18
languageName: node
linkType: hard
"@types/string-hash@npm:1.1.3": "@types/string-hash@npm:1.1.3":
version: 1.1.3 version: 1.1.3
resolution: "@types/string-hash@npm:1.1.3" resolution: "@types/string-hash@npm:1.1.3"
@ -9912,6 +10037,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/wrap-ansi@npm:^3.0.0":
version: 3.0.0
resolution: "@types/wrap-ansi@npm:3.0.0"
checksum: 10/8aa644946ca4e859668c36b8e2bcf2ac4bdee59dac760414730ea57be8a93ae9166ebd40a088f2ab714843aaea2a2a67f0e6e6ec11cfc9c8701b2466ca1c4089
languageName: node
linkType: hard
"@types/ws@npm:^8.5.5": "@types/ws@npm:^8.5.5":
version: 8.5.5 version: 8.5.5
resolution: "@types/ws@npm:8.5.5" resolution: "@types/ws@npm:8.5.5"
@ -11114,7 +11246,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0": "ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0, ansi-escapes@npm:^4.3.2":
version: 4.3.2 version: 4.3.2
resolution: "ansi-escapes@npm:4.3.2" resolution: "ansi-escapes@npm:4.3.2"
dependencies: dependencies:
@ -12814,6 +12946,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"cli-spinners@npm:^2.9.2":
version: 2.9.2
resolution: "cli-spinners@npm:2.9.2"
checksum: 10/a0a863f442df35ed7294424f5491fa1756bd8d2e4ff0c8736531d886cec0ece4d85e8663b77a5afaf1d296e3cbbebff92e2e99f52bbea89b667cbe789b994794
languageName: node
linkType: hard
"cli-table3@npm:^0.6.1, cli-table3@npm:~0.6.1": "cli-table3@npm:^0.6.1, cli-table3@npm:~0.6.1":
version: 0.6.3 version: 0.6.3
resolution: "cli-table3@npm:0.6.3" resolution: "cli-table3@npm:0.6.3"
@ -12844,6 +12983,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"cli-width@npm:^4.1.0":
version: 4.1.0
resolution: "cli-width@npm:4.1.0"
checksum: 10/b58876fbf0310a8a35c79b72ecfcf579b354e18ad04e6b20588724ea2b522799a758507a37dfe132fafaf93a9922cafd9514d9e1598e6b2cd46694853aed099f
languageName: node
linkType: hard
"cliui@npm:^7.0.2": "cliui@npm:^7.0.2":
version: 7.0.4 version: 7.0.4
resolution: "cliui@npm:7.0.4" resolution: "cliui@npm:7.0.4"
@ -13421,6 +13567,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"cookie@npm:^0.5.0":
version: 0.5.0
resolution: "cookie@npm:0.5.0"
checksum: 10/aae7911ddc5f444a9025fbd979ad1b5d60191011339bce48e555cb83343d0f98b865ff5c4d71fecdfb8555a5cafdc65632f6fce172f32aaf6936830a883a0380
languageName: node
linkType: hard
"copy-to-clipboard@npm:^3.3.1": "copy-to-clipboard@npm:^3.3.1":
version: 3.3.1 version: 3.3.1
resolution: "copy-to-clipboard@npm:3.3.1" resolution: "copy-to-clipboard@npm:3.3.1"
@ -18231,7 +18384,7 @@ __metadata:
mousetrap: "npm:1.6.5" mousetrap: "npm:1.6.5"
mousetrap-global-bind: "npm:1.1.0" mousetrap-global-bind: "npm:1.1.0"
moveable: "npm:0.53.0" moveable: "npm:0.53.0"
msw: "npm:1.3.2" msw: "npm:2.2.0"
mutationobserver-shim: "npm:0.3.7" mutationobserver-shim: "npm:0.3.7"
nanoid: "npm:^5.0.4" nanoid: "npm:^5.0.4"
ngtemplate-loader: "npm:2.1.0" ngtemplate-loader: "npm:2.1.0"
@ -18564,6 +18717,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"headers-polyfill@npm:^4.0.2":
version: 4.0.2
resolution: "headers-polyfill@npm:4.0.2"
checksum: 10/70b53abf48a1d50760150624d6c7ca974a0d286ba102e411538f6dad6687ce51ce7cc60197e326df96f844548d6ff77d900e28c3cdbc0ba1fe09a05eae47156a
languageName: node
linkType: hard
"heimdalljs-logger@npm:^0.1.10, heimdalljs-logger@npm:^0.1.7": "heimdalljs-logger@npm:^0.1.10, heimdalljs-logger@npm:^0.1.7":
version: 0.1.10 version: 0.1.10
resolution: "heimdalljs-logger@npm:0.1.10" resolution: "heimdalljs-logger@npm:0.1.10"
@ -22717,6 +22877,38 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"msw@npm:2.2.0":
version: 2.2.0
resolution: "msw@npm:2.2.0"
dependencies:
"@bundled-es-modules/cookie": "npm:^2.0.0"
"@bundled-es-modules/statuses": "npm:^1.0.1"
"@inquirer/confirm": "npm:^3.0.0"
"@mswjs/cookies": "npm:^1.1.0"
"@mswjs/interceptors": "npm:^0.25.16"
"@open-draft/until": "npm:^2.1.0"
"@types/cookie": "npm:^0.6.0"
"@types/statuses": "npm:^2.0.4"
chalk: "npm:^4.1.2"
graphql: "npm:^16.8.1"
headers-polyfill: "npm:^4.0.2"
is-node-process: "npm:^1.2.0"
outvariant: "npm:^1.4.2"
path-to-regexp: "npm:^6.2.0"
strict-event-emitter: "npm:^0.5.1"
type-fest: "npm:^4.9.0"
yargs: "npm:^17.7.2"
peerDependencies:
typescript: ">= 4.7.x <= 5.3.x"
peerDependenciesMeta:
typescript:
optional: true
bin:
msw: cli/index.js
checksum: 10/36e6a1f0bc89d1c84bf24c1cf7ca7c3fe5d5b032c825d52a832e9b815830976a86c17a8c3f1f506db720f44e99d04c9302e014cf569b51b2d2af62562a9fe62a
languageName: node
linkType: hard
"multicast-dns@npm:^7.2.4": "multicast-dns@npm:^7.2.4":
version: 7.2.4 version: 7.2.4
resolution: "multicast-dns@npm:7.2.4" resolution: "multicast-dns@npm:7.2.4"
@ -22756,7 +22948,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"mute-stream@npm:~1.0.0": "mute-stream@npm:^1.0.0, mute-stream@npm:~1.0.0":
version: 1.0.0 version: 1.0.0
resolution: "mute-stream@npm:1.0.0" resolution: "mute-stream@npm:1.0.0"
checksum: 10/36fc968b0e9c9c63029d4f9dc63911950a3bdf55c9a87f58d3a266289b67180201cade911e7699f8b2fa596b34c9db43dad37649e3f7fdd13c3bb9edb0017ee7 checksum: 10/36fc968b0e9c9c63029d4f9dc63911950a3bdf55c9a87f58d3a266289b67180201cade911e7699f8b2fa596b34c9db43dad37649e3f7fdd13c3bb9edb0017ee7
@ -23607,10 +23799,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"outvariant@npm:^1.2.1, outvariant@npm:^1.4.0": "outvariant@npm:^1.2.1, outvariant@npm:^1.4.0, outvariant@npm:^1.4.2":
version: 1.4.0 version: 1.4.2
resolution: "outvariant@npm:1.4.0" resolution: "outvariant@npm:1.4.2"
checksum: 10/07b9bcb9b3a2ff1b3db02af6b07d70e663082b30ddc08ff475d7c85fc623fdcc4433a4ab5b88f6902b62dbb284eef1be386aa537e14cef0519fad887ec483054 checksum: 10/f16ba035fb65d1cbe7d2e06693dd42183c46bc8456713d9ddb5182d067defa7d78217edab0a2d3e173d3bacd627b2bd692195c7087c225b82548fbf52c677b38
languageName: node languageName: node
linkType: hard linkType: hard
@ -27278,6 +27470,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"run-async@npm:^3.0.0":
version: 3.0.0
resolution: "run-async@npm:3.0.0"
checksum: 10/97fb8747f7765b77ebcd311d3a33548099336f04c6434e0763039b98c1de0f1b4421000695aff8751f309c0b995d8dfd620c1f1e4c35572da38c101488165305
languageName: node
linkType: hard
"run-parallel@npm:^1.1.9": "run-parallel@npm:^1.1.9":
version: 1.2.0 version: 1.2.0
resolution: "run-parallel@npm:1.2.0" resolution: "run-parallel@npm:1.2.0"
@ -27732,6 +27931,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"signal-exit@npm:^4.1.0":
version: 4.1.0
resolution: "signal-exit@npm:4.1.0"
checksum: 10/c9fa63bbbd7431066174a48ba2dd9986dfd930c3a8b59de9c29d7b6854ec1c12a80d15310869ea5166d413b99f041bfa3dd80a7947bcd44ea8e6eb3ffeabfa1f
languageName: node
linkType: hard
"sigstore@npm:^1.3.0, sigstore@npm:^1.4.0": "sigstore@npm:^1.3.0, sigstore@npm:^1.4.0":
version: 1.9.0 version: 1.9.0
resolution: "sigstore@npm:1.9.0" resolution: "sigstore@npm:1.9.0"
@ -28376,6 +28582,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"statuses@npm:^2.0.1":
version: 2.0.1
resolution: "statuses@npm:2.0.1"
checksum: 10/18c7623fdb8f646fb213ca4051be4df7efb3484d4ab662937ca6fbef7ced9b9e12842709872eb3020cc3504b93bde88935c9f6417489627a7786f24f8031cbcb
languageName: node
linkType: hard
"stop-iteration-iterator@npm:^1.0.0": "stop-iteration-iterator@npm:^1.0.0":
version: 1.0.0 version: 1.0.0
resolution: "stop-iteration-iterator@npm:1.0.0" resolution: "stop-iteration-iterator@npm:1.0.0"
@ -28518,6 +28731,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"strict-event-emitter@npm:^0.5.1":
version: 0.5.1
resolution: "strict-event-emitter@npm:0.5.1"
checksum: 10/25c84d88be85940d3547db665b871bfecea4ea0bedfeb22aae8db48126820cfb2b0bc2fba695392592a09b1aa36b686d6eede499e1ecd151593c03fe5a50d512
languageName: node
linkType: hard
"string-hash@npm:^1.1.3": "string-hash@npm:^1.1.3":
version: 1.1.3 version: 1.1.3
resolution: "string-hash@npm:1.1.3" resolution: "string-hash@npm:1.1.3"
@ -29819,6 +30039,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"type-fest@npm:^4.9.0":
version: 4.10.2
resolution: "type-fest@npm:4.10.2"
checksum: 10/2b1ad1270d9fabeeb506ba831d513caeb05bfc852e5e012511d785ce9dc68d773fe0a42bddf857a362c7f3406244809c5b8a698b743bb7617d4a8c470672087f
languageName: node
linkType: hard
"type-is@npm:~1.6.18": "type-is@npm:~1.6.18":
version: 1.6.18 version: 1.6.18
resolution: "type-is@npm:1.6.18" resolution: "type-is@npm:1.6.18"
@ -31485,7 +31712,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"yargs@npm:^17.3.1, yargs@npm:^17.5.1, yargs@npm:^17.6.2": "yargs@npm:^17.3.1, yargs@npm:^17.5.1, yargs@npm:^17.6.2, yargs@npm:^17.7.2":
version: 17.7.2 version: 17.7.2
resolution: "yargs@npm:17.7.2" resolution: "yargs@npm:17.7.2"
dependencies: dependencies: