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",
"lerna": "7.4.1",
"mini-css-extract-plugin": "2.8.0",
"msw": "1.3.2",
"msw": "2.2.0",
"mutationobserver-shim": "0.3.7",
"ngtemplate-loader": "2.1.0",
"node-notifier": "10.0.1",

View File

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

View File

@ -1,3 +1,4 @@
import 'whatwg-fetch';
import { render, waitFor, waitForElementToBeRemoved, within } from '@testing-library/react';
import { setupServer } from 'msw/node';
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 { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types';
import { RuleWithLocation } from 'app/types/unified-alerting';
import 'whatwg-fetch';
import {
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 React from 'react';
import { Provider } from 'react-redux';
import 'whatwg-fetch';
import { setBackendSrv } from '@grafana/runtime';
import { backendSrv } from 'app/core/services/backend_srv';
import { configureStore } from 'app/store/configureStore';
@ -76,8 +75,10 @@ describe('GrafanaAlertmanagerDeliveryWarning', () => {
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={GRAFANA_RULES_SOURCE_NAME} />
);
await waitFor(() => {
expect(container).toBeEmptyDOMElement();
});
});
it('Should render no warning when choice is All but no active AM instances', async () => {
mockAlertmanagerChoiceResponse(server, {
@ -89,9 +90,11 @@ describe('GrafanaAlertmanagerDeliveryWarning', () => {
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={GRAFANA_RULES_SOURCE_NAME} />
);
await waitFor(() => {
expect(container).toBeEmptyDOMElement();
});
});
});
function renderWithStore(element: JSX.Element) {
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
import 'whatwg-fetch';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import { SupportedPlugin } from '../types/pluginBridges';
export const NON_EXISTING_PLUGIN = '__does_not_exist__';
const server = setupServer(
rest.get(`/api/plugins/${NON_EXISTING_PLUGIN}/settings`, async (_req, res, ctx) => res(ctx.status(404))),
rest.get(`/api/plugins/${SupportedPlugin.Incident}/settings`, async (_req, res, ctx) => {
return res(
ctx.json({
http.get(`/api/plugins/${NON_EXISTING_PLUGIN}/settings`, async () =>
HttpResponse.json(
{},
{
status: 404,
}
)
),
http.get(`/api/plugins/${SupportedPlugin.Incident}/settings`, async () => {
return HttpResponse.json({
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 { AlertmanagerChoice, AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types';
@ -14,15 +15,15 @@ import receiversMock from './receivers.mock.json';
export default (server: SetupServer) => {
server.use(
// this endpoint is a grafana built-in alertmanager
rest.get('/api/alertmanager/grafana/config/api/v1/alerts', (_req, res, ctx) =>
res(ctx.json<AlertManagerCortexConfig>(alertmanagerMock))
http.get('/api/alertmanager/grafana/config/api/v1/alerts', () =>
HttpResponse.json<AlertManagerCortexConfig>(alertmanagerMock)
),
// this endpoint is only available for the built-in alertmanager
rest.get('/api/alertmanager/grafana/config/api/v1/receivers', (_req, res, ctx) =>
res(ctx.json<ReceiversStateDTO[]>(receiversMock))
http.get('/api/alertmanager/grafana/config/api/v1/receivers', () =>
HttpResponse.json<ReceiversStateDTO[]>(receiversMock)
),
// 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
@ -41,12 +42,18 @@ export const setupTestEndpointMock = (server: SetupServer) => {
const mock = jest.fn();
server.use(
rest.post('/api/alertmanager/grafana/config/api/v1/receivers/test', async (req, res, ctx) => {
const requestBody = await req.json();
http.post(
'/api/alertmanager/grafana/config/api/v1/receivers/test',
async ({ request }) => {
const requestBody = await request.json();
mock(requestBody);
return res.once(ctx.status(200));
})
return HttpResponse.json({});
},
{
once: true,
}
)
);
return mock;
@ -56,12 +63,18 @@ export const setupSaveEndpointMock = (server: SetupServer) => {
const mock = jest.fn();
server.use(
rest.post('/api/alertmanager/grafana/config/api/v1/alerts', async (req, res, ctx) => {
const requestBody = await req.json();
http.post(
'/api/alertmanager/grafana/config/api/v1/alerts',
async ({ request }) => {
const requestBody = await request.json();
mock(requestBody);
return res.once(ctx.status(200));
})
return HttpResponse.json({});
},
{
once: true,
}
)
);
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 { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types';
@ -10,14 +11,20 @@ export const MIMIR_DATASOURCE_UID = 'mimir';
export default (server: SetupServer) => {
server.use(
rest.get(`/api/alertmanager/${MIMIR_DATASOURCE_UID}/config/api/v1/alerts`, (_req, res, ctx) =>
res(ctx.json<AlertManagerCortexConfig>(mimirAlertmanagerMock))
http.get(`/api/alertmanager/${MIMIR_DATASOURCE_UID}/config/api/v1/alerts`, () =>
HttpResponse.json(mimirAlertmanagerMock)
),
rest.get(`/api/datasources/proxy/uid/${MIMIR_DATASOURCE_UID}/api/v1/status/buildinfo`, (_req, res, ctx) =>
res(ctx.status(404))
http.get(`/api/datasources/proxy/uid/${MIMIR_DATASOURCE_UID}/api/v1/status/buildinfo`, () =>
HttpResponse.json<AlertManagerCortexConfig>(
{
template_files: {},
alertmanager_config: {},
},
{ status: 404 }
)
),
// 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;

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 { AlertmanagerStatus } from 'app/plugins/datasource/alertmanager/types';
@ -10,11 +11,11 @@ export const VANILLA_ALERTMANAGER_DATASOURCE_UID = 'alertmanager';
export default (server: SetupServer) => {
server.use(
rest.get(`/api/alertmanager/${VANILLA_ALERTMANAGER_DATASOURCE_UID}/api/v2/status`, (_req, res, ctx) =>
res(ctx.json<AlertmanagerStatus>(vanillaAlertManagerConfig))
http.get(`/api/alertmanager/${VANILLA_ALERTMANAGER_DATASOURCE_UID}/api/v2/status`, () =>
HttpResponse.json<AlertmanagerStatus>(vanillaAlertManagerConfig)
),
// 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;

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 'whatwg-fetch';
import { AlertmanagersChoiceResponse } from 'app/features/alerting/unified/api/alertmanagerApi';
import { mockAlertmanagerChoiceResponse } from 'app/features/alerting/unified/mocks/alertmanagerApi';
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
function mockFolderAccess(server: SetupServer, accessControl: Partial<Record<AccessControlAction, boolean>>) {
server.use(
rest.get('/api/folders/:uid', (req, res, ctx) => {
const uid = req.params.uid;
http.get('/api/folders/:uid', ({ request }) => {
const url = new URL(request.url);
const uid = url.searchParams.get('uid');
return res(
ctx.json({
return HttpResponse.json({
title: 'My Folder',
uid,
accessControl,
})
);
});
})
);
@ -51,8 +50,8 @@ function mockFolderAccess(server: SetupServer, accessControl: Partial<Record<Acc
function mockGrafanaIncidentPluginSettings(server: SetupServer) {
server.use(
rest.get('/api/plugins/grafana-incident-app/settings', (_, res, ctx) => {
return res(ctx.status(200));
http.get('/api/plugins/grafana-incident-app/settings', () => {
return HttpResponse.json({});
})
);
}

View File

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

View File

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

View File

@ -1,9 +1,9 @@
import 'whatwg-fetch';
import { render, waitFor } from '@testing-library/react';
import { rest } from 'msw';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import React from 'react';
import { byRole, byText } from 'testing-library-selector';
import 'whatwg-fetch';
import { DataFrameJSON } from '@grafana/data';
import { setBackendSrv } from '@grafana/runtime';
@ -20,9 +20,8 @@ beforeAll(() => {
server.listen({ onUnhandledRequest: 'error' });
server.use(
rest.get('/api/v1/rules/history', (req, res, ctx) =>
res(
ctx.json<DataFrameJSON>({
http.get('/api/v1/rules/history', () =>
HttpResponse.json<DataFrameJSON>({
data: {
values: [
[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 () => {
server.use(
rest.get('/api/v1/rules/history', (req, res, ctx) =>
res(ctx.json<DataFrameJSON>({ data: { values: [] }, schema: { fields: [] } }))
http.get('/api/v1/rules/history', () =>
HttpResponse.json<DataFrameJSON>({ data: { values: [] }, schema: { fields: [] } })
)
);

View File

@ -1,10 +1,9 @@
import 'whatwg-fetch';
import { renderHook, waitFor } from '@testing-library/react';
import { rest } from 'msw';
import { http, HttpResponse } from 'msw';
import { SetupServer, setupServer } from 'msw/node';
import { TestProvider } from 'test/helpers/TestProvider';
import 'whatwg-fetch';
import { DataSourceSettings } from '@grafana/data';
import { setBackendSrv } from '@grafana/runtime';
import { backendSrv } from 'app/core/services/backend_srv';
@ -215,8 +214,8 @@ function setupAlertmanagerDataSource(
};
server.use(
rest.get('/api/datasources', (_req, res, ctx) => {
return res(ctx.json([dsSettings]));
http.get('/api/datasources', () => {
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 { uniqueId } from 'lodash';
import { http, HttpResponse } from 'msw';
import { setupServer, SetupServer } from 'msw/node';
import { DataSourceInstanceSettings, PluginMeta } from '@grafana/data';
import { setBackendSrv } from '@grafana/runtime';
@ -190,85 +190,74 @@ export function mockApi(server: SetupServer) {
configure(builder);
server.use(
rest.get(`api/alertmanager/${amName}/config/api/v1/alerts`, (req, res, ctx) =>
res(
ctx.status(200),
ctx.json<AlertManagerCortexConfig>({
http.get(`api/alertmanager/${amName}/config/api/v1/alerts`, () =>
HttpResponse.json<AlertManagerCortexConfig>({
alertmanager_config: builder.build(),
template_files: {},
})
)
)
);
},
eval: (response: AlertingQueryResponse) => {
server.use(
rest.post('/api/v1/eval', (_, res, ctx) => {
return res(ctx.status(200), ctx.json(response));
http.post('/api/v1/eval', () => {
return HttpResponse.json(response);
})
);
},
grafanaNotifiers: (response: NotifierDTO[]) => {
server.use(
rest.get(`api/alert-notifiers`, (req, res, ctx) => res(ctx.status(200), ctx.json<NotifierDTO[]>(response)))
);
server.use(http.get(`api/alert-notifiers`, () => HttpResponse.json(response)));
},
plugins: {
getPluginSettings: (response: PluginMeta) => {
server.use(
rest.get(`api/plugins/${response.id}/settings`, (req, res, ctx) =>
res(ctx.status(200), ctx.json<PluginMeta>(response))
)
);
server.use(http.get(`api/plugins/${response.id}/settings`, () => HttpResponse.json(response)));
},
},
oncall: {
getOnCallIntegrations: (response: OnCallIntegrationDTO[]) => {
server.use(
rest.get(`api/plugin-proxy/grafana-oncall-app/api/internal/v1/alert_receive_channels`, (_, res, ctx) =>
res(ctx.status(200), ctx.json<OnCallIntegrationDTO[]>(response))
http.get(`api/plugin-proxy/grafana-oncall-app/api/internal/v1/alert_receive_channels`, () =>
HttpResponse.json<OnCallIntegrationDTO[]>(response)
)
);
},
features: (response: string[]) => {
server.use(
rest.get(`api/plugin-proxy/grafana-oncall-app/api/internal/v1/features`, (_, res, ctx) =>
res(ctx.status(200), ctx.json<string[]>(response))
)
http.get(`api/plugin-proxy/grafana-oncall-app/api/internal/v1/features`, () => HttpResponse.json(response))
);
},
validateIntegrationName: (invalidNames: string[]) => {
server.use(
rest.get(
http.get(
`api/plugin-proxy/grafana-oncall-app/api/internal/v1/alert_receive_channels/validate_name`,
(req, res, ctx) => {
const isValid = !invalidNames.includes(req.url.searchParams.get('verbal_name') ?? '');
return res(ctx.status(isValid ? 200 : 409), ctx.json<boolean>(isValid));
({ request }) => {
const url = new URL(request.url);
const isValid = !invalidNames.includes(url.searchParams.get('verbal_name') ?? '');
return HttpResponse.json(isValid, {
status: isValid ? 200 : 409,
});
}
)
);
},
createIntegraion: () => {
server.use(
rest.post<CreateIntegrationDTO>(
http.post<{}, CreateIntegrationDTO>(
`api/plugin-proxy/grafana-oncall-app/api/internal/v1/alert_receive_channels`,
async (req, res, ctx) => {
const body = await req.json<CreateIntegrationDTO>();
async ({ request }) => {
const body = await request.json();
const integrationId = uniqueId('oncall-integration-');
return res(
ctx.status(200),
ctx.json<NewOnCallIntegrationDTO>({
return HttpResponse.json<NewOnCallIntegrationDTO>({
id: integrationId,
integration: body.integration,
integration_url: `https://oncall-endpoint.example.com/${integrationId}`,
verbal_name: body.verbal_name,
connected_escalations_chains_count: 0,
})
);
});
}
)
);
@ -281,21 +270,15 @@ export function mockAlertRuleApi(server: SetupServer) {
return {
prometheusRuleNamespaces: (dsName: string, response: PromRulesResponse) => {
server.use(
rest.get(`api/prometheus/${dsName}/api/v1/rules`, (req, res, ctx) =>
res(ctx.status(200), ctx.json<PromRulesResponse>(response))
)
http.get(`api/prometheus/${dsName}/api/v1/rules`, () => HttpResponse.json<PromRulesResponse>(response))
);
},
rulerRules: (dsName: string, response: RulerRulesConfigDTO) => {
server.use(
rest.get(`/api/ruler/${dsName}/api/v1/rules`, (req, res, ctx) => res(ctx.status(200), ctx.json(response)))
);
server.use(http.get(`/api/ruler/${dsName}/api/v1/rules`, () => HttpResponse.json(response)));
},
rulerRuleGroup: (dsName: string, namespace: string, group: string, response: RulerRuleGroupDTO) => {
server.use(
rest.get(`/api/ruler/${dsName}/api/v1/rules/${namespace}/${group}`, (req, res, ctx) =>
res(ctx.status(200), ctx.json(response))
)
http.get(`/api/ruler/${dsName}/api/v1/rules/${namespace}/${group}`, () => HttpResponse.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
*/
discoverDsFeatures: (dsSettings: DataSourceInstanceSettings, response: PromBuildInfoResponse) => {
server.use(
rest.get(`${dsSettings.url}/api/v1/status/buildinfo`, (_, res, ctx) => res(ctx.status(200), ctx.json(response)))
);
server.use(http.get(`${dsSettings.url}/api/v1/status/buildinfo`, () => HttpResponse.json(response)));
},
};
}
@ -323,16 +304,20 @@ export function mockProvisioningApi(server: SetupServer) {
return {
exportRuleGroup: (folderUid: string, groupName: string, response: Record<string, string>) => {
server.use(
rest.get(`/api/v1/provisioning/folder/${folderUid}/rule-groups/${groupName}/export`, (req, res, ctx) =>
res(ctx.status(200), ctx.text(response[req.url.searchParams.get('format') ?? 'yaml']))
)
http.get(`/api/v1/provisioning/folder/${folderUid}/rule-groups/${groupName}/export`, ({ request }) => {
const url = new URL(request.url);
const format = url.searchParams.get('format') ?? 'yaml';
return HttpResponse.text(response[format]);
})
);
},
exportReceiver: (response: Record<string, string>) => {
server.use(
rest.get(`/api/v1/provisioning/contact-points/export/`, (req, res, ctx) =>
res(ctx.status(200), ctx.text(response[req.url.searchParams.get('format') ?? 'yaml']))
)
http.get(`/api/v1/provisioning/contact-points/export/`, ({ request }) => {
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: (ruleUid: string, response: Record<string, string>) => {
server.use(
rest.get('/api/ruler/grafana/api/v1/export/rules', (req, res, ctx) => {
if (req.url.searchParams.get('ruleUid') === ruleUid) {
return res(ctx.status(200), ctx.text(response[req.url.searchParams.get('format') ?? 'yaml']));
http.get('/api/ruler/grafana/api/v1/export/rules', ({ request }) => {
const url = new URL(request.url);
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: (folderUid: string, group: string, response: Record<string, string>) => {
server.use(
rest.get('/api/ruler/grafana/api/v1/export/rules', (req, res, ctx) => {
if (req.url.searchParams.get('folderUid') === folderUid && req.url.searchParams.get('group') === group) {
return res(ctx.status(200), ctx.text(response[req.url.searchParams.get('format') ?? 'yaml']));
http.get('/api/ruler/grafana/api/v1/export/rules', ({ request }) => {
const url = new URL(request.url);
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: (folderUid: string, response: Record<string, string>) => {
server.use(
rest.get('/api/ruler/grafana/api/v1/export/rules', (req, res, ctx) => {
if (req.url.searchParams.get('folderUid') === folderUid) {
return res(ctx.status(200), ctx.text(response[req.url.searchParams.get('format') ?? 'yaml']));
http.get('/api/ruler/grafana/api/v1/export/rules', ({ request }) => {
const url = new URL(request.url);
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>) => {
server.use(
rest.post(`/api/ruler/grafana/api/v1/rules/${namespaceUID}/export`, (req, res, ctx) => {
return res(ctx.status(200), ctx.text(response[req.url.searchParams.get('format') ?? 'yaml']));
http.post(`/api/ruler/grafana/api/v1/rules/${namespaceUID}/export`, ({ request }) => {
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) {
return {
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) {
return {
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) {
return {
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) => {
server.use(
rest.get(`/api/dashboards/uid/${response.dashboard.uid}`, (_, res, ctx) =>
res(ctx.status(200), ctx.json(response))
)
);
server.use(http.get(`/api/dashboards/uid/${response.dashboard.uid}`, () => 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/node';
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';
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) {
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 {
@ -14,7 +15,7 @@ export const defaultAlertmanagerChoiceResponse: AlertmanagersChoiceResponse = {
numExternalAlertmanagers: 0,
};
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 = {
@ -24,7 +25,7 @@ export const emptyExternalAlertmanagersResponse: 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(
@ -33,8 +34,8 @@ export function mockAlertmanagerConfigResponse(
response: AlertManagerCortexConfig
) {
server.use(
rest.get(`/api/alertmanager/${getDatasourceAPIUid(alertManagerSourceName)}/config/api/v1/alerts`, (req, res, ctx) =>
res(ctx.status(200), ctx.json(response))
http.get(`/api/alertmanager/${getDatasourceAPIUid(alertManagerSourceName)}/config/api/v1/alerts`, () =>
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 { PluginMeta } from '@grafana/data';
@ -7,8 +8,11 @@ import { SupportedPlugin } from '../types/pluginBridges';
export function mockPluginSettings(server: SetupServer, plugin: SupportedPlugin, response?: PluginMeta) {
server.use(
rest.get(`/api/plugins/${plugin}/settings`, (_req, res, ctx) => {
return response ? res(ctx.status(200), ctx.json(response)) : res(ctx.status(404));
http.get(`/api/plugins/${plugin}/settings`, () => {
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 { RulerRuleGroupDTO, RulerRulesConfigDTO } from '../../../../types/unified-alerting-dto';
export function mockRulerRulesApiResponse(server: SetupServer, rulesSourceName: string, response: RulerRulesConfigDTO) {
server.use(
rest.get(`/api/ruler/${rulesSourceName}/api/v1/rules`, (req, res, ctx) =>
res(ctx.json<RulerRulesConfigDTO>(response))
)
);
server.use(http.get(`/api/ruler/${rulesSourceName}/api/v1/rules`, () => HttpResponse.json(response)));
}
export function mockRulerRulesGroupApiResponse(
@ -19,8 +16,6 @@ export function mockRulerRulesGroupApiResponse(
response: RulerRuleGroupDTO
) {
server.use(
rest.get(`/api/ruler/${rulesSourceName}/api/v1/rules/${namespace}/${group}`, (req, res, ctx) =>
res(ctx.json(response))
)
http.get(`/api/ruler/${rulesSourceName}/api/v1/rules/${namespace}/${group}`, () => HttpResponse.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 { previewTemplateUrl, TemplatePreviewResponse } from '../api/templateApi';
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) {
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 userEvent from '@testing-library/user-event';
import { rest } from 'msw';
import { HttpResponse, http } from 'msw';
import { setupServer, SetupServer } from 'msw/node';
import React, { ComponentProps } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
@ -120,25 +120,19 @@ describe('browse-dashboards BrowseDashboardsPage', () => {
beforeAll(() => {
server = setupServer(
rest.get('/api/folders/:uid', (_, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
http.get('/api/folders/:uid', () => {
return HttpResponse.json({
title: folderA.item.title,
uid: folderA.item.uid,
})
);
});
}),
rest.get('/api/search', (_, res, ctx) => {
return res(ctx.status(200), ctx.json({}));
http.get('/api/search', () => {
return HttpResponse.json({});
}),
rest.get('/api/search/sorting', (_, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
http.get('/api/search/sorting', () => {
return HttpResponse.json({
sortOptions: [],
})
);
});
})
);
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 { rest } from 'msw';
import { http, HttpResponse } from 'msw';
import { SetupServer, setupServer } from 'msw/node';
import React from 'react';
import { TestProvider } from 'test/helpers/TestProvider';
@ -47,20 +47,17 @@ describe('browse-dashboards BrowseFolderAlertingPage', () => {
beforeAll(() => {
server = setupServer(
rest.get('/api/folders/:uid', (_, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
http.get('/api/folders/:uid', () => {
return HttpResponse.json({
title: mockFolderName,
uid: mockFolderUid,
})
);
});
}),
rest.get('api/ruler/grafana/api/v1/rules', (_, res, ctx) => {
return res(ctx.status(200), ctx.json(mockRulerRulesResponse));
http.get('api/ruler/grafana/api/v1/rules', () => {
return HttpResponse.json(mockRulerRulesResponse);
}),
rest.get('api/prometheus/grafana/api/v1/rules', (_, res, ctx) => {
return res(ctx.status(200), ctx.json(mockPrometheusRulesResponse));
http.get('api/prometheus/grafana/api/v1/rules', () => {
return HttpResponse.json(mockPrometheusRulesResponse);
})
);
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 { rest } from 'msw';
import { http, HttpResponse } from 'msw';
import { SetupServer, setupServer } from 'msw/node';
import React from 'react';
import { TestProvider } from 'test/helpers/TestProvider';
@ -47,25 +47,19 @@ describe('browse-dashboards BrowseFolderLibraryPanelsPage', () => {
beforeAll(() => {
server = setupServer(
rest.get('/api/folders/:uid', (_, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
http.get('/api/folders/:uid', () => {
return HttpResponse.json({
title: mockFolderName,
uid: mockFolderUid,
})
);
});
}),
rest.get('/api/library-elements', (_, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
http.get('/api/library-elements', () => {
return HttpResponse.json({
result: mockLibraryElementsResponse,
})
);
});
}),
rest.get('/api/search/sorting', (_, res, ctx) => {
return res(ctx.status(200), ctx.json({}));
http.get('/api/search/sorting', () => {
return HttpResponse.json({});
})
);
server.listen();

View File

@ -1,9 +1,9 @@
import 'whatwg-fetch';
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { rest } from 'msw';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import 'whatwg-fetch';
import { BootData, DataQuery } from '@grafana/data/src';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { reportInteraction, setEchoSrv } from '@grafana/runtime';
@ -87,20 +87,27 @@ afterEach(() => {
});
const getNonExistentPublicDashboardResponse = () =>
rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => {
return res(
ctx.status(404),
ctx.json({
http.get('/api/dashboards/uid/:dashboardUid/public-dashboards', () => {
return HttpResponse.json(
{
message: 'Public dashboard not found',
messageId: 'publicdashboards.notFound',
statusCode: 404,
traceID: '',
})
},
{
status: 404,
}
);
});
const getErrorPublicDashboardResponse = () =>
rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => {
return res(ctx.status(500));
http.get('/api/dashboards/uid/:dashboardUid/public-dashboards', () => {
return HttpResponse.json(
{},
{
status: 500,
}
);
});
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 () => {
server.use(
rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
http.get('/api/dashboards/uid/:dashboardUid/public-dashboards', () => {
return HttpResponse.json({
...pubdashResponse,
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 () => {
server.use(
rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
http.get('/api/dashboards/uid/:dashboardUid/public-dashboards', ({ request }) => {
const url = new URL(request.url);
const dashboardUid = url.searchParams.get('dashboardUid');
return HttpResponse.json({
isEnabled: false,
annotationsEnabled: false,
uid: 'a-uid',
dashboardUid: req.params.dashboardUid,
dashboardUid,
accessToken: 'an-access-token',
})
);
});
})
);
@ -339,15 +342,14 @@ describe('SharePublic - Report interactions', () => {
jest.clearAllMocks();
server.use(getExistentPublicDashboardResponse());
server.use(
rest.patch('/api/dashboards/uid/:dashboardUid/public-dashboards/:uid', (req, res, ctx) =>
res(
ctx.status(200),
ctx.json({
http.patch('/api/dashboards/uid/:dashboardUid/public-dashboards/:uid', ({ request }) => {
const url = new URL(request.url);
const dashboardUid = url.searchParams.get('dashboardUid');
return HttpResponse.json({
...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 { rest } from 'msw';
import { http, HttpResponse } from 'msw';
import React from 'react';
import { Provider } from 'react-redux';
@ -32,15 +33,14 @@ export const pubdashResponse: sharePublicDashboardUtils.PublicDashboard = {
};
export const getExistentPublicDashboardResponse = (publicDashboard?: Partial<PublicDashboard>) =>
rest.get('/api/dashboards/uid/:dashboardUid/public-dashboards', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
http.get('/api/dashboards/uid/:dashboardUid/public-dashboards', ({ request }) => {
const url = new URL(request.url);
const dashboardUid = url.searchParams.get('dashboardUid');
return HttpResponse.json({
...pubdashResponse,
...publicDashboard,
dashboardUid: req.params.dashboardUid,
})
);
dashboardUid,
});
});
export const renderSharePublicDashboard = async (

View File

@ -1,8 +1,8 @@
import 'whatwg-fetch';
import { render, screen, waitForElementToBeRemoved, within } from '@testing-library/react';
import { rest } from 'msw';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import React from 'react';
import 'whatwg-fetch';
import { BrowserRouter } from 'react-router-dom';
import { TestProvider } from 'test/helpers/TestProvider';
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
@ -60,10 +60,13 @@ const paginationResponse: Omit<PublicDashboardListWithPaginationResponse, 'publi
};
const server = setupServer(
rest.get('/api/dashboards/public-dashboards', (_, res, ctx) =>
res(ctx.status(200), ctx.json({ ...paginationResponse, publicDashboards: publicDashboardListResponse }))
http.get('/api/dashboards/public-dashboards', () =>
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', () => ({
@ -122,8 +125,8 @@ describe('Show table', () => {
};
server.use(
rest.get('/api/dashboards/public-dashboards', (req, res, ctx) => {
return res(ctx.status(200), ctx.json(emptyListRS));
http.get('/api/dashboards/public-dashboards', () => {
return HttpResponse.json(emptyListRS);
})
);
@ -171,8 +174,8 @@ describe('Orphaned public dashboard', () => {
publicDashboards: [...publicDashboardListResponse, ...orphanedDashboardListResponse],
};
server.use(
rest.get('/api/dashboards/public-dashboards', (req, res, ctx) => {
return res(ctx.status(200), ctx.json(response));
http.get('/api/dashboards/public-dashboards', () => {
return HttpResponse.json(response);
})
);
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';
export const mockSystemModule = `System.register(['./dependencyA'], function (_export, _context) {
@ -85,38 +86,104 @@ export const mockModuleWithDefineMethod = `ace.define(function() {
});`;
const server = setupServer(
rest.get('/public/plugins/mockAmdModule/module.js', async (_req, res, ctx) =>
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModule))
http.get(
'/public/plugins/mockAmdModule/module.js',
() =>
new HttpResponse(mockAmdModule, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
rest.get('/public/plugins/mockSystemModule/module.js', async (_req, res, ctx) =>
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockSystemModule))
http.get(
'/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) =>
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModule))
http.get(
'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) =>
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModuleNamedNoDeps))
http.get(
'/public/plugins/mockAmdModuleNamedNoDeps/module.js',
() =>
new HttpResponse(mockAmdModuleNamedNoDeps, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
rest.get('/public/plugins/mockAmdModuleNamedWithDeps/module.js', async (_req, res, ctx) =>
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModuleNamedWithDeps))
http.get(
'/public/plugins/mockAmdModuleNamedWithDeps/module.js',
() =>
new HttpResponse(mockAmdModuleNamedWithDeps, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
rest.get('/public/plugins/mockAmdModuleNamedWithDeps2/module.js', async (_req, res, ctx) =>
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModuleNamedWithDeps2))
http.get(
'/public/plugins/mockAmdModuleNamedWithDeps2/module.js',
() =>
new HttpResponse(mockAmdModuleNamedWithDeps2, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
rest.get('/public/plugins/mockAmdModuleNamedWithDeps3/module.js', async (_req, res, ctx) =>
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModuleNamedWithDeps3))
http.get(
'/public/plugins/mockAmdModuleNamedWithDeps3/module.js',
() =>
new HttpResponse(mockAmdModuleNamedWithDeps3, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
rest.get('/public/plugins/mockAmdModuleOnlyFunction/module.js', async (_req, res, ctx) =>
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModuleOnlyFunction))
http.get(
'/public/plugins/mockAmdModuleOnlyFunction/module.js',
() =>
new HttpResponse(mockAmdModuleOnlyFunction, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
rest.get('/public/plugins/mockAmdModuleWithComments/module.js', async (_req, res, ctx) =>
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModuleWithComments))
http.get(
'/public/plugins/mockAmdModuleWithComments/module.js',
() =>
new HttpResponse(mockAmdModuleWithComments, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
rest.get('/public/plugins/mockAmdModuleWithComments2/module.js', async (_req, res, ctx) =>
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockAmdModuleWithComments2))
http.get(
'/public/plugins/mockAmdModuleWithComments2/module.js',
() =>
new HttpResponse(mockAmdModuleWithComments2, {
headers: {
'Content-Type': 'text/javascript',
},
})
),
rest.get('/public/plugins/mockModuleWithDefineMethod/module.js', async (_req, res, ctx) =>
res(ctx.status(200), ctx.set('Content-Type', 'text/javascript'), ctx.body(mockModuleWithDefineMethod))
http.get(
'/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 originalResolve = systemJSPrototype.resolve;
systemJSPrototype.resolve = (moduleId: string) => moduleId;
systemJSPrototype.shouldFetch = () => true;
beforeAll(() => {
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());
afterAll(() => {
@ -43,7 +45,11 @@ describe('SystemJS Loader Hooks', () => {
describe('decorateSystemJSFetch', () => {
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 basicExpected = `(function(define) {
${mockAmdModule}
@ -51,7 +57,7 @@ describe('SystemJS Loader Hooks', () => {
expect(basicSource).toBe(basicExpected);
const mockAmdModuleNamedNoDepsResult = await decorateSystemJSFetch(
originalFetch,
systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleNamedNoDeps/module.js',
{}
);
@ -63,7 +69,7 @@ describe('SystemJS Loader Hooks', () => {
expect(mockAmdModuleNamedNoDepsSource).toBe(mockAmdModuleNamedNoDepsExpected);
const mockAmdModuleNamedWithDepsResult = await decorateSystemJSFetch(
originalFetch,
systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleNamedWithDeps/module.js',
{}
);
@ -75,7 +81,7 @@ describe('SystemJS Loader Hooks', () => {
expect(mockAmdModuleNamedWithDepsSource).toBe(mockAmdModuleNamedWithDepsExpected);
const mockAmdModuleNamedWithDeps2Result = await decorateSystemJSFetch(
originalFetch,
systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleNamedWithDeps2/module.js',
{}
);
@ -87,7 +93,7 @@ describe('SystemJS Loader Hooks', () => {
expect(mockAmdModuleNamedWithDeps2Source).toBe(mockAmdModuleNamedWithDeps2Expected);
const mockAmdModuleNamedWithDeps3Result = await decorateSystemJSFetch(
originalFetch,
systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleNamedWithDeps3/module.js',
{}
);
@ -99,7 +105,7 @@ describe('SystemJS Loader Hooks', () => {
expect(mockAmdModuleNamedWithDeps3Source).toBe(mockAmdModuleNamedWithDeps3Expected);
const mockAmdModuleOnlyFunctionResult = await decorateSystemJSFetch(
originalFetch,
systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleOnlyFunction/module.js',
{}
);
@ -111,7 +117,7 @@ describe('SystemJS Loader Hooks', () => {
expect(mockAmdModuleOnlyFunctionSource).toBe(mockAmdModuleOnlyFunctionExpected);
const mockAmdModuleWithCommentsResult = await decorateSystemJSFetch(
originalFetch,
systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleWithComments/module.js',
{}
);
@ -123,7 +129,7 @@ describe('SystemJS Loader Hooks', () => {
expect(mockAmdModuleWithCommentsSource).toBe(mockAmdModuleWithCommentsExpected);
const mockAmdModuleWithComments2Result = await decorateSystemJSFetch(
originalFetch,
systemJSPrototype.fetch,
'/public/plugins/mockAmdModuleWithComments2/module.js',
{}
);
@ -136,14 +142,14 @@ describe('SystemJS Loader Hooks', () => {
});
it("doesn't wrap system modules in an AMD iife", async () => {
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();
expect(source).toBe(mockSystemModule);
});
it("doesn't wrap modules with a define method in an AMD iife", async () => {
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();
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 () => {
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 cdnResult = await decorateSystemJSFetch(originalFetch, cdnUrl, {});
const cdnResult = await decorateSystemJSFetch(systemJSPrototype.fetch, cdnUrl, {});
const cdnSource = await cdnResult.text();
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 result = await decorateSystemJSFetch(originalFetch, url, {});
const result = await decorateSystemJSFetch(systemJSPrototype.fetch, url, {});
const source = await result.text();
expect(source).toContain('var pluginPath = "/public/plugins/";');
});

View File

@ -27,6 +27,23 @@ module.exports = (path, options) => {
delete pkg['exports'];
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;
},
});

245
yarn.lock
View File

@ -1865,6 +1865,24 @@ __metadata:
languageName: node
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":
version: 1.5.0
resolution: "@colors/colors@npm:1.5.0"
@ -4298,6 +4316,45 @@ __metadata:
languageName: node
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":
version: 3.5.1
resolution: "@internationalized/date@npm:3.5.1"
@ -5134,6 +5191,13 @@ __metadata:
languageName: node
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":
version: 0.17.10
resolution: "@mswjs/interceptors@npm:0.17.10"
@ -5150,6 +5214,20 @@ __metadata:
languageName: node
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":
version: 3.0.7
resolution: "@ndelangen/get-tarball@npm:3.0.7"
@ -5588,6 +5666,23 @@ __metadata:
languageName: node
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":
version: 1.0.3
resolution: "@open-draft/until@npm:1.0.3"
@ -5595,6 +5690,13 @@ __metadata:
languageName: node
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":
version: 0.45.1
resolution: "@opentelemetry/api-logs@npm:0.45.1"
@ -8581,6 +8683,13 @@ __metadata:
languageName: node
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":
version: 6.0.3
resolution: "@types/cross-spawn@npm:6.0.3"
@ -9363,6 +9472,15 @@ __metadata:
languageName: node
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":
version: 2.6.4
resolution: "@types/node-fetch@npm:2.6.4"
@ -9382,7 +9500,7 @@ __metadata:
languageName: node
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
resolution: "@types/node@npm:20.11.17"
dependencies:
@ -9808,6 +9926,13 @@ __metadata:
languageName: node
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":
version: 1.1.3
resolution: "@types/string-hash@npm:1.1.3"
@ -9912,6 +10037,13 @@ __metadata:
languageName: node
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":
version: 8.5.5
resolution: "@types/ws@npm:8.5.5"
@ -11114,7 +11246,7 @@ __metadata:
languageName: node
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
resolution: "ansi-escapes@npm:4.3.2"
dependencies:
@ -12814,6 +12946,13 @@ __metadata:
languageName: node
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":
version: 0.6.3
resolution: "cli-table3@npm:0.6.3"
@ -12844,6 +12983,13 @@ __metadata:
languageName: node
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":
version: 7.0.4
resolution: "cliui@npm:7.0.4"
@ -13421,6 +13567,13 @@ __metadata:
languageName: node
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":
version: 3.3.1
resolution: "copy-to-clipboard@npm:3.3.1"
@ -18231,7 +18384,7 @@ __metadata:
mousetrap: "npm:1.6.5"
mousetrap-global-bind: "npm:1.1.0"
moveable: "npm:0.53.0"
msw: "npm:1.3.2"
msw: "npm:2.2.0"
mutationobserver-shim: "npm:0.3.7"
nanoid: "npm:^5.0.4"
ngtemplate-loader: "npm:2.1.0"
@ -18564,6 +18717,13 @@ __metadata:
languageName: node
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":
version: 0.1.10
resolution: "heimdalljs-logger@npm:0.1.10"
@ -22717,6 +22877,38 @@ __metadata:
languageName: node
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":
version: 7.2.4
resolution: "multicast-dns@npm:7.2.4"
@ -22756,7 +22948,7 @@ __metadata:
languageName: node
linkType: hard
"mute-stream@npm:~1.0.0":
"mute-stream@npm:^1.0.0, mute-stream@npm:~1.0.0":
version: 1.0.0
resolution: "mute-stream@npm:1.0.0"
checksum: 10/36fc968b0e9c9c63029d4f9dc63911950a3bdf55c9a87f58d3a266289b67180201cade911e7699f8b2fa596b34c9db43dad37649e3f7fdd13c3bb9edb0017ee7
@ -23607,10 +23799,10 @@ __metadata:
languageName: node
linkType: hard
"outvariant@npm:^1.2.1, outvariant@npm:^1.4.0":
version: 1.4.0
resolution: "outvariant@npm:1.4.0"
checksum: 10/07b9bcb9b3a2ff1b3db02af6b07d70e663082b30ddc08ff475d7c85fc623fdcc4433a4ab5b88f6902b62dbb284eef1be386aa537e14cef0519fad887ec483054
"outvariant@npm:^1.2.1, outvariant@npm:^1.4.0, outvariant@npm:^1.4.2":
version: 1.4.2
resolution: "outvariant@npm:1.4.2"
checksum: 10/f16ba035fb65d1cbe7d2e06693dd42183c46bc8456713d9ddb5182d067defa7d78217edab0a2d3e173d3bacd627b2bd692195c7087c225b82548fbf52c677b38
languageName: node
linkType: hard
@ -27278,6 +27470,13 @@ __metadata:
languageName: node
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":
version: 1.2.0
resolution: "run-parallel@npm:1.2.0"
@ -27732,6 +27931,13 @@ __metadata:
languageName: node
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":
version: 1.9.0
resolution: "sigstore@npm:1.9.0"
@ -28376,6 +28582,13 @@ __metadata:
languageName: node
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":
version: 1.0.0
resolution: "stop-iteration-iterator@npm:1.0.0"
@ -28518,6 +28731,13 @@ __metadata:
languageName: node
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":
version: 1.1.3
resolution: "string-hash@npm:1.1.3"
@ -29819,6 +30039,13 @@ __metadata:
languageName: node
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":
version: 1.6.18
resolution: "type-is@npm:1.6.18"
@ -31485,7 +31712,7 @@ __metadata:
languageName: node
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
resolution: "yargs@npm:17.7.2"
dependencies: