diff --git a/packages/manager/.changeset/pr-10070-tests-1705515017364.md b/packages/manager/.changeset/pr-10070-tests-1705515017364.md new file mode 100644 index 00000000000..b945522d1ce --- /dev/null +++ b/packages/manager/.changeset/pr-10070-tests-1705515017364.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tests +--- + +Add Cypress tests for restricted user billing flows ([#10070](https://github.com/linode/manager/pull/10070)) diff --git a/packages/manager/.changeset/pr-10070-tests-1705515031938.md b/packages/manager/.changeset/pr-10070-tests-1705515031938.md new file mode 100644 index 00000000000..d731be27795 --- /dev/null +++ b/packages/manager/.changeset/pr-10070-tests-1705515031938.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tests +--- + +Upgrade to Vitest 1.2.0 ([#10070](https://github.com/linode/manager/pull/10070)) diff --git a/packages/manager/cypress/e2e/core/billing/restricted-user-billing.spec.ts b/packages/manager/cypress/e2e/core/billing/restricted-user-billing.spec.ts new file mode 100644 index 00000000000..370be9b3c05 --- /dev/null +++ b/packages/manager/cypress/e2e/core/billing/restricted-user-billing.spec.ts @@ -0,0 +1,339 @@ +/** + * @file Integration tests for restricted user billing flows. + */ + +import { paymentMethodFactory, profileFactory } from '@src/factories'; +import { accountUserFactory } from '@src/factories/accountUsers'; +import { grantsFactory } from '@src/factories/grants'; +import { mockGetPaymentMethods, mockGetUser } from 'support/intercepts/account'; +import { + mockAppendFeatureFlags, + mockGetFeatureFlagClientstream, +} from 'support/intercepts/feature-flags'; +import { + mockGetProfile, + mockGetProfileGrants, +} from 'support/intercepts/profile'; +import { ui } from 'support/ui'; +import { makeFeatureFlagData } from 'support/util/feature-flags'; +import { randomLabel } from 'support/util/random'; + +// Tooltip message that appears on disabled billing action buttons for restricted users. +const restrictedUserTooltip = + 'To modify this content, please contact your administrator.'; + +// Tooltip message that appears on disabled billing action buttons for child users. +const childUserTooltip = + 'To modify this content, please contact your business partner.'; + +// Mock credit card payment method to use in tests. +const mockPaymentMethods = [ + paymentMethodFactory.build({ + data: { + card_type: 'Visa', + expiry: '12/2026', + last_four: '1234', + }, + is_default: false, + }), + paymentMethodFactory.build({ + data: { + card_type: 'Visa', + expiry: '12/2026', + last_four: '5678', + }, + is_default: true, + }), +]; + +/** + * Asserts that the billing contact "Edit" button is disabled. + * + * Additionally confirms that clicking the "Edit" button reveals a tooltip and + * does not open the "Edit Billing Contact Info" drawer. + * + * @param tooltipText - Expected tooltip message to be shown to the user. + */ +const assertEditBillingInfoDisabled = (tooltipText: string) => { + // Confirm Billing Contact section "Edit" button is disabled, then click it. + cy.get('[data-qa-contact-summary]') + .should('be.visible') + .within(() => { + ui.button + .findByTitle('Edit') + .should('be.visible') + .should('be.disabled') + .click(); + }); + + // Assert that "Edit Contact Billing Info" drawer does not open and that tooltip is revealed. + cy.get(`[data-qa-drawer-title="Edit Billing Contact Info"]`).should( + 'not.exist' + ); + ui.tooltip.findByText(tooltipText).should('be.visible'); +}; + +/** + * Asserts that the billing contact "Edit" button is enabled. + * + * Additionally confirms that clicking the "Edit" button opens the "Edit Billing + * Contact Info" drawer, then closes the drawer. + */ +const assertEditBillingInfoEnabled = () => { + cy.get('[data-qa-contact-summary]') + .should('be.visible') + .within(() => { + ui.button + .findByTitle('Edit') + .should('be.visible') + .should('be.enabled') + .click(); + }); + + ui.drawer + .findByTitle('Edit Billing Contact Info') + .should('be.visible') + .within(() => { + ui.drawerCloseButton.find().click(); + }); +}; + +/** + * Asserts that the "Add Payment Method" button is disabled. + * + * Additionally confirms that clicking the "Add Payment Method" button reveals + * a tooltip and does not open the "Add Payment Method" drawer. + * + * @param tooltipText - Expected tooltip message to be shown to the user. + */ +const assertAddPaymentMethodDisabled = (tooltipText: string) => { + // Confirm that payment method action menu items are disabled. + ui.actionMenu + .findByTitle('Action menu for card ending in 1234') + .should('be.visible') + .should('be.enabled') + .click(); + + ['Make a Payment', 'Make Default', 'Delete'].forEach((menuItem: string) => { + ui.actionMenuItem.findByTitle(menuItem).should('be.disabled'); + }); + + // Dismiss action menu. + cy.get('[data-qa-action-menu="true"]').click(); + + // Confirm Billing Summary section "Add Payment Method" button is disabled, then click it. + cy.get('[data-qa-billing-summary]') + .should('be.visible') + .within(() => { + ui.button + .findByTitle('Add Payment Method') + .should('be.visible') + .should('be.disabled') + .click(); + }); + + // Assert that "Add Payment Method" drawer does not open and that tooltip is revealed. + cy.get(`[data-qa-drawer-title="Add Payment Method"]`).should('not.exist'); + ui.tooltip.findByText(tooltipText).should('be.visible'); +}; + +/** + * Asserts that the "Add Payment Method" button is enabled. + * + * Additionally confirms that clicking the "Add Payment Method" button opens the + * "Add Payment Method" drawer, then closes the drawer. + */ +const assertAddPaymentMethodEnabled = () => { + // Confirm that payment method action menu items are enabled. + ui.actionMenu + .findByTitle('Action menu for card ending in 1234') + .should('be.visible') + .should('be.enabled') + .click(); + + ['Make a Payment', 'Make Default', 'Delete'].forEach((menuItem: string) => { + ui.actionMenuItem.findByTitle(menuItem).should('be.enabled'); + }); + + // Dismiss action menu. + cy.get('[data-qa-action-menu="true"]').click(); + + cy.get('[data-qa-billing-summary]') + .should('be.visible') + .within(() => { + ui.button + .findByTitle('Add Payment Method') + .should('be.visible') + .should('be.enabled') + .click(); + }); + + ui.drawer + .findByTitle('Add Payment Method') + .should('be.visible') + .within(() => { + ui.drawerCloseButton.find().click(); + }); +}; + +describe('restricted user billing flows', () => { + beforeEach(() => { + mockGetPaymentMethods(mockPaymentMethods); + }); + + // TODO Delete all of these tests when Parent/Child launches and flag is removed. + describe('Parent/Child feature disabled', () => { + beforeEach(() => { + // Mock the Parent/Child feature flag to be enabled. + mockAppendFeatureFlags({ + parentChildAccountAccess: makeFeatureFlagData(false), + }); + mockGetFeatureFlagClientstream(); + }); + + /* + * - Smoke test to confirm that regular users can edit billing information. + * - Confirms that billing action buttons are enabled and open their respective drawers on click. + * - Confirms that payment method action menu items are enabled. + */ + it('can edit billing information', () => { + // The flow prior to Parent/Child does not account for user privileges, instead relying + // on the API to forbid actions when the user does not have the required privileges. + // Because the API is doing the heavy lifting, we only need to ensure that the billing action + // buttons behave as expected for this smoke test. + const mockProfile = profileFactory.build({ + username: randomLabel(), + restricted: false, + }); + + const mockUser = accountUserFactory.build({ + username: mockProfile.username, + user_type: null, + restricted: false, + }); + + // Confirm button behavior for regular users. + mockGetProfile(mockProfile); + mockGetUser(mockUser); + cy.visitWithLogin('/account/billing'); + assertEditBillingInfoEnabled(); + assertAddPaymentMethodEnabled(); + }); + }); + + describe('Parent/Child feature enabled', () => { + beforeEach(() => { + // Mock the Parent/Child feature flag to be enabled. + // TODO Delete this `beforeEach()` block when Parent/Child launches and flag is removed. + mockAppendFeatureFlags({ + parentChildAccountAccess: makeFeatureFlagData(true), + }); + mockGetFeatureFlagClientstream(); + }); + + /* + * - Confirms that users with read-only account access cannot edit billing information. + * - Confirms UX enhancements are applied when parent/child feature flag is enabled. + * - Confirms that "Edit" and "Add Payment Method" buttons are disabled and have informational tooltips. + * - Confirms that clicking "Edit" and "Add Payment Method" does not open their respective drawers when disabled. + * - Confirms that button tooltip text reflects read-only account access. + * - Confirms that payment method action menu items are disabled. + */ + it('cannot edit billing information with read-only account access', () => { + const mockProfile = profileFactory.build({ + username: randomLabel(), + restricted: true, + }); + + const mockUser = accountUserFactory.build({ + username: mockProfile.username, + restricted: true, + user_type: null, + }); + + const mockGrants = grantsFactory.build({ + global: { + account_access: 'read_only', + }, + }); + + mockGetProfile(mockProfile); + mockGetProfileGrants(mockGrants); + mockGetUser(mockUser); + cy.visitWithLogin('/account/billing'); + + assertEditBillingInfoDisabled(restrictedUserTooltip); + assertAddPaymentMethodDisabled(restrictedUserTooltip); + }); + + /* + * - Confirms that child users cannot edit billing information. + * - Confirms that UX enhancements are applied when parent/child feature flag is enabled. + * - Confirms that "Edit" and "Add Payment Method" buttons are disabled and have informational tooltips. + * - Confirms that clicking "Edit" and "Add Payment Method" does not open their respective drawers when disabled. + * - Confirms that button tooltip text reflects child user access. + * - Confirms that payment method action menu items are disabled. + */ + it('cannot edit billing information as child account', () => { + const mockProfile = profileFactory.build({ + username: randomLabel(), + }); + + const mockUser = accountUserFactory.build({ + username: mockProfile.username, + user_type: 'child', + }); + + mockGetProfile(mockProfile); + mockGetUser(mockUser); + cy.visitWithLogin('/account/billing'); + + assertEditBillingInfoDisabled(childUserTooltip); + assertAddPaymentMethodDisabled(childUserTooltip); + }); + + /* + * - Smoke test to confirm that regular and parent users can edit billing information. + * - Confirms that billing action buttons are enabled and open their respective drawers on click. + */ + it('can edit billing information as a regular user and as a parent user', () => { + const mockProfileRegular = profileFactory.build({ + username: randomLabel(), + restricted: false, + }); + + const mockUserRegular = accountUserFactory.build({ + username: mockProfileRegular.username, + user_type: null, + restricted: false, + }); + + const mockProfileParent = profileFactory.build({ + username: randomLabel(), + restricted: false, + }); + + const mockUserParent = accountUserFactory.build({ + username: mockProfileParent.username, + user_type: 'parent', + restricted: false, + }); + + // Confirm button behavior for regular users. + mockGetProfile(mockProfileRegular); + mockGetUser(mockUserRegular); + cy.visitWithLogin('/account/billing'); + cy.findByText(mockProfileRegular.username); + assertEditBillingInfoEnabled(); + assertAddPaymentMethodEnabled(); + + // Confirm button behavior for parent users. + mockGetProfile(mockProfileParent); + mockGetUser(mockUserParent); + cy.visitWithLogin('/account/billing'); + cy.findByText(mockProfileParent.username); + assertEditBillingInfoEnabled(); + assertAddPaymentMethodEnabled(); + }); + }); +}); diff --git a/packages/manager/cypress/support/intercepts/profile.ts b/packages/manager/cypress/support/intercepts/profile.ts index 6a83e58ef51..60e6313fdca 100644 --- a/packages/manager/cypress/support/intercepts/profile.ts +++ b/packages/manager/cypress/support/intercepts/profile.ts @@ -8,6 +8,7 @@ import { paginateResponse } from 'support/util/paginate'; import { makeResponse } from 'support/util/response'; import type { + Grants, OAuthClient, Profile, SecurityQuestionsData, @@ -46,7 +47,24 @@ export const mockGetProfile = (profile: Profile): Cypress.Chainable => { export const mockUpdateProfile = ( profile: Profile ): Cypress.Chainable => { - return cy.intercept('PUT', apiMatcher(`profile`), makeResponse(profile)); + return cy.intercept('PUT', apiMatcher('profile'), makeResponse(profile)); +}; + +/** + * Intercepts GET request to fetch profile grants and mocks response. + * + * @param grants - Grants object with which to mock response. + * + * @returns Cypress chainable. + */ +export const mockGetProfileGrants = ( + grants: Grants +): Cypress.Chainable => { + return cy.intercept( + 'GET', + apiMatcher('profile/grants'), + makeResponse(grants) + ); }; /** diff --git a/packages/manager/cypress/support/ui/index.ts b/packages/manager/cypress/support/ui/index.ts index 22044a1c082..26d8b89ac17 100644 --- a/packages/manager/cypress/support/ui/index.ts +++ b/packages/manager/cypress/support/ui/index.ts @@ -15,6 +15,7 @@ import * as select from './select'; import * as tabList from './tab-list'; import * as toast from './toast'; import * as toggle from './toggle'; +import * as tooltip from './tooltip'; import * as userMenu from './user-menu'; export const ui = { @@ -35,5 +36,6 @@ export const ui = { ...toast, ...tabList, ...toggle, + ...tooltip, ...userMenu, }; diff --git a/packages/manager/cypress/support/ui/tooltip.ts b/packages/manager/cypress/support/ui/tooltip.ts new file mode 100644 index 00000000000..afd2aba84fc --- /dev/null +++ b/packages/manager/cypress/support/ui/tooltip.ts @@ -0,0 +1,11 @@ +/** + * Tooltip UI helper. + */ +export const tooltip = { + /** + * Finds a tooltip that has the given text. + */ + findByText: (text: string): Cypress.Chainable => { + return cy.document().its('body').find(`[data-qa-tooltip="${text}"]`); + }, +}; diff --git a/packages/manager/package.json b/packages/manager/package.json index 52fb9ecdc28..42616f772c0 100644 --- a/packages/manager/package.json +++ b/packages/manager/package.json @@ -209,7 +209,7 @@ "ts-node": "^10.9.2", "vite": "^5.0.7", "vite-plugin-svgr": "^3.2.0", - "vitest": "^1.0.4" + "vitest": "^1.2.0" }, "browserslist": [ ">1%", diff --git a/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.tsx b/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.tsx index fe28b09b3dc..93fee8560f6 100644 --- a/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.tsx +++ b/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.tsx @@ -16,6 +16,14 @@ import { queryKey } from 'src/queries/accountPayment'; import { ThirdPartyPayment } from './ThirdPartyPayment'; interface Props { + /** + * Whether the user is a child user. + */ + isChildUser?: boolean | undefined; + /** + * Whether the user is a restricted user. + */ + isRestrictedUser?: boolean | undefined; /** * Function called when the delete button in the Action Menu is pressed. */ @@ -32,7 +40,7 @@ interface Props { */ export const PaymentMethodRow = (props: Props) => { const theme = useTheme(); - const { onDelete, paymentMethod } = props; + const { isRestrictedUser, onDelete, paymentMethod } = props; const { is_default, type } = paymentMethod; const history = useHistory(); const { enqueueSnackbar } = useSnackbar(); @@ -51,6 +59,7 @@ export const PaymentMethodRow = (props: Props) => { const actions: Action[] = [ { + disabled: isRestrictedUser, onClick: () => { history.push({ pathname: '/account/billing/make-payment/', @@ -60,7 +69,7 @@ export const PaymentMethodRow = (props: Props) => { title: 'Make a Payment', }, { - disabled: paymentMethod.is_default, + disabled: isRestrictedUser || paymentMethod.is_default, onClick: () => makeDefault(paymentMethod.id), title: 'Make Default', tooltip: paymentMethod.is_default @@ -68,7 +77,7 @@ export const PaymentMethodRow = (props: Props) => { : undefined, }, { - disabled: paymentMethod.is_default, + disabled: isRestrictedUser || paymentMethod.is_default, onClick: onDelete, title: 'Delete', tooltip: paymentMethod.is_default diff --git a/packages/manager/src/components/Tooltip.tsx b/packages/manager/src/components/Tooltip.tsx index 0c575080fee..c325f07cc4d 100644 --- a/packages/manager/src/components/Tooltip.tsx +++ b/packages/manager/src/components/Tooltip.tsx @@ -7,7 +7,7 @@ import type { TooltipProps } from '@mui/material/Tooltip'; * Tooltips display informative text when users hover over, focus on, or tap an element. */ export const Tooltip = (props: TooltipProps) => { - return <_Tooltip {...props} />; + return <_Tooltip data-qa-tooltip={props.title} {...props} />; }; export { tooltipClasses }; export type { TooltipProps }; diff --git a/packages/manager/src/features/Billing/BillingPanels/ContactInfoPanel/ContactInformation.test.tsx b/packages/manager/src/features/Billing/BillingPanels/ContactInfoPanel/ContactInformation.test.tsx index 6904733f08c..653217ba7a1 100644 --- a/packages/manager/src/features/Billing/BillingPanels/ContactInfoPanel/ContactInformation.test.tsx +++ b/packages/manager/src/features/Billing/BillingPanels/ContactInfoPanel/ContactInformation.test.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; +import { grantsFactory } from 'src/factories/grants'; import { accountUserFactory } from 'src/factories/accountUsers'; import { renderWithTheme } from 'src/utilities/testHelpers'; @@ -35,14 +36,13 @@ vi.mock('src/queries/accountUsers', async () => { }; }); -// TODO: When we figure out issue with Vitest circular dependencies -// vi.mock('src/queries/profile', async () => { -// const actual = await vi.importActual('src/queries/profile'); -// return { -// ...actual, -// useGrants: queryMocks.useGrants, -// }; -// }); +vi.mock('src/queries/profile', async () => { + const actual = await vi.importActual('src/queries/profile'); + return { + ...actual, + useGrants: queryMocks.useGrants, + }; +}); queryMocks.useAccountUser.mockReturnValue({ data: accountUserFactory.build({ user_type: 'parent' }), @@ -64,21 +64,20 @@ describe('Edit Contact Information', () => { ); }); - // TODO: When we figure out issue with Vitest circular dependencies - // it('should be disabled for non-parent/child restricted users', () => { - // queryMocks.useGrants.mockReturnValue({ - // data: grantsFactory.build({ - // global: { - // account_access: 'read_only', - // }, - // }), - // }); + it('should be disabled for non-parent/child restricted users', () => { + queryMocks.useGrants.mockReturnValue({ + data: grantsFactory.build({ + global: { + account_access: 'read_only', + }, + }), + }); - // const { getByTestId } = renderWithTheme(); + const { getByTestId } = renderWithTheme(); - // expect(getByTestId(EDIT_BUTTON_ID)).toHaveAttribute( - // 'aria-disabled', - // 'true' - // ); - // }); + expect(getByTestId(EDIT_BUTTON_ID)).toHaveAttribute( + 'aria-disabled', + 'true' + ); + }); }); diff --git a/packages/manager/src/features/Billing/BillingPanels/PaymentInfoPanel/PaymentInformation.tsx b/packages/manager/src/features/Billing/BillingPanels/PaymentInfoPanel/PaymentInformation.tsx index 30a71314c5e..edfdb3a76cd 100644 --- a/packages/manager/src/features/Billing/BillingPanels/PaymentInfoPanel/PaymentInformation.tsx +++ b/packages/manager/src/features/Billing/BillingPanels/PaymentInfoPanel/PaymentInformation.tsx @@ -123,6 +123,8 @@ const PaymentInformation = (props: Props) => { {!isAkamaiCustomer ? ( void; paymentMethods: PaymentMethod[] | undefined; @@ -17,6 +19,8 @@ interface Props { const PaymentMethods = ({ error, + isChildUser, + isRestrictedUser, loading, openDeleteDialog, paymentMethods, @@ -59,6 +63,8 @@ const PaymentMethods = ({ <> {paymentMethods.map((paymentMethod: PaymentMethod) => ( openDeleteDialog(paymentMethod)} paymentMethod={paymentMethod} diff --git a/packages/manager/src/features/Billing/billingUtils.ts b/packages/manager/src/features/Billing/billingUtils.ts index 964f11f8eba..1b28b49429c 100644 --- a/packages/manager/src/features/Billing/billingUtils.ts +++ b/packages/manager/src/features/Billing/billingUtils.ts @@ -71,5 +71,5 @@ export const getDisabledTooltipText = ({ ? `${RESTRICTED_SECTION_EDIT_MESSAGE} ${ isChildUser ? BUSINESS_PARTNER : ADMINISTRATOR }.` - : undefined; + : ''; }; diff --git a/packages/manager/src/features/Billing/constants.ts b/packages/manager/src/features/Billing/constants.ts index a0e5902c360..22ddbf7255f 100644 --- a/packages/manager/src/features/Billing/constants.ts +++ b/packages/manager/src/features/Billing/constants.ts @@ -1,4 +1,4 @@ export const RESTRICTED_SECTION_EDIT_MESSAGE = - 'To edit this section, please contact your'; + 'To modify this content, please contact your'; export const ADD_PAYMENT_METHOD = 'Add Payment Method'; export const EDIT_BILLING_CONTACT = 'Edit'; diff --git a/yarn.lock b/yarn.lock index 98aceb8e809..6035d09a326 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5244,13 +5244,13 @@ "@vitest/utils" "1.0.1" chai "^4.3.10" -"@vitest/expect@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.0.4.tgz#2751018b6e527841043e046ff424304453a0a024" - integrity sha512-/NRN9N88qjg3dkhmFcCBwhn/Ie4h064pY3iv7WLRsDJW7dXnEgeoa8W9zy7gIPluhz6CkgqiB3HmpIXgmEY5dQ== +"@vitest/expect@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.2.0.tgz#de93f5c32c2781c41415a8c3a6e48e1c023d6613" + integrity sha512-H+2bHzhyvgp32o7Pgj2h9RTHN0pgYaoi26Oo3mE+dCi1PAqV31kIIVfTbqMO3Bvshd5mIrJLc73EwSRrbol9Lw== dependencies: - "@vitest/spy" "1.0.4" - "@vitest/utils" "1.0.4" + "@vitest/spy" "1.2.0" + "@vitest/utils" "1.2.0" chai "^4.3.10" "@vitest/runner@1.0.1": @@ -5262,12 +5262,12 @@ p-limit "^5.0.0" pathe "^1.1.1" -"@vitest/runner@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.0.4.tgz#c4dcb88c07f40b91293ff1331747ee58fad6d5e4" - integrity sha512-rhOQ9FZTEkV41JWXozFM8YgOqaG9zA7QXbhg5gy6mFOVqh4PcupirIJ+wN7QjeJt8S8nJRYuZH1OjJjsbxAXTQ== +"@vitest/runner@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.2.0.tgz#84775f0f5c48620ff1943a45c19863355791c6d9" + integrity sha512-vaJkDoQaNUTroT70OhM0NPznP7H3WyRwt4LvGwCVYs/llLaqhoSLnlIhUClZpbF5RgAee29KRcNz0FEhYcgxqA== dependencies: - "@vitest/utils" "1.0.4" + "@vitest/utils" "1.2.0" p-limit "^5.0.0" pathe "^1.1.1" @@ -5280,10 +5280,10 @@ pathe "^1.1.1" pretty-format "^29.7.0" -"@vitest/snapshot@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.0.4.tgz#7020983b3963b473237fea08d347ea83b266b9bb" - integrity sha512-vkfXUrNyNRA/Gzsp2lpyJxh94vU2OHT1amoD6WuvUAA12n32xeVZQ0KjjQIf8F6u7bcq2A2k969fMVxEsxeKYA== +"@vitest/snapshot@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.2.0.tgz#2fcddb5c6e8a9d2fc9f18ea2f8fd39b1b6e691b4" + integrity sha512-P33EE7TrVgB3HDLllrjK/GG6WSnmUtWohbwcQqmm7TAk9AVHpdgf7M3F3qRHKm6vhr7x3eGIln7VH052Smo6Kw== dependencies: magic-string "^0.30.5" pathe "^1.1.1" @@ -5296,10 +5296,10 @@ dependencies: tinyspy "^2.2.0" -"@vitest/spy@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.0.4.tgz#e182c78fb9b1178ff789ad7eb4560ba6750e6e9b" - integrity sha512-9ojTFRL1AJVh0hvfzAQpm0QS6xIS+1HFIw94kl/1ucTfGCaj1LV/iuJU4Y6cdR03EzPDygxTHwE1JOm+5RCcvA== +"@vitest/spy@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.2.0.tgz#61104de4c19a3addefff021d884c9e20dc17ebcd" + integrity sha512-MNxSAfxUaCeowqyyGwC293yZgk7cECZU9wGb8N1pYQ0yOn/SIr8t0l9XnGRdQZvNV/ZHBYu6GO/W3tj5K3VN1Q== dependencies: tinyspy "^2.2.0" @@ -5334,6 +5334,16 @@ loupe "^2.3.7" pretty-format "^29.7.0" +"@vitest/utils@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.2.0.tgz#deb9bdc3d094bf47f93a592a6a0b3946aa575e7a" + integrity sha512-FyD5bpugsXlwVpTcGLDf3wSPYy8g541fQt14qtzo8mJ4LdEpDKZ9mQy2+qdJm2TZRpjY5JLXihXCgIxiRJgi5g== + dependencies: + diff-sequences "^29.6.3" + estree-walker "^3.0.3" + loupe "^2.3.7" + pretty-format "^29.7.0" + "@xmldom/xmldom@^0.8.3": version "0.8.10" resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99" @@ -5410,6 +5420,11 @@ acorn-walk@^8.3.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.0.tgz#2097665af50fd0cf7a2dfccd2b9368964e66540f" integrity sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA== +acorn-walk@^8.3.1: + version "8.3.2" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" + integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== + acorn@^7.1.1, acorn@^7.4.1: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" @@ -8128,6 +8143,13 @@ estree-walker@^2.0.2: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -14431,10 +14453,10 @@ vite-node@1.0.1: picocolors "^1.0.0" vite "^5.0.0-beta.15 || ^5.0.0" -vite-node@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.0.4.tgz#36d6c49e3b5015967d883845561ed67abe6553cc" - integrity sha512-9xQQtHdsz5Qn8hqbV7UKqkm8YkJhzT/zr41Dmt5N7AlD8hJXw/Z7y0QiD5I8lnTthV9Rvcvi0QW7PI0Fq83ZPg== +vite-node@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.2.0.tgz#9a359804469203a54ac49daad3065f2fd0bfb9c3" + integrity sha512-ETnQTHeAbbOxl7/pyBck9oAPZZZo+kYnFt1uQDD+hPReOc+wCjXw4r4jHriBRuVDB5isHmPXxrfc1yJnfBERqg== dependencies: cac "^6.7.14" debug "^4.3.4" @@ -14500,17 +14522,17 @@ vitest@^1.0.1: vite-node "1.0.1" why-is-node-running "^2.2.2" -vitest@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.0.4.tgz#c4b39ba4fcba674499c90e28f4d8dd16fa1d4eb3" - integrity sha512-s1GQHp/UOeWEo4+aXDOeFBJwFzL6mjycbQwwKWX2QcYfh/7tIerS59hWQ20mxzupTJluA2SdwiBuWwQHH67ckg== - dependencies: - "@vitest/expect" "1.0.4" - "@vitest/runner" "1.0.4" - "@vitest/snapshot" "1.0.4" - "@vitest/spy" "1.0.4" - "@vitest/utils" "1.0.4" - acorn-walk "^8.3.0" +vitest@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.2.0.tgz#2ddff4a32ed992339655f243525c0e187b5af6d9" + integrity sha512-Ixs5m7BjqvLHXcibkzKRQUvD/XLw0E3rvqaCMlrm/0LMsA0309ZqYvTlPzkhh81VlEyVZXFlwWnkhb6/UMtcaQ== + dependencies: + "@vitest/expect" "1.2.0" + "@vitest/runner" "1.2.0" + "@vitest/snapshot" "1.2.0" + "@vitest/spy" "1.2.0" + "@vitest/utils" "1.2.0" + acorn-walk "^8.3.1" cac "^6.7.14" chai "^4.3.10" debug "^4.3.4" @@ -14524,7 +14546,7 @@ vitest@^1.0.4: tinybench "^2.5.1" tinypool "^0.8.1" vite "^5.0.0" - vite-node "1.0.4" + vite-node "1.2.0" why-is-node-running "^2.2.2" w3c-xmlserializer@^4.0.0: