Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ export const EmptyState = (): React.ReactElement => {

return (
<Flex flexDirection="column" gap="$20" alignItems="center" justifyContent="center" data-testid="empty-state">
<SmileyFaceIcon className={styles.icon} />
<SmileyFaceIcon data-testid="empty-state-image" className={styles.icon} />
<Flex alignItems="center" flexDirection="column" gap="$0">
<Text.Body.Large>{t('notificationsCenter.emptyState.title')}</Text.Body.Large>
<Text.Body.Small color="secondary">{t('notificationsCenter.emptyState.description')}</Text.Body.Small>
<Text.Body.Large data-testid="empty-state-title">{t('notificationsCenter.emptyState.title')}</Text.Body.Large>
<Text.Body.Small color="secondary" data-testid="empty-state-description">
{t('notificationsCenter.emptyState.description')}
</Text.Body.Small>
</Flex>
</Flex>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@ export const SubscriptionsDropDown = ({
const { t } = useTranslation();

return (
<Menu className={classnames(styles.container, { [styles.popupView]: popupView })}>
<Menu
className={classnames(styles.container, { [styles.popupView]: popupView })}
data-testid="subscriptions-dropdown"
>
<Flex className={styles.content} gap="$24" flexDirection="column" justifyContent="space-between">
<Text.Label color="secondary">{t('notificationsCenter.chooseSubject')}</Text.Label>
<Text.Label color="secondary" data-testid="subscriptions-dropdown-description">
{t('notificationsCenter.chooseSubject')}
</Text.Label>
<Flex w="$fill" gap="$24" flexDirection="column" justifyContent="space-between">
{topics.map((topic) => (
<Flex
Expand All @@ -35,13 +40,18 @@ export const SubscriptionsDropDown = ({
alignItems="center"
justifyContent="space-between"
>
<Text.Body.Normal weight="$medium" className={styles.toggleLabel} color="secondary">
<Text.Body.Normal
weight="$medium"
className={styles.toggleLabel}
color="secondary"
data-testid="subscriptions-toggle-topic-name"
>
{topic.name}
</Text.Body.Normal>
<ToggleSwitch
key={topic.id}
defaultChecked={topic.subscribed}
data-testid={`subscriptions-toggle-${topic.id}`}
testId={`subscriptions-${topic.id}-`}
onCheckedChange={(isChecked) => onTopicChange(topic, isChecked)}
/>
</Flex>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ class NotificationCenterAssert {
await NotificationCenter.sectionTitleCounter.waitForDisplayed();
expect(await NotificationCenter.getCounterValue()).to.be.greaterThan(0);

await NotificationCenter.subscriptionsDropdown.waitForDisplayed();
expect(await NotificationCenter.subscriptionsDropdown.getText()).to.equal(
await NotificationCenter.subscriptionsButton.waitForDisplayed();
expect(await NotificationCenter.subscriptionsButton.getText()).to.equal(
await t('notificationsCenter.subscriptions')
);
if (mode === 'extended') {
Expand Down Expand Up @@ -65,7 +65,11 @@ class NotificationCenterAssert {
}
}

async assertSeeExpectedNumberOfUnreadNotifications(expectedCount: number, location: 'menu' | 'page') {
async assertSeeExpectedNumberOfNotifications(
expectedCount: number,
location: 'menu' | 'page',
status: 'read' | 'unread'
) {
for (let i = 1; i <= expectedCount; i++) {
const notification = new NotificationListItem(location, i);
await notification.title.waitForDisplayed();
Expand All @@ -77,7 +81,7 @@ class NotificationCenterAssert {
expect(publisher).to.not.be.empty;

const isUnread = await notification.isUnread();
expect(isUnread).to.be.true;
expect(isUnread).to.equal(status === 'unread');
}
}

Expand All @@ -97,6 +101,10 @@ class NotificationCenterAssert {
}
);
}

async assertSeeMarkAllAsReadButton(shouldSee: boolean) {
await NotificationCenter.markAllAsReadButton.waitForDisplayed({ reverse: !shouldSee });
}
}

export default new NotificationCenterAssert();
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import NotificationsEmptyState from '../../elements/notifications/NotificationsEmptyState';
import { expect } from 'chai';
import { t } from '../../utils/translationService';

class NotificationsEmptyStateAssert {
async assertSeeEmptyState(location: 'menu' | 'page') {
const emptyState = new NotificationsEmptyState(location);

await emptyState.emptyStateImage.waitForDisplayed();

await emptyState.emptyStateTitle.waitForDisplayed();
expect(await emptyState.getTitle()).to.equal(await t('notificationsCenter.emptyState.title'));

await emptyState.emptyStateDescription.waitForDisplayed();
expect(await emptyState.getDescription()).to.equal(await t('notificationsCenter.emptyState.description'));
}
}

export default new NotificationsEmptyStateAssert();
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class NotificationsMenuAssert {

return { unreadCount, readCount };
}

async assertSeeMarkAllAsReadButton(shouldSee: boolean) {
await NotificationsMenu.markAllAsReadButton.waitForDisplayed({ reverse: !shouldSee });
}
}

export default new NotificationsMenuAssert();
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import SubscriptionsDropdown from '../../elements/notifications/SubscriptionsDropdown';
import { expect } from 'chai';
import { t } from '../../utils/translationService';

class SubscriptionsDropdownAssert {
async assertSeeSubscriptionsDropdown() {
await SubscriptionsDropdown.dropdownMenu.waitForDisplayed();

await SubscriptionsDropdown.dropdownDescription.waitForDisplayed();
expect(await SubscriptionsDropdown.getDropdownDescriptionText()).to.equal(
await t('notificationsCenter.chooseSubject')
);
}

async assertTopicSubscriptionState(topicId: string, shouldBeSubscribed: boolean) {
const isSubscribed = await SubscriptionsDropdown.isTopicSubscribed(topicId);
expect(isSubscribed).to.equal(shouldBeSubscribed);
}
}

export default new SubscriptionsDropdownAssert();
10 changes: 8 additions & 2 deletions packages/e2e-tests/src/assert/topNavigationAssert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,14 @@ class TopNavigationAssert {
}

async assertSeeUnreadNotificationsCounter(expectedCount: number) {
await MenuHeader.unreadNotificationsCounter.waitForDisplayed();
expect(await MenuHeader.unreadNotificationsCounter.getText()).to.equal(expectedCount > 9 ? '9+' : expectedCount);
if (expectedCount === 0) {
await MenuHeader.unreadNotificationsCounter.waitForDisplayed({ reverse: true });
} else {
await MenuHeader.unreadNotificationsCounter.waitForDisplayed();
expect(await MenuHeader.unreadNotificationsCounter.getText()).to.equal(
expectedCount > 9 ? '9+' : expectedCount.toString()
);
}
}

async assertLogoPresent() {
Expand Down
36 changes: 20 additions & 16 deletions packages/e2e-tests/src/elements/notifications/NotificationCenter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class NotificationCenter {
private readonly SECTION_TITLE = '[data-testid="section-title"]';
private readonly NAVIGATION_BUTTON_BACK = '[data-testid="navigation-button-arrow"]';
private readonly SECTION_TITLE_COUNTER = '[data-testid="section-title-counter"]';
private readonly SUBSCRIPTIONS_DROPDOWN = '[data-testid="subscriptions"]';
private readonly SUBSCRIPTIONS_BUTTON = '[data-testid="subscriptions"]';
private readonly MARK_ALL_AS_READ_BUTTON = '[data-testid="mark-all-as-read-button"]';
private readonly NOTIFICATIONS_LIST = '[data-testid="notifications-list"]';

Expand All @@ -22,8 +22,8 @@ class NotificationCenter {
return $(this.SECTION_TITLE_COUNTER);
}

get subscriptionsDropdown(): ChainablePromiseElement<WebdriverIO.Element> {
return $(this.SUBSCRIPTIONS_DROPDOWN);
get subscriptionsButton(): ChainablePromiseElement<WebdriverIO.Element> {
return $(this.SUBSCRIPTIONS_BUTTON);
}

get markAllAsReadButton(): ChainablePromiseElement<WebdriverIO.Element> {
Expand All @@ -34,19 +34,23 @@ class NotificationCenter {
return $(this.NOTIFICATIONS_LIST);
}

async clickBackButton(): Promise<void> {
await this.navigationButtonBack.waitForClickable();
await this.navigationButtonBack.click();
}

async clickSubscriptionsDropdown(): Promise<void> {
await this.subscriptionsDropdown.waitForClickable();
await this.subscriptionsDropdown.click();
}

async clickMarkAllAsReadButton(): Promise<void> {
await this.markAllAsReadButton.waitForClickable();
await this.markAllAsReadButton.click();
async clickOnButton(button: 'Back' | 'Subscriptions' | 'Mark all as read') {
switch (button) {
case 'Back':
await this.navigationButtonBack.waitForClickable();
await this.navigationButtonBack.click();
break;
case 'Subscriptions':
await this.subscriptionsButton.waitForClickable();
await this.subscriptionsButton.click();
break;
case 'Mark all as read':
await this.markAllAsReadButton.waitForClickable();
await this.markAllAsReadButton.click();
break;
default:
throw new Error(`Unsupported button: ${button}`);
}
}

async getCounterValue(): Promise<number> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* global WebdriverIO */
import type { ChainablePromiseElement } from 'webdriverio';

class NotificationsEmptyState {
private readonly EMPTY_STATE_IMAGE = '[data-testid="empty-state-image"]';
private readonly EMPTY_STATE_TITLE = '[data-testid="empty-state-title"]';
private readonly EMPTY_STATE_DESCRIPTION = '[data-testid="empty-state-description"]';

private static readonly ANT_DROPDOWN_MENU = '.ant-dropdown-menu';
private static readonly PAGE_CONTENT = ':is(#content, #contentLayout)';

private readonly location: 'menu' | 'page';

constructor(location: 'menu' | 'page') {
this.location = location;
}

private getSelector(elementSelector: string): string {
const wrapper =
this.location === 'menu' ? NotificationsEmptyState.ANT_DROPDOWN_MENU : NotificationsEmptyState.PAGE_CONTENT;
return `${wrapper} ${elementSelector}`;
}

get emptyStateImage(): ChainablePromiseElement<WebdriverIO.Element> {
return $(this.getSelector(this.EMPTY_STATE_IMAGE));
}

get emptyStateTitle(): ChainablePromiseElement<WebdriverIO.Element> {
return $(this.getSelector(this.EMPTY_STATE_TITLE));
}

get emptyStateDescription(): ChainablePromiseElement<WebdriverIO.Element> {
return $(this.getSelector(this.EMPTY_STATE_DESCRIPTION));
}

async getTitle(): Promise<string> {
return await this.emptyStateTitle.getText();
}

async getDescription(): Promise<string> {
return await this.emptyStateDescription.getText();
}
}

export default NotificationsEmptyState;
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* global WebdriverIO */
import type { ChainablePromiseElement } from 'webdriverio';

class SubscriptionsDropdown {
private readonly DROPDOWN_MENU = '[data-testid="subscriptions-dropdown"]';
private readonly DROPDOWN_DESCRIPTION = '[data-testid="subscriptions-dropdown-description"]';

get dropdownDescription(): ChainablePromiseElement<WebdriverIO.Element> {
return $(this.DROPDOWN_DESCRIPTION);
}

private getTopicToggleSwitchSelector(topicId: string): string {
return `[data-testid="subscriptions-${topicId}-toggle-switch"]`;
}

get dropdownMenu(): ChainablePromiseElement<WebdriverIO.Element> {
return $(this.DROPDOWN_MENU);
}

getTopicToggleSwitch(topicId: string): ChainablePromiseElement<WebdriverIO.Element> {
return $(this.getTopicToggleSwitchSelector(topicId));
}

async getDropdownDescriptionText(): Promise<string> {
return await this.dropdownDescription.getText();
}

async isTopicSubscribed(topicId: string): Promise<boolean> {
const toggleSwitch = this.getTopicToggleSwitch(topicId);
const ariaChecked = await toggleSwitch.getAttribute('aria-checked');
return ariaChecked === 'true';
}

async toggleTopic(topicId: string): Promise<void> {
const toggleSwitch = this.getTopicToggleSwitch(topicId);
await toggleSwitch.waitForClickable();
await toggleSwitch.click();
}

async enableTopic(topicId: string): Promise<void> {
const isSubscribed = await this.isTopicSubscribed(topicId);
if (!isSubscribed) {
await this.toggleTopic(topicId);
}
}

async disableTopic(topicId: string): Promise<void> {
const isSubscribed = await this.isTopicSubscribed(topicId);
if (isSubscribed) {
await this.toggleTopic(topicId);
}
}
}

export default new SubscriptionsDropdown();
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Feature: Notification Center - extended view
And I click "Notifications" button on page header
When I click on "View all" button in the "Notifications menu"
Then "Notification center" is displayed in extended mode
And "Notifications page" contains 3 unread notifications with all details
And "Notifications center" contains 3 unread notifications with all details
And the dynamically added notification is displayed in the "Notifications center" with unread marker

@LW-tbd4
Expand Down Expand Up @@ -75,3 +75,50 @@ Feature: Notification Center - extended view
Then Remove notification modal is displayed
When I click "Cancel" button in the remove notification modal
Then the dynamically added notification is displayed in the "Notifications center" with unread marker

@LW-tbd8
Scenario: Extended View - Notification Center - remove all notifications and verify empty state
And I visit Notifications page in extended mode
And "Notifications center" contains 2 unread notifications with all details
And I click on remove button for notification number 1 in the "Notifications center"
And I click "Remove" button in the remove notification modal
And I click on remove button for notification number 1 in the "Notifications center"
When I click "Remove" button in the remove notification modal
Then Notifications empty state is displayed in the "Notifications center"
When I click "Notifications" button on page header
Then Notifications empty state is displayed in the "Notifications menu"

@LW-tbd9
Scenario Outline: Extended View - Notification Center - mark all notifications as read from <location>
And I add a new notification dynamically
And "Notifications" button indicates 3 unread notifications
And <navigation_step>
And "Notifications <location>" contains 3 unread notifications with all details
When I click on "Mark all as read" button in the "Notifications <location>"
And I do not see "Mark all as read" button in "Notifications <location>"
Then "Notifications <location>" contains 3 read notifications with all details
And "Notifications" button indicates 0 unread notifications
Examples:
| location | navigation_step |
| menu | I click "Notifications" button on page header |
| center | I visit Notifications page in extended mode |

@LW-tbd10
Scenario: Extended View - Notification Center - manage topic subscriptions and verify notification filtering
And I visit Notifications page in extended mode
And "Notifications center" contains 2 unread notifications with all details
When I click on "Subscriptions" button in the "Notifications center"
Then Subscriptions dropdown is displayed
And topic "topic-1" is enabled in subscriptions dropdown
And topic "topic-2" is enabled in subscriptions dropdown
When I disable topic "topic-2" in subscriptions dropdown
Then topic "topic-2" is disabled in subscriptions dropdown
And I click on "Subscriptions" button in the "Notifications center"
When I add a new notification dynamically
Then the dynamically added notification is not displayed in the "Notifications center" with unread marker
And I click on "Subscriptions" button in the "Notifications center"
When I enable topic "topic-2" in subscriptions dropdown
And topic "topic-2" is enabled in subscriptions dropdown
And I click on "Subscriptions" button in the "Notifications center"
And I add a new notification dynamically
Then the dynamically added notification is displayed in the "Notifications center" with unread marker
Loading
Loading