diff --git a/.eslintrc b/.eslintrc index 9ad2a337f47..b34a9c242e0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,7 +1,18 @@ { "extends": ["@grafana/eslint-config", "plugin:react/jsx-runtime"], "root": true, - "plugins": ["@emotion", "lodash", "jest", "import", "jsx-a11y", "@grafana", "no-barrel-files"], + "plugins": [ + "@emotion", + "lodash", + "jest", + "import", + "jsx-a11y", + "@grafana", + "no-barrel-files", + // Included so betterer doesn't fail when processing all files, + // as other parts of the code use testing-library plugin + "testing-library", + ], "settings": { "import/internal-regex": "^(app/)|(@grafana)", "import/external-module-folders": ["node_modules", ".yarn"], diff --git a/package.json b/package.json index 19f20a27b4c..16aae3333e1 100644 --- a/package.json +++ b/package.json @@ -178,6 +178,7 @@ "eslint-plugin-no-barrel-files": "^1.1.0", "eslint-plugin-react": "7.34.2", "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-testing-library": "^6.2.2", "eslint-scope": "^8.0.0", "eslint-webpack-plugin": "4.2.0", "expose-loader": "5.0.0", diff --git a/public/app/features/alerting/.eslintrc b/public/app/features/alerting/.eslintrc new file mode 100644 index 00000000000..5d4da843de0 --- /dev/null +++ b/public/app/features/alerting/.eslintrc @@ -0,0 +1,9 @@ +{ + "plugins": ["testing-library"], + "overrides": [ + { + "files": ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)"], + "extends": ["plugin:testing-library/react"], + }, + ], +} diff --git a/public/app/features/alerting/unified/CloneRuleEditor.test.tsx b/public/app/features/alerting/unified/CloneRuleEditor.test.tsx index d8893fbdfe3..3e5d853cb72 100644 --- a/public/app/features/alerting/unified/CloneRuleEditor.test.tsx +++ b/public/app/features/alerting/unified/CloneRuleEditor.test.tsx @@ -104,20 +104,20 @@ describe('CloneRuleEditor', function () { await waitFor(() => { expect(ui.inputs.name.get()).toHaveValue(`${grafanaRulerRule.grafana_alert.title} (copy)`); - expect(ui.inputs.folderContainer.get()).toHaveTextContent('folder-one'); - expect(ui.inputs.group.get()).toHaveTextContent(grafanaRulerRule.grafana_alert.rule_group); - expect( - byRole('listitem', { - name: 'severity: critical', - }).get() - ).toBeInTheDocument(); - expect( - byRole('listitem', { - name: 'region: nasa', - }).get() - ).toBeInTheDocument(); - expect(ui.inputs.annotationValue(0).get()).toHaveTextContent(grafanaRulerRule.annotations[Annotation.summary]); }); + expect(ui.inputs.folderContainer.get()).toHaveTextContent('folder-one'); + expect(ui.inputs.group.get()).toHaveTextContent(grafanaRulerRule.grafana_alert.rule_group); + expect( + byRole('listitem', { + name: 'severity: critical', + }).get() + ).toBeInTheDocument(); + expect( + byRole('listitem', { + name: 'region: nasa', + }).get() + ).toBeInTheDocument(); + expect(ui.inputs.annotationValue(0).get()).toHaveTextContent(grafanaRulerRule.annotations[Annotation.summary]); }); }); @@ -174,21 +174,21 @@ describe('CloneRuleEditor', function () { await waitFor(() => { expect(ui.inputs.name.get()).toHaveValue('First Ruler Rule (copy)'); - expect(ui.inputs.expr.get()).toHaveValue('vector(1) > 0'); - expect(ui.inputs.namespace.get()).toHaveTextContent('namespace-one'); - expect(ui.inputs.group.get()).toHaveTextContent('group1'); - expect( - byRole('listitem', { - name: 'severity: critical', - }).get() - ).toBeInTheDocument(); - expect( - byRole('listitem', { - name: 'region: nasa', - }).get() - ).toBeInTheDocument(); - expect(ui.inputs.annotationValue(0).get()).toHaveTextContent('This is a very important alert rule'); }); + expect(ui.inputs.expr.get()).toHaveValue('vector(1) > 0'); + expect(ui.inputs.namespace.get()).toHaveTextContent('namespace-one'); + expect(ui.inputs.group.get()).toHaveTextContent('group1'); + expect( + byRole('listitem', { + name: 'severity: critical', + }).get() + ).toBeInTheDocument(); + expect( + byRole('listitem', { + name: 'region: nasa', + }).get() + ).toBeInTheDocument(); + expect(ui.inputs.annotationValue(0).get()).toHaveTextContent('This is a very important alert rule'); }); }); diff --git a/public/app/features/alerting/unified/GrafanaRuleQueryViewer.test.tsx b/public/app/features/alerting/unified/GrafanaRuleQueryViewer.test.tsx index 0eee3f57c69..b057ab9186e 100644 --- a/public/app/features/alerting/unified/GrafanaRuleQueryViewer.test.tsx +++ b/public/app/features/alerting/unified/GrafanaRuleQueryViewer.test.tsx @@ -1,4 +1,4 @@ -import { render, waitFor } from '@testing-library/react'; +import { render, waitFor, screen } from '@testing-library/react'; import { TestProvider } from 'test/helpers/TestProvider'; import { DataSourceRef } from '@grafana/schema'; @@ -73,12 +73,11 @@ describe('GrafanaRuleQueryViewer', () => { getExpression('C', { type: '' }), getExpression('D', { type: '' }), ]; - const { getByTestId } = render( - , - { wrapper: TestProvider } - ); + render(, { + wrapper: TestProvider, + }); - await waitFor(() => expect(getByTestId('queries-container')).toHaveStyle('flex-wrap: wrap')); - expect(getByTestId('expressions-container')).toHaveStyle('flex-wrap: wrap'); + await waitFor(() => expect(screen.getByTestId('queries-container')).toHaveStyle('flex-wrap: wrap')); + expect(screen.getByTestId('expressions-container')).toHaveStyle('flex-wrap: wrap'); }); }); diff --git a/public/app/features/alerting/unified/RuleEditorCloudOnlyAllowed.test.tsx b/public/app/features/alerting/unified/RuleEditorCloudOnlyAllowed.test.tsx index 6db787d57ac..477324181e0 100644 --- a/public/app/features/alerting/unified/RuleEditorCloudOnlyAllowed.test.tsx +++ b/public/app/features/alerting/unified/RuleEditorCloudOnlyAllowed.test.tsx @@ -183,7 +183,7 @@ describe('RuleEditor cloud: checking editable data sources', () => { // render rule editor, select mimir/loki managed alerts renderRuleEditor(); - await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')); + await waitForElementToBeRemoved(screen.queryAllByTestId('Spinner')); await ui.inputs.name.find(); diff --git a/public/app/features/alerting/unified/RuleEditorCloudRules.test.tsx b/public/app/features/alerting/unified/RuleEditorCloudRules.test.tsx index d6af58c6bb3..224fa93a8ec 100644 --- a/public/app/features/alerting/unified/RuleEditorCloudRules.test.tsx +++ b/public/app/features/alerting/unified/RuleEditorCloudRules.test.tsx @@ -113,13 +113,13 @@ describe('RuleEditor cloud', () => { const user = userEvent.setup(); renderRuleEditor(); - await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')); + await waitForElementToBeRemoved(screen.queryAllByTestId('Spinner')); const removeExpressionsButtons = screen.getAllByLabelText('Remove expression'); expect(removeExpressionsButtons).toHaveLength(2); // Needs to wait for featrue discovery API call to finish - Check if ruler enabled - await waitFor(() => expect(screen.getByText('Data source-managed')).toBeInTheDocument()); + expect(await screen.findByText('Data source-managed')).toBeInTheDocument(); const switchToCloudButton = screen.getByText('Data source-managed'); expect(switchToCloudButton).toBeInTheDocument(); diff --git a/public/app/features/alerting/unified/RuleEditorGrafanaRules.test.tsx b/public/app/features/alerting/unified/RuleEditorGrafanaRules.test.tsx index 3728c5e88c5..ebd179642d5 100644 --- a/public/app/features/alerting/unified/RuleEditorGrafanaRules.test.tsx +++ b/public/app/features/alerting/unified/RuleEditorGrafanaRules.test.tsx @@ -113,7 +113,7 @@ describe('RuleEditor grafana managed rules', () => { ] as DashboardSearchHit[]); renderRuleEditor(); - await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')); + await waitForElementToBeRemoved(screen.queryAllByTestId('Spinner')); await userEvent.type(await ui.inputs.name.find(), 'my great new rule'); diff --git a/public/app/features/alerting/unified/RuleEditorRecordingRule.test.tsx b/public/app/features/alerting/unified/RuleEditorRecordingRule.test.tsx index 5e02450bf44..523f4dcf3d4 100644 --- a/public/app/features/alerting/unified/RuleEditorRecordingRule.test.tsx +++ b/public/app/features/alerting/unified/RuleEditorRecordingRule.test.tsx @@ -147,7 +147,7 @@ describe('RuleEditor recording rules', () => { }); renderRuleEditor(undefined, true); - await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')); + await waitForElementToBeRemoved(screen.queryAllByTestId('Spinner')); await userEvent.type(await ui.inputs.name.find(), 'my great new recording rule'); const dataSourceSelect = ui.inputs.dataSource.get(); diff --git a/public/app/features/alerting/unified/RuleList.test.tsx b/public/app/features/alerting/unified/RuleList.test.tsx index fdb5bd8280e..6d9673b1c7a 100644 --- a/public/app/features/alerting/unified/RuleList.test.tsx +++ b/public/app/features/alerting/unified/RuleList.test.tsx @@ -2,7 +2,7 @@ import { SerializedError } from '@reduxjs/toolkit'; import userEvent from '@testing-library/user-event'; import { SetupServer } from 'msw/node'; import { TestProvider } from 'test/helpers/TestProvider'; -import { prettyDOM, render, screen, waitFor, within } from 'test/test-utils'; +import { render, screen, waitFor, within } from 'test/test-utils'; import { byRole, byTestId, byText } from 'testing-library-selector'; import { PluginExtensionTypes } from '@grafana/data'; @@ -728,7 +728,6 @@ describe('RuleList', () => { await userEvent.click(ui.editCloudGroupIcon.get(groups[0])); await waitFor(() => expect(ui.editGroupModal.dialog.get()).toBeInTheDocument()); - prettyDOM(ui.editGroupModal.dialog.get()); expect(ui.editGroupModal.namespaceInput.get()).toHaveDisplayValue('namespace1'); expect(ui.editGroupModal.ruleGroupInput.get()).toHaveDisplayValue('group1'); diff --git a/public/app/features/alerting/unified/Settings.test.tsx b/public/app/features/alerting/unified/Settings.test.tsx index 892c5a35969..2537fb1e800 100644 --- a/public/app/features/alerting/unified/Settings.test.tsx +++ b/public/app/features/alerting/unified/Settings.test.tsx @@ -49,10 +49,8 @@ describe('Alerting settings', () => { it('should render the page with Built-in only enabled, others disabled', async () => { render(); - await waitFor(() => { - expect(ui.builtInAlertmanagerSection.get()).toBeInTheDocument(); - expect(ui.otherAlertmanagerSection.get()).toBeInTheDocument(); - }); + expect(await ui.builtInAlertmanagerSection.find()).toBeInTheDocument(); + expect(ui.otherAlertmanagerSection.get()).toBeInTheDocument(); // check internal alertmanager configuration expect(ui.builtInAlertmanagerCard.get()).toBeInTheDocument(); diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx index 1f3dc40a3ad..ecef0d1e31d 100644 --- a/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx +++ b/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx @@ -90,11 +90,8 @@ describe('contact points', () => { it('should show / hide loading states, have all actions enabled', async () => { renderWithProvider(); - await waitFor(async () => { - expect(screen.getByText('Loading...')).toBeInTheDocument(); - await waitForElementToBeRemoved(screen.getByText('Loading...')); - expect(screen.queryByTestId(selectors.components.Alert.alertV2('error'))).not.toBeInTheDocument(); - }); + await waitForElementToBeRemoved(screen.queryByText('Loading...')); + expect(screen.queryByTestId(selectors.components.Alert.alertV2('error'))).not.toBeInTheDocument(); expect(screen.getByText('grafana-default-email')).toBeInTheDocument(); expect(screen.getAllByTestId('contact-point')).toHaveLength(5); @@ -130,9 +127,7 @@ describe('contact points', () => { renderWithProvider(); // wait for loading to be done - await waitFor(async () => { - expect(screen.queryByText('Loading...')).not.toBeInTheDocument(); - }); + await waitForElementToBeRemoved(screen.queryByText('Loading...')); // should disable create contact point expect(screen.getByRole('link', { name: 'add contact point' })).toHaveAttribute('aria-disabled', 'true'); @@ -252,8 +247,8 @@ describe('contact points', () => { await userEvent.type(searchInput, 'slack'); expect(searchInput).toHaveValue('slack'); + expect(await screen.findByText('Slack with multiple channels')).toBeInTheDocument(); await waitFor(() => { - expect(screen.getByText('Slack with multiple channels')).toBeInTheDocument(); expect(screen.getAllByTestId('contact-point')).toHaveLength(1); }); @@ -286,11 +281,8 @@ describe('contact points', () => { it('should show / hide loading states, have the right actions enabled', async () => { renderWithProvider(, undefined, { alertmanagerSourceName: MIMIR_DATASOURCE_UID }); - await waitFor(async () => { - expect(screen.getByText('Loading...')).toBeInTheDocument(); - await waitForElementToBeRemoved(screen.getByText('Loading...')); - expect(screen.queryByTestId(selectors.components.Alert.alertV2('error'))).not.toBeInTheDocument(); - }); + await waitForElementToBeRemoved(screen.queryByText('Loading...')); + expect(screen.queryByTestId(selectors.components.Alert.alertV2('error'))).not.toBeInTheDocument(); expect(screen.getByText('mixed')).toBeInTheDocument(); expect(screen.getByText('some webhook')).toBeInTheDocument(); @@ -344,11 +336,8 @@ describe('contact points', () => { alertmanagerSourceName: VANILLA_ALERTMANAGER_DATASOURCE_UID, }); - await waitFor(async () => { - expect(screen.getByText('Loading...')).toBeInTheDocument(); - await waitForElementToBeRemoved(screen.getByText('Loading...')); - expect(screen.queryByTestId(selectors.components.Alert.alertV2('error'))).not.toBeInTheDocument(); - }); + await waitForElementToBeRemoved(screen.queryByText('Loading...')); + expect(screen.queryByTestId(selectors.components.Alert.alertV2('error'))).not.toBeInTheDocument(); expect(screen.queryByRole('link', { name: 'add contact point' })).not.toBeInTheDocument(); diff --git a/public/app/features/alerting/unified/components/mute-timings/MuteTimingsTable.test.tsx b/public/app/features/alerting/unified/components/mute-timings/MuteTimingsTable.test.tsx index 6ceffac48e0..f14519d217d 100644 --- a/public/app/features/alerting/unified/components/mute-timings/MuteTimingsTable.test.tsx +++ b/public/app/features/alerting/unified/components/mute-timings/MuteTimingsTable.test.tsx @@ -1,4 +1,4 @@ -import { render, waitFor } from '@testing-library/react'; +import { render, waitFor, screen } from '@testing-library/react'; import { Provider } from 'react-redux'; import { Router } from 'react-router-dom'; @@ -36,15 +36,15 @@ describe('MuteTimingsTable', () => { AccessControlAction.AlertingNotificationsRead, AccessControlAction.AlertingNotificationsWrite, ]); - const { findByRole } = renderWithProvider(); - expect(await findByRole('button', { name: /export all/i })).toBeInTheDocument(); + renderWithProvider(); + expect(await screen.findByRole('button', { name: /export all/i })).toBeInTheDocument(); }); it('It does not show export button when not allowed ', async () => { // when not allowed grantUserPermissions([]); - const { queryByRole } = renderWithProvider(); + renderWithProvider(); await waitFor(() => { - expect(queryByRole('button', { name: /export all/i })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /export all/i })).not.toBeInTheDocument(); }); }); it('It does not show export button when not supported ', async () => { @@ -53,9 +53,9 @@ describe('MuteTimingsTable', () => { AccessControlAction.AlertingNotificationsRead, AccessControlAction.AlertingNotificationsWrite, ]); - const { queryByRole } = renderWithProvider('potato'); + renderWithProvider('potato'); await waitFor(() => { - expect(queryByRole('button', { name: /export all/i })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /export all/i })).not.toBeInTheDocument(); }); }); }); diff --git a/public/app/features/alerting/unified/components/receivers/TemplatePreview.test.tsx b/public/app/features/alerting/unified/components/receivers/TemplatePreview.test.tsx index a4c936dbbc5..7abcf79a6e6 100644 --- a/public/app/features/alerting/unified/components/receivers/TemplatePreview.test.tsx +++ b/public/app/features/alerting/unified/components/receivers/TemplatePreview.test.tsx @@ -129,12 +129,12 @@ describe('TemplatePreview component', () => { { wrapper: getProviderWraper() } ); + const previews = ui.resultItems.getAll; await waitFor(() => { - const previews = ui.resultItems.getAll(); - expect(previews).toHaveLength(2); - expect(previews[0]).toHaveTextContent('This is the template result bla bla bla'); - expect(previews[1]).toHaveTextContent('This is the template2 result bla bla bla'); + expect(previews()).toHaveLength(2); }); + expect(previews()[0]).toHaveTextContent('This is the template result bla bla bla'); + expect(previews()[1]).toHaveTextContent('This is the template2 result bla bla bla'); }); it('Should render preview response with some errors, if payload has correct format ', async () => { @@ -157,15 +157,14 @@ describe('TemplatePreview component', () => { { wrapper: getProviderWraper() } ); + const alerts = () => screen.getAllByTestId(Components.Alert.alertV2('error')); await waitFor(() => { - const alerts = screen.getAllByTestId(Components.Alert.alertV2('error')); - const previewContent = screen.getByRole('listitem'); - - expect(alerts).toHaveLength(2); - expect(alerts[0]).toHaveTextContent(/Unexpected "{" in operand/i); - expect(alerts[1]).toHaveTextContent(/Unexpected "{" in operand/i); - - expect(previewContent).toHaveTextContent('This is the template result bla bla bla'); + expect(alerts()).toHaveLength(2); }); + expect(alerts()[0]).toHaveTextContent(/Unexpected "{" in operand/i); + expect(alerts()[1]).toHaveTextContent(/Unexpected "{" in operand/i); + + const previewContent = screen.getByRole('listitem'); + expect(previewContent).toHaveTextContent('This is the template result bla bla bla'); }); }); diff --git a/public/app/features/alerting/unified/components/receivers/form/GrafanaReceiverForm.test.tsx b/public/app/features/alerting/unified/components/receivers/form/GrafanaReceiverForm.test.tsx index 105e6553fc5..f4832592872 100644 --- a/public/app/features/alerting/unified/components/receivers/form/GrafanaReceiverForm.test.tsx +++ b/public/app/features/alerting/unified/components/receivers/form/GrafanaReceiverForm.test.tsx @@ -51,9 +51,11 @@ describe('GrafanaReceiverForm', () => { await clickSelectOption(byTestId('items.0.type').get(), 'Grafana OnCall'); // Clicking on a disable element shouldn't change the form value. email is the default value + // eslint-disable-next-line testing-library/no-node-access expect(ui.integrationType.get().closest('form')).toHaveFormValues({ 'items.0.type': 'email' }); await clickSelectOption(byTestId('items.0.type').get(), 'Alertmanager'); + // eslint-disable-next-line testing-library/no-node-access expect(ui.integrationType.get().closest('form')).toHaveFormValues({ 'items.0.type': 'prometheus-alertmanager' }); }); @@ -73,6 +75,7 @@ describe('GrafanaReceiverForm', () => { await clickSelectOption(byTestId('items.0.type').get(), 'Grafana OnCall'); + // eslint-disable-next-line testing-library/no-node-access expect(ui.integrationType.get().closest('form')).toHaveFormValues({ 'items.0.type': 'oncall' }); expect(ui.onCallIntegrationType.get()).toBeInTheDocument(); @@ -87,6 +90,7 @@ describe('GrafanaReceiverForm', () => { await user.type(ui.newOnCallIntegrationName.get(), 'emea-oncall'); + // eslint-disable-next-line testing-library/no-node-access expect(ui.integrationType.get().closest('form')).toHaveFormValues({ 'items.0.settings.integration_type': 'new_oncall_integration', 'items.0.settings.integration_name': 'emea-oncall', @@ -98,6 +102,7 @@ describe('GrafanaReceiverForm', () => { await clickSelectOption(ui.existingOnCallIntegrationSelect(0).get(), 'apac-oncall'); + // eslint-disable-next-line testing-library/no-node-access expect(ui.integrationType.get().closest('form')).toHaveFormValues({ 'items.0.settings.url': 'https://apac.oncall.example.com', 'items.0.settings.integration_name': undefined, diff --git a/public/app/features/alerting/unified/components/rule-editor/AnnotationsStep.test.tsx b/public/app/features/alerting/unified/components/rule-editor/AnnotationsStep.test.tsx index ca31bc42c17..565f7ed6cf6 100644 --- a/public/app/features/alerting/unified/components/rule-editor/AnnotationsStep.test.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/AnnotationsStep.test.tsx @@ -1,6 +1,6 @@ import userEvent from '@testing-library/user-event'; import { FormProvider, useForm } from 'react-hook-form'; -import { findByRole, findByText, findByTitle, getByTestId, queryByText, render } from 'test/test-utils'; +import { screen, render, within } from 'test/test-utils'; import { byRole, byTestId } from 'testing-library-selector'; import 'core-js/stable/structured-clone'; @@ -97,10 +97,10 @@ describe('AnnotationsField', function () { await user.click(ui.setDashboardButton.get()); expect(ui.dashboardPicker.confirmButton.get()).toBeDisabled(); - await user.click(await findByTitle(ui.dashboardPicker.dialog.get(), 'My dashboard')); + await user.click(await screen.findByTitle('My dashboard')); expect(ui.dashboardPicker.confirmButton.get()).toBeDisabled(); - await user.click(await findByText(ui.dashboardPicker.dialog.get(), 'First panel')); + await user.click(await screen.findByText('First panel')); expect(ui.dashboardPicker.confirmButton.get()).toBeEnabled(); }); @@ -125,9 +125,9 @@ describe('AnnotationsField', function () { render(); await user.click(ui.setDashboardButton.get()); - await user.click(await findByTitle(ui.dashboardPicker.dialog.get(), 'My dashboard')); + await user.click(await screen.findByTitle('My dashboard')); - await user.click(await findByText(ui.dashboardPicker.dialog.get(), 'Second panel')); + await user.click(await screen.findByText('Second panel')); await user.click(ui.dashboardPicker.confirmButton.get()); @@ -163,10 +163,10 @@ describe('AnnotationsField', function () { await user.click(ui.setDashboardButton.get()); expect(ui.dashboardPicker.confirmButton.get()).toBeDisabled(); - await user.click(await findByTitle(ui.dashboardPicker.dialog.get(), 'My dashboard')); + await user.click(await screen.findByTitle('My dashboard')); - expect(await findByText(ui.dashboardPicker.dialog.get(), 'First panel')).toBeInTheDocument(); - expect(await queryByText(ui.dashboardPicker.dialog.get(), 'Row panel')).not.toBeInTheDocument(); + expect(await screen.findByText('First panel')).toBeInTheDocument(); + expect(screen.queryByText('Row panel')).not.toBeInTheDocument(); }); it('should show panels within collapsed rows', async function () { @@ -198,11 +198,11 @@ describe('AnnotationsField', function () { await user.click(ui.setDashboardButton.get()); expect(ui.dashboardPicker.confirmButton.get()).toBeDisabled(); - await user.click(await findByTitle(ui.dashboardPicker.dialog.get(), 'My dashboard')); + await user.click(await screen.findByTitle('My dashboard')); - expect(await findByText(ui.dashboardPicker.dialog.get(), 'First panel')).toBeInTheDocument(); - expect(await queryByText(ui.dashboardPicker.dialog.get(), 'Row panel')).not.toBeInTheDocument(); - expect(await findByText(ui.dashboardPicker.dialog.get(), 'Panel within collapsed row')).toBeInTheDocument(); + expect(await screen.findByText('First panel')).toBeInTheDocument(); + expect(screen.queryByText('Row panel')).not.toBeInTheDocument(); + expect(await screen.findByText('Panel within collapsed row')).toBeInTheDocument(); }); // this test _should_ work in theory but something is stopping the 'onClick' function on the dashboard item @@ -252,11 +252,11 @@ describe('AnnotationsField', function () { expect(annotationValueElements[0]).toHaveTextContent('dash-test-uid'); expect(annotationValueElements[1]).toHaveTextContent('1'); - const { confirmButton, dialog } = ui.dashboardPicker; + const { confirmButton } = ui.dashboardPicker; await user.click(ui.setDashboardButton.get()); - await user.click(await findByRole(dialog.get(), 'button', { name: /My other dashboard/ })); - await user.click(await findByRole(dialog.get(), 'button', { name: /Third panel/ })); + await user.click(await screen.findByRole('button', { name: /My other dashboard/ })); + await user.click(await screen.findByRole('button', { name: /Third panel/ })); await user.click(confirmButton.get()); expect(ui.dashboardPicker.dialog.query()).not.toBeInTheDocument(); @@ -296,14 +296,12 @@ describe('AnnotationsField', function () { render(); - const { dialog } = ui.dashboardPicker; - await user.click(ui.setDashboardButton.get()); - await user.click(await findByTitle(dialog.get(), 'My dashboard')); + await user.click(await screen.findByTitle('My dashboard')); - const warnedPanel = await findByRole(dialog.get(), 'button', { name: /First panel/ }); + const warnedPanel = await screen.findByRole('button', { name: /First panel/ }); - expect(getByTestId(warnedPanel, 'warning-icon')).toBeInTheDocument(); + expect(within(warnedPanel).getByTestId('warning-icon')).toBeInTheDocument(); }); it('should render when panels do not contain certain fields', async () => { diff --git a/public/app/features/alerting/unified/components/rule-editor/DashboardPicker.test.tsx b/public/app/features/alerting/unified/components/rule-editor/DashboardPicker.test.tsx index 5d49288dbb7..b310095991b 100644 --- a/public/app/features/alerting/unified/components/rule-editor/DashboardPicker.test.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/DashboardPicker.test.tsx @@ -1,4 +1,4 @@ -import { render, waitFor } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { noop } from 'lodash'; import { Props } from 'react-virtualized-auto-sizer'; import { byRole } from 'testing-library-selector'; @@ -63,17 +63,15 @@ describe('DashboardPicker', () => { wrapper: TestProvider, }); - await waitFor(() => { - expect(ui.dashboardButton(/Dashboard 1/).get()).toBeInTheDocument(); - expect(ui.dashboardButton(/Dashboard 2/).get()).toBeInTheDocument(); - expect(ui.dashboardButton(/Dashboard 3/).get()).toBeInTheDocument(); + expect(await ui.dashboardButton(/Dashboard 1/).find()).toBeInTheDocument(); + expect(ui.dashboardButton(/Dashboard 2/).get()).toBeInTheDocument(); + expect(ui.dashboardButton(/Dashboard 3/).get()).toBeInTheDocument(); - const panels = ui.dashboardButton(//).getAll(); - expect(panels).toHaveLength(3); + const panels = ui.dashboardButton(//).getAll(); + expect(panels).toHaveLength(3); - panels.forEach((panel) => { - expect(panel).not.toBeDisabled(); - }); + panels.forEach((panel) => { + expect(panel).not.toBeDisabled(); }); }); }); diff --git a/public/app/features/alerting/unified/components/rule-editor/EvaluationGroupQuickPick.test.tsx b/public/app/features/alerting/unified/components/rule-editor/EvaluationGroupQuickPick.test.tsx index d1a927fbbdf..3effadef96f 100644 --- a/public/app/features/alerting/unified/components/rule-editor/EvaluationGroupQuickPick.test.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/EvaluationGroupQuickPick.test.tsx @@ -1,6 +1,4 @@ -import { screen } from '@testing-library/dom'; -import { render } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; +import { render, screen, userEvent } from 'test/test-utils'; import { getEvaluationGroupOptions, EvaluationGroupQuickPick } from './EvaluationGroupQuickPick'; diff --git a/public/app/features/alerting/unified/components/rule-editor/PendingPeriodQuickPick.test.tsx b/public/app/features/alerting/unified/components/rule-editor/PendingPeriodQuickPick.test.tsx index d77c2f74426..fa7bbac5699 100644 --- a/public/app/features/alerting/unified/components/rule-editor/PendingPeriodQuickPick.test.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/PendingPeriodQuickPick.test.tsx @@ -1,6 +1,4 @@ -import { screen } from '@testing-library/dom'; -import { render } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; +import { render, screen, userEvent } from 'test/test-utils'; import { PendingPeriodQuickPick } from './PendingPeriodQuickPick'; diff --git a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/SimplifiedRuleEditor.test.tsx b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/SimplifiedRuleEditor.test.tsx index be567a58d46..dbc3cea4714 100644 --- a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/SimplifiedRuleEditor.test.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/simplifiedRouting/SimplifiedRuleEditor.test.tsx @@ -93,6 +93,7 @@ describe('Can create a new grafana managed alert unsing simplified routing', () const user = userEvent.setup(); renderSimplifiedRuleEditor(); + await waitForElementToBeRemoved(screen.queryAllByTestId('Spinner')); await user.type(await ui.inputs.name.find(), 'my great new rule'); @@ -111,7 +112,7 @@ describe('Can create a new grafana managed alert unsing simplified routing', () it('simplified routing is not available when Grafana AM is not enabled', async () => { setAlertmanagerChoices(AlertmanagerChoice.External, 1); renderSimplifiedRuleEditor(); - await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')); + await waitForElementToBeRemoved(screen.queryAllByTestId('Spinner')); expect(ui.inputs.simplifiedRouting.contactPointRouting.query()).not.toBeInTheDocument(); }); @@ -121,6 +122,7 @@ describe('Can create a new grafana managed alert unsing simplified routing', () const contactPointName = 'lotsa-emails'; renderSimplifiedRuleEditor(); + await waitForElementToBeRemoved(screen.queryAllByTestId('Spinner')); await user.type(await ui.inputs.name.find(), 'my great new rule'); diff --git a/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationPreview.test.tsx b/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationPreview.test.tsx index 17cb3f64394..e0fc849dcd8 100644 --- a/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationPreview.test.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationPreview.test.tsx @@ -147,14 +147,14 @@ describe('NotificationPreview', () => { // we expect the alert manager label to be missing as there is only one alert manager configured to receive alerts await waitFor(() => { expect(ui.grafanaAlertManagerLabel.query()).not.toBeInTheDocument(); - expect(ui.otherAlertManagerLabel.query()).not.toBeInTheDocument(); }); + expect(ui.otherAlertManagerLabel.query()).not.toBeInTheDocument(); + const matchingPoliciesElements = ui.route.queryAll; await waitFor(() => { - const matchingPoliciesElements = ui.route.queryAll(); - expect(matchingPoliciesElements).toHaveLength(1); - expect(matchingPoliciesElements[0]).toHaveTextContent(/tomato = red/); + expect(matchingPoliciesElements()).toHaveLength(1); }); + expect(matchingPoliciesElements()[0]).toHaveTextContent(/tomato = red/); }); it('should render notification preview with alert manager sections, when having more than one alert manager configured to receive alerts', async () => { // two alert managers configured to receive alerts @@ -174,8 +174,8 @@ describe('NotificationPreview', () => { // we expect the alert manager label to be present as there is more than one alert manager configured to receive alerts await waitFor(() => { expect(ui.grafanaAlertManagerLabel.query()).toBeInTheDocument(); - expect(ui.otherAlertManagerLabel.query()).toBeInTheDocument(); }); + expect(ui.otherAlertManagerLabel.query()).toBeInTheDocument(); const matchingPoliciesElements = ui.route.queryAll(); expect(matchingPoliciesElements).toHaveLength(2); diff --git a/public/app/features/alerting/unified/components/rule-viewer/RuleViewer.test.tsx b/public/app/features/alerting/unified/components/rule-viewer/RuleViewer.test.tsx index 40a0aa4a1fe..ed59a8e9791 100644 --- a/public/app/features/alerting/unified/components/rule-viewer/RuleViewer.test.tsx +++ b/public/app/features/alerting/unified/components/rule-viewer/RuleViewer.test.tsx @@ -170,10 +170,8 @@ describe('RuleViewer', () => { } // actions - await waitFor(() => { - expect(ELEMENTS.actions.edit.get()).toBeInTheDocument(); - expect(ELEMENTS.actions.more.button.get()).toBeInTheDocument(); - }); + expect(await ELEMENTS.actions.edit.find()).toBeInTheDocument(); + expect(ELEMENTS.actions.more.button.get()).toBeInTheDocument(); // check the "more actions" button await userEvent.click(ELEMENTS.actions.more.button.get()); diff --git a/public/app/features/alerting/unified/components/rules/state-history/LogRecordViewer.test.tsx b/public/app/features/alerting/unified/components/rules/state-history/LogRecordViewer.test.tsx index 6ff72a88fb8..3ad55149827 100644 --- a/public/app/features/alerting/unified/components/rules/state-history/LogRecordViewer.test.tsx +++ b/public/app/features/alerting/unified/components/rules/state-history/LogRecordViewer.test.tsx @@ -1,4 +1,4 @@ -import { getByTestId, render } from '@testing-library/react'; +import { screen, render } from '@testing-library/react'; import { byRole } from 'testing-library-selector'; import { LogRecordViewerByTimestamp } from './LogRecordViewer'; @@ -22,8 +22,8 @@ describe('LogRecordViewerByTimestamp', () => { const logElement = ui.log.get(); expect(logElement).toBeInTheDocument(); - const entry1 = getByTestId(logElement, 1681739580000); - const entry2 = getByTestId(logElement, 1681739600000); + const entry1 = screen.getByTestId(1681739580000); + const entry2 = screen.getByTestId(1681739600000); expect(entry1).toHaveTextContent('foo=bar'); expect(entry1).toHaveTextContent('severity=warning'); diff --git a/public/app/features/alerting/unified/hooks/useAbilities.test.tsx b/public/app/features/alerting/unified/hooks/useAbilities.test.tsx index ddea4463b14..9b24cabbffe 100644 --- a/public/app/features/alerting/unified/hooks/useAbilities.test.tsx +++ b/public/app/features/alerting/unified/hooks/useAbilities.test.tsx @@ -39,10 +39,10 @@ describe('alertmanager abilities', () => { }) ); - const abilities = renderHook(() => useAllAlertmanagerAbilities(), { + const { result } = renderHook(() => useAllAlertmanagerAbilities(), { wrapper: createAlertmanagerWrapper('does-not-exist'), }); - expect(abilities.result.current).toMatchSnapshot(); + expect(result.current).toMatchSnapshot(); }); it('should report everything is supported for builtin alertmanager', () => { @@ -55,36 +55,36 @@ describe('alertmanager abilities', () => { grantUserPermissions([AccessControlAction.AlertingNotificationsRead, AccessControlAction.AlertingInstanceRead]); - const abilities = renderHook(() => useAllAlertmanagerAbilities(), { + const { result } = renderHook(() => useAllAlertmanagerAbilities(), { wrapper: createAlertmanagerWrapper(GRAFANA_RULES_SOURCE_NAME), }); - Object.values(abilities.result.current).forEach(([supported]) => { + Object.values(result.current).forEach(([supported]) => { expect(supported).toBe(true); }); // since we only granted "read" permissions, only those should be allowed - const viewAbility = renderHook(() => useAlertmanagerAbility(AlertmanagerAction.ViewSilence), { + const { result: viewResult } = renderHook(() => useAlertmanagerAbility(AlertmanagerAction.ViewSilence), { wrapper: createAlertmanagerWrapper(GRAFANA_RULES_SOURCE_NAME), }); - const [viewSupported, viewAllowed] = viewAbility.result.current; + const [viewSupported, viewAllowed] = viewResult.current; expect(viewSupported).toBe(true); expect(viewAllowed).toBe(true); // editing should not be allowed, but supported - const editAbility = renderHook(() => useAlertmanagerAbility(AlertmanagerAction.ViewSilence), { + const { result: editResult } = renderHook(() => useAlertmanagerAbility(AlertmanagerAction.ViewSilence), { wrapper: createAlertmanagerWrapper(GRAFANA_RULES_SOURCE_NAME), }); - const [editSupported, editAllowed] = editAbility.result.current; + const [editSupported, editAllowed] = editResult.current; expect(editSupported).toBe(true); expect(editAllowed).toBe(true); // record the snapshot to prevent future regressions - expect(abilities.result.current).toMatchSnapshot(); + expect(result.current).toMatchSnapshot(); }); it('should report everything except exporting for Mimir alertmanager', () => { @@ -105,11 +105,11 @@ describe('alertmanager abilities', () => { AccessControlAction.AlertingInstancesExternalWrite, ]); - const abilities = renderHook(() => useAllAlertmanagerAbilities(), { + const { result } = renderHook(() => useAllAlertmanagerAbilities(), { wrapper: createAlertmanagerWrapper('mimir'), }); - expect(abilities.result.current).toMatchSnapshot(); + expect(result.current).toMatchSnapshot(); }); it('should be able to return multiple abilities', () => { @@ -122,7 +122,7 @@ describe('alertmanager abilities', () => { grantUserPermissions([AccessControlAction.AlertingNotificationsRead]); - const abilities = renderHook( + const { result } = renderHook( () => useAlertmanagerAbilities([ AlertmanagerAction.ViewContactPoint, @@ -134,10 +134,10 @@ describe('alertmanager abilities', () => { } ); - expect(abilities.result.current).toHaveLength(3); - expect(abilities.result.current[0]).toStrictEqual([true, true]); - expect(abilities.result.current[1]).toStrictEqual([true, false]); - expect(abilities.result.current[2]).toStrictEqual([true, true]); + expect(result.current).toHaveLength(3); + expect(result.current[0]).toStrictEqual([true, true]); + expect(result.current[1]).toStrictEqual([true, false]); + expect(result.current[2]).toStrictEqual([true, true]); }); }); @@ -163,17 +163,17 @@ describe('AlertRule abilities', () => { it('should report that all actions are supported for a Grafana Managed alert rule', async () => { const rule = getGrafanaRule(); - const abilities = renderHook(() => useAllAlertRuleAbilities(rule), { wrapper: TestProvider }); + const { result } = renderHook(() => useAllAlertRuleAbilities(rule), { wrapper: TestProvider }); await waitFor(() => { - const results = Object.values(abilities.result.current); + const results = Object.values(result.current); for (const [supported, _allowed] of results) { expect(supported).toBe(true); } }); - expect(abilities.result.current).toMatchSnapshot(); + expect(result.current).toMatchSnapshot(); }); it('grants correct silence permissions for folder with silence create permission', async () => { @@ -201,13 +201,13 @@ describe('AlertRule abilities', () => { it('should report no permissions while we are loading data for cloud rule', async () => { const rule = getCloudRule(); - const abilities = renderHook(() => useAllAlertRuleAbilities(rule), { wrapper: TestProvider }); + const { result } = renderHook(() => useAllAlertRuleAbilities(rule), { wrapper: TestProvider }); await waitFor(() => { - expect(abilities.result.current).not.toBeUndefined(); + expect(result.current).not.toBeUndefined(); }); - expect(abilities.result.current).toMatchSnapshot(); + expect(result.current).toMatchSnapshot(); }); it('should not allow certain actions for provisioned rules', () => {}); diff --git a/public/app/features/alerting/unified/hooks/useExternalAMSelector.test.tsx b/public/app/features/alerting/unified/hooks/useExternalAMSelector.test.tsx index f41b489fb3b..a8f2c3224ff 100644 --- a/public/app/features/alerting/unified/hooks/useExternalAMSelector.test.tsx +++ b/public/app/features/alerting/unified/hooks/useExternalAMSelector.test.tsx @@ -23,12 +23,10 @@ describe('useExternalDataSourceAlertmanagers', () => { const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: TestProvider }); await waitFor(() => { // Assert - const { current } = result; - - expect(current).toHaveLength(1); - expect(current[0].dataSourceSettings.uid).toBe('1'); - expect(current[0].dataSourceSettings.url).toBe('http://grafana.com'); + expect(result.current).toHaveLength(1); }); + expect(result.current[0].dataSourceSettings.uid).toBe('1'); + expect(result.current[0].dataSourceSettings.url).toBe('http://grafana.com'); }); it('Should have uninterested state if data source does not want alerts', async () => { @@ -40,11 +38,9 @@ describe('useExternalDataSourceAlertmanagers', () => { const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: TestProvider }); await waitFor(() => { // Assert - const { current } = result; - - expect(current).toHaveLength(1); - expect(current[0].status).toBe('uninterested'); + expect(result.current).toHaveLength(1); }); + expect(result.current[0].status).toBe('uninterested'); }); it('Should have active state if available in the activeAlertManagers', async () => { @@ -61,11 +57,9 @@ describe('useExternalDataSourceAlertmanagers', () => { const { result } = renderHook(() => useExternalDataSourceAlertmanagers(), { wrapper: TestProvider }); await waitFor(() => { // Assert - const { current } = result; - - expect(current).toHaveLength(1); - expect(current[0].status).toBe('active'); + expect(result.current).toHaveLength(1); }); + expect(result.current[0].status).toBe('active'); }); it('Should have dropped state if available in the droppedAlertManagers', async () => { @@ -83,11 +77,9 @@ describe('useExternalDataSourceAlertmanagers', () => { await waitFor(() => { // Assert - const { current } = result; - - expect(current).toHaveLength(1); - expect(current[0].status).toBe('dropped'); + expect(result.current).toHaveLength(1); }); + expect(result.current[0].status).toBe('dropped'); }); it('Should have pending state if not available neither in dropped nor in active alertManagers', async () => { @@ -105,11 +97,9 @@ describe('useExternalDataSourceAlertmanagers', () => { await waitFor(() => { // Assert - const { current } = result; - - expect(current).toHaveLength(1); - expect(current[0].status).toBe('pending'); + expect(result.current).toHaveLength(1); }); + expect(result.current[0].status).toBe('pending'); }); it('Should match Alertmanager url when datasource url does not have protocol specified', async () => { @@ -127,12 +117,10 @@ describe('useExternalDataSourceAlertmanagers', () => { await waitFor(() => { // Assert - const { current } = result; - - expect(current).toHaveLength(1); - expect(current[0].status).toBe('active'); - expect(current[0].dataSourceSettings.url).toBe('localhost:9093'); + expect(result.current).toHaveLength(1); }); + expect(result.current[0].status).toBe('active'); + expect(result.current[0].dataSourceSettings.url).toBe('localhost:9093'); }); it('Should have inconclusive state when there are many Alertmanagers of the same URL on both active and inactive', async () => { @@ -154,8 +142,8 @@ describe('useExternalDataSourceAlertmanagers', () => { await waitFor(() => { // Assert expect(result.current).toHaveLength(1); - expect(result.current[0].status).toBe('inconclusive'); }); + expect(result.current[0].status).toBe('inconclusive'); }); it('Should have not have inconclusive state when all Alertmanagers of the same URL are active', async () => { @@ -177,8 +165,8 @@ describe('useExternalDataSourceAlertmanagers', () => { await waitFor(() => { // Assert expect(result.current).toHaveLength(1); - expect(result.current[0].status).toBe('active'); }); + expect(result.current[0].status).toBe('active'); }); }); diff --git a/yarn.lock b/yarn.lock index afe45b0b2fe..1596d9f79cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -285,7 +285,21 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.0, @babel/helper-plugin-utils@npm:^7.24.5, @babel/helper-plugin-utils@npm:^7.24.7, @babel/helper-plugin-utils@npm:^7.24.8, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.0, @babel/helper-plugin-utils@npm:^7.24.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": + version: 7.24.5 + resolution: "@babel/helper-plugin-utils@npm:7.24.5" + checksum: 10/6e11ca5da73e6bd366848236568c311ac10e433fc2034a6fe6243af28419b07c93b4386f87bbc940aa058b7c83f370ef58f3b0fd598106be040d21a3d1c14276 + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-plugin-utils@npm:7.24.7" + checksum: 10/dad51622f0123fdba4e2d40a81a6b7d6ef4b1491b2f92fd9749447a36bde809106cf117358705057a2adc8fd73d5dc090222e0561b1213dae8601c8367f5aac8 + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.24.8": version: 7.24.8 resolution: "@babel/helper-plugin-utils@npm:7.24.8" checksum: 10/adbc9fc1142800a35a5eb0793296924ee8057fe35c61657774208670468a9fbfbb216f2d0bc46c680c5fefa785e5ff917cc1674b10bd75cdf9a6aa3444780630 @@ -347,6 +361,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-string-parser@npm:7.24.7" + checksum: 10/603d8d962bbe89907aa99a8f19a006759ab7b2464615f20a6a22e3e2e8375af37ddd0e5175c9e622e1c4b2d83607ffb41055a59d0ce34404502af30fde573a5c + languageName: node + linkType: hard + "@babel/helper-string-parser@npm:^7.24.8": version: 7.24.8 resolution: "@babel/helper-string-parser@npm:7.24.8" @@ -1605,7 +1626,18 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.22.5, @babel/types@npm:^7.24.0, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.24.9, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.22.5, @babel/types@npm:^7.24.0, @babel/types@npm:^7.24.7, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": + version: 7.24.7 + resolution: "@babel/types@npm:7.24.7" + dependencies: + "@babel/helper-string-parser": "npm:^7.24.7" + "@babel/helper-validator-identifier": "npm:^7.24.7" + to-fast-properties: "npm:^2.0.0" + checksum: 10/ad3c8c0d6fb4acb0bb74bb5b4bb849b181bf6185677ef9c59c18856c81e43628d0858253cf232f0eca806f02e08eff85a1d3e636a3e94daea737597796b0b430 + languageName: node + linkType: hard + +"@babel/types@npm:^7.24.8, @babel/types@npm:^7.24.9": version: 7.24.9 resolution: "@babel/types@npm:7.24.9" dependencies: @@ -2460,7 +2492,16 @@ __metadata: languageName: node linkType: hard -"@floating-ui/dom@npm:^1.0.0, @floating-ui/dom@npm:^1.0.1": +"@floating-ui/core@npm:^1.6.0": + version: 1.6.0 + resolution: "@floating-ui/core@npm:1.6.0" + dependencies: + "@floating-ui/utils": "npm:^0.2.1" + checksum: 10/d6a47cacde193cd8ccb4c268b91ccc4ca254dffaec6242b07fd9bcde526044cc976d27933a7917f9a671de0a0e27f8d358f46400677dbd0c8199de293e9746e1 + languageName: node + linkType: hard + +"@floating-ui/dom@npm:^1.0.0": version: 1.6.5 resolution: "@floating-ui/dom@npm:1.6.5" dependencies: @@ -2470,6 +2511,16 @@ __metadata: languageName: node linkType: hard +"@floating-ui/dom@npm:^1.0.1": + version: 1.6.1 + resolution: "@floating-ui/dom@npm:1.6.1" + dependencies: + "@floating-ui/core": "npm:^1.6.0" + "@floating-ui/utils": "npm:^0.2.1" + checksum: 10/c010feb55be37662eb4cc8d0a22e21359c25247bbdcd9557617fd305cf08c8f020435b17e4b4f410201ba9abe3a0dd96b5c42d56e85f7a5e11e7d30b85afc116 + languageName: node + linkType: hard + "@floating-ui/react-dom@npm:^2.1.1": version: 2.1.1 resolution: "@floating-ui/react-dom@npm:2.1.1" @@ -2503,6 +2554,13 @@ __metadata: languageName: node linkType: hard +"@floating-ui/utils@npm:^0.2.1": + version: 0.2.1 + resolution: "@floating-ui/utils@npm:0.2.1" + checksum: 10/33c9ab346e7b05c5a1e6a95bc902aafcfc2c9d513a147e2491468843bd5607531b06d0b9aa56aa491cbf22a6c2495c18ccfc4c0344baec54a689a7bb8e4898d6 + languageName: node + linkType: hard + "@formatjs/ecma402-abstract@npm:1.12.0": version: 1.12.0 resolution: "@formatjs/ecma402-abstract@npm:1.12.0" @@ -4953,7 +5011,7 @@ __metadata: languageName: node linkType: hard -"@npmcli/package-json@npm:5.2.0, @npmcli/package-json@npm:^5.0.0, @npmcli/package-json@npm:^5.1.0": +"@npmcli/package-json@npm:5.2.0, @npmcli/package-json@npm:^5.1.0": version: 5.2.0 resolution: "@npmcli/package-json@npm:5.2.0" dependencies: @@ -4968,6 +5026,21 @@ __metadata: languageName: node linkType: hard +"@npmcli/package-json@npm:^5.0.0": + version: 5.1.0 + resolution: "@npmcli/package-json@npm:5.1.0" + dependencies: + "@npmcli/git": "npm:^5.0.0" + glob: "npm:^10.2.2" + hosted-git-info: "npm:^7.0.0" + json-parse-even-better-errors: "npm:^3.0.0" + normalize-package-data: "npm:^6.0.0" + proc-log: "npm:^4.0.0" + semver: "npm:^7.5.3" + checksum: 10/0e5cb5eff32cf80234525160a702c91a38e4b98ab74e34e2632b43c4350dbad170bd835989cc7d6e18d24798e3242e45b60f3d5e26bd128fe1c4529931105f8e + languageName: node + linkType: hard + "@npmcli/promise-spawn@npm:^7.0.0": version: 7.0.2 resolution: "@npmcli/promise-spawn@npm:7.0.2" @@ -5833,7 +5906,7 @@ __metadata: languageName: node linkType: hard -"@radix-ui/react-portal@npm:1.0.4, @radix-ui/react-portal@npm:^1.0.1": +"@radix-ui/react-portal@npm:1.0.4": version: 1.0.4 resolution: "@radix-ui/react-portal@npm:1.0.4" dependencies: @@ -5853,6 +5926,26 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-portal@npm:^1.0.1": + version: 1.0.3 + resolution: "@radix-ui/react-portal@npm:1.0.3" + dependencies: + "@babel/runtime": "npm:^7.13.10" + "@radix-ui/react-primitive": "npm:1.0.3" + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10/d352bcd6ad65eb43c9e0d72d0755c2aae85e03fb287770866262be3a2d5302b2885aee3cd99f2bbf62ecd14fcb1460703f1dcdc40351f77ad887b931c6f0012a + languageName: node + linkType: hard + "@radix-ui/react-presence@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/react-presence@npm:1.0.1" @@ -8980,7 +9073,16 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:20.14.13, @types/node@npm:>=13.7.0, @types/node@npm:^20.11.16": +"@types/node@npm:*, @types/node@npm:>=13.7.0, @types/node@npm:^20.11.16": + version: 20.14.2 + resolution: "@types/node@npm:20.14.2" + dependencies: + undici-types: "npm:~5.26.4" + checksum: 10/c38e47b190fa0a8bdfde24b036dddcf9401551f2fb170a90ff33625c7d6f218907e81c74e0fa6e394804a32623c24c60c50e249badc951007830f0d02c48ee0f + languageName: node + linkType: hard + +"@types/node@npm:20.14.13": version: 20.14.13 resolution: "@types/node@npm:20.14.13" dependencies: @@ -9278,7 +9380,7 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:7.5.8, @types/semver@npm:^7.3.4, @types/semver@npm:^7.5.0, @types/semver@npm:^7.5.8": +"@types/semver@npm:7.5.8, @types/semver@npm:^7.3.12, @types/semver@npm:^7.3.4, @types/semver@npm:^7.5.0, @types/semver@npm:^7.5.8": version: 7.5.8 resolution: "@types/semver@npm:7.5.8" checksum: 10/3496808818ddb36deabfe4974fd343a78101fa242c4690044ccdc3b95dcf8785b494f5d628f2f47f38a702f8db9c53c67f47d7818f2be1b79f2efb09692e1178 @@ -9606,6 +9708,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/scope-manager@npm:5.62.0" + dependencies: + "@typescript-eslint/types": "npm:5.62.0" + "@typescript-eslint/visitor-keys": "npm:5.62.0" + checksum: 10/e827770baa202223bc0387e2fd24f630690809e460435b7dc9af336c77322290a770d62bd5284260fa881c86074d6a9fd6c97b07382520b115f6786b8ed499da + languageName: node + linkType: hard + "@typescript-eslint/scope-manager@npm:6.18.1": version: 6.18.1 resolution: "@typescript-eslint/scope-manager@npm:6.18.1" @@ -9670,6 +9782,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/types@npm:5.62.0" + checksum: 10/24e8443177be84823242d6729d56af2c4b47bfc664dd411a1d730506abf2150d6c31bdefbbc6d97c8f91043e3a50e0c698239dcb145b79bb6b0c34469aaf6c45 + languageName: node + linkType: hard + "@typescript-eslint/types@npm:6.18.1": version: 6.18.1 resolution: "@typescript-eslint/types@npm:6.18.1" @@ -9691,6 +9810,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.62.0" + dependencies: + "@typescript-eslint/types": "npm:5.62.0" + "@typescript-eslint/visitor-keys": "npm:5.62.0" + debug: "npm:^4.3.4" + globby: "npm:^11.1.0" + is-glob: "npm:^4.0.3" + semver: "npm:^7.3.7" + tsutils: "npm:^3.21.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/06c975eb5f44b43bd19fadc2e1023c50cf87038fe4c0dd989d4331c67b3ff509b17fa60a3251896668ab4d7322bdc56162a9926971218d2e1a1874d2bef9a52e + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:6.18.1": version: 6.18.1 resolution: "@typescript-eslint/typescript-estree@npm:6.18.1" @@ -9782,6 +9919,24 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:^5.58.0": + version: 5.62.0 + resolution: "@typescript-eslint/utils@npm:5.62.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.2.0" + "@types/json-schema": "npm:^7.0.9" + "@types/semver": "npm:^7.3.12" + "@typescript-eslint/scope-manager": "npm:5.62.0" + "@typescript-eslint/types": "npm:5.62.0" + "@typescript-eslint/typescript-estree": "npm:5.62.0" + eslint-scope: "npm:^5.1.1" + semver: "npm:^7.3.7" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: 10/15ef13e43998a082b15f85db979f8d3ceb1f9ce4467b8016c267b1738d5e7cdb12aa90faf4b4e6dd6486c236cf9d33c463200465cf25ff997dbc0f12358550a1 + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:^6.0.0 || ^7.0.0": version: 7.8.0 resolution: "@typescript-eslint/utils@npm:7.8.0" @@ -9799,6 +9954,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.62.0" + dependencies: + "@typescript-eslint/types": "npm:5.62.0" + eslint-visitor-keys: "npm:^3.3.0" + checksum: 10/dc613ab7569df9bbe0b2ca677635eb91839dfb2ca2c6fa47870a5da4f160db0b436f7ec0764362e756d4164e9445d49d5eb1ff0b87f4c058946ae9d8c92eb388 + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:6.18.1": version: 6.18.1 resolution: "@typescript-eslint/visitor-keys@npm:6.18.1" @@ -14011,7 +14176,7 @@ __metadata: languageName: node linkType: hard -"dedent@npm:1.5.3, dedent@npm:^1.0.0": +"dedent@npm:1.5.3": version: 1.5.3 resolution: "dedent@npm:1.5.3" peerDependencies: @@ -14030,6 +14195,18 @@ __metadata: languageName: node linkType: hard +"dedent@npm:^1.0.0": + version: 1.5.1 + resolution: "dedent@npm:1.5.1" + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + checksum: 10/fc00a8bc3dfb7c413a778dc40ee8151b6c6ff35159d641f36ecd839c1df5c6e0ec5f4992e658c82624a1a62aaecaffc23b9c965ceb0bbf4d698bfc16469ac27d + languageName: node + linkType: hard + "deep-freeze@npm:0.0.1": version: 0.0.1 resolution: "deep-freeze@npm:0.0.1" @@ -14771,7 +14948,7 @@ __metadata: languageName: node linkType: hard -"envinfo@npm:7.13.0, envinfo@npm:^7.7.3": +"envinfo@npm:7.13.0": version: 7.13.0 resolution: "envinfo@npm:7.13.0" bin: @@ -14780,6 +14957,15 @@ __metadata: languageName: node linkType: hard +"envinfo@npm:^7.7.3": + version: 7.8.1 + resolution: "envinfo@npm:7.8.1" + bin: + envinfo: dist/cli.js + checksum: 10/e7a2d71c7dfe398a4ffda0e844e242d2183ef2627f98e74e4cd71edd2af691c8707a2b34aacef92538c27b3daf9a360d32202f33c0a9f27f767c4e1c6ba8b522 + languageName: node + linkType: hard + "eol@npm:^0.9.1": version: 0.9.1 resolution: "eol@npm:0.9.1" @@ -15386,7 +15572,18 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:5.1.1": +"eslint-plugin-testing-library@npm:^6.2.2": + version: 6.2.2 + resolution: "eslint-plugin-testing-library@npm:6.2.2" + dependencies: + "@typescript-eslint/utils": "npm:^5.58.0" + peerDependencies: + eslint: ^7.5.0 || ^8.0.0 + checksum: 10/61947d0b81de1565c8627ec2d1e6636a8b6613cfe554a4671d011b3e88dfd77b498ce83b15bcf0a2df5570c44ad1d46d54058ed488f4e515d764196cbc6d65cf + languageName: node + linkType: hard + +"eslint-scope@npm:5.1.1, eslint-scope@npm:^5.1.1": version: 5.1.1 resolution: "eslint-scope@npm:5.1.1" dependencies: @@ -17268,6 +17465,7 @@ __metadata: eslint-plugin-no-barrel-files: "npm:^1.1.0" eslint-plugin-react: "npm:7.34.2" eslint-plugin-react-hooks: "npm:4.6.0" + eslint-plugin-testing-library: "npm:^6.2.2" eslint-scope: "npm:^8.0.0" eslint-webpack-plugin: "npm:4.2.0" expose-loader: "npm:5.0.0" @@ -18147,7 +18345,16 @@ __metadata: languageName: node linkType: hard -"i18next@npm:^23.0.0, i18next@npm:^23.11.5, i18next@npm:^23.5.1": +"i18next@npm:^23.0.0, i18next@npm:^23.5.1": + version: 23.11.3 + resolution: "i18next@npm:23.11.3" + dependencies: + "@babel/runtime": "npm:^7.23.2" + checksum: 10/9d562ade19d0beba16683ff94967a6dedc0a32ce335d203c5a160f075ac5a9a7a9adb164085a6b7b69328568bc932a65b92664834c2bf3e15d8f3bff90f15353 + languageName: node + linkType: hard + +"i18next@npm:^23.11.5": version: 23.11.5 resolution: "i18next@npm:23.11.5" dependencies: @@ -24316,7 +24523,7 @@ __metadata: languageName: node linkType: hard -"postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.0.15, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.1.0": +"postcss-selector-parser@npm:^6.0.10": version: 6.1.1 resolution: "postcss-selector-parser@npm:6.1.1" dependencies: @@ -24326,6 +24533,16 @@ __metadata: languageName: node linkType: hard +"postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.0.15, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4, postcss-selector-parser@npm:^6.1.0": + version: 6.1.0 + resolution: "postcss-selector-parser@npm:6.1.0" + dependencies: + cssesc: "npm:^3.0.0" + util-deprecate: "npm:^1.0.2" + checksum: 10/2f9e5045b8bbe674fed3b79dbcd3daf21f5188cd7baf179beac513710ec3d75a8fc8184a262c3aec1c628ad3fd8bdb29c5d8530f1c9c5a61a18e1980bb000945 + languageName: node + linkType: hard + "postcss-svgo@npm:^6.0.2": version: 6.0.2 resolution: "postcss-svgo@npm:6.0.2" @@ -29279,13 +29496,24 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.10.0, tslib@npm:^1.13.0": +"tslib@npm:^1.10.0, tslib@npm:^1.13.0, tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: 10/7dbf34e6f55c6492637adb81b555af5e3b4f9cc6b998fb440dac82d3b42bdc91560a35a5fb75e20e24a076c651438234da6743d139e4feabf0783f3cdfe1dddb languageName: node linkType: hard +"tsutils@npm:^3.21.0": + version: 3.21.0 + resolution: "tsutils@npm:3.21.0" + dependencies: + tslib: "npm:^1.8.1" + peerDependencies: + typescript: ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + checksum: 10/ea036bec1dd024e309939ffd49fda7a351c0e87a1b8eb049570dd119d447250e2c56e0e6c00554e8205760e7417793fdebff752a46e573fbe07d4f375502a5b2 + languageName: node + linkType: hard + "tuf-js@npm:^2.2.1": version: 2.2.1 resolution: "tuf-js@npm:2.2.1" @@ -29399,13 +29627,20 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^4.18.2, type-fest@npm:^4.9.0": +"type-fest@npm:^4.18.2": version: 4.18.3 resolution: "type-fest@npm:4.18.3" checksum: 10/eb750920d0ef3639177f581edd6489d972c5c5827abb602a9c9662889aad148a7d558257e36c563f1beb81a2e417faec52ecec9799b28531d8335856f91e6dff 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"