diff --git a/apps/meteor/client/components/ABAC/ABACUpsellModal/ABACUpsellModal.spec.tsx b/apps/meteor/client/components/ABAC/ABACUpsellModal/ABACUpsellModal.spec.tsx new file mode 100644 index 0000000000000..c2fd14fb4feb9 --- /dev/null +++ b/apps/meteor/client/components/ABAC/ABACUpsellModal/ABACUpsellModal.spec.tsx @@ -0,0 +1,111 @@ +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { axe } from 'jest-axe'; + +import ABACUpsellModal from './ABACUpsellModal'; + +// Mock the hooks used by ABACUpsellModal +jest.mock('../../../hooks/useHasLicenseModule', () => ({ + useHasLicenseModule: jest.fn(() => false), +})); + +jest.mock('../../GenericUpsellModal/hooks', () => ({ + useUpsellActions: jest.fn(() => ({ + shouldShowUpsell: true, + cloudWorkspaceHadTrial: false, + handleManageSubscription: jest.fn(), + handleTalkToSales: jest.fn(), + })), +})); + +// Mock getURL utility +jest.mock('../../../../app/utils/client', () => ({ + getURL: (url: string) => url, +})); + +const appRoot = mockAppRoot() + .withTranslations('en', 'core', { + Premium_capability: 'Premium capability', + Attribute_based_access_control: 'Attribute-Based Access Control', + Attribute_based_access_control_title: 'Automate complex access management across your entire organization', + Attribute_based_access_control_description: + 'ABAC automates room access, granting or revoking access based on dynamic user attributes rather than fixed roles.', + Upgrade: 'Upgrade', + Cancel: 'Cancel', + }) + .build(); + +describe('ABACUpsellModal', () => { + const mockOnClose = jest.fn(); + const mockOnConfirm = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should render the modal with correct content', () => { + const { baseElement } = render(, { wrapper: appRoot }); + expect(baseElement).toMatchSnapshot(); + }); + + it('should have no accessibility violations', async () => { + const { container } = render(, { wrapper: appRoot }); + const results = await axe(container); + expect(results).toHaveNoViolations(); + }); + + it('should call onConfirm when upgrade button is clicked', async () => { + const user = userEvent.setup(); + render(, { wrapper: appRoot }); + + const upgradeButton = screen.getByRole('button', { name: 'Upgrade' }); + await user.click(upgradeButton); + + expect(mockOnConfirm).toHaveBeenCalledTimes(1); + expect(mockOnClose).not.toHaveBeenCalled(); + }); + + it('should call onClose when cancel button is clicked', async () => { + const user = userEvent.setup(); + render(, { wrapper: appRoot }); + + const cancelButton = screen.getByRole('button', { name: 'Cancel' }); + await user.click(cancelButton); + + expect(mockOnClose).toHaveBeenCalledTimes(1); + expect(mockOnConfirm).not.toHaveBeenCalled(); + }); + + it('should call onClose when close button is clicked', async () => { + const user = userEvent.setup(); + render(, { wrapper: appRoot }); + + // Look for close button (usually has aria-label or is the X button) + const closeButton = screen.getByRole('button', { name: /close/i }); + await user.click(closeButton); + + expect(mockOnClose).toHaveBeenCalledTimes(1); + expect(mockOnConfirm).not.toHaveBeenCalled(); + }); + + it('should handle multiple button clicks correctly', async () => { + const user = userEvent.setup(); + render(, { wrapper: appRoot }); + + const upgradeButton = screen.getByRole('button', { name: 'Upgrade' }); + const cancelButton = screen.getByRole('button', { name: 'Cancel' }); + + // Click upgrade first + await user.click(upgradeButton); + expect(mockOnConfirm).toHaveBeenCalledTimes(1); + + // Click cancel + await user.click(cancelButton); + expect(mockOnClose).toHaveBeenCalledTimes(1); + + // Total calls + expect(mockOnConfirm).toHaveBeenCalledTimes(1); + expect(mockOnClose).toHaveBeenCalledTimes(1); + }); +}); diff --git a/apps/meteor/client/components/ABAC/ABACUpsellModal/ABACUpsellModal.stories.tsx b/apps/meteor/client/components/ABAC/ABACUpsellModal/ABACUpsellModal.stories.tsx new file mode 100644 index 0000000000000..f82c261020522 --- /dev/null +++ b/apps/meteor/client/components/ABAC/ABACUpsellModal/ABACUpsellModal.stories.tsx @@ -0,0 +1,42 @@ +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { action } from '@storybook/addon-actions'; +import type { Meta } from '@storybook/react'; + +import ABACUpsellModal from './ABACUpsellModal'; + +const meta = { + component: ABACUpsellModal, + parameters: { + layout: 'centered', + }, + args: { + onClose: action('onClose'), + }, + decorators: [ + (Story) => { + const AppRoot = mockAppRoot() + .withTranslations('en', 'core', { + Attribute_based_access_control: 'Attribute-Based Access Control', + Attribute_based_access_control_title: 'Automate complex access management across your entire organization', + Attribute_based_access_control_description: + 'ABAC automates room access, granting or revoking access based on dynamic user attributes rather than fixed roles.', + }) + .build(); + + return ( + + + + ); + }, + ], +} satisfies Meta; + +export default meta; + +export const Default = { + args: { + onClose: action('onClose'), + onConfirm: action('onConfirm'), + }, +}; diff --git a/apps/meteor/client/components/ABAC/ABACUpsellModal/ABACUpsellModal.tsx b/apps/meteor/client/components/ABAC/ABACUpsellModal/ABACUpsellModal.tsx new file mode 100644 index 0000000000000..c2877136ea16e --- /dev/null +++ b/apps/meteor/client/components/ABAC/ABACUpsellModal/ABACUpsellModal.tsx @@ -0,0 +1,29 @@ +import { useTranslation } from 'react-i18next'; + +import { getURL } from '../../../../app/utils/client'; +import GenericUpsellModal from '../../GenericUpsellModal'; + +type ABACUpsellModalProps = { + onClose: () => void; + onConfirm: () => void; +}; + +const ABACUpsellModal = ({ onClose, onConfirm }: ABACUpsellModalProps) => { + const { t } = useTranslation(); + + return ( + + ); +}; + +export default ABACUpsellModal; diff --git a/apps/meteor/client/components/ABAC/ABACUpsellModal/__snapshots__/ABACUpsellModal.spec.tsx.snap b/apps/meteor/client/components/ABAC/ABACUpsellModal/__snapshots__/ABACUpsellModal.spec.tsx.snap new file mode 100644 index 0000000000000..51209c5122adc --- /dev/null +++ b/apps/meteor/client/components/ABAC/ABACUpsellModal/__snapshots__/ABACUpsellModal.spec.tsx.snap @@ -0,0 +1,111 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`ABACUpsellModal should render the modal with correct content 1`] = ` + +
+ +
+
+
+
+
+ Premium capability +
+

