Skip to content

Commit d8270d8

Browse files
chore: new ABAC upsell modal (RocketChat#37124)
1 parent 1657571 commit d8270d8

6 files changed

Lines changed: 392 additions & 0 deletions

File tree

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { mockAppRoot } from '@rocket.chat/mock-providers';
2+
import { render, screen } from '@testing-library/react';
3+
import userEvent from '@testing-library/user-event';
4+
import { axe } from 'jest-axe';
5+
6+
import ABACUpsellModal from './ABACUpsellModal';
7+
8+
// Mock the hooks used by ABACUpsellModal
9+
jest.mock('../../../hooks/useHasLicenseModule', () => ({
10+
useHasLicenseModule: jest.fn(() => false),
11+
}));
12+
13+
jest.mock('../../GenericUpsellModal/hooks', () => ({
14+
useUpsellActions: jest.fn(() => ({
15+
shouldShowUpsell: true,
16+
cloudWorkspaceHadTrial: false,
17+
handleManageSubscription: jest.fn(),
18+
handleTalkToSales: jest.fn(),
19+
})),
20+
}));
21+
22+
// Mock getURL utility
23+
jest.mock('../../../../app/utils/client', () => ({
24+
getURL: (url: string) => url,
25+
}));
26+
27+
const appRoot = mockAppRoot()
28+
.withTranslations('en', 'core', {
29+
Premium_capability: 'Premium capability',
30+
Attribute_based_access_control: 'Attribute-Based Access Control',
31+
Attribute_based_access_control_title: 'Automate complex access management across your entire organization',
32+
Attribute_based_access_control_description:
33+
'ABAC automates room access, granting or revoking access based on dynamic user attributes rather than fixed roles.',
34+
Upgrade: 'Upgrade',
35+
Cancel: 'Cancel',
36+
})
37+
.build();
38+
39+
describe('ABACUpsellModal', () => {
40+
const mockOnClose = jest.fn();
41+
const mockOnConfirm = jest.fn();
42+
43+
beforeEach(() => {
44+
jest.clearAllMocks();
45+
});
46+
47+
it('should render the modal with correct content', () => {
48+
const { baseElement } = render(<ABACUpsellModal onClose={mockOnClose} onConfirm={mockOnConfirm} />, { wrapper: appRoot });
49+
expect(baseElement).toMatchSnapshot();
50+
});
51+
52+
it('should have no accessibility violations', async () => {
53+
const { container } = render(<ABACUpsellModal onClose={mockOnClose} onConfirm={mockOnConfirm} />, { wrapper: appRoot });
54+
const results = await axe(container);
55+
expect(results).toHaveNoViolations();
56+
});
57+
58+
it('should call onConfirm when upgrade button is clicked', async () => {
59+
const user = userEvent.setup();
60+
render(<ABACUpsellModal onClose={mockOnClose} onConfirm={mockOnConfirm} />, { wrapper: appRoot });
61+
62+
const upgradeButton = screen.getByRole('button', { name: 'Upgrade' });
63+
await user.click(upgradeButton);
64+
65+
expect(mockOnConfirm).toHaveBeenCalledTimes(1);
66+
expect(mockOnClose).not.toHaveBeenCalled();
67+
});
68+
69+
it('should call onClose when cancel button is clicked', async () => {
70+
const user = userEvent.setup();
71+
render(<ABACUpsellModal onClose={mockOnClose} onConfirm={mockOnConfirm} />, { wrapper: appRoot });
72+
73+
const cancelButton = screen.getByRole('button', { name: 'Cancel' });
74+
await user.click(cancelButton);
75+
76+
expect(mockOnClose).toHaveBeenCalledTimes(1);
77+
expect(mockOnConfirm).not.toHaveBeenCalled();
78+
});
79+
80+
it('should call onClose when close button is clicked', async () => {
81+
const user = userEvent.setup();
82+
render(<ABACUpsellModal onClose={mockOnClose} onConfirm={mockOnConfirm} />, { wrapper: appRoot });
83+
84+
// Look for close button (usually has aria-label or is the X button)
85+
const closeButton = screen.getByRole('button', { name: /close/i });
86+
await user.click(closeButton);
87+
88+
expect(mockOnClose).toHaveBeenCalledTimes(1);
89+
expect(mockOnConfirm).not.toHaveBeenCalled();
90+
});
91+
92+
it('should handle multiple button clicks correctly', async () => {
93+
const user = userEvent.setup();
94+
render(<ABACUpsellModal onClose={mockOnClose} onConfirm={mockOnConfirm} />, { wrapper: appRoot });
95+
96+
const upgradeButton = screen.getByRole('button', { name: 'Upgrade' });
97+
const cancelButton = screen.getByRole('button', { name: 'Cancel' });
98+
99+
// Click upgrade first
100+
await user.click(upgradeButton);
101+
expect(mockOnConfirm).toHaveBeenCalledTimes(1);
102+
103+
// Click cancel
104+
await user.click(cancelButton);
105+
expect(mockOnClose).toHaveBeenCalledTimes(1);
106+
107+
// Total calls
108+
expect(mockOnConfirm).toHaveBeenCalledTimes(1);
109+
expect(mockOnClose).toHaveBeenCalledTimes(1);
110+
});
111+
});
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { mockAppRoot } from '@rocket.chat/mock-providers';
2+
import { action } from '@storybook/addon-actions';
3+
import type { Meta } from '@storybook/react';
4+
5+
import ABACUpsellModal from './ABACUpsellModal';
6+
7+
const meta = {
8+
component: ABACUpsellModal,
9+
parameters: {
10+
layout: 'centered',
11+
},
12+
args: {
13+
onClose: action('onClose'),
14+
},
15+
decorators: [
16+
(Story) => {
17+
const AppRoot = mockAppRoot()
18+
.withTranslations('en', 'core', {
19+
Attribute_based_access_control: 'Attribute-Based Access Control',
20+
Attribute_based_access_control_title: 'Automate complex access management across your entire organization',
21+
Attribute_based_access_control_description:
22+
'ABAC automates room access, granting or revoking access based on dynamic user attributes rather than fixed roles.',
23+
})
24+
.build();
25+
26+
return (
27+
<AppRoot>
28+
<Story />
29+
</AppRoot>
30+
);
31+
},
32+
],
33+
} satisfies Meta<typeof ABACUpsellModal>;
34+
35+
export default meta;
36+
37+
export const Default = {
38+
args: {
39+
onClose: action('onClose'),
40+
onConfirm: action('onConfirm'),
41+
},
42+
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useTranslation } from 'react-i18next';
2+
3+
import { getURL } from '../../../../app/utils/client';
4+
import GenericUpsellModal from '../../GenericUpsellModal';
5+
6+
type ABACUpsellModalProps = {
7+
onClose: () => void;
8+
onConfirm: () => void;
9+
};
10+
11+
const ABACUpsellModal = ({ onClose, onConfirm }: ABACUpsellModalProps) => {
12+
const { t } = useTranslation();
13+
14+
return (
15+
<GenericUpsellModal
16+
tagline={t('Premium_capability')}
17+
title={t('Attribute_based_access_control')}
18+
subtitle={t('Attribute_based_access_control_title')}
19+
description={t('Attribute_based_access_control_description')}
20+
img={getURL('images/abac-upsell-modal.svg')}
21+
onClose={onClose}
22+
onConfirm={onConfirm}
23+
onCancel={onClose}
24+
imgHeight={256}
25+
/>
26+
);
27+
};
28+
29+
export default ABACUpsellModal;
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2+
3+
exports[`ABACUpsellModal should render the modal with correct content 1`] = `
4+
<body>
5+
<div>
6+
<dialog
7+
aria-labelledby=":r0:-title"
8+
aria-modal="true"
9+
class="rcx-box rcx-box--full rcx-modal"
10+
open=""
11+
>
12+
<div
13+
class="rcx-box rcx-box--full rcx-modal__inner rcx-css-1e2ego0"
14+
>
15+
<header
16+
class="rcx-box rcx-box--full rcx-modal__header"
17+
>
18+
<div
19+
class="rcx-box rcx-box--full rcx-modal__header-inner"
20+
>
21+
<div
22+
class="rcx-box rcx-box--full rcx-modal__header-text rcx-css-trljwa rcx-css-lma364"
23+
>
24+
<div
25+
class="rcx-box rcx-box--full rcx-modal__tagline rcx-css-ar0y0g"
26+
>
27+
Premium capability
28+
</div>
29+
<h2
30+
class="rcx-box rcx-box--full rcx-modal__title"
31+
id=":r0:-title"
32+
>
33+
Attribute-Based Access Control
34+
</h2>
35+
</div>
36+
<button
37+
aria-label="Close"
38+
class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-css-trljwa rcx-css-lma364"
39+
type="button"
40+
>
41+
<i
42+
aria-hidden="true"
43+
class="rcx-box rcx-box--full rcx-icon--name-cross rcx-icon rcx-css-4pvxx3"
44+
>
45+
46+
</i>
47+
</button>
48+
</div>
49+
</header>
50+
<div
51+
class="rcx-box rcx-box--full rcx-modal__content rcx-css-1vw7itl"
52+
>
53+
<div
54+
class="rcx-box rcx-box--full rcx-modal__content-wrapper rcx-css-r1bpeb"
55+
>
56+
<figure
57+
class="rcx-modal__hero-image-wrapper"
58+
>
59+
<img
60+
alt=""
61+
class="rcx-box rcx-box--full rcx-modal__hero-image rcx-css-1qfl5uy"
62+
src="images/abac-upsell-modal.svg"
63+
/>
64+
</figure>
65+
<h3
66+
class="rcx-box rcx-box--full rcx-css-1sfto34"
67+
>
68+
Automate complex access management across your entire organization
69+
</h3>
70+
<div
71+
class="rcx-box rcx-box--full rcx-css-1s3t8yc"
72+
style="white-space: break-spaces;"
73+
>
74+
ABAC automates room access, granting or revoking access based on dynamic user attributes rather than fixed roles.
75+
</div>
76+
</div>
77+
</div>
78+
<div
79+
class="rcx-box rcx-box--full rcx-modal__footer rcx-css-17mu816"
80+
>
81+
<div
82+
class="rcx-button-group rcx-button-group--align-end"
83+
role="group"
84+
>
85+
<button
86+
class="rcx-box rcx-box--full rcx-button--secondary rcx-button rcx-button-group__item"
87+
type="button"
88+
>
89+
<span
90+
class="rcx-button--content"
91+
>
92+
Cancel
93+
</span>
94+
</button>
95+
<button
96+
class="rcx-box rcx-box--full rcx-button--primary rcx-button rcx-button-group__item"
97+
type="button"
98+
>
99+
<span
100+
class="rcx-button--content"
101+
>
102+
Upgrade
103+
</span>
104+
</button>
105+
</div>
106+
</div>
107+
</div>
108+
</dialog>
109+
</div>
110+
</body>
111+
`;

0 commit comments

Comments
 (0)