From fc958d75f529530509f15da0e6c339fb00a067e9 Mon Sep 17 00:00:00 2001 From: Kyle Watson Date: Thu, 16 Nov 2023 11:27:03 +0100 Subject: [PATCH] test(backstage-plugin): Add msw and refactor tests Add msw to mock backend and avoid mocking the service. Simplify some test cases by checking for only the needed elements. Add error handling to the data service. Addresses #71 --- .../plugins/open-dora/package.json | 13 +- .../DashboardComponent.test.tsx | 318 +++++++----------- .../plugins/open-dora/src/jest.polyfills.js | 30 ++ .../plugins/open-dora/src/mswHandlers.ts | 10 + .../src/services/GroupDataService.ts | 16 +- .../plugins/open-dora/src/setupTests.ts | 8 + backstage-plugin/yarn.lock | 203 +++++------ 7 files changed, 306 insertions(+), 292 deletions(-) create mode 100644 backstage-plugin/plugins/open-dora/src/jest.polyfills.js create mode 100644 backstage-plugin/plugins/open-dora/src/mswHandlers.ts diff --git a/backstage-plugin/plugins/open-dora/package.json b/backstage-plugin/plugins/open-dora/package.json index b1d1f87..7e3944f 100644 --- a/backstage-plugin/plugins/open-dora/package.json +++ b/backstage-plugin/plugins/open-dora/package.json @@ -49,7 +49,8 @@ "@testing-library/user-event": "^14.0.0", "@types/node": "*", "cross-fetch": "^3.1.5", - "msw": "^1.0.0" + "msw": "^2.0.6", + "undici": "^5.27.2" }, "files": [ "dist", @@ -64,6 +65,14 @@ "lines": 100, "statements": 100 } - } + }, + "testEnvironmentOptions": { + "customExportConditions": [ + "" + ] + }, + "setupFiles": [ + "./jest.polyfills.js" + ] } } diff --git a/backstage-plugin/plugins/open-dora/src/components/DashboardComponent/DashboardComponent.test.tsx b/backstage-plugin/plugins/open-dora/src/components/DashboardComponent/DashboardComponent.test.tsx index 96f5776..d54af70 100644 --- a/backstage-plugin/plugins/open-dora/src/components/DashboardComponent/DashboardComponent.test.tsx +++ b/backstage-plugin/plugins/open-dora/src/components/DashboardComponent/DashboardComponent.test.tsx @@ -1,38 +1,32 @@ +import type { EntityRelation } from '@backstage/catalog-model'; +import { ApiProvider } from '@backstage/core-app-api'; +import { EntityProvider } from '@backstage/plugin-catalog-react'; +import { + MockConfigApi, + renderInTestApp, + TestApiRegistry, +} from '@backstage/test-utils'; +import { act, fireEvent, screen } from '@testing-library/react'; +import { delay, http, HttpResponse } from 'msw'; import React from 'react'; +import { + GroupDataService, + groupDataServiceApiRef, +} from '../../services/GroupDataService'; +import { server } from '../../setupTests'; import { DashboardComponent, EntityDashboardComponent, } from './DashboardComponent'; -import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; -import { groupDataServiceApiRef } from '../../services/GroupDataService'; -import { ApiProvider } from '@backstage/core-app-api'; -import { fireEvent, screen, act } from '@testing-library/react'; -import { MetricData } from '../../models/MetricData'; -import { EntityProvider } from '@backstage/plugin-catalog-react'; -import type { EntityRelation } from '@backstage/catalog-model'; -async function renderComponentWithApis( - component: JSX.Element, - mockData?: jest.Mock, -) { - const groupDataApiMock = { - retrieveMetricDataPoints: - mockData ?? - jest - .fn() - .mockResolvedValueOnce({ - aggregation: 'weekly', - dataPoints: [{ key: '10/23', value: 1.0 }], - }) - .mockResolvedValueOnce({ - aggregation: 'weekly', - dataPoints: [{ key: '11/23', value: 2.0 }], - }), - }; +async function renderComponentWithApis(component: JSX.Element) { + const mockConfig = new MockConfigApi({ + 'open-dora': { apiBaseUrl: 'http://localhost:10666' }, + }); const apiRegistry = TestApiRegistry.from([ groupDataServiceApiRef, - groupDataApiMock, + new GroupDataService({ configApi: mockConfig }), ]); return await renderInTestApp( @@ -41,8 +35,8 @@ async function renderComponentWithApis( } describe('DashboardComponent', () => { - function renderDashboardComponent(mockData?: jest.Mock) { - return renderComponentWithApis(, mockData); + function renderDashboardComponent() { + return renderComponentWithApis(); } it('should show a dropdown with the aggregation choices', async () => { @@ -62,172 +56,143 @@ describe('DashboardComponent', () => { expect(queryByText('OpenDORA (by Devoteam)')).not.toBeNull(); }); - it('should show a graph for deployment frequency data', async () => { - const { queryByText } = await renderDashboardComponent( - jest - .fn() - .mockResolvedValueOnce({ - aggregation: 'weekly', - dataPoints: [ - { key: 'count_first_key', value: 1.0 }, - { key: 'count_second_key', value: 1.0 }, - { key: 'count_third_key', value: 1.0 }, - ], - }) - .mockResolvedValueOnce({ + it('should show graphs for deployment frequency data', async () => { + server.use( + http.get('http://localhost:10666/dora/api/metric', ({ request }) => { + const type = new URL(request.url).searchParams.get('type'); + return HttpResponse.json({ aggregation: 'weekly', dataPoints: [ - { key: 'average_first_key', value: 2.0 }, - { key: 'average_second_key', value: 2.0 }, - { key: 'average_third_key', value: 2.0 }, + { key: `${type}_first_key`, value: 1.0 }, + { key: `${type}_second_key`, value: 1.0 }, + { key: `${type}_third_key`, value: 1.0 }, ], - }), + }); + }), ); + const { queryByText } = await renderDashboardComponent(); expect(queryByText('Deployment Frequency')).not.toBeNull(); - expect(queryByText('count_first_key')).not.toBeNull(); - expect(queryByText('count_second_key')).not.toBeNull(); - expect(queryByText('count_third_key')).not.toBeNull(); + expect(queryByText('df_count_first_key')).not.toBeNull(); + expect(queryByText('df_count_second_key')).not.toBeNull(); + expect(queryByText('df_count_third_key')).not.toBeNull(); expect(queryByText('Deployment Frequency Average')).not.toBeNull(); - expect(queryByText('average_first_key')).not.toBeNull(); - expect(queryByText('average_second_key')).not.toBeNull(); - expect(queryByText('average_third_key')).not.toBeNull(); + expect(queryByText('df_average_first_key')).not.toBeNull(); + expect(queryByText('df_average_second_key')).not.toBeNull(); + expect(queryByText('df_average_third_key')).not.toBeNull(); }); it('should retrieve new data when the aggregation is changed', async () => { - const apiMock = jest - .fn() - .mockResolvedValueOnce({ - aggregation: 'weekly', - dataPoints: [ - { key: 'count_first_key', value: 1.0 }, - { key: 'count_second_key', value: 1.0 }, - { key: 'count_third_key', value: 1.0 }, - ], - }) - .mockResolvedValueOnce({ - aggregation: 'weekly', - dataPoints: [ - { key: 'average_first_key', value: 2.0 }, - { key: 'average_second_key', value: 2.0 }, - { key: 'average_third_key', value: 2.0 }, - ], - }) - .mockResolvedValueOnce({ - aggregation: 'monthly', - dataPoints: [ - { key: 'count_new_first_key', value: 1.0 }, - { key: 'count_new_second_key', value: 1.0 }, - { key: 'count_new_third_key', value: 1.0 }, - ], - }) - .mockResolvedValueOnce({ - aggregation: 'monthly', - dataPoints: [ - { key: 'average_new_first_key', value: 2.0 }, - { key: 'average_new_second_key', value: 2.0 }, - { key: 'average_new_third_key', value: 2.0 }, - ], - }); - const { queryByText, getByText } = await renderDashboardComponent(apiMock); - - expect(apiMock).toHaveBeenCalledTimes(2); - expect(apiMock).toHaveBeenCalledWith({ - type: 'df_count', - aggregation: 'weekly', - }); - expect(apiMock).toHaveBeenLastCalledWith({ - type: 'df_average', - aggregation: 'weekly', - }); + server.use( + http.get('http://localhost:10666/dora/api/metric', ({ request }) => { + const params = new URL(request.url).searchParams; + const type = params.get('type'); + const aggregation = params.get('aggregation'); + + return HttpResponse.json({ + aggregation: aggregation, + dataPoints: [{ key: `${aggregation}_${type}_first_key`, value: 1.0 }], + }); + }), + ); + const { queryByText, getByText } = await renderDashboardComponent(); - expect(queryByText('count_first_key')).not.toBeNull(); - expect(queryByText('count_second_key')).not.toBeNull(); - expect(queryByText('count_third_key')).not.toBeNull(); + expect(queryByText('weekly_df_count_first_key')).not.toBeNull(); + expect(queryByText('weekly_df_average_first_key')).not.toBeNull(); + + expect(queryByText('monthly_df_count_first_key')).toBeNull(); + expect(queryByText('monthly_df_average_first_key')).toBeNull(); fireEvent.mouseDown(getByText('Weekly')); await act(async () => { fireEvent.click(screen.getByText('Monthly')); }); - expect(apiMock).toHaveBeenCalledTimes(4); - expect(apiMock).toHaveBeenCalledWith({ - type: 'df_count', - aggregation: 'monthly', - }); - expect(apiMock).toHaveBeenLastCalledWith({ - type: 'df_average', - aggregation: 'monthly', - }); - - expect(queryByText('count_first_key')).toBeNull(); - expect(queryByText('count_second_key')).toBeNull(); - expect(queryByText('count_third_key')).toBeNull(); - expect(queryByText('count_new_first_key')).not.toBeNull(); - expect(queryByText('count_new_second_key')).not.toBeNull(); - expect(queryByText('count_new_third_key')).not.toBeNull(); + expect(queryByText('weekly_df_count_first_key')).toBeNull(); + expect(queryByText('weekly_df_average_first_key')).toBeNull(); - expect(queryByText('average_first_key')).toBeNull(); - expect(queryByText('average_second_key')).toBeNull(); - expect(queryByText('average_third_key')).toBeNull(); - - expect(queryByText('average_new_first_key')).not.toBeNull(); - expect(queryByText('average_new_second_key')).not.toBeNull(); - expect(queryByText('average_new_third_key')).not.toBeNull(); + expect(queryByText('monthly_df_count_first_key')).not.toBeNull(); + expect(queryByText('monthly_df_average_first_key')).not.toBeNull(); }); it('should show loading indicator when waiting on data to return', async () => { - jest.useFakeTimers(); - - const apiMock = jest - .fn() - .mockImplementationOnce(() => { - return new Promise(resolve => { - resolve({ - aggregation: 'monthly', - dataPoints: [{ key: 'count_data_key', value: 1.0 }], - }); - }); - }) - .mockImplementationOnce(() => { - return new Promise(resolve => { - setTimeout(() => { - resolve({ - aggregation: 'monthly', - dataPoints: [{ key: 'average_data_key', value: 1.0 }], - }); - }, 1000); + jest.useFakeTimers({ + legacyFakeTimers: true, + }); + + server.use( + http.get('http://localhost:10666/dora/api/metric', async () => { + await delay(10000); + + return HttpResponse.json({ + aggregation: 'weekly', + dataPoints: [{ key: `first_key`, value: 1.0 }], }); - }); - const { queryByText, queryByRole, findByRole } = - await renderDashboardComponent(apiMock); + }), + ); - expect(await findByRole('progressbar')).not.toBeNull(); - expect(queryByText('average_data_key')).toBeNull(); + const { queryByText, queryByRole, findAllByRole, queryAllByText } = + await renderDashboardComponent(); + + expect(await findAllByRole('progressbar')).toHaveLength(2); + expect(await queryByText('first_key')).toBeNull(); await act(async () => { jest.runAllTimers(); }); expect(queryByRole('progressbar')).toBeNull(); - expect(queryByText('average_data_key')).not.toBeNull(); + expect(queryAllByText('first_key')).toHaveLength(2); }); it('should show the error returned from the service', async () => { - const { queryAllByText } = await renderDashboardComponent( - jest.fn().mockRejectedValue({ status: 500, message: 'server error' }), + server.use( + http.get('http://localhost:10666/dora/api/metric', () => { + return new HttpResponse(null, { status: 401 }); + }), + ); + const { queryAllByText, getByText } = await renderDashboardComponent(); + expect(queryAllByText('Error: Unauthorized')).toHaveLength(2); + + server.use( + http.get('http://localhost:10666/dora/api/metric', () => { + return HttpResponse.error(); + }), ); - expect(queryAllByText('server error')).not.toBeNull(); - expect(queryAllByText('server error')).toHaveLength(2); + + // Trigger another request + fireEvent.mouseDown(getByText('Weekly')); + await act(async () => { + fireEvent.click(screen.getByText('Monthly')); + }); + + expect(queryAllByText('Error: Failed to fetch')).toHaveLength(2); }); }); describe('EntityDashboardComponent', () => { - function renderEntityDashboardComponent( - mockData?: jest.Mock, - relations?: EntityRelation[], - ) { + function renderEntityDashboardComponent(relations?: EntityRelation[]) { + server.use( + http.get('http://localhost:10666/dora/api/metric', ({ request }) => { + const params = new URL(request.url).searchParams; + const type = params.get('type'); + const aggregation = params.get('aggregation'); + const project = params.get('project'); + const team = params.get('team'); + + return HttpResponse.json({ + aggregation: aggregation, + dataPoints: [ + { + key: `${project}_${team}_${aggregation}_${type}_first_key`, + value: 1.0, + }, + ], + }); + }), + ); + return renderComponentWithApis( { > , - mockData, ); } it('should send component info to the service from the context', async () => { - const apiMock = jest.fn().mockResolvedValue({ - aggregation: 'weekly', - dataPoints: [{ key: 'first_key', value: 1.0 }], - }); - - await renderEntityDashboardComponent(apiMock, [ + const { queryByText } = await renderEntityDashboardComponent([ { targetRef: 'kind:namespace/owner-name', type: 'ownedBy' }, ]); - expect(apiMock).toHaveBeenCalledTimes(2); - - expect(apiMock).toHaveBeenLastCalledWith({ - type: 'df_average', - aggregation: 'weekly', - project: 'entity-name', - team: 'owner-name', - }); + expect( + queryByText('entity-name_owner-name_weekly_df_average_first_key'), + ).not.toBeNull(); }); it('should send component info without owner info', async () => { - const apiMock = jest.fn().mockResolvedValue({ - aggregation: 'weekly', - dataPoints: [{ key: 'first_key', value: 1.0 }], - }); - - await renderEntityDashboardComponent(apiMock); + const { queryByText } = await renderEntityDashboardComponent(); - expect(apiMock).toHaveBeenCalledTimes(2); - expect(apiMock).toHaveBeenCalledWith({ - type: 'df_count', - aggregation: 'weekly', - project: 'entity-name', - }); - - expect(apiMock).toHaveBeenLastCalledWith({ - type: 'df_average', - aggregation: 'weekly', - project: 'entity-name', - }); + expect( + queryByText('entity-name_null_weekly_df_average_first_key'), + ).not.toBeNull(); }); }); diff --git a/backstage-plugin/plugins/open-dora/src/jest.polyfills.js b/backstage-plugin/plugins/open-dora/src/jest.polyfills.js new file mode 100644 index 0000000..9598a44 --- /dev/null +++ b/backstage-plugin/plugins/open-dora/src/jest.polyfills.js @@ -0,0 +1,30 @@ +// jest.polyfills.js +/** + * @note The block below contains polyfills for Node.js globals + * required for Jest to function when running JSDOM tests. + * These HAVE to be require's and HAVE to be in this exact + * order, since "undici" depends on the "TextEncoder" global API. + * + * Consider migrating to a more modern test runner if + * you don't want to deal with this. + */ + +const { TextDecoder, TextEncoder } = require('node:util'); + +Object.defineProperties(globalThis, { + TextDecoder: { value: TextDecoder }, + TextEncoder: { value: TextEncoder }, +}); + +const { Blob, File } = require('node:buffer'); +const { fetch, Headers, FormData, Request, Response } = require('undici'); + +Object.defineProperties(globalThis, { + fetch: { value: fetch, writable: true }, + Blob: { value: Blob }, + File: { value: File }, + Headers: { value: Headers }, + FormData: { value: FormData }, + Request: { value: Request }, + Response: { value: Response }, +}); diff --git a/backstage-plugin/plugins/open-dora/src/mswHandlers.ts b/backstage-plugin/plugins/open-dora/src/mswHandlers.ts new file mode 100644 index 0000000..0847419 --- /dev/null +++ b/backstage-plugin/plugins/open-dora/src/mswHandlers.ts @@ -0,0 +1,10 @@ +import { http, HttpResponse } from 'msw'; + +export const handlers = [ + http.get('http://localhost:10666/dora/api/metric', () => { + return HttpResponse.json({ + aggregation: 'weekly', + dataPoints: [{ key: '10/23', value: 1.0 }], + }); + }), +]; diff --git a/backstage-plugin/plugins/open-dora/src/services/GroupDataService.ts b/backstage-plugin/plugins/open-dora/src/services/GroupDataService.ts index cdef898..d680c72 100644 --- a/backstage-plugin/plugins/open-dora/src/services/GroupDataService.ts +++ b/backstage-plugin/plugins/open-dora/src/services/GroupDataService.ts @@ -17,6 +17,7 @@ export class GroupDataService { aggregation?: string; }) { const baseUrl = this.options.configApi.getString('open-dora.apiBaseUrl'); + const url = new URL(baseUrl); url.pathname = 'dora/api/metric'; for (const [key, value] of Object.entries(params)) { @@ -27,6 +28,19 @@ export class GroupDataService { const data = await fetch(url.toString(), { method: 'GET', }); - return (await data.json()) as MetricData; + + if (!data.ok) { + throw new Error(data.statusText); + } + + const response = await data.json(); + if ( + response.aggregation === undefined || + response.dataPoints === undefined + ) { + throw new Error('Unexpected response'); + } + + return response as MetricData; } } diff --git a/backstage-plugin/plugins/open-dora/src/setupTests.ts b/backstage-plugin/plugins/open-dora/src/setupTests.ts index 48c09b5..2ac8f20 100644 --- a/backstage-plugin/plugins/open-dora/src/setupTests.ts +++ b/backstage-plugin/plugins/open-dora/src/setupTests.ts @@ -1,2 +1,10 @@ import '@testing-library/jest-dom'; import 'cross-fetch/polyfill'; +import { setupServer } from 'msw/node'; +import { handlers } from './mswHandlers'; + +export const server = setupServer(...handlers); + +beforeAll(() => server.listen()); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); diff --git a/backstage-plugin/yarn.lock b/backstage-plugin/yarn.lock index 4416a39..6847da0 100644 --- a/backstage-plugin/yarn.lock +++ b/backstage-plugin/yarn.lock @@ -1641,6 +1641,27 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@bundled-es-modules/cookie@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz#c3b82703969a61cf6a46e959a012b2c257f6b164" + integrity sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw== + dependencies: + cookie "^0.5.0" + +"@bundled-es-modules/js-levenshtein@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@bundled-es-modules/js-levenshtein/-/js-levenshtein-2.0.1.tgz#b02bbbd546358ab77080a430f0911cfc2b3779c4" + integrity sha512-DERMS3yfbAljKsQc0U2wcqGKUWpdFjwqWuoMugEJlqBnKO180/n+4SR/J8MRDt1AN48X1ovgoD9KrdVXcaa3Rg== + dependencies: + js-levenshtein "^1.1.6" + +"@bundled-es-modules/statuses@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz#761d10f44e51a94902c4da48675b71a76cc98872" + integrity sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg== + dependencies: + statuses "^2.0.1" + "@changesets/types@^4.0.1": version "4.1.0" resolved "https://registry.yarnpkg.com/@changesets/types/-/types-4.1.0.tgz#fb8f7ca2324fd54954824e864f9a61a82cb78fe0" @@ -2286,6 +2307,11 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.37.0.tgz#cf1b5fa24217fe007f6487a26d765274925efa7d" integrity sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A== +"@fastify/busboy@^2.0.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.0.tgz#0709e9f4cb252351c609c6e6d8d6779a8d25edff" + integrity sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA== + "@floating-ui/core@^1.4.1": version "1.4.1" resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.4.1.tgz#0d633f4b76052668afb932492ac452f7ebe97f17" @@ -3450,27 +3476,22 @@ prop-types "^15.7.2" react-is "^16.8.0 || ^17.0.0" -"@mswjs/cookies@^0.2.2": - version "0.2.2" - resolved "https://registry.yarnpkg.com/@mswjs/cookies/-/cookies-0.2.2.tgz#b4e207bf6989e5d5427539c2443380a33ebb922b" - integrity sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g== - dependencies: - "@types/set-cookie-parser" "^2.4.0" - set-cookie-parser "^2.4.6" +"@mswjs/cookies@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@mswjs/cookies/-/cookies-1.1.0.tgz#1528eb43630caf83a1d75d5332b30e75e9bb1b5b" + integrity sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw== -"@mswjs/interceptors@^0.17.5": - version "0.17.9" - resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.17.9.tgz#0096fc88fea63ee42e36836acae8f4ae33651c04" - integrity sha512-4LVGt03RobMH/7ZrbHqRxQrS9cc2uh+iNKSj8UWr8M26A2i793ju+csaB5zaqYltqJmA2jUq4VeYfKmVqvsXQg== +"@mswjs/interceptors@^0.25.11": + version "0.25.11" + resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.25.11.tgz#65014cd0951cc4c6916f52d80bdfefe439a62b21" + integrity sha512-27aonWAjdeoZN4j4j6QvePOSOacQUucFRUESAU8FUXsmmagDjmyOi4/NHizxzY/NHSk/HAyqF/IMhl+9puhqNw== dependencies: - "@open-draft/until" "^1.0.3" - "@types/debug" "^4.1.7" - "@xmldom/xmldom" "^0.8.3" - debug "^4.3.3" - headers-polyfill "^3.1.0" + "@open-draft/deferred-promise" "^2.2.0" + "@open-draft/logger" "^0.3.0" + "@open-draft/until" "^2.0.0" + is-node-process "^1.2.0" outvariant "^1.2.1" - strict-event-emitter "^0.2.4" - web-encoding "^1.1.5" + strict-event-emitter "^0.5.1" "@mui/base@5.0.0-beta.14": version "5.0.0-beta.14" @@ -3997,10 +4018,23 @@ dependencies: "@octokit/openapi-types" "^16.0.0" -"@open-draft/until@^1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" - integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== +"@open-draft/deferred-promise@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz#4a822d10f6f0e316be4d67b4d4f8c9a124b073bd" + integrity sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA== + +"@open-draft/logger@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@open-draft/logger/-/logger-0.3.0.tgz#2b3ab1242b360aa0adb28b85f5d7da1c133a0954" + integrity sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ== + dependencies: + is-node-process "^1.2.0" + outvariant "^1.4.0" + +"@open-draft/until@^2.0.0", "@open-draft/until@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-2.1.0.tgz#0acf32f470af2ceaf47f095cdecd40d68666efda" + integrity sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg== "@pmmmwh/react-refresh-webpack-plugin@^0.5.7": version "0.5.10" @@ -4528,7 +4562,7 @@ resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== -"@types/debug@^4.0.0", "@types/debug@^4.1.7": +"@types/debug@^4.0.0": version "4.1.7" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82" integrity sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg== @@ -4764,7 +4798,7 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/react-dom@<18.0.0", "@types/react-dom@^17": +"@types/react-dom@<18.0.0": version "17.0.20" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.20.tgz#e0c8901469d732b36d8473b40b679ad899da1b53" integrity sha512-4pzIjSxDueZZ90F52mU3aPoogkHIoSIDG+oQ+wQK7Cy2B9S+MvOqY0uEA/qawKz381qrEDkvpwyt8Bm31I8sbA== @@ -4867,13 +4901,6 @@ "@types/mime" "*" "@types/node" "*" -"@types/set-cookie-parser@^2.4.0": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@types/set-cookie-parser/-/set-cookie-parser-2.4.2.tgz#b6a955219b54151bfebd4521170723df5e13caad" - integrity sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w== - dependencies: - "@types/node" "*" - "@types/sockjs@^0.3.33": version "0.3.33" resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" @@ -4886,6 +4913,11 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/statuses@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/statuses/-/statuses-2.0.4.tgz#041143ba4a918e8f080f8b0ffbe3d4cb514e2315" + integrity sha512-eqNDvZsCNY49OAXB0Firg/Sc2BgoWsntsLUdybGFOhAfCD6QJ2n9HXUIHGqt5qjrxmMv4wS8WLAw43ZkKcJ8Pw== + "@types/styled-jsx@^2.2.8": version "2.2.9" resolved "https://registry.yarnpkg.com/@types/styled-jsx/-/styled-jsx-2.2.9.tgz#e50b3f868c055bcbf9bc353eca6c10fdad32a53f" @@ -5236,11 +5268,6 @@ "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" -"@xmldom/xmldom@^0.8.3": - version "0.8.6" - resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.6.tgz#8a1524eb5bd5e965c1e3735476f0262469f71440" - integrity sha512-uRjjusqpoqfmRkTaNuLJ2VohVr67Q5YwDATW3VU7PfzTj6IRaihGrYI7zckGZjxQPBIp63nfvJbM+Yu5ICh0Bg== - "@xobotyi/scrollbar-width@^1.9.5": version "1.9.5" resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" @@ -5269,11 +5296,6 @@ js-yaml "^3.10.0" tslib "^2.4.0" -"@zxing/text-encoding@0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b" - integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA== - JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -6202,14 +6224,6 @@ chalk@2.4.2, chalk@^2.0.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" - integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chalk@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" @@ -6731,16 +6745,11 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.5.0: +cookie@0.5.0, cookie@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cookie@^0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== - copy-to-clipboard@^3.3.1: version "3.3.3" resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" @@ -8181,7 +8190,7 @@ eventemitter3@^4.0.0, eventemitter3@^4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -events@^3.0.0, events@^3.2.0, events@^3.3.0: +events@^3.0.0, events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -8935,11 +8944,16 @@ graphql-tag@^2.10.3: dependencies: tslib "^2.1.0" -"graphql@^15.0.0 || ^16.0.0", graphql@^16.0.0: +graphql@^16.0.0: version "16.6.0" resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.6.0.tgz#c2dcffa4649db149f6282af726c8c83f1c7c5fdb" integrity sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw== +graphql@^16.8.1: + version "16.8.1" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" + integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== + gzip-size@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" @@ -9081,10 +9095,10 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -headers-polyfill@^3.1.0, headers-polyfill@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-3.1.2.tgz#9a4dcb545c5b95d9569592ef7ec0708aab763fbe" - integrity sha512-tWCK4biJ6hcLqTviLXVR9DTRfYGQMXEIUj3gwJ2rZ5wO/at3XtkI4g8mCvFdUF9l1KMBNCfmNAdnahm1cgavQA== +headers-polyfill@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-4.0.2.tgz#9115a76eee3ce8fbf95b6e3c6bf82d936785b44a" + integrity sha512-EWGTfnTqAO2L/j5HZgoM/3z82L7necsJ0pO9Tp0X1wil3PDLrkypTBRgVO2ExehEEvUycejZD3FuRaXpZZc3kw== highlight.js@^10.4.1, highlight.js@~10.7.0: version "10.7.3" @@ -11832,28 +11846,31 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -msw@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/msw/-/msw-1.2.1.tgz#9dd347583eeba5e5c7f33b54be5600a899dc61bd" - integrity sha512-bF7qWJQSmKn6bwGYVPXOxhexTCGD5oJSZg8yt8IBClxvo3Dx/1W0zqE1nX9BSWmzRsCKWfeGWcB/vpqV6aclpw== - dependencies: - "@mswjs/cookies" "^0.2.2" - "@mswjs/interceptors" "^0.17.5" - "@open-draft/until" "^1.0.3" +msw@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/msw/-/msw-2.0.6.tgz#635ddfc0908c481f10a5b0c696c3732bc693d615" + integrity sha512-74nN9ADQxHsVx8YGOq5yeDE4bye1mVMAYXojKl0qWraRunTK8UjAVk3wSmOOvVVIDJsyySyjDZ4oYvRuzuMO2g== + dependencies: + "@bundled-es-modules/cookie" "^2.0.0" + "@bundled-es-modules/js-levenshtein" "^2.0.1" + "@bundled-es-modules/statuses" "^1.0.1" + "@mswjs/cookies" "^1.1.0" + "@mswjs/interceptors" "^0.25.11" + "@open-draft/until" "^2.1.0" "@types/cookie" "^0.4.1" "@types/js-levenshtein" "^1.1.1" - chalk "4.1.1" + "@types/statuses" "^2.0.1" + chalk "^4.1.2" chokidar "^3.4.2" - cookie "^0.4.2" - graphql "^15.0.0 || ^16.0.0" - headers-polyfill "^3.1.2" + graphql "^16.8.1" + headers-polyfill "^4.0.1" inquirer "^8.2.0" is-node-process "^1.2.0" js-levenshtein "^1.1.6" node-fetch "^2.6.7" outvariant "^1.4.0" path-to-regexp "^6.2.0" - strict-event-emitter "^0.4.3" + strict-event-emitter "^0.5.0" type-fest "^2.19.0" yargs "^17.3.1" @@ -14359,11 +14376,6 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -set-cookie-parser@^2.4.6: - version "2.6.0" - resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz#131921e50f62ff1a66a461d7d62d7b21d5d15a51" - integrity sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ== - set-harmonic-interval@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" @@ -14722,7 +14734,7 @@ stacktrace-js@^2.0.2: stack-generator "^2.0.5" stacktrace-gps "^3.0.4" -statuses@2.0.1: +statuses@2.0.1, statuses@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== @@ -14763,17 +14775,10 @@ stream-http@^2.7.2: to-arraybuffer "^1.0.0" xtend "^4.0.0" -strict-event-emitter@^0.2.4: - version "0.2.8" - resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.2.8.tgz#b4e768927c67273c14c13d20e19d5e6c934b47ca" - integrity sha512-KDf/ujU8Zud3YaLtMCcTI4xkZlZVIYxTLr+XIULexP+77EEVWixeXroLUXQXiVtH4XH2W7jr/3PT1v3zBuvc3A== - dependencies: - events "^3.3.0" - -strict-event-emitter@^0.4.3: - version "0.4.6" - resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz#ff347c8162b3e931e3ff5f02cfce6772c3b07eb3" - integrity sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg== +strict-event-emitter@^0.5.0, strict-event-emitter@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz#1602ece81c51574ca39c6815e09f1a3e8550bd93" + integrity sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ== strict-uri-encode@^2.0.0: version "2.0.0" @@ -15504,6 +15509,13 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +undici@^5.27.2: + version "5.27.2" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.27.2.tgz#a270c563aea5b46cc0df2550523638c95c5d4411" + integrity sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ== + dependencies: + "@fastify/busboy" "^2.0.0" + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" @@ -15889,15 +15901,6 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -web-encoding@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/web-encoding/-/web-encoding-1.1.5.tgz#fc810cf7667364a6335c939913f5051d3e0c4864" - integrity sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA== - dependencies: - util "^0.12.3" - optionalDependencies: - "@zxing/text-encoding" "0.9.0" - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"