+ Attribute-Based Access Control +

+
+ +
+
+
+
+
+ +
+

+ Automate complex access management across your entire organization +

+
+ ABAC automates room access, granting or revoking access based on dynamic user attributes rather than fixed roles. +
+
+
+ +
+
+
+ +`; diff --git a/apps/meteor/public/images/abac-upsell-modal.svg b/apps/meteor/public/images/abac-upsell-modal.svg new file mode 100644 index 0000000000000..9809ac8f3349c --- /dev/null +++ b/apps/meteor/public/images/abac-upsell-modal.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 8afb816dc0407..03afd7698f387 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -693,6 +693,9 @@ "AtlassianCrowd_Description": "Integrate Atlassian Crowd.", "Attachment_File_Uploaded": "File Uploaded", "Attribute_handling": "Attribute handling", + "Attribute_based_access_control": "Attribute-Based Access Control", + "Attribute_based_access_control_title": "Automate complex access management across your entire organization", + "Attribute_based_access_control_description": "ABAC automates room access, granting or revoking access based on dynamic user attributes rather than fixed roles.", "Audio": "Audio", "Audio_Notification_Value_Description": "Can be any custom sound or the default ones: beep, chelle, ding, droplet, highbell, seasons", "Audio_Notifications_Default_Alert": "Audio Notifications Default Alert",